Rollup merge of #24571 - steveklabnik:editing, r=alexcrichton
A bunch of chapters, fixes an issue or two as well. r? @alexcrichton
This commit is contained in:
commit
ff12b7b308
10 changed files with 405 additions and 504 deletions
|
@ -14,7 +14,6 @@
|
|||
* [Concurrency](concurrency.md)
|
||||
* [Error Handling](error-handling.md)
|
||||
* [FFI](ffi.md)
|
||||
* [Deref coercions](deref-coercions.md)
|
||||
* [Syntax and Semantics](syntax-and-semantics.md)
|
||||
* [Variable Bindings](variable-bindings.md)
|
||||
* [Functions](functions.md)
|
||||
|
@ -30,15 +29,15 @@
|
|||
* [Move semantics](move-semantics.md)
|
||||
* [Enums](enums.md)
|
||||
* [Match](match.md)
|
||||
* [Patterns](patterns.md)
|
||||
* [Structs](structs.md)
|
||||
* [Patterns](patterns.md)
|
||||
* [Method Syntax](method-syntax.md)
|
||||
* [Drop](drop.md)
|
||||
* [Vectors](vectors.md)
|
||||
* [Strings](strings.md)
|
||||
* [Generics](generics.md)
|
||||
* [Traits](traits.md)
|
||||
* [Operators and Overloading](operators-and-overloading.md)
|
||||
* [Generics](generics.md)
|
||||
* [Drop](drop.md)
|
||||
* [if let](if-let.md)
|
||||
* [Trait Objects](trait-objects.md)
|
||||
* [Closures](closures.md)
|
||||
|
@ -53,6 +52,7 @@
|
|||
* [Casting between types](casting-between-types.md)
|
||||
* [Associated Types](associated-types.md)
|
||||
* [Unsized Types](unsized-types.md)
|
||||
* [Deref coercions](deref-coercions.md)
|
||||
* [Macros](macros.md)
|
||||
* [`unsafe` Code](unsafe-code.md)
|
||||
* [Nightly Rust](nightly-rust.md)
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
% Enums
|
||||
|
||||
Finally, Rust has a "sum type", an *enum*. Enums are an incredibly useful
|
||||
feature of Rust, and are used throughout the standard library. An `enum` is
|
||||
a type which relates a set of alternates to a specific name. For example, below
|
||||
we define `Character` to be either a `Digit` or something else. These
|
||||
can be used via their fully scoped names: `Character::Other` (more about `::`
|
||||
below).
|
||||
Rust has a ‘sum type’, an `enum`. Enums are an incredibly useful feature of
|
||||
Rust, and are used throughout the standard library. An `enum` is a type which
|
||||
relates a set of alternates to a specific name. For example, below we define
|
||||
`Character` to be either a `Digit` or something else.
|
||||
|
||||
```rust
|
||||
enum Character {
|
||||
|
@ -14,14 +12,14 @@ enum Character {
|
|||
}
|
||||
```
|
||||
|
||||
Most normal types are allowed as the variant components of an `enum`. Here are
|
||||
some examples:
|
||||
Most types are allowed as the variant components of an `enum`. Here are some
|
||||
examples:
|
||||
|
||||
```rust
|
||||
struct Empty;
|
||||
struct Color(i32, i32, i32);
|
||||
struct Length(i32);
|
||||
struct Status { Health: i32, Mana: i32, Attack: i32, Defense: i32 }
|
||||
struct Stats { Health: i32, Mana: i32, Attack: i32, Defense: i32 }
|
||||
struct HeightDatabase(Vec<i32>);
|
||||
```
|
||||
|
||||
|
@ -30,12 +28,12 @@ In `Character`, for instance, `Digit` gives a meaningful name for an `i32`
|
|||
value, where `Other` is only a name. However, the fact that they represent
|
||||
distinct categories of `Character` is a very useful property.
|
||||
|
||||
As with structures, the variants of an enum by default are not comparable with
|
||||
equality operators (`==`, `!=`), have no ordering (`<`, `>=`, etc.), and do not
|
||||
support other binary operations such as `*` and `+`. As such, the following code
|
||||
is invalid for the example `Character` type:
|
||||
The variants of an `enum` by default are not comparable with equality operators
|
||||
(`==`, `!=`), have no ordering (`<`, `>=`, etc.), and do not support other
|
||||
binary operations such as `*` and `+`. As such, the following code is invalid
|
||||
for the example `Character` type:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
// These assignments both succeed
|
||||
let ten = Character::Digit(10);
|
||||
let four = Character::Digit(4);
|
||||
|
@ -50,98 +48,21 @@ let four_is_smaller = four <= ten;
|
|||
let four_equals_ten = four == ten;
|
||||
```
|
||||
|
||||
This may seem rather limiting, but it's a limitation which we can overcome.
|
||||
There are two ways: by implementing equality ourselves, or by pattern matching
|
||||
variants with [`match`][match] expressions, which you'll learn in the next
|
||||
chapter. We don't know enough about Rust to implement equality yet, but we can
|
||||
use the `Ordering` enum from the standard library, which does:
|
||||
We use the `::` syntax to use the name of each variant: They’re scoped by the name
|
||||
of the `enum` itself. This allows both of these to work:
|
||||
|
||||
```
|
||||
enum Ordering {
|
||||
Less,
|
||||
Equal,
|
||||
Greater,
|
||||
}
|
||||
```rust,ignore
|
||||
Character::Digit(10);
|
||||
Hand::Digit;
|
||||
```
|
||||
|
||||
Because `Ordering` has already been defined for us, we will import it with the
|
||||
`use` keyword. Here's an example of how it is used:
|
||||
Both variants are named `Digit`, but since they’re scoped to the `enum` name,
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering;
|
||||
Not supporting these operations may seem rather limiting, but it’s a limitation
|
||||
which we can overcome. There are two ways: by implementing equality ourselves,
|
||||
or by pattern matching variants with [`match`][match] expressions, which you’ll
|
||||
learn in the next section. We don’t know enough about Rust to implement
|
||||
equality yet, but we’ll find out in the [`traits`][traits] section.
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
let ordering = cmp(x, y); // ordering: Ordering
|
||||
|
||||
if ordering == Ordering::Less {
|
||||
println!("less");
|
||||
} else if ordering == Ordering::Greater {
|
||||
println!("greater");
|
||||
} else if ordering == Ordering::Equal {
|
||||
println!("equal");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `::` symbol is used to indicate a namespace. In this case, `Ordering` lives
|
||||
in the `cmp` submodule of the `std` module. We'll talk more about modules later
|
||||
in the guide. For now, all you need to know is that you can `use` things from
|
||||
the standard library if you need them.
|
||||
|
||||
Okay, let's talk about the actual code in the example. `cmp` is a function that
|
||||
compares two things, and returns an `Ordering`. We return either
|
||||
`Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on
|
||||
whether the first value is less than, greater than, or equal to the second. Note
|
||||
that each variant of the `enum` is namespaced under the `enum` itself: it's
|
||||
`Ordering::Greater`, not `Greater`.
|
||||
|
||||
The `ordering` variable has the type `Ordering`, and so contains one of the
|
||||
three values. We then do a bunch of `if`/`else` comparisons to check which
|
||||
one it is.
|
||||
|
||||
This `Ordering::Greater` notation is too long. Let's use another form of `use`
|
||||
to import the `enum` variants instead. This will avoid full scoping:
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering::{self, Equal, Less, Greater};
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Less }
|
||||
else if a > b { Greater }
|
||||
else { Equal }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
let ordering = cmp(x, y); // ordering: Ordering
|
||||
|
||||
if ordering == Less { println!("less"); }
|
||||
else if ordering == Greater { println!("greater"); }
|
||||
else if ordering == Equal { println!("equal"); }
|
||||
}
|
||||
```
|
||||
|
||||
Importing variants is convenient and compact, but can also cause name conflicts,
|
||||
so do this with caution. For this reason, it's normally considered better style
|
||||
to `use` an enum rather than its variants directly.
|
||||
|
||||
As you can see, `enum`s are quite a powerful tool for data representation, and
|
||||
are even more useful when they're [generic][generics] across types. Before we
|
||||
get to generics, though, let's talk about how to use enums with pattern
|
||||
matching, a tool that will let us deconstruct sum types (the type theory term
|
||||
for enums) like `Ordering` in a very elegant way that avoids all these messy
|
||||
and brittle `if`/`else`s.
|
||||
|
||||
[match]: ./match.html
|
||||
[generics]: ./generics.html
|
||||
[match]: match.html
|
||||
[traits]: traits.html
|
||||
|
|
|
@ -1,31 +1,13 @@
|
|||
% Generics
|
||||
|
||||
Sometimes, when writing a function or data type, we may want it to work for
|
||||
multiple types of arguments. For example, remember our `OptionalInt` type?
|
||||
multiple types of arguments. Luckily, Rust has a feature that gives us a better
|
||||
way: generics. Generics are called ‘parametric polymorphism’ in type theory,
|
||||
which means that they are types or functions that have multiple forms (‘poly’
|
||||
is multiple, ‘morph’ is form) over a given parameter (‘parametric’).
|
||||
|
||||
```{rust}
|
||||
enum OptionalInt {
|
||||
Value(i32),
|
||||
Missing,
|
||||
}
|
||||
```
|
||||
|
||||
If we wanted to also have an `OptionalFloat64`, we would need a new enum:
|
||||
|
||||
```{rust}
|
||||
enum OptionalFloat64 {
|
||||
Valuef64(f64),
|
||||
Missingf64,
|
||||
}
|
||||
```
|
||||
|
||||
This is really unfortunate. Luckily, Rust has a feature that gives us a better
|
||||
way: generics. Generics are called *parametric polymorphism* in type theory,
|
||||
which means that they are types or functions that have multiple forms (*poly*
|
||||
is multiple, *morph* is form) over a given parameter (*parametric*).
|
||||
|
||||
Anyway, enough with type theory declarations, let's check out the generic form
|
||||
of `OptionalInt`. It is actually provided by Rust itself, and looks like this:
|
||||
Anyway, enough with type theory, let’s check out some generic code. Rust’s
|
||||
standard library provides a type, `Option<T>`, that’s generic:
|
||||
|
||||
```rust
|
||||
enum Option<T> {
|
||||
|
@ -34,41 +16,40 @@ enum Option<T> {
|
|||
}
|
||||
```
|
||||
|
||||
The `<T>` part, which you've seen a few times before, indicates that this is
|
||||
The `<T>` part, which you’ve seen a few times before, indicates that this is
|
||||
a generic data type. Inside the declaration of our enum, wherever we see a `T`,
|
||||
we substitute that type for the same type used in the generic. Here's an
|
||||
we substitute that type for the same type used in the generic. Here’s an
|
||||
example of using `Option<T>`, with some extra type annotations:
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
let x: Option<i32> = Some(5);
|
||||
```
|
||||
|
||||
In the type declaration, we say `Option<i32>`. Note how similar this looks to
|
||||
`Option<T>`. So, in this particular `Option`, `T` has the value of `i32`. On
|
||||
the right-hand side of the binding, we do make a `Some(T)`, where `T` is `5`.
|
||||
Since that's an `i32`, the two sides match, and Rust is happy. If they didn't
|
||||
match, we'd get an error:
|
||||
Since that’s an `i32`, the two sides match, and Rust is happy. If they didn’t
|
||||
match, we’d get an error:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
let x: Option<f64> = Some(5);
|
||||
// error: mismatched types: expected `core::option::Option<f64>`,
|
||||
// found `core::option::Option<_>` (expected f64 but found integral variable)
|
||||
```
|
||||
|
||||
That doesn't mean we can't make `Option<T>`s that hold an `f64`! They just have to
|
||||
match up:
|
||||
That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They just have
|
||||
to match up:
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
let x: Option<i32> = Some(5);
|
||||
let y: Option<f64> = Some(5.0f64);
|
||||
```
|
||||
|
||||
This is just fine. One definition, multiple uses.
|
||||
|
||||
Generics don't have to only be generic over one type. Consider Rust's built-in
|
||||
`Result<T, E>` type:
|
||||
Generics don’t have to only be generic over one type. Consider another type from Rust’s standard library that’s similar, `Result<T, E>`:
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
enum Result<T, E> {
|
||||
Ok(T),
|
||||
Err(E),
|
||||
|
@ -76,9 +57,9 @@ enum Result<T, E> {
|
|||
```
|
||||
|
||||
This type is generic over _two_ types: `T` and `E`. By the way, the capital letters
|
||||
can be any letter you'd like. We could define `Result<T, E>` as:
|
||||
can be any letter you’d like. We could define `Result<T, E>` as:
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
enum Result<A, Z> {
|
||||
Ok(A),
|
||||
Err(Z),
|
||||
|
@ -86,92 +67,58 @@ enum Result<A, Z> {
|
|||
```
|
||||
|
||||
if we wanted to. Convention says that the first generic parameter should be
|
||||
`T`, for 'type,' and that we use `E` for 'error.' Rust doesn't care, however.
|
||||
`T`, for ‘type’, and that we use `E` for ‘error’. Rust doesn’t care, however.
|
||||
|
||||
The `Result<T, E>` type is intended to be used to return the result of a
|
||||
computation, and to have the ability to return an error if it didn't work out.
|
||||
Here's an example:
|
||||
computation, and to have the ability to return an error if it didn’t work out.
|
||||
|
||||
```{rust}
|
||||
let x: Result<f64, String> = Ok(2.3f64);
|
||||
let y: Result<f64, String> = Err("There was an error.".to_string());
|
||||
```
|
||||
## Generic functions
|
||||
|
||||
This particular Result will return an `f64` if there's a success, and a
|
||||
`String` if there's a failure. Let's write a function that uses `Result<T, E>`:
|
||||
We can write functions that take generic types with a similar syntax:
|
||||
|
||||
```{rust}
|
||||
fn inverse(x: f64) -> Result<f64, String> {
|
||||
if x == 0.0f64 { return Err("x cannot be zero!".to_string()); }
|
||||
|
||||
Ok(1.0f64 / x)
|
||||
```rust
|
||||
fn takes_anything<T>(x: T) {
|
||||
// do something with x
|
||||
}
|
||||
```
|
||||
|
||||
We don't want to take the inverse of zero, so we check to make sure that we
|
||||
weren't passed zero. If we were, then we return an `Err`, with a message. If
|
||||
it's okay, we return an `Ok`, with the answer.
|
||||
The syntax has two parts: the `<T>` says “this function is generic over one
|
||||
type, `T`”, and the `x: T` says “x has the type `T`.”
|
||||
|
||||
Why does this matter? Well, remember how `match` does exhaustive matches?
|
||||
Here's how this function gets used:
|
||||
Multiple arguments can have the same generic type:
|
||||
|
||||
```{rust}
|
||||
# fn inverse(x: f64) -> Result<f64, String> {
|
||||
# if x == 0.0f64 { return Err("x cannot be zero!".to_string()); }
|
||||
# Ok(1.0f64 / x)
|
||||
# }
|
||||
let x = inverse(25.0f64);
|
||||
|
||||
match x {
|
||||
Ok(x) => println!("The inverse of 25 is {}", x),
|
||||
Err(msg) => println!("Error: {}", msg),
|
||||
```rust
|
||||
fn takes_two_of_the_same_things<T>(x: T, y: T) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The `match` enforces that we handle the `Err` case. In addition, because the
|
||||
answer is wrapped up in an `Ok`, we can't just use the result without doing
|
||||
the match:
|
||||
We could write a version that takes multiple types:
|
||||
|
||||
```{rust,ignore}
|
||||
let x = inverse(25.0f64);
|
||||
println!("{}", x + 2.0f64); // error: binary operation `+` cannot be applied
|
||||
// to type `core::result::Result<f64,collections::string::String>`
|
||||
```
|
||||
|
||||
This function is great, but there's one other problem: it only works for 64 bit
|
||||
floating point values. What if we wanted to handle 32 bit floating point as
|
||||
well? We'd have to write this:
|
||||
|
||||
```{rust}
|
||||
fn inverse32(x: f32) -> Result<f32, String> {
|
||||
if x == 0.0f32 { return Err("x cannot be zero!".to_string()); }
|
||||
|
||||
Ok(1.0f32 / x)
|
||||
```rust
|
||||
fn takes_two_things<T, U>(x: T, y: U) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Bummer. What we need is a *generic function*. Luckily, we can write one!
|
||||
However, it won't _quite_ work yet. Before we get into that, let's talk syntax.
|
||||
A generic version of `inverse` would look something like this:
|
||||
Generic functions are most useful with ‘trait bounds’, which we’ll cover in the
|
||||
[section on traits][traits].
|
||||
|
||||
```{rust,ignore}
|
||||
fn inverse<T>(x: T) -> Result<T, String> {
|
||||
if x == 0.0 { return Err("x cannot be zero!".to_string()); }
|
||||
[traits]: traits.html
|
||||
|
||||
Ok(1.0 / x)
|
||||
## Generic structs
|
||||
|
||||
You can store a generic type in a `struct` as well:
|
||||
|
||||
```
|
||||
struct Point<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
}
|
||||
|
||||
let int_origin = Point { x: 0, y: 0 };
|
||||
let float_origin = Point { x: 0.0, y: 0.0 };
|
||||
```
|
||||
|
||||
Just like how we had `Option<T>`, we use a similar syntax for `inverse<T>`.
|
||||
We can then use `T` inside the rest of the signature: `x` has type `T`, and half
|
||||
of the `Result` has type `T`. However, if we try to compile that example, we'll get
|
||||
an error:
|
||||
|
||||
```text
|
||||
error: binary operation `==` cannot be applied to type `T`
|
||||
```
|
||||
|
||||
Because `T` can be _any_ type, it may be a type that doesn't implement `==`,
|
||||
and therefore, the first line would be wrong. What do we do?
|
||||
|
||||
To fix this example, we need to learn about another Rust feature: traits.
|
||||
Similarly to functions, the `<T>` is where we declare the generic parameters,
|
||||
and we then use `x: T` in the type declaration, too.
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
% Match
|
||||
|
||||
Often, a simple `if`/`else` isn’t enough, because you have more than two
|
||||
possible options. Also, `else` conditions can get incredibly complicated, so
|
||||
what’s the solution?
|
||||
|
||||
Rust has a keyword, `match`, that allows you to replace complicated `if`/`else`
|
||||
Often, a simple [`if`][if]/`else` isn’t enough, because you have more than two
|
||||
possible options. Also, conditions can get quite complex. Rust
|
||||
has a keyword, `match`, that allows you to replace complicated `if`/`else`
|
||||
groupings with something more powerful. Check it out:
|
||||
|
||||
```rust
|
||||
|
@ -20,16 +18,18 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
`match` takes an expression and then branches based on its value. Each *arm* of
|
||||
[if]: if.html
|
||||
|
||||
`match` takes an expression and then branches based on its value. Each ‘arm’ of
|
||||
the branch is of the form `val => expression`. When the value matches, that arm’s
|
||||
expression will be evaluated. It’s called `match` because of the term ‘pattern
|
||||
matching’, which `match` is an implementation of. There’s an [entire section on
|
||||
patterns][patterns] coming up next, that covers all the options that fit here.
|
||||
patterns][patterns] that covers all the patterns that are possible here.
|
||||
|
||||
[patterns]: patterns.html
|
||||
|
||||
So what’s the big advantage here? Well, there are a few. First of all, `match`
|
||||
enforces *exhaustiveness checking*. Do you see that last arm, the one with the
|
||||
So what’s the big advantage? Well, there are a few. First of all, `match`
|
||||
enforces ‘exhaustiveness checking’. Do you see that last arm, the one with the
|
||||
underscore (`_`)? If we remove that arm, Rust will give us an error:
|
||||
|
||||
```text
|
||||
|
@ -37,11 +37,12 @@ error: non-exhaustive patterns: `_` not covered
|
|||
```
|
||||
|
||||
In other words, Rust is trying to tell us we forgot a value. Because `x` is an
|
||||
integer, Rust knows that it can have a number of different values – for example,
|
||||
`6`. Without the `_`, however, there is no arm that could match, and so Rust refuses
|
||||
to compile. `_` acts like a ‘catch-all arm’. If none of the other arms match,
|
||||
the arm with `_` will, and since we have this catch-all arm, we now have an arm
|
||||
for every possible value of `x`, and so our program will compile successfully.
|
||||
integer, Rust knows that it can have a number of different values – for
|
||||
example, `6`. Without the `_`, however, there is no arm that could match, and
|
||||
so Rust refuses to compile the code. `_` acts like a ‘catch-all arm’. If none
|
||||
of the other arms match, the arm with `_` will, and since we have this
|
||||
catch-all arm, we now have an arm for every possible value of `x`, and so our
|
||||
program will compile successfully.
|
||||
|
||||
`match` is also an expression, which means we can use it on the right-hand
|
||||
side of a `let` binding or directly where an expression is used:
|
||||
|
@ -59,4 +60,4 @@ let numer = match x {
|
|||
};
|
||||
```
|
||||
|
||||
Sometimes, it’s a nice way of converting things.
|
||||
Sometimes it’s a nice way of converting something from one type to another.
|
||||
|
|
|
@ -3,27 +3,26 @@
|
|||
Functions are great, but if you want to call a bunch of them on some data, it
|
||||
can be awkward. Consider this code:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
baz(bar(foo)));
|
||||
```
|
||||
|
||||
We would read this left-to right, and so we see "baz bar foo." But this isn't the
|
||||
order that the functions would get called in, that's inside-out: "foo bar baz."
|
||||
Wouldn't it be nice if we could do this instead?
|
||||
We would read this left-to right, and so we see ‘baz bar foo’. But this isn’t the
|
||||
order that the functions would get called in, that’s inside-out: ‘foo bar baz’.
|
||||
Wouldn’t it be nice if we could do this instead?
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
foo.bar().baz();
|
||||
```
|
||||
|
||||
Luckily, as you may have guessed with the leading question, you can! Rust provides
|
||||
the ability to use this *method call syntax* via the `impl` keyword.
|
||||
the ability to use this ‘method call syntax’ via the `impl` keyword.
|
||||
|
||||
## Method calls
|
||||
|
||||
Here's how it works:
|
||||
Here’s how it works:
|
||||
|
||||
```{rust}
|
||||
# #![feature(core)]
|
||||
```rust
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
|
@ -44,15 +43,23 @@ fn main() {
|
|||
|
||||
This will print `12.566371`.
|
||||
|
||||
We've made a struct that represents a circle. We then write an `impl` block,
|
||||
and inside it, define a method, `area`. Methods take a special first
|
||||
parameter, of which there are three variants: `self`, `&self`, and `&mut self`.
|
||||
You can think of this first parameter as being the `foo` in `foo.bar()`. The three
|
||||
variants correspond to the three kinds of things `foo` could be: `self` if it's
|
||||
just a value on the stack, `&self` if it's a reference, and `&mut self` if it's
|
||||
a mutable reference. We should default to using `&self`, as you should prefer
|
||||
borrowing over taking ownership, as well as taking immutable references
|
||||
over mutable ones. Here's an example of all three variants:
|
||||
|
||||
|
||||
We’ve made a struct that represents a circle. We then write an `impl` block,
|
||||
and inside it, define a method, `area`.
|
||||
|
||||
Methods take a special first parameter, of which there are three variants:
|
||||
`self`, `&self`, and `&mut self`. You can think of this first parameter as
|
||||
being the `foo` in `foo.bar()`. The three variants correspond to the three
|
||||
kinds of things `foo` could be: `self` if it’s just a value on the stack,
|
||||
`&self` if it’s a reference, and `&mut self` if it’s a mutable reference.
|
||||
Because we took the `&self` parameter to `area`, we can use it just like any
|
||||
other parameter. Because we know it’s a `Circle`, we can access the `radius`
|
||||
just like we would with any other struct.
|
||||
|
||||
We should default to using `&self`, as you should prefer borrowing over taking
|
||||
ownership, as well as taking immutable references over mutable ones. Here’s an
|
||||
example of all three variants:
|
||||
|
||||
```rust
|
||||
struct Circle {
|
||||
|
@ -76,20 +83,13 @@ impl Circle {
|
|||
}
|
||||
```
|
||||
|
||||
Finally, as you may remember, the value of the area of a circle is `π*r²`.
|
||||
Because we took the `&self` parameter to `area`, we can use it just like any
|
||||
other parameter. Because we know it's a `Circle`, we can access the `radius`
|
||||
just like we would with any other struct. An import of π and some
|
||||
multiplications later, and we have our area.
|
||||
|
||||
## Chaining method calls
|
||||
|
||||
So, now we know how to call a method, such as `foo.bar()`. But what about our
|
||||
original example, `foo.bar().baz()`? This is called 'method chaining', and we
|
||||
original example, `foo.bar().baz()`? This is called ‘method chaining’, and we
|
||||
can do it by returning `self`.
|
||||
|
||||
```
|
||||
# #![feature(core)]
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
|
@ -124,13 +124,13 @@ fn grow(&self) -> Circle {
|
|||
# Circle } }
|
||||
```
|
||||
|
||||
We just say we're returning a `Circle`. With this method, we can grow a new
|
||||
We just say we’re returning a `Circle`. With this method, we can grow a new
|
||||
circle to any arbitrary size.
|
||||
|
||||
## Static methods
|
||||
|
||||
You can also define methods that do not take a `self` parameter. Here's a
|
||||
pattern that's very common in Rust code:
|
||||
You can also define methods that do not take a `self` parameter. Here’s a
|
||||
pattern that’s very common in Rust code:
|
||||
|
||||
```
|
||||
struct Circle {
|
||||
|
@ -154,20 +154,19 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
This *static method* builds a new `Circle` for us. Note that static methods
|
||||
This ‘static method’ builds a new `Circle` for us. Note that static methods
|
||||
are called with the `Struct::method()` syntax, rather than the `ref.method()`
|
||||
syntax.
|
||||
|
||||
## Builder Pattern
|
||||
|
||||
Let's say that we want our users to be able to create Circles, but we will
|
||||
Let’s say that we want our users to be able to create Circles, but we will
|
||||
allow them to only set the properties they care about. Otherwise, the `x`
|
||||
and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn't
|
||||
and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn’t
|
||||
have method overloading, named arguments, or variable arguments. We employ
|
||||
the builder pattern instead. It looks like this:
|
||||
|
||||
```
|
||||
# #![feature(core)]
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
|
@ -224,9 +223,9 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
What we've done here is make another struct, `CircleBuilder`. We've defined our
|
||||
builder methods on it. We've also defined our `area()` method on `Circle`. We
|
||||
What we’ve done here is make another struct, `CircleBuilder`. We’ve defined our
|
||||
builder methods on it. We’ve also defined our `area()` method on `Circle`. We
|
||||
also made one more method on `CircleBuilder`: `finalize()`. This method creates
|
||||
our final `Circle` from the builder. Now, we've used the type system to enforce
|
||||
our final `Circle` from the builder. Now, we’ve used the type system to enforce
|
||||
our concerns: we can use the methods on `CircleBuilder` to constrain making
|
||||
`Circle`s in any way we choose.
|
||||
|
|
|
@ -21,6 +21,8 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `one`.
|
||||
|
||||
# Multiple patterns
|
||||
|
||||
You can match multiple patterns with `|`:
|
||||
|
@ -35,6 +37,8 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `one or two`.
|
||||
|
||||
# Ranges
|
||||
|
||||
You can match a range of values with `...`:
|
||||
|
@ -48,7 +52,21 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
Ranges are mostly used with integers and single characters.
|
||||
This prints `one through five`.
|
||||
|
||||
Ranges are mostly used with integers and `char`s:
|
||||
|
||||
```rust
|
||||
let x = '💅';
|
||||
|
||||
match x {
|
||||
'a' ... 'j' => println!("early letter"),
|
||||
'k' ... 'z' => println!("late letter"),
|
||||
_ => println!("something else"),
|
||||
}
|
||||
```
|
||||
|
||||
This prints `something else`
|
||||
|
||||
# Bindings
|
||||
|
||||
|
@ -64,6 +82,8 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `got a range element 1`.
|
||||
|
||||
# Ignoring variants
|
||||
|
||||
If you’re matching on an enum which has variants, you can use `..` to
|
||||
|
@ -83,6 +103,8 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `Got an int!`.
|
||||
|
||||
# Guards
|
||||
|
||||
You can introduce ‘match guards’ with `if`:
|
||||
|
@ -102,6 +124,8 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `Got an int!`
|
||||
|
||||
# ref and ref mut
|
||||
|
||||
If you want to get a [reference][ref], use the `ref` keyword:
|
||||
|
@ -114,6 +138,8 @@ match x {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `Got a reference to 5`.
|
||||
|
||||
[ref]: references-and-borrowing.html
|
||||
|
||||
Here, the `r` inside the `match` has the type `&i32`. In other words, the `ref`
|
||||
|
@ -130,7 +156,7 @@ match x {
|
|||
|
||||
# Destructuring
|
||||
|
||||
If you have a compound data type, like a `struct`, you can destructure it
|
||||
If you have a compound data type, like a [`struct`][struct], you can destructure it
|
||||
inside of a pattern:
|
||||
|
||||
```rust
|
||||
|
@ -146,6 +172,8 @@ match origin {
|
|||
}
|
||||
```
|
||||
|
||||
[struct]: structs.html
|
||||
|
||||
If we only care about some of the values, we don’t have to give them all names:
|
||||
|
||||
```rust
|
||||
|
@ -161,6 +189,8 @@ match origin {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `x is 0`.
|
||||
|
||||
You can do this kind of match on any member, not just the first:
|
||||
|
||||
```rust
|
||||
|
@ -176,6 +206,8 @@ match origin {
|
|||
}
|
||||
```
|
||||
|
||||
This prints `y is 0`.
|
||||
|
||||
This ‘destructuring’ behavior works on any compound data type, like
|
||||
[tuples][tuples] or [enums][enums].
|
||||
|
||||
|
@ -187,10 +219,10 @@ This ‘destructuring’ behavior works on any compound data type, like
|
|||
Whew! That’s a lot of different ways to match things, and they can all be
|
||||
mixed and matched, depending on what you’re doing:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
match x {
|
||||
Foo { x: Some(ref name), y: None } => ...
|
||||
}
|
||||
```
|
||||
|
||||
Patterns are very powerful. Make good use of them.
|
||||
Patterns are very powerful. Make good use of them.
|
||||
|
|
|
@ -1,36 +1,34 @@
|
|||
% Strings
|
||||
|
||||
Strings are an important concept for any programmer to master. Rust's string
|
||||
Strings are an important concept for any programmer to master. Rust’s string
|
||||
handling system is a bit different from other languages, due to its systems
|
||||
focus. Any time you have a data structure of variable size, things can get
|
||||
tricky, and strings are a re-sizable data structure. That being said, Rust's
|
||||
tricky, and strings are a re-sizable data structure. That being said, Rust’s
|
||||
strings also work differently than in some other systems languages, such as C.
|
||||
|
||||
Let's dig into the details. A *string* is a sequence of Unicode scalar values
|
||||
encoded as a stream of UTF-8 bytes. All strings are guaranteed to be
|
||||
validly encoded UTF-8 sequences. Additionally, strings are not null-terminated
|
||||
and can contain null bytes.
|
||||
Let’s dig into the details. A ‘string’ is a sequence of Unicode scalar values
|
||||
encoded as a stream of UTF-8 bytes. All strings are guaranteed to be a valid
|
||||
encoding of UTF-8 sequences. Additionally, unlike some systems languages,
|
||||
strings are not null-terminated and can contain null bytes.
|
||||
|
||||
Rust has two main types of strings: `&str` and `String`.
|
||||
Rust has two main types of strings: `&str` and `String`. Let’s talk about
|
||||
`&str` first. These are called ‘string slices’. String literals are of the type
|
||||
`&'static str`:
|
||||
|
||||
The first kind is a `&str`. These are called *string slices*. String literals
|
||||
are of the type `&str`:
|
||||
|
||||
```{rust}
|
||||
let string = "Hello there."; // string: &str
|
||||
```rust
|
||||
let string = "Hello there."; // string: &'static str
|
||||
```
|
||||
|
||||
This string is statically allocated, meaning that it's saved inside our
|
||||
This string is statically allocated, meaning that it’s saved inside our
|
||||
compiled program, and exists for the entire duration it runs. The `string`
|
||||
binding is a reference to this statically allocated string. String slices
|
||||
have a fixed size, and cannot be mutated.
|
||||
|
||||
A `String`, on the other hand, is a heap-allocated string. This string
|
||||
is growable, and is also guaranteed to be UTF-8. `String`s are
|
||||
commonly created by converting from a string slice using the
|
||||
`to_string` method.
|
||||
A `String`, on the other hand, is a heap-allocated string. This string is
|
||||
growable, and is also guaranteed to be UTF-8. `String`s are commonly created by
|
||||
converting from a string slice using the `to_string` method.
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
let mut s = "Hello".to_string(); // mut s: String
|
||||
println!("{}", s);
|
||||
|
||||
|
@ -54,8 +52,78 @@ fn main() {
|
|||
Viewing a `String` as a `&str` is cheap, but converting the `&str` to a
|
||||
`String` involves allocating memory. No reason to do that unless you have to!
|
||||
|
||||
That's the basics of strings in Rust! They're probably a bit more complicated
|
||||
than you are used to, if you come from a scripting language, but when the
|
||||
low-level details matter, they really matter. Just remember that `String`s
|
||||
allocate memory and control their data, while `&str`s are a reference to
|
||||
another string, and you'll be all set.
|
||||
## Indexing
|
||||
|
||||
Because strings are valid UTF-8, strings do not support indexing:
|
||||
|
||||
```rust,ignore
|
||||
let s = "hello";
|
||||
|
||||
println!("The first letter of s is {}", s[0]); // ERROR!!!
|
||||
```
|
||||
|
||||
Usually, access to a vector with `[]` is very fast. But, because each character
|
||||
in a UTF-8 encoded string can be multiple bytes, you have to walk over the
|
||||
string to find the nᵗʰ letter of a string. This is a significantly more
|
||||
expensive operation, and we don’t want to be misleading. Furthermore, ‘letter’
|
||||
isn’t something defined in Unicode, exactly. We can choose to look at a string as
|
||||
individual bytes, or as codepoints:
|
||||
|
||||
```rust
|
||||
let hachiko = "忠犬ハチ公";
|
||||
|
||||
for b in hachiko.as_bytes() {
|
||||
print!("{}, ", b);
|
||||
}
|
||||
|
||||
println!("");
|
||||
|
||||
for c in hachiko.chars() {
|
||||
print!("{}, ", c);
|
||||
}
|
||||
|
||||
println!("");
|
||||
```
|
||||
|
||||
This prints:
|
||||
|
||||
```text
|
||||
229, 191, 160, 231, 138, 172, 227, 131, 143, 227, 131, 129, 229, 133, 172,
|
||||
忠, 犬, ハ, チ, 公,
|
||||
```
|
||||
|
||||
As you can see, there are more bytes than `char`s.
|
||||
|
||||
You can get something similar to an index like this:
|
||||
|
||||
```rust
|
||||
# let hachiko = "忠犬ハチ公";
|
||||
let dog = hachiko.chars().nth(1); // kinda like hachiko[1]
|
||||
```
|
||||
|
||||
This emphasizes that we have to go through the whole list of `chars`.
|
||||
|
||||
## Concatenation
|
||||
|
||||
If you have a `String`, you can concatenate a `&str` to the end of it:
|
||||
|
||||
```rust
|
||||
let hello = "Hello ".to_string();
|
||||
let world = "world!";
|
||||
|
||||
let hello_world = hello + world;
|
||||
```
|
||||
|
||||
But if you have two `String`s, you need an `&`:
|
||||
|
||||
```rust
|
||||
let hello = "Hello ".to_string();
|
||||
let world = "world!".to_string();
|
||||
|
||||
let hello_world = hello + &world;
|
||||
```
|
||||
|
||||
This is because `&String` can automatically coerece to a `&str`. This is a
|
||||
feature called ‘[`Deref` coercions][dc]’.
|
||||
|
||||
[dc]: deref-coercions.html
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
% Structs
|
||||
|
||||
Structs are a way of creating more complex datatypes. For example, if we were
|
||||
Structs are a way of creating more complex data types. For example, if we were
|
||||
doing calculations involving coordinates in 2D space, we would need both an `x`
|
||||
and a `y` value:
|
||||
|
||||
|
@ -24,12 +24,12 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
There’s a lot going on here, so let’s break it down. We declare a struct with
|
||||
the `struct` keyword, and then with a name. By convention, structs begin with a
|
||||
capital letter and are also camel cased: `PointInSpace`, not `Point_In_Space`.
|
||||
There’s a lot going on here, so let’s break it down. We declare a `struct` with
|
||||
the `struct` keyword, and then with a name. By convention, `struct`s begin with
|
||||
a capital letter and are camel cased: `PointInSpace`, not `Point_In_Space`.
|
||||
|
||||
We can create an instance of our struct via `let`, as usual, but we use a `key:
|
||||
value` style syntax to set each field. The order doesn't need to be the same as
|
||||
value` style syntax to set each field. The order doesn’t need to be the same as
|
||||
in the original declaration.
|
||||
|
||||
Finally, because fields have names, we can access the field through dot
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
% Traits
|
||||
|
||||
Do you remember the `impl` keyword, used to call a function with method
|
||||
syntax?
|
||||
Do you remember the `impl` keyword, used to call a function with [method
|
||||
syntax][methodsyntax]?
|
||||
|
||||
```{rust}
|
||||
# #![feature(core)]
|
||||
```rust
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
|
@ -18,11 +17,12 @@ impl Circle {
|
|||
}
|
||||
```
|
||||
|
||||
[methodsyntax]: method-syntax.html
|
||||
|
||||
Traits are similar, except that we define a trait with just the method
|
||||
signature, then implement the trait for that struct. Like this:
|
||||
|
||||
```{rust}
|
||||
# #![feature(core)]
|
||||
```rust
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
|
@ -41,20 +41,13 @@ impl HasArea for Circle {
|
|||
```
|
||||
|
||||
As you can see, the `trait` block looks very similar to the `impl` block,
|
||||
but we don't define a body, just a type signature. When we `impl` a trait,
|
||||
but we don’t define a body, just a type signature. When we `impl` a trait,
|
||||
we use `impl Trait for Item`, rather than just `impl Item`.
|
||||
|
||||
So what's the big deal? Remember the error we were getting with our generic
|
||||
`inverse` function?
|
||||
|
||||
```text
|
||||
error: binary operation `==` cannot be applied to type `T`
|
||||
```
|
||||
|
||||
We can use traits to constrain our generics. Consider this function, which
|
||||
does not compile, and gives us a similar error:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
fn print_area<T>(shape: T) {
|
||||
println!("This shape has an area of {}", shape.area());
|
||||
}
|
||||
|
@ -66,11 +59,11 @@ Rust complains:
|
|||
error: type `T` does not implement any method in scope named `area`
|
||||
```
|
||||
|
||||
Because `T` can be any type, we can't be sure that it implements the `area`
|
||||
method. But we can add a *trait constraint* to our generic `T`, ensuring
|
||||
Because `T` can be any type, we can’t be sure that it implements the `area`
|
||||
method. But we can add a ‘trait constraint’ to our generic `T`, ensuring
|
||||
that it does:
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
# trait HasArea {
|
||||
# fn area(&self) -> f64;
|
||||
# }
|
||||
|
@ -83,10 +76,9 @@ The syntax `<T: HasArea>` means `any type that implements the HasArea trait`.
|
|||
Because traits define function type signatures, we can be sure that any type
|
||||
which implements `HasArea` will have an `.area()` method.
|
||||
|
||||
Here's an extended example of how this works:
|
||||
Here’s an extended example of how this works:
|
||||
|
||||
```{rust}
|
||||
# #![feature(core)]
|
||||
```rust
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
@ -144,10 +136,10 @@ This shape has an area of 3.141593
|
|||
This shape has an area of 1
|
||||
```
|
||||
|
||||
As you can see, `print_area` is now generic, but also ensures that we
|
||||
have passed in the correct types. If we pass in an incorrect type:
|
||||
As you can see, `print_area` is now generic, but also ensures that we have
|
||||
passed in the correct types. If we pass in an incorrect type:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
print_area(5);
|
||||
```
|
||||
|
||||
|
@ -157,11 +149,11 @@ We get a compile-time error:
|
|||
error: failed to find an implementation of trait main::HasArea for int
|
||||
```
|
||||
|
||||
So far, we've only added trait implementations to structs, but you can
|
||||
implement a trait for any type. So technically, we _could_ implement
|
||||
`HasArea` for `i32`:
|
||||
So far, we’ve only added trait implementations to structs, but you can
|
||||
implement a trait for any type. So technically, we _could_ implement `HasArea`
|
||||
for `i32`:
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
@ -181,102 +173,57 @@ It is considered poor style to implement methods on such primitive types, even
|
|||
though it is possible.
|
||||
|
||||
This may seem like the Wild West, but there are two other restrictions around
|
||||
implementing traits that prevent this from getting out of hand. First, traits
|
||||
must be `use`d in any scope where you wish to use the trait's method. So for
|
||||
example, this does not work:
|
||||
implementing traits that prevent this from getting out of hand. The first is
|
||||
that if the trait isn’t defined in your scope, it doesn’t apply. Here’s an
|
||||
example: the standard library provides a [`Write`][write] trait which adds
|
||||
extra functionality to `File`s, for doing file I/O. By default, a `File`
|
||||
won’t have its methods:
|
||||
|
||||
```{rust,ignore}
|
||||
mod shapes {
|
||||
use std::f64::consts;
|
||||
[write]: ../std/io/trait.Write.html
|
||||
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
impl HasArea for Circle {
|
||||
fn area(&self) -> f64 {
|
||||
consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = shapes::Circle {
|
||||
x: 0.0f64,
|
||||
y: 0.0f64,
|
||||
radius: 1.0f64,
|
||||
};
|
||||
|
||||
println!("{}", c.area());
|
||||
}
|
||||
```rust,ignore
|
||||
let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
|
||||
let result = f.write("whatever".as_bytes());
|
||||
# result.unwrap(); // ignore the erorr
|
||||
```
|
||||
|
||||
Now that we've moved the structs and traits into their own module, we get an
|
||||
error:
|
||||
Here’s the error:
|
||||
|
||||
```text
|
||||
error: type `shapes::Circle` does not implement any method in scope named `area`
|
||||
error: type `std::fs::File` does not implement any method in scope named `write`
|
||||
|
||||
let result = f.write(b”whatever”);
|
||||
^~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
If we add a `use` line right above `main` and make the right things public,
|
||||
everything is fine:
|
||||
We need to `use` the `Write` trait first:
|
||||
|
||||
```{rust}
|
||||
# #![feature(core)]
|
||||
mod shapes {
|
||||
use std::f64::consts;
|
||||
```rust,ignore
|
||||
use std::io::Write;
|
||||
|
||||
pub trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
pub struct Circle {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub radius: f64,
|
||||
}
|
||||
|
||||
impl HasArea for Circle {
|
||||
fn area(&self) -> f64 {
|
||||
consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use shapes::HasArea;
|
||||
|
||||
fn main() {
|
||||
let c = shapes::Circle {
|
||||
x: 0.0f64,
|
||||
y: 0.0f64,
|
||||
radius: 1.0f64,
|
||||
};
|
||||
|
||||
println!("{}", c.area());
|
||||
}
|
||||
let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
|
||||
let result = f.write("whatever".as_bytes());
|
||||
# result.unwrap(); // ignore the erorr
|
||||
```
|
||||
|
||||
This will compile without error.
|
||||
|
||||
This means that even if someone does something bad like add methods to `int`,
|
||||
it won't affect you, unless you `use` that trait.
|
||||
it won’t affect you, unless you `use` that trait.
|
||||
|
||||
There's one more restriction on implementing traits. Either the trait or the
|
||||
type you're writing the `impl` for must be inside your crate. So, we could
|
||||
implement the `HasArea` type for `i32`, because `HasArea` is in our crate. But
|
||||
There’s one more restriction on implementing traits. Either the trait or the
|
||||
type you’re writing the `impl` for must be defined by you. So, we could
|
||||
implement the `HasArea` type for `i32`, because `HasArea` is in our code. But
|
||||
if we tried to implement `Float`, a trait provided by Rust, for `i32`, we could
|
||||
not, because both the trait and the type aren't in our crate.
|
||||
not, because neither the trait nor the type are in our code.
|
||||
|
||||
One last thing about traits: generic functions with a trait bound use
|
||||
*monomorphization* (*mono*: one, *morph*: form), so they are statically
|
||||
dispatched. What's that mean? Check out the chapter on [trait
|
||||
objects](trait-objects.html) for more.
|
||||
‘monomorphization’ (mono: one, morph: form), so they are statically dispatched.
|
||||
What’s that mean? Check out the chapter on [trait objects][to] for more details.
|
||||
|
||||
## Multiple trait bounds
|
||||
[to]: trait-objects.html
|
||||
|
||||
# Multiple trait bounds
|
||||
|
||||
You’ve seen that you can bound a generic type parameter with a trait:
|
||||
|
||||
|
@ -299,10 +246,10 @@ fn foo<T: Clone + Debug>(x: T) {
|
|||
|
||||
`T` now needs to be both `Clone` as well as `Debug`.
|
||||
|
||||
## Where clause
|
||||
# Where clause
|
||||
|
||||
Writing functions with only a few generic types and a small number of trait
|
||||
bounds isn't too bad, but as the number increases, the syntax gets increasingly
|
||||
bounds isn’t too bad, but as the number increases, the syntax gets increasingly
|
||||
awkward:
|
||||
|
||||
```
|
||||
|
@ -318,7 +265,7 @@ fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
|
|||
The name of the function is on the far left, and the parameter list is on the
|
||||
far right. The bounds are getting in the way.
|
||||
|
||||
Rust has a solution, and it's called a '`where` clause':
|
||||
Rust has a solution, and it’s called a ‘`where` clause’:
|
||||
|
||||
```
|
||||
use std::fmt::Debug;
|
||||
|
@ -389,84 +336,9 @@ This shows off the additional feature of `where` clauses: they allow bounds
|
|||
where the left-hand side is an arbitrary type (`i32` in this case), not just a
|
||||
plain type parameter (like `T`).
|
||||
|
||||
## Our `inverse` Example
|
||||
# Default methods
|
||||
|
||||
Back in [Generics](generics.html), we were trying to write code like this:
|
||||
|
||||
```{rust,ignore}
|
||||
fn inverse<T>(x: T) -> Result<T, String> {
|
||||
if x == 0.0 { return Err("x cannot be zero!".to_string()); }
|
||||
|
||||
Ok(1.0 / x)
|
||||
}
|
||||
```
|
||||
|
||||
If we try to compile it, we get this error:
|
||||
|
||||
```text
|
||||
error: binary operation `==` cannot be applied to type `T`
|
||||
```
|
||||
|
||||
This is because `T` is too generic: we don't know if a random `T` can be
|
||||
compared. For that, we can use trait bounds. It doesn't quite work, but try
|
||||
this:
|
||||
|
||||
```{rust,ignore}
|
||||
fn inverse<T: PartialEq>(x: T) -> Result<T, String> {
|
||||
if x == 0.0 { return Err("x cannot be zero!".to_string()); }
|
||||
|
||||
Ok(1.0 / x)
|
||||
}
|
||||
```
|
||||
|
||||
You should get this error:
|
||||
|
||||
```text
|
||||
error: mismatched types:
|
||||
expected `T`,
|
||||
found `_`
|
||||
(expected type parameter,
|
||||
found floating-point variable)
|
||||
```
|
||||
|
||||
So this won't work. While our `T` is `PartialEq`, we expected to have another `T`,
|
||||
but instead, we found a floating-point variable. We need a different bound. `Float`
|
||||
to the rescue:
|
||||
|
||||
```
|
||||
# #![feature(std_misc)]
|
||||
use std::num::Float;
|
||||
|
||||
fn inverse<T: Float>(x: T) -> Result<T, String> {
|
||||
if x == Float::zero() { return Err("x cannot be zero!".to_string()) }
|
||||
|
||||
let one: T = Float::one();
|
||||
Ok(one / x)
|
||||
}
|
||||
```
|
||||
|
||||
We've had to replace our generic `0.0` and `1.0` with the appropriate methods
|
||||
from the `Float` trait. Both `f32` and `f64` implement `Float`, so our function
|
||||
works just fine:
|
||||
|
||||
```
|
||||
# #![feature(std_misc)]
|
||||
# use std::num::Float;
|
||||
# fn inverse<T: Float>(x: T) -> Result<T, String> {
|
||||
# if x == Float::zero() { return Err("x cannot be zero!".to_string()) }
|
||||
# let one: T = Float::one();
|
||||
# Ok(one / x)
|
||||
# }
|
||||
println!("the inverse of {} is {:?}", 2.0f32, inverse(2.0f32));
|
||||
println!("the inverse of {} is {:?}", 2.0f64, inverse(2.0f64));
|
||||
|
||||
println!("the inverse of {} is {:?}", 0.0f32, inverse(0.0f32));
|
||||
println!("the inverse of {} is {:?}", 0.0f64, inverse(0.0f64));
|
||||
```
|
||||
|
||||
## Default methods
|
||||
|
||||
There's one last feature of traits we should cover: default methods. It's
|
||||
There’s one last feature of traits we should cover: default methods. It’s
|
||||
easiest just to show an example:
|
||||
|
||||
```rust
|
||||
|
@ -477,8 +349,8 @@ trait Foo {
|
|||
}
|
||||
```
|
||||
|
||||
Implementors of the `Foo` trait need to implement `bar()`, but they don't
|
||||
need to implement `baz()`. They'll get this default behavior. They can
|
||||
Implementors of the `Foo` trait need to implement `bar()`, but they don’t
|
||||
need to implement `baz()`. They’ll get this default behavior. They can
|
||||
override the default if they so choose:
|
||||
|
||||
```rust
|
||||
|
@ -506,3 +378,43 @@ default.baz(); // prints "We called baz."
|
|||
let over = OverrideDefault;
|
||||
over.baz(); // prints "Override baz!"
|
||||
```
|
||||
|
||||
# Inheritance
|
||||
|
||||
Sometimes, implementing a trait requires implementing another trait:
|
||||
|
||||
```rust
|
||||
trait Foo {
|
||||
fn foo(&self);
|
||||
}
|
||||
|
||||
trait FooBar : Foo {
|
||||
fn foobar(&self);
|
||||
}
|
||||
```
|
||||
|
||||
Implementors of `FooBar` must also implement `Foo`, like this:
|
||||
|
||||
```rust
|
||||
# trait Foo {
|
||||
# fn foo(&self);
|
||||
# }
|
||||
# trait FooBar : Foo {
|
||||
# fn foobar(&self);
|
||||
# }
|
||||
struct Baz;
|
||||
|
||||
impl Foo for Baz {
|
||||
fn foo(&self) { println!("foo"); }
|
||||
}
|
||||
|
||||
impl FooBar for Baz {
|
||||
fn foobar(&self) { println!("foobar"); }
|
||||
}
|
||||
```
|
||||
|
||||
If we forget to implement `Foo`, Rust will tell us:
|
||||
|
||||
```text
|
||||
error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]
|
||||
```
|
||||
|
|
|
@ -1,37 +1,58 @@
|
|||
% Vectors
|
||||
|
||||
A *vector* is a dynamic or "growable" array, implemented as the standard
|
||||
library type [`Vec<T>`](../std/vec/) (Where `<T>` is a [Generic](./generics.md)
|
||||
statement). Vectors always allocate their data on the heap. Vectors are to
|
||||
[slices][slices] what [`String`][string] is to `&str`. You can
|
||||
create them with the `vec!` macro:
|
||||
A ‘vector’ is a dynamic or ‘growable’ array, implemented as the standard
|
||||
library type [`Vec<T>`][vec]. That `<T>` is a [generic][generic], meaning we
|
||||
can have vectors of any type. Vectors always allocate their data on the heap.
|
||||
You can create them with the `vec!` macro:
|
||||
|
||||
```{rust}
|
||||
let v = vec![1, 2, 3]; // v: Vec<i32>
|
||||
```rust
|
||||
let v = vec![1, 2, 3, 4, 5]; // v: Vec<i32>
|
||||
```
|
||||
|
||||
[slices]: primitive-types.html#slices
|
||||
[string]: strings.html
|
||||
|
||||
(Notice that unlike the `println!` macro we've used in the past, we use square
|
||||
brackets `[]` with `vec!`. Rust allows you to use either in either situation,
|
||||
(Notice that unlike the `println!` macro we’ve used in the past, we use square
|
||||
brackets `[]` with `vec!` macro. Rust allows you to use either in either situation,
|
||||
this is just convention.)
|
||||
|
||||
There's an alternate form of `vec!` for repeating an initial value:
|
||||
There’s an alternate form of `vec!` for repeating an initial value:
|
||||
|
||||
```
|
||||
let v = vec![0; 10]; // ten zeroes
|
||||
```
|
||||
|
||||
You can get the length of, iterate over, and subscript vectors just like
|
||||
arrays. In addition, (mutable) vectors can grow automatically:
|
||||
## Accessing elements
|
||||
|
||||
```{rust}
|
||||
let mut nums = vec![1, 2, 3]; // mut nums: Vec<i32>
|
||||
To get the value at a particular index in the vector, we use `[]`s:
|
||||
|
||||
nums.push(4);
|
||||
```rust
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
println!("The length of nums is now {}", nums.len()); // Prints 4
|
||||
println!("The third element of v is {}", v[2]);
|
||||
```
|
||||
|
||||
Vectors have many more useful methods.
|
||||
The indices count from `0`, so the third element is `v[2]`.
|
||||
|
||||
## Iterating
|
||||
|
||||
Once you have a vector, you can iterate through its elements with `for`. There
|
||||
are three versions:
|
||||
|
||||
```rust
|
||||
let mut v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
for i in &v {
|
||||
println!("A reference to {}", i);
|
||||
}
|
||||
|
||||
for i in &mut v {
|
||||
println!("A mutable reference to {}", i);
|
||||
}
|
||||
|
||||
for i in v {
|
||||
println!("Take ownership of the vector and its element {}", i);
|
||||
}
|
||||
```
|
||||
|
||||
Vectors have many more useful methods, which you can read about in [their
|
||||
API documentation][vec].
|
||||
|
||||
[vec]: ../std/vec/index.html
|
||||
|
|
Loading…
Add table
Reference in a new issue