Skip to content

Improve supertraits documentation #341

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
May 26, 2018
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
86 changes: 45 additions & 41 deletions src/items/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ interface consists of [associated items], which come in three varieties:

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

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

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

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

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

## Supertraits

Trait bounds on `Self` are considered "supertraits". These are required to be
acyclic. Supertraits are somewhat different from other constraints in that
they affect what methods are available in the vtable when the trait is used as
a [trait object]. Consider the following example:
**Supertraits** are traits that are required to be implemented for a type to
implement a specific trait. Furthermore, anywhere a [generic] or [trait object]
is bounded by a trait, it has access to the associated items of its supertraits.

Supertraits are declared by trait bounds on the `Self` type of a trait and
transitively the supertraits of the traits declared in those trait bounds. It is
an error for a trait to be its own supertrait.

The trait with a supertrait is called a **subtrait** of its supertrait.

The following is an example of declaring `Shape` to be a supertrait of `Circle`.

```rust
trait Shape { fn area(&self) -> f64; }
trait Circle : Shape { fn radius(&self) -> f64; }
```

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

```rust
struct Foo;

trait Shape { fn area(&self) -> f64; }
trait Circle : Shape { fn radius(&self) -> f64; }
impl Shape for Foo {
fn area(&self) -> f64 {
0.0
}
}
impl Circle for Foo {
fn radius(&self) -> f64 {
println!("calling area: {}", self.area());
trait Circle where Self: Shape { fn radius(&self) -> f64; }
```

0.0
This next example gives `radius` a default implementation using the `area`
function from `Shape`.

```rust
# trait Shape { fn area(&self) -> f64; }
trait Circle where Self: Shape {
fn radius(&self) -> f64 {
// A = pi * r^2
// so algebraically,
// r = sqrt(A / pi)
(self.area() /std::f64::consts::PI).sqrt()
}
}

let c = Foo;
c.radius();
```

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

```rust
# trait Shape { fn area(&self) -> f64; }
# trait Circle : Shape { fn radius(&self) -> f64; }
fn radius_times_area<T: Circle>(c: T) -> f64 {
// `c` is both a Circle and a Shape
c.radius() * c.area()
fn print_area_and_radius<C: Circle>(c: C) {
// Here we call the area method from the supertrait `Shape` of `Circle`.
println!("Area: {}", c.area());
println!("Radius: {}", c.radius());
}
```

Likewise, supertrait methods may also be called on trait objects.
Similarly, here is an example of calling supertrait methods on trait objects.

```rust
# trait Shape { fn area(&self) -> f64; }
# trait Circle : Shape { fn radius(&self) -> f64; }
# impl Shape for i32 { fn area(&self) -> f64 { 0.0 } }
# impl Circle for i32 { fn radius(&self) -> f64 { 0.0 } }
# let mycircle = 0i32;
let mycircle = Box::new(mycircle) as Box<Circle>;
let nonsense = mycircle.radius() * mycircle.area();
# struct UnitCircle;
# impl Shape for UnitCircle { fn area(&self) -> f64 { std::f64::consts::PI } }
# impl Circle for UnitCircle { fn radius(&self) -> f64 { 1.0 } }
# let circle = UnitCircle;
let circle = Box::new(circle) as Box<dyn Circle>;
let nonsense = circle.radius() * circle.area();
```

[bounds]: trait-bounds.html
Expand All @@ -121,3 +121,7 @@ let nonsense = mycircle.radius() * mycircle.area();
[RFC 255]: https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md
[associated items]: items/associated-items.html
[method]: items/associated-items.html#methods
[implementations]: items/implementations.html
[generics]: items/generics.html
[where clauses]: items/generics.html#where-clauses
[generic functions]: items/functions.html#generic-functions
7 changes: 4 additions & 3 deletions src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ A *trait object* is an opaque value of another type that implements a set of
traits. The set of traits is made up of an [object safe] *base trait* plus any
number of [auto traits].

Trait objects implement the base trait, its auto traits, and any super traits
Trait objects implement the base trait, its auto traits, and any [supertraits]
of the base trait.

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

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

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