]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | % Using traits for bounds on generics |
2 | ||
3 | The most widespread use of traits is for writing generic functions or types. For | |
4 | example, the following signature describes a function for consuming any iterator | |
5 | yielding items of type `A` to produce a collection of `A`: | |
6 | ||
a7813a04 | 7 | ```rust,ignore |
85aaf69f SL |
8 | fn from_iter<T: Iterator<A>>(iterator: T) -> SomeCollection<A> |
9 | ``` | |
10 | ||
62682a34 | 11 | Here, the `Iterator` trait specifies an interface that a type `T` must |
85aaf69f SL |
12 | explicitly implement to be used by this generic function. |
13 | ||
14 | **Pros**: | |
15 | ||
16 | * _Reusability_. Generic functions can be applied to an open-ended collection of | |
17 | types, while giving a clear contract for the functionality those types must | |
18 | provide. | |
19 | * _Static dispatch and optimization_. Each use of a generic function is | |
20 | specialized ("monomorphized") to the particular types implementing the trait | |
21 | bounds, which means that (1) invocations of trait methods are static, direct | |
22 | calls to the implementation and (2) the compiler can inline and otherwise | |
23 | optimize these calls. | |
24 | * _Inline layout_. If a `struct` and `enum` type is generic over some type | |
25 | parameter `T`, values of type `T` will be laid out _inline_ in the | |
26 | `struct`/`enum`, without any indirection. | |
27 | * _Inference_. Since the type parameters to generic functions can usually be | |
28 | inferred, generic functions can help cut down on verbosity in code where | |
29 | explicit conversions or other method calls would usually be necessary. See the | |
54a0048b | 30 | overloading/implicits use case below. |
c1a9b12d | 31 | * _Precise types_. Because generics give a _name_ to the specific type |
85aaf69f SL |
32 | implementing a trait, it is possible to be precise about places where that |
33 | exact type is required or produced. For example, a function | |
34 | ||
a7813a04 | 35 | ```rust,ignore |
85aaf69f SL |
36 | fn binary<T: Trait>(x: T, y: T) -> T |
37 | ``` | |
38 | ||
39 | is guaranteed to consume and produce elements of exactly the same type `T`; it | |
40 | cannot be invoked with parameters of different types that both implement | |
41 | `Trait`. | |
42 | ||
43 | **Cons**: | |
44 | ||
45 | * _Code size_. Specializing generic functions means that the function body is | |
46 | duplicated. The increase in code size must be weighed against the performance | |
47 | benefits of static dispatch. | |
48 | * _Homogeneous types_. This is the other side of the "precise types" coin: if | |
49 | `T` is a type parameter, it stands for a _single_ actual type. So for example | |
50 | a `Vec<T>` contains elements of a single concrete type (and, indeed, the | |
51 | vector representation is specialized to lay these out in line). Sometimes | |
52 | heterogeneous collections are useful; see | |
54a0048b | 53 | trait objects below. |
85aaf69f SL |
54 | * _Signature verbosity_. Heavy use of generics can bloat function signatures. |
55 | **[Ed. note]** This problem may be mitigated by some language improvements; stay tuned. | |
56 | ||
57 | ### Favor widespread traits. **[FIXME: needs RFC]** | |
58 | ||
59 | Generic types are a form of abstraction, which entails a mental indirection: if | |
60 | a function takes an argument of type `T` bounded by `Trait`, clients must first | |
61 | think about the concrete types that implement `Trait` to understand how and when | |
62 | the function is callable. | |
63 | ||
64 | To keep the cost of abstraction low, favor widely-known traits. Whenever | |
65 | possible, implement and use traits provided as part of the standard library. Do | |
66 | not introduce new traits for generics lightly; wait until there are a wide range | |
67 | of types that can implement the type. |