From 0e92165eafee26e0fb27f9a3756e8a68884d685d Mon Sep 17 00:00:00 2001 From: Leif Arne Storset Date: Sun, 19 Jul 2015 13:22:39 +0200 Subject: [PATCH 1/7] Show impl This includes a new example with Rectangle, instead of reusing HasArea, because fn area would require the Mul trait, and the added complexity of that would be better left for the Operators and Overloading chapter. Squashed at reviewer's request: Move teaser for trait bounds to bottom --- src/doc/trpl/generics.md | 30 +++++++++++++++++++---- src/doc/trpl/traits.md | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/doc/trpl/generics.md b/src/doc/trpl/generics.md index c28d7c71608bb..a8fdec805de65 100644 --- a/src/doc/trpl/generics.md +++ b/src/doc/trpl/generics.md @@ -101,11 +101,6 @@ fn takes_two_things(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 `` 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 { +# x: T, +# y: T, +# } +# +impl Point { + 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`, and later you’ll meet universal +container types like [`Vec`][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 diff --git a/src/doc/trpl/traits.md b/src/doc/trpl/traits.md index 687e2bbf00e78..e7542756d52e4 100644 --- a/src/doc/trpl/traits.md +++ b/src/doc/trpl/traits.md @@ -152,6 +152,57 @@ We get a compile-time error: error: the trait `HasArea` is not implemented for the type `_` [E0277] ``` +## Traits bounds for generic structs + +Trait constraints also can apply to implementations for generic structs. Just +append the constraint when you declare type parameters. Here is a new type +type `Rectangle` and its operation `is_square()`: + +```rust +struct Rectangle { + x: T, + y: T, + width: T, + height: T, +} + +impl Rectangle { + 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 Rectangle { ... } +``` + +Now, a rectangle can be defined in terms of any type that can be compared for +equality. + +[PartialEq]: ../core/cmp/trait.PartialEq.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`: From 556b0815d779a50a151c44032febf3ce253e621e Mon Sep 17 00:00:00 2001 From: Leif Arne Storset Date: Sun, 19 Jul 2015 16:23:40 +0200 Subject: [PATCH 2/7] Using operator traits in generic structs --- src/doc/trpl/operators-and-overloading.md | 52 +++++++++++++++++++++++ src/doc/trpl/traits.md | 6 +++ 2 files changed, 58 insertions(+) diff --git a/src/doc/trpl/operators-and-overloading.md b/src/doc/trpl/operators-and-overloading.md index 6a594659c37d2..e53664eeb5526 100644 --- a/src/doc/trpl/operators-and-overloading.md +++ b/src/doc/trpl/operators-and-overloading.md @@ -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 { + fn area(&self) -> T; +} + +struct Square { + x: T, + y: T, + side: T, +} + +impl HasArea for Square + where T: Mul + 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 HasArea for Square + where T: Mul + 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. diff --git a/src/doc/trpl/traits.md b/src/doc/trpl/traits.md index e7542756d52e4..0f2efd4657bf7 100644 --- a/src/doc/trpl/traits.md +++ b/src/doc/trpl/traits.md @@ -199,7 +199,13 @@ 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 From 427736931bb1cd34c97e4e1f79a5a84430415207 Mon Sep 17 00:00:00 2001 From: Leif Arne Storset Date: Sat, 25 Jul 2015 13:52:14 +0200 Subject: [PATCH 3/7] Copyedit generics.md and traits.md Squashed at reviewer's request: Add heading at the end of the introductory material Spice up introductory paragraphs a bit Use quotes instead of for phrase Remove "other" in "other restrictions" (it's not obvious that any other restrictions have been mentioned) "Default methods" is a second-level heading, but is not a subsection of "Where clause" Reword "Default methods" introduction: it's not the "last feature" on this page --- src/doc/trpl/generics.md | 4 ++-- src/doc/trpl/traits.md | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/doc/trpl/generics.md b/src/doc/trpl/generics.md index a8fdec805de65..59dc8e9ed942e 100644 --- a/src/doc/trpl/generics.md +++ b/src/doc/trpl/generics.md @@ -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`, that’s generic: ```rust @@ -27,7 +27,7 @@ let x: Option = Some(5); In the type declaration, we say `Option`. Note how similar this looks to `Option`. 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: diff --git a/src/doc/trpl/traits.md b/src/doc/trpl/traits.md index 0f2efd4657bf7..88062bc08a671 100644 --- a/src/doc/trpl/traits.md +++ b/src/doc/trpl/traits.md @@ -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(shape: T) { @@ -75,7 +78,7 @@ fn print_area(shape: T) { } ``` -The syntax `` means `any type that implements the HasArea trait`. +The syntax `` 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. @@ -154,8 +157,8 @@ error: the trait `HasArea` is not implemented for the type `_` [E0277] ## Traits bounds for generic structs -Trait constraints also can apply to implementations for generic structs. Just -append the constraint when you declare type parameters. Here is a new type +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` and its operation `is_square()`: ```rust @@ -232,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 @@ -397,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 { From db1f17a64b646ecd4e593177f1d62d5c68cf0d6b Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 5 Aug 2015 12:30:00 -0400 Subject: [PATCH 4/7] work -> compile in Guessing Game 'work' can refer to the game itself, ie, 'this compiles but the game isn't finished,' so 'compile' is a more clear way to describe the problem. Thanks jhun on irc --- src/doc/trpl/guessing-game.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/trpl/guessing-game.md b/src/doc/trpl/guessing-game.md index 63a1c10f841ce..4a35022b03c91 100644 --- a/src/doc/trpl/guessing-game.md +++ b/src/doc/trpl/guessing-game.md @@ -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 From 4a68a7e1986c55855727c818ef272108f8fed32e Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 5 Aug 2015 12:16:37 -0400 Subject: [PATCH 5/7] Make note of Hash in Borrow's docs This should be a bit more prominent. Fixes #27109 --- src/libcollections/borrow.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libcollections/borrow.rs b/src/libcollections/borrow.rs index 1c75636cb052d..a7b4c17314bcb 100644 --- a/src/libcollections/borrow.rs +++ b/src/libcollections/borrow.rs @@ -38,6 +38,9 @@ use self::Cow::*; /// type can be borrowed as multiple different types. In particular, `Vec: /// Borrow>` and `Vec: 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. /// From 8f828a3a9d66a3ff2465861f1d8f12dd0f088d23 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 5 Aug 2015 13:23:22 -0400 Subject: [PATCH 6/7] Expand further on <> syntax in TRPL. We haven't discussed this syntax yet, so provide a basic explanation and link up to later chapters. Fixes #26917 --- src/doc/trpl/lifetimes.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/doc/trpl/lifetimes.md b/src/doc/trpl/lifetimes.md index 8e02367b921bc..bdb22fb4a6906 100644 --- a/src/doc/trpl/lifetimes.md +++ b/src/doc/trpl/lifetimes.md @@ -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>(...) From 340c25aebfee25534b8b95fa7ed7afd35e0b2e4b Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 4 Aug 2015 16:43:45 -0400 Subject: [PATCH 7/7] Clarify claims about PhantomData. This wording was too strong. Fixes #27523 --- src/libcore/marker.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index ebd6ba544e460..c0956753c988c 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -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` and "phantom types" are unrelated. 👻👻👻 +/// # A ghastly note 👻👻👻 +/// +/// Though they both have scary names, `PhantomData` and 'phantom types' are related, but not +/// identical. Phantom types are a more general concept that don't require `PhantomData` to +/// implement, but `PhantomData` is the most common way to implement them in a correct manner. /// /// # Examples ///