Skip to content

Commit cdcf28d

Browse files
committed
auto merge of #11270 : lkuper/rust/trait-docs, r=cmr
I've been wanting to explain default methods in the tutorial for ages. While I was at it I tried to clarify some other parts of the traits section. I'm sure this could be improved further; suggestions welcome.
2 parents 8adbb38 + 8224816 commit cdcf28d

File tree

1 file changed

+114
-43
lines changed

1 file changed

+114
-43
lines changed

doc/tutorial.md

Lines changed: 114 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,28 +2035,30 @@ C++ templates.
20352035
20362036
## Traits
20372037
2038-
Within a generic function the operations available on generic types
2039-
are very limited. After all, since the function doesn't know what
2040-
types it is operating on, it can't safely modify or query their
2041-
values. This is where _traits_ come into play. Traits are Rust's most
2042-
powerful tool for writing polymorphic code. Java developers will see
2043-
them as similar to Java interfaces, and Haskellers will notice their
2044-
similarities to type classes. Rust's traits are a form of *bounded
2045-
polymorphism*: a trait is a way of limiting the set of possible types
2046-
that a type parameter could refer to.
2047-
2048-
As motivation, let us consider copying in Rust.
2049-
The `clone` method is not defined for all Rust types.
2050-
One reason is user-defined destructors:
2051-
copying a type that has a destructor
2052-
could result in the destructor running multiple times.
2053-
Therefore, types with destructors cannot be copied
2054-
unless you explicitly implement `Clone` for them.
2038+
Within a generic function -- that is, a function parameterized by a
2039+
type parameter, say, `T` -- the operations we can do on arguments of
2040+
type `T` are quite limited. After all, since we don't know what type
2041+
`T` will be instantiated with, we can't safely modify or query values
2042+
of type `T`. This is where _traits_ come into play. Traits are Rust's
2043+
most powerful tool for writing polymorphic code. Java developers will
2044+
see them as similar to Java interfaces, and Haskellers will notice
2045+
their similarities to type classes. Rust's traits give us a way to
2046+
express *bounded polymorphism*: by limiting the set of possible types
2047+
that a type parameter could refer to, they expand the number of
2048+
operations we can safely perform on arguments of that type.
2049+
2050+
As motivation, let us consider copying of values in Rust. The `clone`
2051+
method is not defined for values of every type. One reason is
2052+
user-defined destructors: copying a value of a type that has a
2053+
destructor could result in the destructor running multiple times.
2054+
Therefore, values of types that have destructors cannot be copied
2055+
unless we explicitly implement `clone` for them.
20552056
20562057
This complicates handling of generic functions.
2057-
If you have a type parameter `T`, can you copy values of that type?
2058-
In Rust, you can't,
2059-
and if you try to run the following code the compiler will complain.
2058+
If we have a function with a type parameter `T`,
2059+
can we copy values of type `T` inside that function?
2060+
In Rust, we can't,
2061+
and if we try to run the following code the compiler will complain.
20602062
20612063
~~~~ {.xfail-test}
20622064
// This does not compile
@@ -2066,11 +2068,10 @@ fn head_bad<T>(v: &[T]) -> T {
20662068
~~~~
20672069
20682070
However, we can tell the compiler
2069-
that the `head` function is only for copyable types:
2070-
that is, those that implement the `Clone` trait.
2071-
In that case,
2072-
we can explicitly create a second copy of the value we are returning
2073-
using the `clone` keyword:
2071+
that the `head` function is only for copyable types.
2072+
In Rust, copyable types are those that _implement the `Clone` trait_.
2073+
We can then explicitly create a second copy of the value we are returning
2074+
by calling the `clone` method:
20742075
20752076
~~~~
20762077
// This does
@@ -2079,12 +2080,14 @@ fn head<T: Clone>(v: &[T]) -> T {
20792080
}
20802081
~~~~
20812082
2082-
This says that we can call `head` on any type `T`
2083-
as long as that type implements the `Clone` trait.
2083+
The bounded type parameter `T: Clone` says that `head`
2084+
can be called on an argument of type `&[T]` for any `T`,
2085+
so long as there is an implementation of the
2086+
`Clone` trait for `T`.
20842087
When instantiating a generic function,
2085-
you can only instantiate it with types
2088+
we can only instantiate it with types
20862089
that implement the correct trait,
2087-
so you could not apply `head` to a type
2090+
so we could not apply `head` to a vector whose elements are of some type
20882091
that does not implement `Clone`.
20892092
20902093
While most traits can be defined and implemented by user code,
@@ -2110,7 +2113,7 @@ have the `'static` lifetime.
21102113
> iterations of the language, and often still are.
21112114
21122115
Additionally, the `Drop` trait is used to define destructors. This
2113-
trait defines one method called `drop`, which is automatically
2116+
trait provides one method called `drop`, which is automatically
21142117
called when a value of the type that implements this trait is
21152118
destroyed, either because the value went out of scope or because the
21162119
garbage collector reclaimed it.
@@ -2134,29 +2137,36 @@ may call it.
21342137
21352138
## Declaring and implementing traits
21362139
2137-
A trait consists of a set of methods without bodies,
2138-
or may be empty, as is the case with `Send` and `Freeze`.
2140+
At its simplest, a trait is a set of zero or more _method signatures_.
21392141
For example, we could declare the trait
21402142
`Printable` for things that can be printed to the console,
2141-
with a single method:
2143+
with a single method signature:
21422144
21432145
~~~~
21442146
trait Printable {
21452147
fn print(&self);
21462148
}
21472149
~~~~
21482150
2149-
Traits may be implemented for specific types with [impls]. An impl
2150-
that implements a trait includes the name of the trait at the start of
2151-
the definition, as in the following impls of `Printable` for `int`
2152-
and `~str`.
2151+
We say that the `Printable` trait _provides_ a `print` method with the
2152+
given signature. This means that we can call `print` on an argument
2153+
of any type that implements the `Printable` trait.
2154+
2155+
Rust's built-in `Send` and `Freeze` types are examples of traits that
2156+
don't provide any methods.
2157+
2158+
Traits may be implemented for specific types with [impls]. An impl for
2159+
a particular trait gives an implementation of the methods that
2160+
trait provides. For instance, the following impls of
2161+
`Printable` for `int` and `~str` give implementations of the `print`
2162+
method.
21532163
21542164
[impls]: #methods
21552165
21562166
~~~~
21572167
# trait Printable { fn print(&self); }
21582168
impl Printable for int {
2159-
fn print(&self) { println!("{}", *self) }
2169+
fn print(&self) { println!("{:?}", *self) }
21602170
}
21612171
21622172
impl Printable for ~str {
@@ -2167,10 +2177,71 @@ impl Printable for ~str {
21672177
# (~"foo").print();
21682178
~~~~
21692179
2170-
Methods defined in an implementation of a trait may be called just like
2171-
any other method, using dot notation, as in `1.print()`. Traits may
2172-
themselves contain type parameters. A trait for generalized sequence
2173-
types might look like the following:
2180+
Methods defined in an impl for a trait may be called just like
2181+
any other method, using dot notation, as in `1.print()`.
2182+
2183+
## Default method implementations in trait definitions
2184+
2185+
Sometimes, a method that a trait provides will have the same
2186+
implementation for most or all of the types that implement that trait.
2187+
For instance, suppose that we wanted `bool`s and `f32`s to be
2188+
printable, and that we wanted the implementation of `print` for those
2189+
types to be exactly as it is for `int`, above:
2190+
2191+
~~~~
2192+
# trait Printable { fn print(&self); }
2193+
impl Printable for f32 {
2194+
fn print(&self) { println!("{:?}", *self) }
2195+
}
2196+
2197+
impl Printable for bool {
2198+
fn print(&self) { println!("{:?}", *self) }
2199+
}
2200+
2201+
# true.print();
2202+
# 3.14159.print();
2203+
~~~~
2204+
2205+
This works fine, but we've now repeated the same definition of `print`
2206+
in three places. Instead of doing that, we can simply include the
2207+
definition of `print` right in the trait definition, instead of just
2208+
giving its signature. That is, we can write the following:
2209+
2210+
~~~~
2211+
trait Printable {
2212+
// Default method implementation
2213+
fn print(&self) { println!("{:?}", *self) }
2214+
}
2215+
2216+
impl Printable for int {}
2217+
2218+
impl Printable for ~str {
2219+
fn print(&self) { println(*self) }
2220+
}
2221+
2222+
impl Printable for bool {}
2223+
2224+
impl Printable for f32 {}
2225+
2226+
# 1.print();
2227+
# (~"foo").print();
2228+
# true.print();
2229+
# 3.14159.print();
2230+
~~~~
2231+
2232+
Here, the impls of `Printable` for `int`, `bool`, and `f32` don't
2233+
need to provide an implementation of `print`, because in the absence
2234+
of a specific implementation, Rust just uses the _default method_
2235+
provided in the trait definition. Depending on the trait, default
2236+
methods can save a great deal of boilerplate code from having to be
2237+
written in impls. Of course, individual impls can still override the
2238+
default method for `print`, as is being done above in the impl for
2239+
`~str`.
2240+
2241+
## Type-parameterized traits
2242+
2243+
Traits may be parameterized by type variables. For example, a trait
2244+
for generalized sequence types might look like the following:
21742245
21752246
~~~~
21762247
trait Seq<T> {
@@ -3023,7 +3094,7 @@ they model most closely what people expect to shadow.
30233094

30243095
## Package ids
30253096

3026-
If you use `extern mod`, per default `rustc` will look for libraries in the the library search path (which you can
3097+
If you use `extern mod`, per default `rustc` will look for libraries in the library search path (which you can
30273098
extend with the `-L` switch).
30283099

30293100
However, Rust also ships with rustpkg, a package manager that is able to automatically download and build

0 commit comments

Comments
 (0)