small edits for recently written book chapters
This commit is contained in:
parent
3860240b0e
commit
0070625495
5 changed files with 145 additions and 145 deletions
|
@ -1,8 +1,8 @@
|
||||||
% Associated Types
|
% Associated Types
|
||||||
|
|
||||||
Associated types are a powerful part of Rust's type system. They're related to
|
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
|
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
|
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
|
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
|
and the edge type. So you might write a trait, `Graph<N, E>`, that looks like
|
||||||
this:
|
this:
|
||||||
|
@ -48,11 +48,11 @@ fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> uint { ... }
|
||||||
|
|
||||||
No need to deal with the `E`dge type here!
|
No need to deal with the `E`dge type here!
|
||||||
|
|
||||||
Let's go over all this in more detail.
|
Let’s go over all this in more detail.
|
||||||
|
|
||||||
## Defining associated types
|
## Defining associated types
|
||||||
|
|
||||||
Let's build that `Graph` trait. Here's the definition:
|
Let’s build that `Graph` trait. Here’s the definition:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
trait Graph {
|
trait Graph {
|
||||||
|
@ -86,7 +86,7 @@ trait Graph {
|
||||||
## Implementing associated types
|
## Implementing associated types
|
||||||
|
|
||||||
Just like any trait, traits that use associated types use the `impl` keyword to
|
Just like any trait, traits that use associated types use the `impl` keyword to
|
||||||
provide implementations. Here's a simple implementation of Graph:
|
provide implementations. Here’s a simple implementation of Graph:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# trait Graph {
|
# trait Graph {
|
||||||
|
@ -118,13 +118,13 @@ impl Graph for MyGraph {
|
||||||
This silly implementation always returns `true` and an empty `Vec<Edge>`, but it
|
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
|
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
|
`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
|
more sense to use a different type, that would work as well, we’re just going to
|
||||||
use `struct`s for all three here.
|
use `struct`s for all three here.
|
||||||
|
|
||||||
Next is the `impl` line, which is just like implementing any other trait.
|
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
|
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
|
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
|
for goes on the right. Finally, we use the concrete types in our function
|
||||||
declarations.
|
declarations.
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
% Closures
|
% Closures
|
||||||
|
|
||||||
Rust not only has named functions, but anonymous functions as well. Anonymous
|
Rust not only has named functions, but anonymous functions as well. Anonymous
|
||||||
functions that have an associated environment are called 'closures', because they
|
functions that have an associated environment are called ‘closures’, because they
|
||||||
close over an environment. Rust has a really great implementation of them, as
|
close over an environment. Rust has a really great implementation of them, as
|
||||||
we'll see.
|
we’ll see.
|
||||||
|
|
||||||
# Syntax
|
# Syntax
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ let plus_one = |x: i32| x + 1;
|
||||||
assert_eq!(2, plus_one(1));
|
assert_eq!(2, plus_one(1));
|
||||||
```
|
```
|
||||||
|
|
||||||
We create a binding, `plus_one`, and assign it to a closure. The closure's
|
We create a binding, `plus_one`, and assign it to a closure. The closure’s
|
||||||
arguments go between the pipes (`|`), and the body is an expression, in this
|
arguments go between the pipes (`|`), and the body is an expression, in this
|
||||||
case, `x + 1`. Remember that `{ }` is an expression, so we can have multi-line
|
case, `x + 1`. Remember that `{ }` is an expression, so we can have multi-line
|
||||||
closures too:
|
closures too:
|
||||||
|
@ -33,7 +33,7 @@ let plus_two = |x| {
|
||||||
assert_eq!(4, plus_two(2));
|
assert_eq!(4, plus_two(2));
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll notice a few things about closures that are a bit different than regular
|
You’ll notice a few things about closures that are a bit different than regular
|
||||||
functions defined with `fn`. The first of which is that we did not need to
|
functions defined with `fn`. The first of which is that we did not need to
|
||||||
annotate the types of arguments the closure takes or the values it returns. We
|
annotate the types of arguments the closure takes or the values it returns. We
|
||||||
can:
|
can:
|
||||||
|
@ -44,13 +44,13 @@ let plus_one = |x: i32| -> i32 { x + 1 };
|
||||||
assert_eq!(2, plus_one(1));
|
assert_eq!(2, plus_one(1));
|
||||||
```
|
```
|
||||||
|
|
||||||
But we don't have to. Why is this? Basically, it was chosen for ergonomic reasons.
|
But we don’t have to. Why is this? Basically, it was chosen for ergonomic reasons.
|
||||||
While specifying the full type for named functions is helpful with things like
|
While specifying the full type for named functions is helpful with things like
|
||||||
documentation and type inference, the types of closures are rarely documented
|
documentation and type inference, the types of closures are rarely documented
|
||||||
since they’re anonymous, and they don’t cause the kinds of error-at-a-distance
|
since they’re anonymous, and they don’t cause the kinds of error-at-a-distance
|
||||||
that inferring named function types can.
|
that inferring named function types can.
|
||||||
|
|
||||||
The second is that the syntax is similar, but a bit different. I've added spaces
|
The second is that the syntax is similar, but a bit different. I’ve added spaces
|
||||||
here to make them look a little closer:
|
here to make them look a little closer:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -59,11 +59,11 @@ let plus_one_v2 = |x: i32 | -> i32 { x + 1 };
|
||||||
let plus_one_v3 = |x: i32 | x + 1 ;
|
let plus_one_v3 = |x: i32 | x + 1 ;
|
||||||
```
|
```
|
||||||
|
|
||||||
Small differences, but they're similar in ways.
|
Small differences, but they’re similar in ways.
|
||||||
|
|
||||||
# Closures and their environment
|
# Closures and their environment
|
||||||
|
|
||||||
Closures are called such because they 'close over their environment.' It
|
Closures are called such because they ‘close over their environment’. It
|
||||||
looks like this:
|
looks like this:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -105,7 +105,7 @@ fn main() {
|
||||||
^
|
^
|
||||||
```
|
```
|
||||||
|
|
||||||
A verbose yet helpful error message! As it says, we can't take a mutable borrow
|
A verbose yet helpful error message! As it says, we can’t take a mutable borrow
|
||||||
on `num` because the closure is already borrowing it. If we let the closure go
|
on `num` because the closure is already borrowing it. If we let the closure go
|
||||||
out of scope, we can:
|
out of scope, we can:
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ let takes_nums = || nums;
|
||||||
```
|
```
|
||||||
|
|
||||||
`Vec<T>` has ownership over its contents, and therefore, when we refer to it
|
`Vec<T>` has ownership over its contents, and therefore, when we refer to it
|
||||||
in our closure, we have to take ownership of `nums`. It's the same as if we'd
|
in our closure, we have to take ownership of `nums`. It’s the same as if we’d
|
||||||
passed `nums` to a function that took ownership of it.
|
passed `nums` to a function that took ownership of it.
|
||||||
|
|
||||||
## `move` closures
|
## `move` closures
|
||||||
|
@ -156,7 +156,7 @@ let owns_num = move |x: i32| x + num;
|
||||||
|
|
||||||
Now, even though the keyword is `move`, the variables follow normal move semantics.
|
Now, even though the keyword is `move`, the variables follow normal move semantics.
|
||||||
In this case, `5` implements `Copy`, and so `owns_num` takes ownership of a copy
|
In this case, `5` implements `Copy`, and so `owns_num` takes ownership of a copy
|
||||||
of `num`. So what's the difference?
|
of `num`. So what’s the difference?
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let mut num = 5;
|
let mut num = 5;
|
||||||
|
@ -171,11 +171,11 @@ assert_eq!(10, num);
|
||||||
```
|
```
|
||||||
|
|
||||||
So in this case, our closure took a mutable reference to `num`, and then when
|
So in this case, our closure took a mutable reference to `num`, and then when
|
||||||
we called `add_num`, it mutated the underlying value, as we'd expect. We also
|
we called `add_num`, it mutated the underlying value, as we’d expect. We also
|
||||||
needed to declare `add_num` as `mut` too, because we’re mutating its
|
needed to declare `add_num` as `mut` too, because we’re mutating its
|
||||||
environment.
|
environment.
|
||||||
|
|
||||||
If we change to a `move` closure, it's different:
|
If we change to a `move` closure, it’s different:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let mut num = 5;
|
let mut num = 5;
|
||||||
|
@ -203,8 +203,8 @@ you tons of control over what your code does, and closures are no different.
|
||||||
|
|
||||||
# Closure implementation
|
# Closure implementation
|
||||||
|
|
||||||
Rust's implementation of closures is a bit different than other languages. They
|
Rust’s implementation of closures is a bit different than other languages. They
|
||||||
are effectively syntax sugar for traits. You'll want to make sure to have read
|
are effectively syntax sugar for traits. You’ll want to make sure to have read
|
||||||
the [traits chapter][traits] before this one, as well as the chapter on [trait
|
the [traits chapter][traits] before this one, as well as the chapter on [trait
|
||||||
objects][trait-objects].
|
objects][trait-objects].
|
||||||
|
|
||||||
|
@ -237,9 +237,9 @@ pub trait FnOnce<Args> {
|
||||||
# }
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll notice a few differences between these traits, but a big one is `self`:
|
You’ll notice a few differences between these traits, but a big one is `self`:
|
||||||
`Fn` takes `&self`, `FnMut` takes `&mut self`, and `FnOnce` takes `self`. This
|
`Fn` takes `&self`, `FnMut` takes `&mut self`, and `FnOnce` takes `self`. This
|
||||||
covers all three kinds of `self` via the usual method call syntax. But we've
|
covers all three kinds of `self` via the usual method call syntax. But we’ve
|
||||||
split them up into three traits, rather than having a single one. This gives us
|
split them up into three traits, rather than having a single one. This gives us
|
||||||
a large amount of control over what kind of closures we can take.
|
a large amount of control over what kind of closures we can take.
|
||||||
|
|
||||||
|
@ -253,7 +253,7 @@ Now that we know that closures are traits, we already know how to accept and
|
||||||
return closures: just like any other trait!
|
return closures: just like any other trait!
|
||||||
|
|
||||||
This also means that we can choose static vs dynamic dispatch as well. First,
|
This also means that we can choose static vs dynamic dispatch as well. First,
|
||||||
let's write a function which takes something callable, calls it, and returns
|
let’s write a function which takes something callable, calls it, and returns
|
||||||
the result:
|
the result:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -271,7 +271,7 @@ assert_eq!(3, answer);
|
||||||
We pass our closure, `|x| x + 2`, to `call_with_one`. It just does what it
|
We pass our closure, `|x| x + 2`, to `call_with_one`. It just does what it
|
||||||
suggests: it calls the closure, giving it `1` as an argument.
|
suggests: it calls the closure, giving it `1` as an argument.
|
||||||
|
|
||||||
Let's examine the signature of `call_with_one` in more depth:
|
Let’s examine the signature of `call_with_one` in more depth:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn call_with_one<F>(some_closure: F) -> i32
|
fn call_with_one<F>(some_closure: F) -> i32
|
||||||
|
@ -280,7 +280,7 @@ fn call_with_one<F>(some_closure: F) -> i32
|
||||||
```
|
```
|
||||||
|
|
||||||
We take one parameter, and it has the type `F`. We also return a `i32`. This part
|
We take one parameter, and it has the type `F`. We also return a `i32`. This part
|
||||||
isn't interesting. The next part is:
|
isn’t interesting. The next part is:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# fn call_with_one<F>(some_closure: F) -> i32
|
# fn call_with_one<F>(some_closure: F) -> i32
|
||||||
|
@ -292,9 +292,9 @@ Because `Fn` is a trait, we can bound our generic with it. In this case, our clo
|
||||||
takes a `i32` as an argument and returns an `i32`, and so the generic bound we use
|
takes a `i32` as an argument and returns an `i32`, and so the generic bound we use
|
||||||
is `Fn(i32) -> i32`.
|
is `Fn(i32) -> i32`.
|
||||||
|
|
||||||
There's one other key point here: because we're bounding a generic with a
|
There’s one other key point here: because we’re bounding a generic with a
|
||||||
trait, this will get monomorphized, and therefore, we'll be doing static
|
trait, this will get monomorphized, and therefore, we’ll be doing static
|
||||||
dispatch into the closure. That's pretty neat. In many langauges, closures are
|
dispatch into the closure. That’s pretty neat. In many langauges, closures are
|
||||||
inherently heap allocated, and will always involve dynamic dispatch. In Rust,
|
inherently heap allocated, and will always involve dynamic dispatch. In Rust,
|
||||||
we can stack allocate our closure environment, and statically dispatch the
|
we can stack allocate our closure environment, and statically dispatch the
|
||||||
call. This happens quite often with iterators and their adapters, which often
|
call. This happens quite often with iterators and their adapters, which often
|
||||||
|
@ -320,7 +320,7 @@ to our closure when we pass it to `call_with_one`, so we use `&||`.
|
||||||
|
|
||||||
It’s very common for functional-style code to return closures in various
|
It’s very common for functional-style code to return closures in various
|
||||||
situations. If you try to return a closure, you may run into an error. At
|
situations. If you try to return a closure, you may run into an error. At
|
||||||
first, it may seem strange, but we'll figure it out. Here's how you'd probably
|
first, it may seem strange, but we’ll figure it out. Here’s how you’d probably
|
||||||
try to return a closure from a function:
|
try to return a closure from a function:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
|
@ -361,7 +361,7 @@ In order to return something from a function, Rust needs to know what
|
||||||
size the return type is. But since `Fn` is a trait, it could be various
|
size the return type is. But since `Fn` is a trait, it could be various
|
||||||
things of various sizes: many different types can implement `Fn`. An easy
|
things of various sizes: many different types can implement `Fn`. An easy
|
||||||
way to give something a size is to take a reference to it, as references
|
way to give something a size is to take a reference to it, as references
|
||||||
have a known size. So we'd write this:
|
have a known size. So we’d write this:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn factory() -> &(Fn(i32) -> Vec<i32>) {
|
fn factory() -> &(Fn(i32) -> Vec<i32>) {
|
||||||
|
@ -385,7 +385,7 @@ fn factory() -> &(Fn(i32) -> i32) {
|
||||||
```
|
```
|
||||||
|
|
||||||
Right. Because we have a reference, we need to give it a lifetime. But
|
Right. Because we have a reference, we need to give it a lifetime. But
|
||||||
our `factory()` function takes no arguments, so elision doesn't kick in
|
our `factory()` function takes no arguments, so elision doesn’t kick in
|
||||||
here. What lifetime can we choose? `'static`:
|
here. What lifetime can we choose? `'static`:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
|
@ -414,7 +414,7 @@ error: mismatched types:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This error is letting us know that we don't have a `&'static Fn(i32) -> i32`,
|
This error is letting us know that we don’t have a `&'static Fn(i32) -> i32`,
|
||||||
we have a `[closure <anon>:7:9: 7:20]`. Wait, what?
|
we have a `[closure <anon>:7:9: 7:20]`. Wait, what?
|
||||||
|
|
||||||
Because each closure generates its own environment `struct` and implementation
|
Because each closure generates its own environment `struct` and implementation
|
||||||
|
@ -422,7 +422,7 @@ of `Fn` and friends, these types are anonymous. They exist just solely for
|
||||||
this closure. So Rust shows them as `closure <anon>`, rather than some
|
this closure. So Rust shows them as `closure <anon>`, rather than some
|
||||||
autogenerated name.
|
autogenerated name.
|
||||||
|
|
||||||
But why doesn't our closure implement `&'static Fn`? Well, as we discussed before,
|
But why doesn’t our closure implement `&'static Fn`? Well, as we discussed before,
|
||||||
closures borrow their environment. And in this case, our environment is based
|
closures borrow their environment. And in this case, our environment is based
|
||||||
on a stack-allocated `5`, the `num` variable binding. So the borrow has a lifetime
|
on a stack-allocated `5`, the `num` variable binding. So the borrow has a lifetime
|
||||||
of the stack frame. So if we returned this closure, the function call would be
|
of the stack frame. So if we returned this closure, the function call would be
|
||||||
|
@ -445,7 +445,7 @@ assert_eq!(6, answer);
|
||||||
# }
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
We use a trait object, by `Box`ing up the `Fn`. There's just one last problem:
|
We use a trait object, by `Box`ing up the `Fn`. There’s just one last problem:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
error: `num` does not live long enough
|
error: `num` does not live long enough
|
||||||
|
@ -471,5 +471,5 @@ assert_eq!(6, answer);
|
||||||
```
|
```
|
||||||
|
|
||||||
By making the inner closure a `move Fn`, we create a new stack frame for our
|
By making the inner closure a `move Fn`, we create a new stack frame for our
|
||||||
closure. By `Box`ing it up, we've given it a known size, and allowing it to
|
closure. By `Box`ing it up, we’ve given it a known size, and allowing it to
|
||||||
escape our stack frame.
|
escape our stack frame.
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
% Crates and Modules
|
% Crates and Modules
|
||||||
|
|
||||||
When a project starts getting large, it's considered good software
|
When a project starts getting large, it’s considered good software
|
||||||
engineering practice to split it up into a bunch of smaller pieces, and then
|
engineering practice to split it up into a bunch of smaller pieces, and then
|
||||||
fit them together. It's also important to have a well-defined interface, so
|
fit them together. It’s also important to have a well-defined interface, so
|
||||||
that some of your functionality is private, and some is public. To facilitate
|
that some of your functionality is private, and some is public. To facilitate
|
||||||
these kinds of things, Rust has a module system.
|
these kinds of things, Rust has a module system.
|
||||||
|
|
||||||
# Basic terminology: Crates and Modules
|
# Basic terminology: Crates and Modules
|
||||||
|
|
||||||
Rust has two distinct terms that relate to the module system: *crate* and
|
Rust has two distinct terms that relate to the module system: ‘crate’ and
|
||||||
*module*. A crate is synonymous with a *library* or *package* in other
|
‘module’. A crate is synonymous with a ‘library’ or ‘package’ in other
|
||||||
languages. Hence "Cargo" as the name of Rust's package management tool: you
|
languages. Hence “Cargo” as the name of Rust’s package management tool: you
|
||||||
ship your crates to others with Cargo. Crates can produce an executable or a
|
ship your crates to others with Cargo. Crates can produce an executable or a
|
||||||
library, depending on the project.
|
library, depending on the project.
|
||||||
|
|
||||||
|
@ -18,10 +18,10 @@ Each crate has an implicit *root module* that contains the code for that crate.
|
||||||
You can then define a tree of sub-modules under that root module. Modules allow
|
You can then define a tree of sub-modules under that root module. Modules allow
|
||||||
you to partition your code within the crate itself.
|
you to partition your code within the crate itself.
|
||||||
|
|
||||||
As an example, let's make a *phrases* crate, which will give us various phrases
|
As an example, let’s make a *phrases* crate, which will give us various phrases
|
||||||
in different languages. To keep things simple, we'll stick to "greetings" and
|
in different languages. To keep things simple, we’ll stick to ‘greetings’ and
|
||||||
"farewells" as two kinds of phrases, and use English and Japanese (日本語) as
|
‘farewells’ as two kinds of phrases, and use English and Japanese (日本語) as
|
||||||
two languages for those phrases to be in. We'll use this module layout:
|
two languages for those phrases to be in. We’ll use this module layout:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
+-----------+
|
+-----------+
|
||||||
|
@ -47,7 +47,7 @@ In this example, `phrases` is the name of our crate. All of the rest are
|
||||||
modules. You can see that they form a tree, branching out from the crate
|
modules. You can see that they form a tree, branching out from the crate
|
||||||
*root*, which is the root of the tree: `phrases` itself.
|
*root*, which is the root of the tree: `phrases` itself.
|
||||||
|
|
||||||
Now that we have a plan, let's define these modules in code. To start,
|
Now that we have a plan, let’s define these modules in code. To start,
|
||||||
generate a new crate with Cargo:
|
generate a new crate with Cargo:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -72,7 +72,7 @@ above.
|
||||||
|
|
||||||
# Defining Modules
|
# Defining Modules
|
||||||
|
|
||||||
To define each of our modules, we use the `mod` keyword. Let's make our
|
To define each of our modules, we use the `mod` keyword. Let’s make our
|
||||||
`src/lib.rs` look like this:
|
`src/lib.rs` look like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -101,7 +101,7 @@ Within a given `mod`, you can declare sub-`mod`s. We can refer to sub-modules
|
||||||
with double-colon (`::`) notation: our four nested modules are
|
with double-colon (`::`) notation: our four nested modules are
|
||||||
`english::greetings`, `english::farewells`, `japanese::greetings`, and
|
`english::greetings`, `english::farewells`, `japanese::greetings`, and
|
||||||
`japanese::farewells`. Because these sub-modules are namespaced under their
|
`japanese::farewells`. Because these sub-modules are namespaced under their
|
||||||
parent module, the names don't conflict: `english::greetings` and
|
parent module, the names don’t conflict: `english::greetings` and
|
||||||
`japanese::greetings` are distinct, even though their names are both
|
`japanese::greetings` are distinct, even though their names are both
|
||||||
`greetings`.
|
`greetings`.
|
||||||
|
|
||||||
|
@ -116,11 +116,11 @@ build deps examples libphrases-a7448e02a0468eaa.rlib native
|
||||||
```
|
```
|
||||||
|
|
||||||
`libphrase-hash.rlib` is the compiled crate. Before we see how to use this
|
`libphrase-hash.rlib` is the compiled crate. Before we see how to use this
|
||||||
crate from another crate, let's break it up into multiple files.
|
crate from another crate, let’s break it up into multiple files.
|
||||||
|
|
||||||
# Multiple file crates
|
# Multiple file crates
|
||||||
|
|
||||||
If each crate were just one file, these files would get very large. It's often
|
If each crate were just one file, these files would get very large. It’s often
|
||||||
easier to split up crates into multiple files, and Rust supports this in two
|
easier to split up crates into multiple files, and Rust supports this in two
|
||||||
ways.
|
ways.
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ mod english;
|
||||||
If we do that, Rust will expect to find either a `english.rs` file, or a
|
If we do that, Rust will expect to find either a `english.rs` file, or a
|
||||||
`english/mod.rs` file with the contents of our module.
|
`english/mod.rs` file with the contents of our module.
|
||||||
|
|
||||||
Note that in these files, you don't need to re-declare the module: that's
|
Note that in these files, you don’t need to re-declare the module: that’s
|
||||||
already been done with the initial `mod` declaration.
|
already been done with the initial `mod` declaration.
|
||||||
|
|
||||||
Using these two techniques, we can break up our crate into two directories and
|
Using these two techniques, we can break up our crate into two directories and
|
||||||
|
@ -180,7 +180,7 @@ mod japanese;
|
||||||
|
|
||||||
These two declarations tell Rust to look for either `src/english.rs` and
|
These two declarations tell Rust to look for either `src/english.rs` and
|
||||||
`src/japanese.rs`, or `src/english/mod.rs` and `src/japanese/mod.rs`, depending
|
`src/japanese.rs`, or `src/english/mod.rs` and `src/japanese/mod.rs`, depending
|
||||||
on our preference. In this case, because our modules have sub-modules, we've
|
on our preference. In this case, because our modules have sub-modules, we’ve
|
||||||
chosen the second. Both `src/english/mod.rs` and `src/japanese/mod.rs` look
|
chosen the second. Both `src/english/mod.rs` and `src/japanese/mod.rs` look
|
||||||
like this:
|
like this:
|
||||||
|
|
||||||
|
@ -192,11 +192,11 @@ mod farewells;
|
||||||
Again, these declarations tell Rust to look for either
|
Again, these declarations tell Rust to look for either
|
||||||
`src/english/greetings.rs` and `src/japanese/greetings.rs` or
|
`src/english/greetings.rs` and `src/japanese/greetings.rs` or
|
||||||
`src/english/farewells/mod.rs` and `src/japanese/farewells/mod.rs`. Because
|
`src/english/farewells/mod.rs` and `src/japanese/farewells/mod.rs`. Because
|
||||||
these sub-modules don't have their own sub-modules, we've chosen to make them
|
these sub-modules don’t have their own sub-modules, we’ve chosen to make them
|
||||||
`src/english/greetings.rs` and `src/japanese/farewells.rs`. Whew!
|
`src/english/greetings.rs` and `src/japanese/farewells.rs`. Whew!
|
||||||
|
|
||||||
The contents of `src/english/greetings.rs` and `src/japanese/farewells.rs` are
|
The contents of `src/english/greetings.rs` and `src/japanese/farewells.rs` are
|
||||||
both empty at the moment. Let's add some functions.
|
both empty at the moment. Let’s add some functions.
|
||||||
|
|
||||||
Put this in `src/english/greetings.rs`:
|
Put this in `src/english/greetings.rs`:
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ fn hello() -> String {
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, you can copy and paste this from this web page, or just type
|
Of course, you can copy and paste this from this web page, or just type
|
||||||
something else. It's not important that you actually put "konnichiwa" to learn
|
something else. It’s not important that you actually put ‘konnichiwa’ to learn
|
||||||
about the module system.
|
about the module system.
|
||||||
|
|
||||||
Put this in `src/japanese/farewells.rs`:
|
Put this in `src/japanese/farewells.rs`:
|
||||||
|
@ -234,17 +234,17 @@ fn goodbye() -> String {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
(This is "Sayōnara", if you're curious.)
|
(This is ‘Sayōnara’, if you’re curious.)
|
||||||
|
|
||||||
Now that we have some functionality in our crate, let's try to use it from
|
Now that we have some functionality in our crate, let’s try to use it from
|
||||||
another crate.
|
another crate.
|
||||||
|
|
||||||
# Importing External Crates
|
# Importing External Crates
|
||||||
|
|
||||||
We have a library crate. Let's make an executable crate that imports and uses
|
We have a library crate. Let’s make an executable crate that imports and uses
|
||||||
our library.
|
our library.
|
||||||
|
|
||||||
Make a `src/main.rs` and put this in it (it won't quite compile yet):
|
Make a `src/main.rs` and put this in it (it won’t quite compile yet):
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
extern crate phrases;
|
extern crate phrases;
|
||||||
|
@ -259,7 +259,7 @@ fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
The `extern crate` declaration tells Rust that we need to compile and link to
|
The `extern crate` declaration tells Rust that we need to compile and link to
|
||||||
the `phrases` crate. We can then use `phrases`' modules in this one. As we
|
the `phrases` crate. We can then use `phrases`’ modules in this one. As we
|
||||||
mentioned earlier, you can use double colons to refer to sub-modules and the
|
mentioned earlier, you can use double colons to refer to sub-modules and the
|
||||||
functions inside of them.
|
functions inside of them.
|
||||||
|
|
||||||
|
@ -267,10 +267,10 @@ Also, Cargo assumes that `src/main.rs` is the crate root of a binary crate,
|
||||||
rather than a library crate. Our package now has two crates: `src/lib.rs` and
|
rather than a library crate. Our package now has two crates: `src/lib.rs` and
|
||||||
`src/main.rs`. This pattern is quite common for executable crates: most
|
`src/main.rs`. This pattern is quite common for executable crates: most
|
||||||
functionality is in a library crate, and the executable crate uses that
|
functionality is in a library crate, and the executable crate uses that
|
||||||
library. This way, other programs can also use the library crate, and it's also
|
library. This way, other programs can also use the library crate, and it’s also
|
||||||
a nice separation of concerns.
|
a nice separation of concerns.
|
||||||
|
|
||||||
This doesn't quite work yet, though. We get four errors that look similar to
|
This doesn’t quite work yet, though. We get four errors that look similar to
|
||||||
this:
|
this:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -287,14 +287,14 @@ note: in expansion of format_args!
|
||||||
phrases/src/main.rs:4:5: 4:76 note: expansion site
|
phrases/src/main.rs:4:5: 4:76 note: expansion site
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, everything is private in Rust. Let's talk about this in some more
|
By default, everything is private in Rust. Let’s talk about this in some more
|
||||||
depth.
|
depth.
|
||||||
|
|
||||||
# Exporting a Public Interface
|
# Exporting a Public Interface
|
||||||
|
|
||||||
Rust allows you to precisely control which aspects of your interface are
|
Rust allows you to precisely control which aspects of your interface are
|
||||||
public, and so private is the default. To make things public, you use the `pub`
|
public, and so private is the default. To make things public, you use the `pub`
|
||||||
keyword. Let's focus on the `english` module first, so let's reduce our `src/main.rs`
|
keyword. Let’s focus on the `english` module first, so let’s reduce our `src/main.rs`
|
||||||
to just this:
|
to just this:
|
||||||
|
|
||||||
```{rust,ignore}
|
```{rust,ignore}
|
||||||
|
@ -306,21 +306,21 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In our `src/lib.rs`, let's add `pub` to the `english` module declaration:
|
In our `src/lib.rs`, let’s add `pub` to the `english` module declaration:
|
||||||
|
|
||||||
```{rust,ignore}
|
```{rust,ignore}
|
||||||
pub mod english;
|
pub mod english;
|
||||||
mod japanese;
|
mod japanese;
|
||||||
```
|
```
|
||||||
|
|
||||||
And in our `src/english/mod.rs`, let's make both `pub`:
|
And in our `src/english/mod.rs`, let’s make both `pub`:
|
||||||
|
|
||||||
```{rust,ignore}
|
```{rust,ignore}
|
||||||
pub mod greetings;
|
pub mod greetings;
|
||||||
pub mod farewells;
|
pub mod farewells;
|
||||||
```
|
```
|
||||||
|
|
||||||
In our `src/english/greetings.rs`, let's add `pub` to our `fn` declaration:
|
In our `src/english/greetings.rs`, let’s add `pub` to our `fn` declaration:
|
||||||
|
|
||||||
```{rust,ignore}
|
```{rust,ignore}
|
||||||
pub fn hello() -> String {
|
pub fn hello() -> String {
|
||||||
|
@ -358,12 +358,12 @@ Goodbye in English: Goodbye.
|
||||||
Now that our functions are public, we can use them. Great! However, typing out
|
Now that our functions are public, we can use them. Great! However, typing out
|
||||||
`phrases::english::greetings::hello()` is very long and repetitive. Rust has
|
`phrases::english::greetings::hello()` is very long and repetitive. Rust has
|
||||||
another keyword for importing names into the current scope, so that you can
|
another keyword for importing names into the current scope, so that you can
|
||||||
refer to them with shorter names. Let's talk about `use`.
|
refer to them with shorter names. Let’s talk about `use`.
|
||||||
|
|
||||||
# Importing Modules with `use`
|
# Importing Modules with `use`
|
||||||
|
|
||||||
Rust has a `use` keyword, which allows us to import names into our local scope.
|
Rust has a `use` keyword, which allows us to import names into our local scope.
|
||||||
Let's change our `src/main.rs` to look like this:
|
Let’s change our `src/main.rs` to look like this:
|
||||||
|
|
||||||
```{rust,ignore}
|
```{rust,ignore}
|
||||||
extern crate phrases;
|
extern crate phrases;
|
||||||
|
@ -378,7 +378,7 @@ fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
The two `use` lines import each module into the local scope, so we can refer to
|
The two `use` lines import each module into the local scope, so we can refer to
|
||||||
the functions by a much shorter name. By convention, when importing functions, it's
|
the functions by a much shorter name. By convention, when importing functions, it’s
|
||||||
considered best practice to import the module, rather than the function directly. In
|
considered best practice to import the module, rather than the function directly. In
|
||||||
other words, you _can_ do this:
|
other words, you _can_ do this:
|
||||||
|
|
||||||
|
@ -395,7 +395,7 @@ fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
But it is not idiomatic. This is significantly more likely to introduce a
|
But it is not idiomatic. This is significantly more likely to introduce a
|
||||||
naming conflict. In our short program, it's not a big deal, but as it grows, it
|
naming conflict. In our short program, it’s not a big deal, but as it grows, it
|
||||||
becomes a problem. If we have conflicting names, Rust will give a compilation
|
becomes a problem. If we have conflicting names, Rust will give a compilation
|
||||||
error. For example, if we made the `japanese` functions public, and tried to do
|
error. For example, if we made the `japanese` functions public, and tried to do
|
||||||
this:
|
this:
|
||||||
|
@ -423,7 +423,7 @@ error: aborting due to previous error
|
||||||
Could not compile `phrases`.
|
Could not compile `phrases`.
|
||||||
```
|
```
|
||||||
|
|
||||||
If we're importing multiple names from the same module, we don't have to type it out
|
If we’re importing multiple names from the same module, we don’t have to type it out
|
||||||
twice. Instead of this:
|
twice. Instead of this:
|
||||||
|
|
||||||
```{rust,ignore}
|
```{rust,ignore}
|
||||||
|
@ -439,11 +439,11 @@ use phrases::english::{greetings, farewells};
|
||||||
|
|
||||||
## Re-exporting with `pub use`
|
## Re-exporting with `pub use`
|
||||||
|
|
||||||
You don't just use `use` to shorten identifiers. You can also use it inside of your crate
|
You don’t just use `use` to shorten identifiers. You can also use it inside of your crate
|
||||||
to re-export a function inside another module. This allows you to present an external
|
to re-export a function inside another module. This allows you to present an external
|
||||||
interface that may not directly map to your internal code organization.
|
interface that may not directly map to your internal code organization.
|
||||||
|
|
||||||
Let's look at an example. Modify your `src/main.rs` to read like this:
|
Let’s look at an example. Modify your `src/main.rs` to read like this:
|
||||||
|
|
||||||
```{rust,ignore}
|
```{rust,ignore}
|
||||||
extern crate phrases;
|
extern crate phrases;
|
||||||
|
@ -494,11 +494,11 @@ mod farewells;
|
||||||
```
|
```
|
||||||
|
|
||||||
The `pub use` declaration brings the function into scope at this part of our
|
The `pub use` declaration brings the function into scope at this part of our
|
||||||
module hierarchy. Because we've `pub use`d this inside of our `japanese`
|
module hierarchy. Because we’ve `pub use`d this inside of our `japanese`
|
||||||
module, we now have a `phrases::japanese::hello()` function and a
|
module, we now have a `phrases::japanese::hello()` function and a
|
||||||
`phrases::japanese::goodbye()` function, even though the code for them lives in
|
`phrases::japanese::goodbye()` function, even though the code for them lives in
|
||||||
`phrases::japanese::greetings::hello()` and
|
`phrases::japanese::greetings::hello()` and
|
||||||
`phrases::japanese::farewells::goodbye()`. Our internal organization doesn't
|
`phrases::japanese::farewells::goodbye()`. Our internal organization doesn’t
|
||||||
define our external interface.
|
define our external interface.
|
||||||
|
|
||||||
Here we have a `pub use` for each function we want to bring into the
|
Here we have a `pub use` for each function we want to bring into the
|
||||||
|
@ -507,13 +507,13 @@ everything from `greetings` into the current scope: `pub use self::greetings::*`
|
||||||
|
|
||||||
What about the `self`? Well, by default, `use` declarations are absolute paths,
|
What about the `self`? Well, by default, `use` declarations are absolute paths,
|
||||||
starting from your crate root. `self` makes that path relative to your current
|
starting from your crate root. `self` makes that path relative to your current
|
||||||
place in the hierarchy instead. There's one more special form of `use`: you can
|
place in the hierarchy instead. There’s one more special form of `use`: you can
|
||||||
`use super::` to reach one level up the tree from your current location. Some
|
`use super::` to reach one level up the tree from your current location. Some
|
||||||
people like to think of `self` as `.` and `super` as `..`, from many shells'
|
people like to think of `self` as `.` and `super` as `..`, from many shells’
|
||||||
display for the current directory and the parent directory.
|
display for the current directory and the parent directory.
|
||||||
|
|
||||||
Outside of `use`, paths are relative: `foo::bar()` refers to a function inside
|
Outside of `use`, paths are relative: `foo::bar()` refers to a function inside
|
||||||
of `foo` relative to where we are. If that's prefixed with `::`, as in
|
of `foo` relative to where we are. If that’s prefixed with `::`, as in
|
||||||
`::foo::bar()`, it refers to a different `foo`, an absolute path from your
|
`::foo::bar()`, it refers to a different `foo`, an absolute path from your
|
||||||
crate root.
|
crate root.
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
% Macros
|
% Macros
|
||||||
|
|
||||||
By now you've learned about many of the tools Rust provides for abstracting and
|
By now you’ve learned about many of the tools Rust provides for abstracting and
|
||||||
reusing code. These units of code reuse have a rich semantic structure. For
|
reusing code. These units of code reuse have a rich semantic structure. For
|
||||||
example, functions have a type signature, type parameters have trait bounds,
|
example, functions have a type signature, type parameters have trait bounds,
|
||||||
and overloaded functions must belong to a particular trait.
|
and overloaded functions must belong to a particular trait.
|
||||||
|
|
||||||
This structure means that Rust's core abstractions have powerful compile-time
|
This structure means that Rust’s core abstractions have powerful compile-time
|
||||||
correctness checking. But this comes at the price of reduced flexibility. If
|
correctness checking. But this comes at the price of reduced flexibility. If
|
||||||
you visually identify a pattern of repeated code, you may find it's difficult
|
you visually identify a pattern of repeated code, you may find it’s difficult
|
||||||
or cumbersome to express that pattern as a generic function, a trait, or
|
or cumbersome to express that pattern as a generic function, a trait, or
|
||||||
anything else within Rust's semantics.
|
anything else within Rust’s semantics.
|
||||||
|
|
||||||
Macros allow us to abstract at a *syntactic* level. A macro invocation is
|
Macros allow us to abstract at a syntactic level. A macro invocation is
|
||||||
shorthand for an "expanded" syntactic form. This expansion happens early in
|
shorthand for an "expanded" syntactic form. This expansion happens early in
|
||||||
compilation, before any static checking. As a result, macros can capture many
|
compilation, before any static checking. As a result, macros can capture many
|
||||||
patterns of code reuse that Rust's core abstractions cannot.
|
patterns of code reuse that Rust’s core abstractions cannot.
|
||||||
|
|
||||||
The drawback is that macro-based code can be harder to understand, because
|
The drawback is that macro-based code can be harder to understand, because
|
||||||
fewer of the built-in rules apply. Like an ordinary function, a well-behaved
|
fewer of the built-in rules apply. Like an ordinary function, a well-behaved
|
||||||
|
@ -23,8 +23,8 @@ difficult to design a well-behaved macro! Additionally, compiler errors in
|
||||||
macro code are harder to interpret, because they describe problems in the
|
macro code are harder to interpret, because they describe problems in the
|
||||||
expanded code, not the source-level form that developers use.
|
expanded code, not the source-level form that developers use.
|
||||||
|
|
||||||
These drawbacks make macros something of a "feature of last resort". That's not
|
These drawbacks make macros something of a "feature of last resort". That’s not
|
||||||
to say that macros are bad; they are part of Rust because sometimes they're
|
to say that macros are bad; they are part of Rust because sometimes they’re
|
||||||
needed for truly concise, well-abstracted code. Just keep this tradeoff in
|
needed for truly concise, well-abstracted code. Just keep this tradeoff in
|
||||||
mind.
|
mind.
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ let x: Vec<u32> = vec![1, 2, 3];
|
||||||
# assert_eq!(x, [1, 2, 3]);
|
# assert_eq!(x, [1, 2, 3]);
|
||||||
```
|
```
|
||||||
|
|
||||||
This can't be an ordinary function, because it takes any number of arguments.
|
This can’t be an ordinary function, because it takes any number of arguments.
|
||||||
But we can imagine it as syntactic shorthand for
|
But we can imagine it as syntactic shorthand for
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -77,20 +77,20 @@ macro_rules! vec {
|
||||||
# }
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
Whoa, that's a lot of new syntax! Let's break it down.
|
Whoa, that’s a lot of new syntax! Let’s break it down.
|
||||||
|
|
||||||
```ignore
|
```ignore
|
||||||
macro_rules! vec { ... }
|
macro_rules! vec { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
This says we're defining a macro named `vec`, much as `fn vec` would define a
|
This says we’re defining a macro named `vec`, much as `fn vec` would define a
|
||||||
function named `vec`. In prose, we informally write a macro's name with an
|
function named `vec`. In prose, we informally write a macro’s name with an
|
||||||
exclamation point, e.g. `vec!`. The exclamation point is part of the invocation
|
exclamation point, e.g. `vec!`. The exclamation point is part of the invocation
|
||||||
syntax and serves to distinguish a macro from an ordinary function.
|
syntax and serves to distinguish a macro from an ordinary function.
|
||||||
|
|
||||||
## Matching
|
## Matching
|
||||||
|
|
||||||
The macro is defined through a series of *rules*, which are pattern-matching
|
The macro is defined through a series of rules, which are pattern-matching
|
||||||
cases. Above, we had
|
cases. Above, we had
|
||||||
|
|
||||||
```ignore
|
```ignore
|
||||||
|
@ -99,13 +99,13 @@ cases. Above, we had
|
||||||
|
|
||||||
This is like a `match` expression arm, but the matching happens on Rust syntax
|
This is like a `match` expression arm, but the matching happens on Rust syntax
|
||||||
trees, at compile time. The semicolon is optional on the last (here, only)
|
trees, at compile time. The semicolon is optional on the last (here, only)
|
||||||
case. The "pattern" on the left-hand side of `=>` is known as a *matcher*.
|
case. The "pattern" on the left-hand side of `=>` is known as a ‘matcher’.
|
||||||
These have [their own little grammar] within the language.
|
These have [their own little grammar] within the language.
|
||||||
|
|
||||||
[their own little grammar]: ../reference.html#macros
|
[their own little grammar]: ../reference.html#macros
|
||||||
|
|
||||||
The matcher `$x:expr` will match any Rust expression, binding that syntax tree
|
The matcher `$x:expr` will match any Rust expression, binding that syntax tree
|
||||||
to the *metavariable* `$x`. The identifier `expr` is a *fragment specifier*;
|
to the ‘metavariable’ `$x`. The identifier `expr` is a ‘fragment specifier’;
|
||||||
the full possibilities are enumerated in the [advanced macros chapter][].
|
the full possibilities are enumerated in the [advanced macros chapter][].
|
||||||
Surrounding the matcher with `$(...),*` will match zero or more expressions,
|
Surrounding the matcher with `$(...),*` will match zero or more expressions,
|
||||||
separated by commas.
|
separated by commas.
|
||||||
|
@ -158,8 +158,8 @@ Each matched expression `$x` will produce a single `push` statement in the
|
||||||
macro expansion. The repetition in the expansion proceeds in "lockstep" with
|
macro expansion. The repetition in the expansion proceeds in "lockstep" with
|
||||||
repetition in the matcher (more on this in a moment).
|
repetition in the matcher (more on this in a moment).
|
||||||
|
|
||||||
Because `$x` was already declared as matching an expression, we don't repeat
|
Because `$x` was already declared as matching an expression, we don’t repeat
|
||||||
`:expr` on the right-hand side. Also, we don't include a separating comma as
|
`:expr` on the right-hand side. Also, we don’t include a separating comma as
|
||||||
part of the repetition operator. Instead, we have a terminating semicolon
|
part of the repetition operator. Instead, we have a terminating semicolon
|
||||||
within the repeated block.
|
within the repeated block.
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ The outer braces are part of the syntax of `macro_rules!`. In fact, you can use
|
||||||
The inner braces are part of the expanded syntax. Remember, the `vec!` macro is
|
The inner braces are part of the expanded syntax. Remember, the `vec!` macro is
|
||||||
used in an expression context. To write an expression with multiple statements,
|
used in an expression context. To write an expression with multiple statements,
|
||||||
including `let`-bindings, we use a block. If your macro expands to a single
|
including `let`-bindings, we use a block. If your macro expands to a single
|
||||||
expression, you don't need this extra layer of braces.
|
expression, you don’t need this extra layer of braces.
|
||||||
|
|
||||||
Note that we never *declared* that the macro produces an expression. In fact,
|
Note that we never *declared* that the macro produces an expression. In fact,
|
||||||
this is not determined until we use the macro as an expression. With care, you
|
this is not determined until we use the macro as an expression. With care, you
|
||||||
|
@ -194,7 +194,7 @@ The repetition operator follows two principal rules:
|
||||||
1. `$(...)*` walks through one "layer" of repetitions, for all of the `$name`s
|
1. `$(...)*` walks through one "layer" of repetitions, for all of the `$name`s
|
||||||
it contains, in lockstep, and
|
it contains, in lockstep, and
|
||||||
2. each `$name` must be under at least as many `$(...)*`s as it was matched
|
2. each `$name` must be under at least as many `$(...)*`s as it was matched
|
||||||
against. If it is under more, it'll be duplicated, as appropriate.
|
against. If it is under more, it’ll be duplicated, as appropriate.
|
||||||
|
|
||||||
This baroque macro illustrates the duplication of variables from outer
|
This baroque macro illustrates the duplication of variables from outer
|
||||||
repetition levels.
|
repetition levels.
|
||||||
|
@ -219,7 +219,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
That's most of the matcher syntax. These examples use `$(...)*`, which is a
|
That’s most of the matcher syntax. These examples use `$(...)*`, which is a
|
||||||
"zero or more" match. Alternatively you can write `$(...)+` for a "one or
|
"zero or more" match. Alternatively you can write `$(...)+` for a "one or
|
||||||
more" match. Both forms optionally include a separator, which can be any token
|
more" match. Both forms optionally include a separator, which can be any token
|
||||||
except `+` or `*`.
|
except `+` or `*`.
|
||||||
|
@ -244,9 +244,9 @@ int main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
After expansion we have `5 * 2 + 3`, and multiplication has greater precedence
|
After expansion we have `5 * 2 + 3`, and multiplication has greater precedence
|
||||||
than addition. If you've used C macros a lot, you probably know the standard
|
than addition. If you’ve used C macros a lot, you probably know the standard
|
||||||
idioms for avoiding this problem, as well as five or six others. In Rust, we
|
idioms for avoiding this problem, as well as five or six others. In Rust, we
|
||||||
don't have to worry about it.
|
don’t have to worry about it.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
macro_rules! five_times {
|
macro_rules! five_times {
|
||||||
|
@ -261,8 +261,8 @@ fn main() {
|
||||||
The metavariable `$x` is parsed as a single expression node, and keeps its
|
The metavariable `$x` is parsed as a single expression node, and keeps its
|
||||||
place in the syntax tree even after substitution.
|
place in the syntax tree even after substitution.
|
||||||
|
|
||||||
Another common problem in macro systems is *variable capture*. Here's a C
|
Another common problem in macro systems is ‘variable capture’. Here’s a C
|
||||||
macro, using [a GNU C extension] to emulate Rust's expression blocks.
|
macro, using [a GNU C extension] to emulate Rust’s expression blocks.
|
||||||
|
|
||||||
[a GNU C extension]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
|
[a GNU C extension]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ macro, using [a GNU C extension] to emulate Rust's expression blocks.
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's a simple use case that goes terribly wrong:
|
Here’s a simple use case that goes terribly wrong:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
const char *state = "reticulating splines";
|
const char *state = "reticulating splines";
|
||||||
|
@ -315,10 +315,10 @@ fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
This works because Rust has a [hygienic macro system][]. Each macro expansion
|
This works because Rust has a [hygienic macro system][]. Each macro expansion
|
||||||
happens in a distinct *syntax context*, and each variable is tagged with the
|
happens in a distinct ‘syntax context’, and each variable is tagged with the
|
||||||
syntax context where it was introduced. It's as though the variable `state`
|
syntax context where it was introduced. It’s as though the variable `state`
|
||||||
inside `main` is painted a different "color" from the variable `state` inside
|
inside `main` is painted a different "color" from the variable `state` inside
|
||||||
the macro, and therefore they don't conflict.
|
the macro, and therefore they don’t conflict.
|
||||||
|
|
||||||
[hygienic macro system]: http://en.wikipedia.org/wiki/Hygienic_macro
|
[hygienic macro system]: http://en.wikipedia.org/wiki/Hygienic_macro
|
||||||
|
|
||||||
|
@ -336,7 +336,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Instead you need to pass the variable name into the invocation, so it's tagged
|
Instead you need to pass the variable name into the invocation, so it’s tagged
|
||||||
with the right syntax context.
|
with the right syntax context.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -368,7 +368,7 @@ fn main() {
|
||||||
|
|
||||||
# Recursive macros
|
# Recursive macros
|
||||||
|
|
||||||
A macro's expansion can include more macro invocations, including invocations
|
A macro’s expansion can include more macro invocations, including invocations
|
||||||
of the very same macro being expanded. These recursive macros are useful for
|
of the very same macro being expanded. These recursive macros are useful for
|
||||||
processing tree-structured input, as illustrated by this (simplistic) HTML
|
processing tree-structured input, as illustrated by this (simplistic) HTML
|
||||||
shorthand:
|
shorthand:
|
||||||
|
@ -429,7 +429,7 @@ they are unstable and require feature gates.
|
||||||
Even when Rust code contains un-expanded macros, it can be parsed as a full
|
Even when Rust code contains un-expanded macros, it can be parsed as a full
|
||||||
[syntax tree][ast]. This property can be very useful for editors and other
|
[syntax tree][ast]. This property can be very useful for editors and other
|
||||||
tools that process code. It also has a few consequences for the design of
|
tools that process code. It also has a few consequences for the design of
|
||||||
Rust's macro system.
|
Rust’s macro system.
|
||||||
|
|
||||||
[ast]: glossary.html#abstract-syntax-tree
|
[ast]: glossary.html#abstract-syntax-tree
|
||||||
|
|
||||||
|
@ -454,13 +454,13 @@ consist of valid Rust tokens. Furthermore, parentheses, brackets, and braces
|
||||||
must be balanced within a macro invocation. For example, `foo!([)` is
|
must be balanced within a macro invocation. For example, `foo!([)` is
|
||||||
forbidden. This allows Rust to know where the macro invocation ends.
|
forbidden. This allows Rust to know where the macro invocation ends.
|
||||||
|
|
||||||
More formally, the macro invocation body must be a sequence of *token trees*.
|
More formally, the macro invocation body must be a sequence of ‘token trees’.
|
||||||
A token tree is defined recursively as either
|
A token tree is defined recursively as either
|
||||||
|
|
||||||
* a sequence of token trees surrounded by matching `()`, `[]`, or `{}`, or
|
* a sequence of token trees surrounded by matching `()`, `[]`, or `{}`, or
|
||||||
* any other single token.
|
* any other single token.
|
||||||
|
|
||||||
Within a matcher, each metavariable has a *fragment specifier*, identifying
|
Within a matcher, each metavariable has a ‘fragment specifier’, identifying
|
||||||
which syntactic form it matches.
|
which syntactic form it matches.
|
||||||
|
|
||||||
* `ident`: an identifier. Examples: `x`; `foo`.
|
* `ident`: an identifier. Examples: `x`; `foo`.
|
||||||
|
@ -482,7 +482,7 @@ There are additional rules regarding the next token after a metavariable:
|
||||||
* `pat` variables must be followed by one of: `=> , =`
|
* `pat` variables must be followed by one of: `=> , =`
|
||||||
* Other variables may be followed by any token.
|
* Other variables may be followed by any token.
|
||||||
|
|
||||||
These rules provide some flexibility for Rust's syntax to evolve without
|
These rules provide some flexibility for Rust’s syntax to evolve without
|
||||||
breaking existing macros.
|
breaking existing macros.
|
||||||
|
|
||||||
The macro system does not deal with parse ambiguity at all. For example, the
|
The macro system does not deal with parse ambiguity at all. For example, the
|
||||||
|
@ -500,7 +500,7 @@ One downside is that scoping works differently for macros, compared to other
|
||||||
constructs in the language.
|
constructs in the language.
|
||||||
|
|
||||||
Definition and expansion of macros both happen in a single depth-first,
|
Definition and expansion of macros both happen in a single depth-first,
|
||||||
lexical-order traversal of a crate's source. So a macro defined at module scope
|
lexical-order traversal of a crate’s source. So a macro defined at module scope
|
||||||
is visible to any subsequent code in the same module, which includes the body
|
is visible to any subsequent code in the same module, which includes the body
|
||||||
of any subsequent child `mod` items.
|
of any subsequent child `mod` items.
|
||||||
|
|
||||||
|
@ -508,8 +508,8 @@ A macro defined within the body of a single `fn`, or anywhere else not at
|
||||||
module scope, is visible only within that item.
|
module scope, is visible only within that item.
|
||||||
|
|
||||||
If a module has the `macro_use` attribute, its macros are also visible in its
|
If a module has the `macro_use` attribute, its macros are also visible in its
|
||||||
parent module after the child's `mod` item. If the parent also has `macro_use`
|
parent module after the child’s `mod` item. If the parent also has `macro_use`
|
||||||
then the macros will be visible in the grandparent after the parent's `mod`
|
then the macros will be visible in the grandparent after the parent’s `mod`
|
||||||
item, and so forth.
|
item, and so forth.
|
||||||
|
|
||||||
The `macro_use` attribute can also appear on `extern crate`. In this context
|
The `macro_use` attribute can also appear on `extern crate`. In this context
|
||||||
|
@ -524,7 +524,7 @@ If the attribute is given simply as `#[macro_use]`, all macros are loaded. If
|
||||||
there is no `#[macro_use]` attribute then no macros are loaded. Only macros
|
there is no `#[macro_use]` attribute then no macros are loaded. Only macros
|
||||||
defined with the `#[macro_export]` attribute may be loaded.
|
defined with the `#[macro_export]` attribute may be loaded.
|
||||||
|
|
||||||
To load a crate's macros *without* linking it into the output, use `#[no_link]`
|
To load a crate’s macros without linking it into the output, use `#[no_link]`
|
||||||
as well.
|
as well.
|
||||||
|
|
||||||
An example:
|
An example:
|
||||||
|
@ -619,12 +619,12 @@ only appear at the root of your crate, not inside `mod`. This ensures that
|
||||||
|
|
||||||
The introductory chapter mentioned recursive macros, but it did not give the
|
The introductory chapter mentioned recursive macros, but it did not give the
|
||||||
full story. Recursive macros are useful for another reason: Each recursive
|
full story. Recursive macros are useful for another reason: Each recursive
|
||||||
invocation gives you another opportunity to pattern-match the macro's
|
invocation gives you another opportunity to pattern-match the macro’s
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
As an extreme example, it is possible, though hardly advisable, to implement
|
As an extreme example, it is possible, though hardly advisable, to implement
|
||||||
the [Bitwise Cyclic Tag](http://esolangs.org/wiki/Bitwise_Cyclic_Tag) automaton
|
the [Bitwise Cyclic Tag](http://esolangs.org/wiki/Bitwise_Cyclic_Tag) automaton
|
||||||
within Rust's macro system.
|
within Rust’s macro system.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
macro_rules! bct {
|
macro_rules! bct {
|
||||||
|
@ -765,9 +765,9 @@ as `unimplemented!` until you’re ready to write them.
|
||||||
|
|
||||||
# Procedural macros
|
# Procedural macros
|
||||||
|
|
||||||
If Rust's macro system can't do what you need, you may want to write a
|
If Rust’s macro system can’t do what you need, you may want to write a
|
||||||
[compiler plugin](plugins.html) instead. Compared to `macro_rules!`
|
[compiler plugin](plugins.html) instead. Compared to `macro_rules!`
|
||||||
macros, this is significantly more work, the interfaces are much less stable,
|
macros, this is significantly more work, the interfaces are much less stable,
|
||||||
and bugs can be much harder to track down. In exchange you get the
|
and bugs can be much harder to track down. In exchange you get the
|
||||||
flexibility of running arbitrary Rust code within the compiler. Syntax
|
flexibility of running arbitrary Rust code within the compiler. Syntax
|
||||||
extension plugins are sometimes called *procedural macros* for this reason.
|
extension plugins are sometimes called ‘procedural macros’ for this reason.
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
% Trait Objects
|
% Trait Objects
|
||||||
|
|
||||||
When code involves polymorphism, there needs to be a mechanism to determine
|
When code involves polymorphism, there needs to be a mechanism to determine
|
||||||
which specific version is actually run. This is called 'dispatch.' There are
|
which specific version is actually run. This is called ‘dispatch’. There are
|
||||||
two major forms of dispatch: static dispatch and dynamic dispatch. While Rust
|
two major forms of dispatch: static dispatch and dynamic dispatch. While Rust
|
||||||
favors static dispatch, it also supports dynamic dispatch through a mechanism
|
favors static dispatch, it also supports dynamic dispatch through a mechanism
|
||||||
called 'trait objects.'
|
called ‘trait objects’.
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
|
|
||||||
For the rest of this chapter, we'll need a trait and some implementations.
|
For the rest of this chapter, we’ll need a trait and some implementations.
|
||||||
Let's make a simple one, `Foo`. It has one method that is expected to return a
|
Let’s make a simple one, `Foo`. It has one method that is expected to return a
|
||||||
`String`.
|
`String`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -18,7 +18,7 @@ trait Foo {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We'll also implement this trait for `u8` and `String`:
|
We’ll also implement this trait for `u8` and `String`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# trait Foo { fn method(&self) -> String; }
|
# trait Foo { fn method(&self) -> String; }
|
||||||
|
@ -53,7 +53,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Rust uses 'monomorphization' to perform static dispatch here. This means that
|
Rust uses ‘monomorphization’ to perform static dispatch here. This means that
|
||||||
Rust will create a special version of `do_something()` for both `u8` and
|
Rust will create a special version of `do_something()` for both `u8` and
|
||||||
`String`, and then replace the call sites with calls to these specialized
|
`String`, and then replace the call sites with calls to these specialized
|
||||||
functions. In other words, Rust generates something like this:
|
functions. In other words, Rust generates something like this:
|
||||||
|
@ -82,7 +82,7 @@ fn main() {
|
||||||
This has a great upside: static dispatch allows function calls to be
|
This has a great upside: static dispatch allows function calls to be
|
||||||
inlined because the callee is known at compile time, and inlining is
|
inlined because the callee is known at compile time, and inlining is
|
||||||
the key to good optimization. Static dispatch is fast, but it comes at
|
the key to good optimization. Static dispatch is fast, but it comes at
|
||||||
a tradeoff: 'code bloat', due to many copies of the same function
|
a tradeoff: ‘code bloat’, due to many copies of the same function
|
||||||
existing in the binary, one for each type.
|
existing in the binary, one for each type.
|
||||||
|
|
||||||
Furthermore, compilers aren’t perfect and may “optimize” code to become slower.
|
Furthermore, compilers aren’t perfect and may “optimize” code to become slower.
|
||||||
|
@ -99,7 +99,7 @@ reason.
|
||||||
|
|
||||||
## Dynamic dispatch
|
## Dynamic dispatch
|
||||||
|
|
||||||
Rust provides dynamic dispatch through a feature called 'trait objects.' Trait
|
Rust provides dynamic dispatch through a feature called ‘trait objects’. Trait
|
||||||
objects, like `&Foo` or `Box<Foo>`, are normal values that store a value of
|
objects, like `&Foo` or `Box<Foo>`, are normal values that store a value of
|
||||||
*any* type that implements the given trait, where the precise type can only be
|
*any* type that implements the given trait, where the precise type can only be
|
||||||
known at runtime.
|
known at runtime.
|
||||||
|
@ -109,12 +109,12 @@ implements the trait by *casting* it (e.g. `&x as &Foo`) or *coercing* it
|
||||||
(e.g. using `&x` as an argument to a function that takes `&Foo`).
|
(e.g. using `&x` as an argument to a function that takes `&Foo`).
|
||||||
|
|
||||||
These trait object coercions and casts also work for pointers like `&mut T` to
|
These trait object coercions and casts also work for pointers like `&mut T` to
|
||||||
`&mut Foo` and `Box<T>` to `Box<Foo>`, but that's all at the moment. Coercions
|
`&mut Foo` and `Box<T>` to `Box<Foo>`, but that’s all at the moment. Coercions
|
||||||
and casts are identical.
|
and casts are identical.
|
||||||
|
|
||||||
This operation can be seen as "erasing" the compiler's knowledge about the
|
This operation can be seen as ‘erasing’ the compiler’s knowledge about the
|
||||||
specific type of the pointer, and hence trait objects are sometimes referred to
|
specific type of the pointer, and hence trait objects are sometimes referred to
|
||||||
as "type erasure".
|
as ‘type erasure’.
|
||||||
|
|
||||||
Coming back to the example above, we can use the same trait to perform dynamic
|
Coming back to the example above, we can use the same trait to perform dynamic
|
||||||
dispatch with trait objects by casting:
|
dispatch with trait objects by casting:
|
||||||
|
@ -167,7 +167,7 @@ on the heap to store it.
|
||||||
|
|
||||||
For `Foo`, we would need to have a value that could be at least either a
|
For `Foo`, we would need to have a value that could be at least either a
|
||||||
`String` (24 bytes) or a `u8` (1 byte), as well as any other type for which
|
`String` (24 bytes) or a `u8` (1 byte), as well as any other type for which
|
||||||
dependent crates may implement `Foo` (any number of bytes at all). There's no
|
dependent crates may implement `Foo` (any number of bytes at all). There’s no
|
||||||
way to guarantee that this last point can work if the values are stored without
|
way to guarantee that this last point can work if the values are stored without
|
||||||
a pointer, because those other types can be arbitrarily large.
|
a pointer, because those other types can be arbitrarily large.
|
||||||
|
|
||||||
|
@ -177,14 +177,14 @@ when we are tossing a trait object around, only the size of the pointer itself.
|
||||||
### Representation
|
### Representation
|
||||||
|
|
||||||
The methods of the trait can be called on a trait object via a special record
|
The methods of the trait can be called on a trait object via a special record
|
||||||
of function pointers traditionally called a 'vtable' (created and managed by
|
of function pointers traditionally called a ‘vtable’ (created and managed by
|
||||||
the compiler).
|
the compiler).
|
||||||
|
|
||||||
Trait objects are both simple and complicated: their core representation and
|
Trait objects are both simple and complicated: their core representation and
|
||||||
layout is quite straight-forward, but there are some curly error messages and
|
layout is quite straight-forward, but there are some curly error messages and
|
||||||
surprising behaviors to discover.
|
surprising behaviors to discover.
|
||||||
|
|
||||||
Let's start simple, with the runtime representation of a trait object. The
|
Let’s start simple, with the runtime representation of a trait object. The
|
||||||
`std::raw` module contains structs with layouts that are the same as the
|
`std::raw` module contains structs with layouts that are the same as the
|
||||||
complicated built-in types, [including trait objects][stdraw]:
|
complicated built-in types, [including trait objects][stdraw]:
|
||||||
|
|
||||||
|
@ -199,12 +199,12 @@ pub struct TraitObject {
|
||||||
|
|
||||||
[stdraw]: ../std/raw/struct.TraitObject.html
|
[stdraw]: ../std/raw/struct.TraitObject.html
|
||||||
|
|
||||||
That is, a trait object like `&Foo` consists of a "data" pointer and a "vtable"
|
That is, a trait object like `&Foo` consists of a ‘data’ pointer and a ‘vtable’
|
||||||
pointer.
|
pointer.
|
||||||
|
|
||||||
The data pointer addresses the data (of some unknown type `T`) that the trait
|
The data pointer addresses the data (of some unknown type `T`) that the trait
|
||||||
object is storing, and the vtable pointer points to the vtable ("virtual method
|
object is storing, and the vtable pointer points to the vtable (‘virtual method
|
||||||
table") corresponding to the implementation of `Foo` for `T`.
|
table’) corresponding to the implementation of `Foo` for `T`.
|
||||||
|
|
||||||
|
|
||||||
A vtable is essentially a struct of function pointers, pointing to the concrete
|
A vtable is essentially a struct of function pointers, pointing to the concrete
|
||||||
|
@ -212,7 +212,7 @@ piece of machine code for each method in the implementation. A method call like
|
||||||
`trait_object.method()` will retrieve the correct pointer out of the vtable and
|
`trait_object.method()` will retrieve the correct pointer out of the vtable and
|
||||||
then do a dynamic call of it. For example:
|
then do a dynamic call of it. For example:
|
||||||
|
|
||||||
```{rust,ignore}
|
```rust,ignore
|
||||||
struct FooVtable {
|
struct FooVtable {
|
||||||
destructor: fn(*mut ()),
|
destructor: fn(*mut ()),
|
||||||
size: usize,
|
size: usize,
|
||||||
|
@ -261,7 +261,7 @@ static Foo_for_String_vtable: FooVtable = FooVtable {
|
||||||
```
|
```
|
||||||
|
|
||||||
The `destructor` field in each vtable points to a function that will clean up
|
The `destructor` field in each vtable points to a function that will clean up
|
||||||
any resources of the vtable's type, for `u8` it is trivial, but for `String` it
|
any resources of the vtable’s type, for `u8` it is trivial, but for `String` it
|
||||||
will free the memory. This is necessary for owning trait objects like
|
will free the memory. This is necessary for owning trait objects like
|
||||||
`Box<Foo>`, which need to clean-up both the `Box` allocation as well as the
|
`Box<Foo>`, which need to clean-up both the `Box` allocation as well as the
|
||||||
internal type when they go out of scope. The `size` and `align` fields store
|
internal type when they go out of scope. The `size` and `align` fields store
|
||||||
|
@ -270,11 +270,11 @@ essentially unused at the moment since the information is embedded in the
|
||||||
destructor, but will be used in the future, as trait objects are progressively
|
destructor, but will be used in the future, as trait objects are progressively
|
||||||
made more flexible.
|
made more flexible.
|
||||||
|
|
||||||
Suppose we've got some values that implement `Foo`, then the explicit form of
|
Suppose we’ve got some values that implement `Foo`, then the explicit form of
|
||||||
construction and use of `Foo` trait objects might look a bit like (ignoring the
|
construction and use of `Foo` trait objects might look a bit like (ignoring the
|
||||||
type mismatches: they're all just pointers anyway):
|
type mismatches: they’re all just pointers anyway):
|
||||||
|
|
||||||
```{rust,ignore}
|
```rust,ignore
|
||||||
let a: String = "foo".to_string();
|
let a: String = "foo".to_string();
|
||||||
let x: u8 = 1;
|
let x: u8 = 1;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue