@@ -2035,28 +2035,30 @@ C++ templates.
2035
2035
2036
2036
## Traits
2037
2037
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.
2055
2056
2056
2057
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.
2060
2062
2061
2063
~~~~ {.xfail-test}
2062
2064
// This does not compile
@@ -2066,11 +2068,10 @@ fn head_bad<T>(v: &[T]) -> T {
2066
2068
~~~~
2067
2069
2068
2070
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:
2074
2075
2075
2076
~~~~
2076
2077
// This does
@@ -2079,12 +2080,13 @@ fn head<T: Clone>(v: &[T]) -> T {
2079
2080
}
2080
2081
~~~~
2081
2082
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.
2084
2086
When instantiating a generic function,
2085
- you can only instantiate it with types
2087
+ we can only instantiate it with types
2086
2088
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
2088
2090
that does not implement `Clone`.
2089
2091
2090
2092
While most traits can be defined and implemented by user code,
@@ -2110,7 +2112,7 @@ have the `'static` lifetime.
2110
2112
> iterations of the language, and often still are.
2111
2113
2112
2114
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
2114
2116
called when a value of the type that implements this trait is
2115
2117
destroyed, either because the value went out of scope or because the
2116
2118
garbage collector reclaimed it.
@@ -2134,43 +2136,110 @@ may call it.
2134
2136
2135
2137
## Declaring and implementing traits
2136
2138
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_.
2139
2140
For example, we could declare the trait
2140
2141
`Printable` for things that can be printed to the console,
2141
- with a single method:
2142
+ with a single method signature :
2142
2143
2143
2144
~~~~
2144
2145
trait Printable {
2145
2146
fn print(&self);
2146
2147
}
2147
2148
~~~~
2148
2149
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.
2153
2162
2154
2163
[impls]: #methods
2155
2164
2156
2165
~~~~
2157
2166
# trait Printable { fn print(&self); }
2158
2167
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) }
2160
2197
}
2161
2198
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
+
2162
2216
impl Printable for ~str {
2163
2217
fn print(&self) { println(*self) }
2164
2218
}
2165
2219
2220
+ impl Printable for bool {}
2221
+
2222
+ impl Printable for float {}
2223
+
2166
2224
# 1.print();
2167
2225
# (~"foo").print();
2226
+ # true.print();
2227
+ # 3.14159.print();
2168
2228
~~~~
2169
2229
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:
2174
2243
2175
2244
~~~~
2176
2245
trait Seq<T> {
0 commit comments