Skip to content

Commit 4070c57

Browse files
authored
Merge pull request #341 from Havvy/trait-decl-where-clauses
Improve supertraits documentation
2 parents e713055 + d513b23 commit 4070c57

File tree

2 files changed

+49
-44
lines changed

2 files changed

+49
-44
lines changed

src/items/traits.md

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ interface consists of [associated items], which come in three varieties:
99

1010
All traits define an implicit type parameter `Self` that refers to "the type
1111
that is implementing this interface". Traits may also contain additional type
12-
parameters. These type parameters (including `Self`) may be constrained by
13-
other traits and so forth as usual.
12+
parameters. These type parameters, including `Self`, may be constrained by
13+
other traits and so forth [as usual][generics].
1414

1515
Traits are implemented for specific types through separate [implementations].
1616

@@ -26,8 +26,7 @@ Generic items may use traits as [bounds] on their type parameters.
2626
## Generic Traits
2727

2828
Type parameters can be specified for a trait to make it generic. These appear
29-
after the trait name, using the same syntax used in [generic
30-
functions](items/functions.html#generic-functions).
29+
after the trait name, using the same syntax used in [generic functions].
3130

3231
```rust
3332
trait Seq<T> {
@@ -48,71 +47,72 @@ Object safe traits can be the base trait of a [trait object]. A trait is
4847
and
4948
* Be a [method] that does not use `Self` except in the type of the receiver.
5049
* It must not have any associated constants.
50+
* All supertraits must also be object safe.
5151

5252
## Supertraits
5353

54-
Trait bounds on `Self` are considered "supertraits". These are required to be
55-
acyclic. Supertraits are somewhat different from other constraints in that
56-
they affect what methods are available in the vtable when the trait is used as
57-
a [trait object]. Consider the following example:
54+
**Supertraits** are traits that are required to be implemented for a type to
55+
implement a specific trait. Furthermore, anywhere a [generic] or [trait object]
56+
is bounded by a trait, it has access to the associated items of its supertraits.
57+
58+
Supertraits are declared by trait bounds on the `Self` type of a trait and
59+
transitively the supertraits of the traits declared in those trait bounds. It is
60+
an error for a trait to be its own supertrait.
61+
62+
The trait with a supertrait is called a **subtrait** of its supertrait.
63+
64+
The following is an example of declaring `Shape` to be a supertrait of `Circle`.
5865

5966
```rust
6067
trait Shape { fn area(&self) -> f64; }
6168
trait Circle : Shape { fn radius(&self) -> f64; }
6269
```
6370

64-
The syntax `Circle : Shape` means that types that implement `Circle` must also
65-
have an implementation for `Shape`. Multiple supertraits are separated by `+`,
66-
`trait Circle : Shape + PartialEq { }`. In an implementation of `Circle` for a
67-
given type `T`, methods can refer to `Shape` methods, since the typechecker
68-
checks that any type with an implementation of `Circle` also has an
69-
implementation of `Shape`:
71+
And the following is the same example, except using [where clauses].
7072

7173
```rust
72-
struct Foo;
73-
7474
trait Shape { fn area(&self) -> f64; }
75-
trait Circle : Shape { fn radius(&self) -> f64; }
76-
impl Shape for Foo {
77-
fn area(&self) -> f64 {
78-
0.0
79-
}
80-
}
81-
impl Circle for Foo {
82-
fn radius(&self) -> f64 {
83-
println!("calling area: {}", self.area());
75+
trait Circle where Self: Shape { fn radius(&self) -> f64; }
76+
```
8477

85-
0.0
78+
This next example gives `radius` a default implementation using the `area`
79+
function from `Shape`.
80+
81+
```rust
82+
# trait Shape { fn area(&self) -> f64; }
83+
trait Circle where Self: Shape {
84+
fn radius(&self) -> f64 {
85+
// A = pi * r^2
86+
// so algebraically,
87+
// r = sqrt(A / pi)
88+
(self.area() /std::f64::consts::PI).sqrt()
8689
}
8790
}
88-
89-
let c = Foo;
90-
c.radius();
9191
```
9292

93-
In type-parameterized functions, methods of the supertrait may be called on
94-
values of subtrait-bound type parameters. Referring to the previous example of
95-
`trait Circle : Shape`:
93+
This next example calls a supertrait method on a generic parameter.
9694

9795
```rust
9896
# trait Shape { fn area(&self) -> f64; }
9997
# trait Circle : Shape { fn radius(&self) -> f64; }
100-
fn radius_times_area<T: Circle>(c: T) -> f64 {
101-
// `c` is both a Circle and a Shape
102-
c.radius() * c.area()
98+
fn print_area_and_radius<C: Circle>(c: C) {
99+
// Here we call the area method from the supertrait `Shape` of `Circle`.
100+
println!("Area: {}", c.area());
101+
println!("Radius: {}", c.radius());
103102
}
104103
```
105104

106-
Likewise, supertrait methods may also be called on trait objects.
105+
Similarly, here is an example of calling supertrait methods on trait objects.
107106

108107
```rust
109108
# trait Shape { fn area(&self) -> f64; }
110109
# trait Circle : Shape { fn radius(&self) -> f64; }
111-
# impl Shape for i32 { fn area(&self) -> f64 { 0.0 } }
112-
# impl Circle for i32 { fn radius(&self) -> f64 { 0.0 } }
113-
# let mycircle = 0i32;
114-
let mycircle = Box::new(mycircle) as Box<Circle>;
115-
let nonsense = mycircle.radius() * mycircle.area();
110+
# struct UnitCircle;
111+
# impl Shape for UnitCircle { fn area(&self) -> f64 { std::f64::consts::PI } }
112+
# impl Circle for UnitCircle { fn radius(&self) -> f64 { 1.0 } }
113+
# let circle = UnitCircle;
114+
let circle = Box::new(circle) as Box<dyn Circle>;
115+
let nonsense = circle.radius() * circle.area();
116116
```
117117

118118
[bounds]: trait-bounds.html
@@ -121,3 +121,7 @@ let nonsense = mycircle.radius() * mycircle.area();
121121
[RFC 255]: https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md
122122
[associated items]: items/associated-items.html
123123
[method]: items/associated-items.html#methods
124+
[implementations]: items/implementations.html
125+
[generics]: items/generics.html
126+
[where clauses]: items/generics.html#where-clauses
127+
[generic functions]: items/functions.html#generic-functions

src/types.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ A *trait object* is an opaque value of another type that implements a set of
545545
traits. The set of traits is made up of an [object safe] *base trait* plus any
546546
number of [auto traits].
547547

548-
Trait objects implement the base trait, its auto traits, and any super traits
548+
Trait objects implement the base trait, its auto traits, and any [supertraits]
549549
of the base trait.
550550

551551
Trait objects are written as the optional keyword `dyn` followed by a set of
@@ -597,8 +597,8 @@ behind some type of pointer; for example `&dyn SomeTrait` or
597597

598598
- a pointer to an instance of a type `T` that implements `SomeTrait`
599599
- a _virtual method table_, often just called a _vtable_, which contains, for
600-
each method of `SomeTrait` that `T` implements, a pointer to `T`'s
601-
implementation (i.e. a function pointer).
600+
each method of `SomeTrait` and its [supertraits] that `T` implements, a
601+
pointer to `T`'s implementation (i.e. a function pointer).
602602

603603
The purpose of trait objects is to permit "late binding" of methods. Calling a
604604
method on a trait object results in virtual dispatch at runtime: that is, a
@@ -712,3 +712,4 @@ impl Printable for String {
712712
[issue 33140]: https://github.com/rust-lang/rust/issues/33140
713713
[_PATH_]: paths.html
714714
[_LIFETIME_OR_LABEL_]: tokens.html#lifetimes-and-loop-labels
715+
[supertraits]: items/traits.html#supertraits

0 commit comments

Comments
 (0)