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:
Steve Klabnik 2015-04-20 21:15:52 -04:00
commit ff12b7b308
10 changed files with 405 additions and 504 deletions

View file

@ -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)

View file

@ -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: Theyre 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 theyre scoped to the `enum` name,
```{rust}
use std::cmp::Ordering;
Not supporting these operations may seem rather limiting, but its a limitation
which we can overcome. There are two ways: by implementing equality ourselves,
or by pattern matching variants with [`match`][match] expressions, which youll
learn in the next section. We dont know enough about Rust to implement
equality yet, but well 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

View file

@ -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, lets check out some generic code. Rusts
standard library provides a type, `Option<T>`, thats 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 youve 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. Heres 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 thats an `i32`, the two sides match, and Rust is happy. If they didnt
match, wed 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 doesnt mean we cant 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 dont have to only be generic over one type. Consider another type from Rusts standard library thats 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 youd 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 doesnt 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 didnt 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 well 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.

View file

@ -1,10 +1,8 @@
% Match
Often, a simple `if`/`else` isnt enough, because you have more than two
possible options. Also, `else` conditions can get incredibly complicated, so
whats the solution?
Rust has a keyword, `match`, that allows you to replace complicated `if`/`else`
Often, a simple [`if`][if]/`else` isnt 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 arms
expression will be evaluated. Its called `match` because of the term pattern
matching, which `match` is an implementation of. Theres 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 whats 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 whats 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, its a nice way of converting things.
Sometimes its a nice way of converting something from one type to another.

View file

@ -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 isnt the
order that the functions would get called in, thats inside-out: foo bar baz.
Wouldnt 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:
Heres 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:
Weve 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 its just a value on the stack,
`&self` if its a reference, and `&mut self` if its a mutable reference.
Because we took the `&self` parameter to `area`, we can use it just like any
other parameter. Because we know its 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. Heres 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 were 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. Heres a
pattern thats 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
Lets 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 doesnt
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 weve done here is make another struct, `CircleBuilder`. Weve defined our
builder methods on it. Weve 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, weve 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.

View file

@ -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 youre 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 dont 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! Thats a lot of different ways to match things, and they can all be
mixed and matched, depending on what youre 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.

View file

@ -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. Rusts 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, Rusts
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.
Lets 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`. Lets 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 its 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 dont want to be misleading. Furthermore, letter
isnt 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

View file

@ -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() {
}
```
Theres a lot going on here, so lets 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`.
Theres a lot going on here, so lets 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 doesnt need to be the same as
in the original declaration.
Finally, because fields have names, we can access the field through dot

View file

@ -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 dont 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 cant 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:
Heres 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, weve 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 isnt defined in your scope, it doesnt apply. Heres 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`
wont 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("Couldnt 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:
Heres 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("Couldnt 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 wont 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
Theres one more restriction on implementing traits. Either the trait or the
type youre 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.
Whats that mean? Check out the chapter on [trait objects][to] for more details.
## Multiple trait bounds
[to]: trait-objects.html
# Multiple trait bounds
Youve 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 isnt 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 its 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
Theres one last feature of traits we should cover: default methods. Its
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 dont
need to implement `baz()`. Theyll 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]
```

View file

@ -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 weve 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:
Theres 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