]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | % The newtype pattern |
2 | ||
3 | A "newtype" is a tuple or `struct` with a single field. The terminology is borrowed from Haskell. | |
4 | ||
5 | Newtypes are a zero-cost abstraction: they introduce a new, distinct name for an | |
6 | existing type, with no runtime overhead when converting between the two types. | |
7 | ||
8 | ### Use newtypes to provide static distinctions. [FIXME: needs RFC] | |
9 | ||
10 | Newtypes can statically distinguish between different interpretations of an | |
11 | underlying type. | |
12 | ||
13 | For example, a `f64` value might be used to represent a quantity in miles or in | |
14 | kilometers. Using newtypes, we can keep track of the intended interpretation: | |
15 | ||
16 | ```rust | |
17 | struct Miles(pub f64); | |
18 | struct Kilometers(pub f64); | |
19 | ||
20 | impl Miles { | |
21 | fn as_kilometers(&self) -> Kilometers { ... } | |
22 | } | |
23 | impl Kilometers { | |
24 | fn as_miles(&self) -> Miles { ... } | |
25 | } | |
26 | ``` | |
27 | ||
28 | Once we have separated these two types, we can statically ensure that we do not | |
29 | confuse them. For example, the function | |
30 | ||
31 | ```rust | |
32 | fn are_we_there_yet(distance_travelled: Miles) -> bool { ... } | |
33 | ``` | |
34 | ||
35 | cannot accidentally be called with a `Kilometers` value. The compiler will | |
36 | remind us to perform the conversion, thus averting certain | |
37 | [catastrophic bugs](http://en.wikipedia.org/wiki/Mars_Climate_Orbiter). | |
38 | ||
39 | ### Use newtypes with private fields for hiding. [FIXME: needs RFC] | |
40 | ||
41 | A newtype can be used to hide representation details while making precise | |
42 | promises to the client. | |
43 | ||
44 | For example, consider a function `my_transform` that returns a compound iterator | |
45 | type `Enumerate<Skip<vec::MoveItems<T>>>`. We wish to hide this type from the | |
62682a34 | 46 | client, so that the client's view of the return type is roughly `Iterator<(usize, |
85aaf69f SL |
47 | T)>`. We can do so using the newtype pattern: |
48 | ||
49 | ```rust | |
50 | struct MyTransformResult<T>(Enumerate<Skip<vec::MoveItems<T>>>); | |
62682a34 | 51 | impl<T> Iterator<(usize, T)> for MyTransformResult<T> { ... } |
85aaf69f SL |
52 | |
53 | fn my_transform<T, Iter: Iterator<T>>(iter: Iter) -> MyTransformResult<T> { | |
54 | ... | |
55 | } | |
56 | ``` | |
57 | ||
58 | Aside from simplifying the signature, this use of newtypes allows us to make a | |
59 | expose and promise less to the client. The client does not know _how_ the result | |
60 | iterator is constructed or represented, which means the representation can | |
61 | change in the future without breaking client code. | |
62 | ||
63 | > **[FIXME]** Interaction with auto-deref. | |
64 | ||
65 | ### Use newtypes to provide cost-free _views_ of another type. **[FIXME]** | |
66 | ||
67 | > **[FIXME]** Describe the pattern of using newtypes to provide a new set of | |
68 | > inherent or trait methods, providing a different perspective on the underlying | |
69 | > type. |