Skip to content

Show impl<T> and explain it for trait bounds and operators #27285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions src/doc/trpl/generics.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:

Expand Down Expand Up @@ -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:
Expand All @@ -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
52 changes: 52 additions & 0 deletions src/doc/trpl/operators-and-overloading.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
74 changes: 67 additions & 7 deletions src/doc/trpl/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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.

Expand Down Expand Up @@ -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`:
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down