diff --git a/src/items/traits.md b/src/items/traits.md index fd814091d..e667670ee 100644 --- a/src/items/traits.md +++ b/src/items/traits.md @@ -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]. @@ -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 { @@ -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(c: T) -> f64 { - // `c` is both a Circle and a Shape - c.radius() * c.area() +fn print_area_and_radius(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; -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; +let nonsense = circle.radius() * circle.area(); ``` [bounds]: trait-bounds.html @@ -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 \ No newline at end of file diff --git a/src/types.md b/src/types.md index 1ecf4a829..cf0a324d5 100644 --- a/src/types.md +++ b/src/types.md @@ -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 @@ -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 @@ -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 \ No newline at end of file