mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-16 17:03:35 +00:00
Auto merge of #27544 - steveklabnik:rollup, r=steveklabnik
- Successful merges: #27285, #27524, #27533, #27535, #27538, #27539 - Failed merges:
This commit is contained in:
commit
a0eb7a2c6d
@ -316,6 +316,35 @@ assert_eq!(3, answer);
|
||||
Now we take a trait object, a `&Fn`. And we have to make a reference
|
||||
to our closure when we pass it to `call_with_one`, so we use `&||`.
|
||||
|
||||
# Function pointers and closures
|
||||
|
||||
A function pointer is kind of like a closure that has no environment. As such,
|
||||
you can pass a function pointer to any function expecting a closure argument,
|
||||
and it will work:
|
||||
|
||||
```rust
|
||||
fn call_with_one(some_closure: &Fn(i32) -> i32) -> i32 {
|
||||
some_closure(1)
|
||||
}
|
||||
|
||||
fn add_one(i: i32) -> i32 {
|
||||
i + 1
|
||||
}
|
||||
|
||||
let f = add_one;
|
||||
|
||||
let answer = call_with_one(&f);
|
||||
|
||||
assert_eq!(2, answer);
|
||||
```
|
||||
|
||||
In this example, we don’t strictly need the intermediate variable `f`,
|
||||
the name of the function works just fine too:
|
||||
|
||||
```ignore
|
||||
let answer = call_with_one(&add_one);
|
||||
```
|
||||
|
||||
# Returning closures
|
||||
|
||||
It’s very common for functional-style code to return closures in various
|
||||
|
@ -227,3 +227,34 @@ as any type:
|
||||
let x: i32 = diverges();
|
||||
let x: String = diverges();
|
||||
```
|
||||
|
||||
## Function pointers
|
||||
|
||||
We can also create variable bindings which point to functions:
|
||||
|
||||
```rust
|
||||
let f: fn(i32) -> i32;
|
||||
```
|
||||
|
||||
`f` is a variable binding which points to a function that takes an `i32` as
|
||||
an argument and returns an `i32`. For example:
|
||||
|
||||
```rust
|
||||
fn plus_one(i: i32) -> i32 {
|
||||
i + 1
|
||||
}
|
||||
|
||||
// without type inference
|
||||
let f: fn(i32) -> i32 = plus_one;
|
||||
|
||||
// with type inference
|
||||
let f = plus_one;
|
||||
```
|
||||
|
||||
We can then use `f` to call the function:
|
||||
|
||||
```rust
|
||||
# fn plus_one(i: i32) -> i32 { i + 1 }
|
||||
# let f = plus_one;
|
||||
let six = f(5);
|
||||
```
|
||||
|
@ -6,7 +6,7 @@ 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, let’s check out some generic code. Rust’s
|
||||
Anyway, enough type theory, let’s check out some generic code. Rust’s
|
||||
standard library provides a type, `Option<T>`, that’s generic:
|
||||
|
||||
```rust
|
||||
@ -27,7 +27,7 @@ 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`.
|
||||
the right-hand side of the binding, we 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:
|
||||
|
||||
@ -101,11 +101,6 @@ fn takes_two_things<T, U>(x: T, y: U) {
|
||||
}
|
||||
```
|
||||
|
||||
Generic functions are most useful with ‘trait bounds’, which we’ll cover in the
|
||||
[section on traits][traits].
|
||||
|
||||
[traits]: traits.html
|
||||
|
||||
## Generic structs
|
||||
|
||||
You can store a generic type in a `struct` as well:
|
||||
@ -122,3 +117,28 @@ let float_origin = Point { x: 0.0, y: 0.0 };
|
||||
|
||||
Similarly to functions, the `<T>` is where we declare the generic parameters,
|
||||
and we then use `x: T` in the type declaration, too.
|
||||
|
||||
When you want to add an implementation for the generic struct, you just
|
||||
declare the type parameter after the `impl`:
|
||||
|
||||
```rust
|
||||
# struct Point<T> {
|
||||
# x: T,
|
||||
# y: T,
|
||||
# }
|
||||
#
|
||||
impl<T> Point<T> {
|
||||
fn swap(&mut self) {
|
||||
std::mem::swap(&mut self.x, &mut self.y);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
So far you’ve seen generics that take absolutely any type. These are useful in
|
||||
many cases: you’ve already seen `Option<T>`, and later you’ll meet universal
|
||||
container types like [`Vec<T>`][Vec]. On the other hand, often you want to
|
||||
trade that flexibility for increased expressive power. Read about [trait
|
||||
bounds][traits] to see why and how.
|
||||
|
||||
[traits]: traits.html
|
||||
[Vec]: ../std/vec/struct.Vec.html
|
||||
|
@ -533,7 +533,7 @@ Great! Next up: let’s compare our guess to the secret guess.
|
||||
# Comparing guesses
|
||||
|
||||
Now that we’ve got user input, let’s compare our guess to the random guess.
|
||||
Here’s our next step, though it doesn’t quite work yet:
|
||||
Here’s our next step, though it doesn’t quite compile yet:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
@ -617,7 +617,7 @@ match guess.cmp(&secret_number) {
|
||||
If it’s `Less`, we print `Too small!`, if it’s `Greater`, `Too big!`, and if
|
||||
`Equal`, `You win!`. `match` is really useful, and is used often in Rust.
|
||||
|
||||
I did mention that this won’t quite work yet, though. Let’s try it:
|
||||
I did mention that this won’t quite compile yet, though. Let’s try it:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
|
@ -77,8 +77,18 @@ Before we get to that, though, let’s break the explicit example down:
|
||||
fn bar<'a>(...)
|
||||
```
|
||||
|
||||
This part declares our lifetimes. This says that `bar` has one lifetime, `'a`.
|
||||
If we had two reference parameters, it would look like this:
|
||||
We previously talked a little about [function syntax][functions], but we didn’t
|
||||
discuss the `<>`s after a function’s name. A function can have ‘generic
|
||||
parameters’ between the `<>`s, of which lifetimes are one kind. We’ll discuss
|
||||
other kinds of generics [later in the book][generics], but for now, let’s
|
||||
just focus on the lifteimes aspect.
|
||||
|
||||
[functions]: functions.html
|
||||
[generics]: generics.html
|
||||
|
||||
We use `<>` to declare our lifetimes. This says that `bar` has one lifetime,
|
||||
`'a`. If we had two reference parameters, it would look like this:
|
||||
|
||||
|
||||
```rust,ignore
|
||||
fn bar<'a, 'b>(...)
|
||||
|
@ -81,3 +81,55 @@ will let you do this:
|
||||
let p: Point = // ...
|
||||
let x: f64 = p + 2i32;
|
||||
```
|
||||
|
||||
# Using operator traits in generic structs
|
||||
|
||||
Now that we know how operator traits are defined, we can define our `HasArea`
|
||||
trait and `Square` struct from the [traits chapter][traits] more generically:
|
||||
|
||||
[traits]: traits.html
|
||||
|
||||
```rust
|
||||
use std::ops::Mul;
|
||||
|
||||
trait HasArea<T> {
|
||||
fn area(&self) -> T;
|
||||
}
|
||||
|
||||
struct Square<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
side: T,
|
||||
}
|
||||
|
||||
impl<T> HasArea<T> for Square<T>
|
||||
where T: Mul<Output=T> + Copy {
|
||||
fn area(&self) -> T {
|
||||
self.side * self.side
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = Square {
|
||||
x: 0.0f64,
|
||||
y: 0.0f64,
|
||||
side: 12.0f64,
|
||||
};
|
||||
|
||||
println!("Area of s: {}", s.area());
|
||||
}
|
||||
```
|
||||
|
||||
For `HasArea` and `Square`, we just declare a type parameter `T` and replace
|
||||
`f64` with it. The `impl` needs more involved modifications:
|
||||
|
||||
```ignore
|
||||
impl<T> HasArea<T> for Square<T>
|
||||
where T: Mul<Output=T> + Copy { ... }
|
||||
```
|
||||
|
||||
The `area` method requires that we can multiply the sides, so we declare that
|
||||
type `T` must implement `std::ops::Mul`. Like `Add`, mentioned above, `Mul`
|
||||
itself takes an `Output` parameter: since we know that numbers don't change
|
||||
type when multiplied, we also set it to `T`. `T` must also support copying, so
|
||||
Rust doesn't try to move `self.side` into the return value.
|
||||
|
@ -47,8 +47,11 @@ 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,
|
||||
we use `impl Trait for Item`, rather than just `impl Item`.
|
||||
|
||||
We can use traits to constrain our generics. Consider this function, which
|
||||
does not compile:
|
||||
## Traits bounds for generic functions
|
||||
|
||||
Traits are useful because they allow a type to make certain promises about its
|
||||
behavior. Generic functions can exploit this to constrain the types they
|
||||
accept. Consider this function, which does not compile:
|
||||
|
||||
```rust,ignore
|
||||
fn print_area<T>(shape: T) {
|
||||
@ -75,7 +78,7 @@ fn print_area<T: HasArea>(shape: T) {
|
||||
}
|
||||
```
|
||||
|
||||
The syntax `<T: HasArea>` means `any type that implements the HasArea trait`.
|
||||
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.
|
||||
|
||||
@ -152,6 +155,63 @@ We get a compile-time error:
|
||||
error: the trait `HasArea` is not implemented for the type `_` [E0277]
|
||||
```
|
||||
|
||||
## Traits bounds for generic structs
|
||||
|
||||
Your generic structs can also benefit from trait constraints. All you need to
|
||||
do is append the constraint when you declare type parameters. Here is a new
|
||||
type `Rectangle<T>` and its operation `is_square()`:
|
||||
|
||||
```rust
|
||||
struct Rectangle<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
width: T,
|
||||
height: T,
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Rectangle<T> {
|
||||
fn is_square(&self) -> bool {
|
||||
self.width == self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut r = Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 47,
|
||||
height: 47,
|
||||
};
|
||||
|
||||
assert!(r.is_square());
|
||||
|
||||
r.height = 42;
|
||||
assert!(!r.is_square());
|
||||
}
|
||||
```
|
||||
|
||||
`is_square()` needs to check that the sides are equal, so the sides must be of
|
||||
a type that implements the [`core::cmp::PartialEq`][PartialEq] trait:
|
||||
|
||||
```ignore
|
||||
impl<T: PartialEq> Rectangle<T> { ... }
|
||||
```
|
||||
|
||||
Now, a rectangle can be defined in terms of any type that can be compared for
|
||||
equality.
|
||||
|
||||
[PartialEq]: ../core/cmp/trait.PartialEq.html
|
||||
|
||||
Here we defined a new struct `Rectangle` that accepts numbers of any
|
||||
precision—really, objects of pretty much any type—as long as they can be
|
||||
compared for equality. Could we do the same for our `HasArea` structs, `Square`
|
||||
and `Circle`? Yes, but they need multiplication, and to work with that we need
|
||||
to know more about [operator traits][operators-and-overloading].
|
||||
|
||||
[operators-and-overloading]: operators-and-overloading.html
|
||||
|
||||
# Rules for implementing traits
|
||||
|
||||
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`:
|
||||
@ -175,7 +235,7 @@ impl HasArea for i32 {
|
||||
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
|
||||
This may seem like the Wild West, but there are two restrictions around
|
||||
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
|
||||
@ -340,10 +400,10 @@ 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`).
|
||||
|
||||
## Default methods
|
||||
# Default methods
|
||||
|
||||
There’s one last feature of traits we should cover: default methods. It’s
|
||||
easiest just to show an example:
|
||||
If you already know how a typical implementor will define a method, you can
|
||||
let your trait supply a default:
|
||||
|
||||
```rust
|
||||
trait Foo {
|
||||
|
@ -38,6 +38,9 @@ use self::Cow::*;
|
||||
/// type can be borrowed as multiple different types. In particular, `Vec<T>:
|
||||
/// Borrow<Vec<T>>` and `Vec<T>: Borrow<[T]>`.
|
||||
///
|
||||
/// If you are implementing `Borrow` and both `Self` and `Borrowed` implement
|
||||
/// `Hash`, `Eq`, and/or `Ord`, they must produce the same result.
|
||||
///
|
||||
/// `Borrow` is very similar to, but different than, `AsRef`. See
|
||||
/// [the book][book] for more.
|
||||
///
|
||||
|
@ -273,7 +273,11 @@ macro_rules! impls{
|
||||
/// even though it does not. This allows you to inform the compiler about certain safety properties
|
||||
/// of your code.
|
||||
///
|
||||
/// Though they both have scary names, `PhantomData<T>` and "phantom types" are unrelated. 👻👻👻
|
||||
/// # A ghastly note 👻👻👻
|
||||
///
|
||||
/// Though they both have scary names, `PhantomData<T>` and 'phantom types' are related, but not
|
||||
/// identical. Phantom types are a more general concept that don't require `PhantomData<T>` to
|
||||
/// implement, but `PhantomData<T>` is the most common way to implement them in a correct manner.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user