-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f354c8a
commit 0027253
Showing
70 changed files
with
9,377 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
% Arrays | ||
|
||
Like many programming languages, Rust has list types to represent a sequence of | ||
things. The most basic is the *array*, a fixed-size list of elements of the | ||
same type. By default, arrays are immutable. | ||
|
||
```{rust} | ||
let a = [1, 2, 3]; // a: [i32; 3] | ||
let mut m = [1, 2, 3]; // mut m: [i32; 3] | ||
``` | ||
|
||
There's a shorthand for initializing each element of an array to the same | ||
value. In this example, each element of `a` will be initialized to `0`: | ||
|
||
```{rust} | ||
let a = [0; 20]; // a: [i32; 20] | ||
``` | ||
|
||
Arrays have type `[T; N]`. We'll talk about this `T` notation later, when we | ||
cover generics. | ||
|
||
You can get the number of elements in an array `a` with `a.len()`, and use | ||
`a.iter()` to iterate over them with a for loop. This code will print each | ||
number in order: | ||
|
||
```{rust} | ||
let a = [1, 2, 3]; | ||
println!("a has {} elements", a.len()); | ||
for e in a.iter() { | ||
println!("{}", e); | ||
} | ||
``` | ||
|
||
You can access a particular element of an array with *subscript notation*: | ||
|
||
```{rust} | ||
let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3] | ||
println!("The second name is: {}", names[1]); | ||
``` | ||
|
||
Subscripts start at zero, like in most programming languages, so the first name | ||
is `names[0]` and the second name is `names[1]`. The above example prints | ||
`The second name is: Brian`. If you try to use a subscript that is not in the | ||
array, you will get an error: array access is bounds-checked at run-time. Such | ||
errant access is the source of many bugs in other systems programming | ||
languages. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
% Associated Types | ||
|
||
Associated types are a powerful part of Rust's type system. They're related to | ||
the idea of a 'type family', in other words, grouping multiple types together. That | ||
description is a bit abstract, so let's dive right into an example. If you want | ||
to write a `Graph` trait, you have two types to be generic over: the node type | ||
and the edge type. So you might write a trait, `Graph<N, E>`, that looks like | ||
this: | ||
|
||
```rust | ||
trait Graph<N, E> { | ||
fn has_edge(&self, &N, &N) -> bool; | ||
fn edges(&self, &N) -> Vec<E>; | ||
// etc | ||
} | ||
``` | ||
|
||
While this sort of works, it ends up being awkward. For example, any function | ||
that wants to take a `Graph` as a parameter now _also_ needs to be generic over | ||
the `N`ode and `E`dge types too: | ||
|
||
```rust,ignore | ||
fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... } | ||
``` | ||
|
||
Our distance calculation works regardless of our `Edge` type, so the `E` stuff in | ||
this signature is just a distraction. | ||
|
||
What we really want to say is that a certain `E`dge and `N`ode type come together | ||
to form each kind of `Graph`. We can do that with associated types: | ||
|
||
```rust | ||
trait Graph { | ||
type N; | ||
type E; | ||
|
||
fn has_edge(&self, &Self::N, &Self::N) -> bool; | ||
fn edges(&self, &Self::N) -> Vec<Self::E>; | ||
// etc | ||
} | ||
``` | ||
|
||
Now, our clients can be abstract over a given `Graph`: | ||
|
||
```rust,ignore | ||
fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> uint { ... } | ||
``` | ||
|
||
No need to deal with the `E`dge type here! | ||
|
||
Let's go over all this in more detail. | ||
|
||
## Defining associated types | ||
|
||
Let's build that `Graph` trait. Here's the definition: | ||
|
||
```rust | ||
trait Graph { | ||
type N; | ||
type E; | ||
|
||
fn has_edge(&self, &Self::N, &Self::N) -> bool; | ||
fn edges(&self, &Self::N) -> Vec<Self::E>; | ||
} | ||
``` | ||
|
||
Simple enough. Associated types use the `type` keyword, and go inside the body | ||
of the trait, with the functions. | ||
|
||
These `type` declarations can have all the same thing as functions do. For example, | ||
if we wanted our `N` type to implement `Display`, so we can print the nodes out, | ||
we could do this: | ||
|
||
```rust | ||
use std::fmt; | ||
|
||
trait Graph { | ||
type N: fmt::Display; | ||
type E; | ||
|
||
fn has_edge(&self, &Self::N, &Self::N) -> bool; | ||
fn edges(&self, &Self::N) -> Vec<Self::E>; | ||
} | ||
``` | ||
|
||
## Implementing associated types | ||
|
||
Just like any trait, traits that use associated types use the `impl` keyword to | ||
provide implementations. Here's a simple implementation of Graph: | ||
|
||
```rust | ||
# trait Graph { | ||
# type N; | ||
# type E; | ||
# fn has_edge(&self, &Self::N, &Self::N) -> bool; | ||
# fn edges(&self, &Self::N) -> Vec<Self::E>; | ||
# } | ||
struct Node; | ||
|
||
struct Edge; | ||
|
||
struct MyGraph; | ||
|
||
impl Graph for MyGraph { | ||
type N = Node; | ||
type E = Edge; | ||
|
||
fn has_edge(&self, n1: &Node, n2: &Node) -> bool { | ||
true | ||
} | ||
|
||
fn edges(&self, n: &Node) -> Vec<Edge> { | ||
Vec::new() | ||
} | ||
} | ||
``` | ||
|
||
This silly implementation always returns `true` and an empty `Vec<Edge>`, but it | ||
gives you an idea of how to implement this kind of thing. We first need three | ||
`struct`s, one for the graph, one for the node, and one for the edge. If it made | ||
more sense to use a different type, that would work as well, we're just going to | ||
use `struct`s for all three here. | ||
|
||
Next is the `impl` line, which is just like implementing any other trait. | ||
|
||
From here, we use `=` to define our associated types. The name the trait uses | ||
goes on the left of the `=`, and the concrete type we're `impl`ementing this | ||
for goes on the right. Finally, we use the concrete types in our function | ||
declarations. | ||
|
||
## Trait objects with associated types | ||
|
||
There’s one more bit of syntax we should talk about: trait objects. If you | ||
try to create a trait object from an associated type, like this: | ||
|
||
```rust,ignore | ||
# trait Graph { | ||
# type N; | ||
# type E; | ||
# fn has_edge(&self, &Self::N, &Self::N) -> bool; | ||
# fn edges(&self, &Self::N) -> Vec<Self::E>; | ||
# } | ||
# struct Node; | ||
# struct Edge; | ||
# struct MyGraph; | ||
# impl Graph for MyGraph { | ||
# type N = Node; | ||
# type E = Edge; | ||
# fn has_edge(&self, n1: &Node, n2: &Node) -> bool { | ||
# true | ||
# } | ||
# fn edges(&self, n: &Node) -> Vec<Edge> { | ||
# Vec::new() | ||
# } | ||
# } | ||
let graph = MyGraph; | ||
let obj = Box::new(graph) as Box<Graph>; | ||
``` | ||
|
||
You’ll get two errors: | ||
|
||
```text | ||
error: the value of the associated type `E` (from the trait `main::Graph`) must | ||
be specified [E0191] | ||
let obj = Box::new(graph) as Box<Graph>; | ||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
24:44 error: the value of the associated type `N` (from the trait | ||
`main::Graph`) must be specified [E0191] | ||
let obj = Box::new(graph) as Box<Graph>; | ||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
``` | ||
|
||
We can’t create a trait object like this, because we don’t know the associated | ||
types. Instead, we can write this: | ||
|
||
```rust | ||
# trait Graph { | ||
# type N; | ||
# type E; | ||
# fn has_edge(&self, &Self::N, &Self::N) -> bool; | ||
# fn edges(&self, &Self::N) -> Vec<Self::E>; | ||
# } | ||
# struct Node; | ||
# struct Edge; | ||
# struct MyGraph; | ||
# impl Graph for MyGraph { | ||
# type N = Node; | ||
# type E = Edge; | ||
# fn has_edge(&self, n1: &Node, n2: &Node) -> bool { | ||
# true | ||
# } | ||
# fn edges(&self, n: &Node) -> Vec<Edge> { | ||
# Vec::new() | ||
# } | ||
# } | ||
let graph = MyGraph; | ||
let obj = Box::new(graph) as Box<Graph<N=Node, E=Edge>>; | ||
``` | ||
|
||
The `N=Node` syntax allows us to provide a concrete type, `Node`, for the `N` | ||
type parameter. Same with `E=Edge`. If we didn’t proide this constraint, we | ||
couldn’t be sure which `impl` to match this trait object to. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
% Attributes | ||
|
||
Coming Soon! |
Oops, something went wrong.