Skip to content

Commit 62b74d9

Browse files
committed
Write up default methods for the tutorial.
1 parent 8adbb38 commit 62b74d9

File tree

1 file changed

+111
-42
lines changed

1 file changed

+111
-42
lines changed

doc/tutorial.md

Lines changed: 111 additions & 42 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,13 @@ 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` is polymorphic
2084+
over any type `T`, so long as there is an implementation of the
2085+
`Clone` trait for that type.
20842086
When instantiating a generic function,
2085-
you can only instantiate it with types
2087+
we can only instantiate it with types
20862088
that implement the correct trait,
2087-
so you could not apply `head` to a type
2089+
so we could not apply `head` to a vector whose elements are of some type
20882090
that does not implement `Clone`.
20892091
20902092
While most traits can be defined and implemented by user code,
@@ -2110,7 +2112,7 @@ have the `'static` lifetime.
21102112
> iterations of the language, and often still are.
21112113
21122114
Additionally, the `Drop` trait is used to define destructors. This
2113-
trait defines one method called `drop`, which is automatically
2115+
trait provides one method called `drop`, which is automatically
21142116
called when a value of the type that implements this trait is
21152117
destroyed, either because the value went out of scope or because the
21162118
garbage collector reclaimed it.
@@ -2134,43 +2136,110 @@ may call it.
21342136
21352137
## Declaring and implementing traits
21362138
2137-
A trait consists of a set of methods without bodies,
2138-
or may be empty, as is the case with `Send` and `Freeze`.
2139+
At its simplest, a trait is a set of zero or more _method signatures_.
21392140
For example, we could declare the trait
21402141
`Printable` for things that can be printed to the console,
2141-
with a single method:
2142+
with a single method signature:
21422143
21432144
~~~~
21442145
trait Printable {
21452146
fn print(&self);
21462147
}
21472148
~~~~
21482149
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`.
2150+
We say that the `Printable` trait _provides_ a `print` method with the
2151+
given signature. This means that we can call `print` on an argument
2152+
of any type that implements the `Printable` trait.
2153+
2154+
Rust's built-in `Send` and `Freeze` types are examples of traits that
2155+
don't provide any methods.
2156+
2157+
Traits may be implemented for specific types with [impls]. An impl for
2158+
a particular trait gives an implementation of the methods that that
2159+
trait provides. For instance, the following the following impls of
2160+
`Printable` for `int` and `~str` give implementations of the `print`
2161+
method.
21532162
21542163
[impls]: #methods
21552164
21562165
~~~~
21572166
# trait Printable { fn print(&self); }
21582167
impl Printable for int {
2159-
fn print(&self) { println!("{}", *self) }
2168+
fn print(&self) { println!("{:?}", *self) }
2169+
}
2170+
2171+
impl Printable for ~str {
2172+
fn print(&self) { println(*self) }
2173+
}
2174+
2175+
# 1.print();
2176+
# (~"foo").print();
2177+
~~~~
2178+
2179+
Methods defined in an impl for a trait may be called just like
2180+
any other method, using dot notation, as in `1.print()`.
2181+
2182+
## Default method implementations in trait definitions
2183+
2184+
Sometimes, a method that a trait provides will have the same
2185+
implementation for most or all of the types that implement that trait.
2186+
For instance, suppose that we wanted `bool`s and `float`s to be
2187+
printable, and that we wanted the implementation of `print` for those
2188+
types to be exactly as it is for `int`, above:
2189+
2190+
~~~~
2191+
impl Printable for float {
2192+
fn print(&self) { println!("{:?}", *self) }
2193+
}
2194+
2195+
impl Printable for bool {
2196+
fn print(&self) { println!("{:?}", *self) }
21602197
}
21612198
2199+
# true.print();
2200+
# 3.14159.print();
2201+
~~~~
2202+
2203+
This works fine, but we've now repeated the same definition of `print`
2204+
in three places. Instead of doing that, we can simply include the
2205+
definition of `print` right in the trait definition, instead of just
2206+
giving its signature. That is, we can write the following:
2207+
2208+
~~~~
2209+
trait Printable {
2210+
// Default method implementation
2211+
fn print(&self) { println!("{:?}", *self) }
2212+
}
2213+
2214+
impl Printable for int {}
2215+
21622216
impl Printable for ~str {
21632217
fn print(&self) { println(*self) }
21642218
}
21652219
2220+
impl Printable for bool {}
2221+
2222+
impl Printable for float {}
2223+
21662224
# 1.print();
21672225
# (~"foo").print();
2226+
# true.print();
2227+
# 3.14159.print();
21682228
~~~~
21692229
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:
2230+
Here, the impls of `Printable` for `int`, `bool`, and `float` don't
2231+
need to provide an implementation of `print`, because in the absence
2232+
of a specific implementation, Rust just uses the _default method_
2233+
provided in the trait definition. Depending on the trait, default
2234+
methods can save a great deal of boilerplate code from having to be
2235+
written in impls. Of course, individual impls can still override the
2236+
default method for `print`, as is being done above in the impl for
2237+
`~str`.
2238+
2239+
## Type-parameterized traits
2240+
2241+
Traits may be parameterized by type variables. For example, a trait
2242+
for generalized sequence types might look like the following:
21742243
21752244
~~~~
21762245
trait Seq<T> {

0 commit comments

Comments
 (0)