]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch19-04-advanced-types.md
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch19-04-advanced-types.md
CommitLineData
cc61c64b
XL
1## Advanced Types
2
2c00a5a8 3The Rust type system has some features that we’ve mentioned in this book but
83c7162d
XL
4haven’t yet discussed. We’ll start by discussing newtypes in general as we
5examine why newtypes are useful as types. Then we’ll move on to type aliases, a
6feature similar to newtypes but with slightly different semantics. We’ll also
7discuss the `!` type and dynamically sized types.
cc61c64b 8
83c7162d
XL
9> Note: The next section assumes you’ve read the earlier section “The Newtype
10> Pattern to Implement External Traits on External Types.”
2c00a5a8 11
83c7162d 12### Using the Newtype Pattern for Type Safety and Abstraction
2c00a5a8 13
94b46f34
XL
14The newtype pattern is useful for tasks beyond those we’ve discussed so far,
15including statically enforcing that values are never confused and indicating
16the units of a value. You saw an example of using newtypes to indicate units in
17Listing 19-23: recall that the `Millimeters` and `Meters` structs wrapped `u32`
18values in a newtype. If we wrote a function with a parameter of type
19`Millimeters`, we couldn’t compile a program that accidentally tried to call
20that function with a value of type `Meters` or a plain `u32`.
83c7162d
XL
21
22Another use of the newtype pattern is in abstracting away some implementation
23details of a type: the new type can expose a public API that is different from
24the API of the private inner type if we used the new type directly to restrict
25the available functionality, for example.
26
27Newtypes can also hide internal implementation. For example, we could provide a
2c00a5a8
XL
28`People` type to wrap a `HashMap<i32, String>` that stores a person’s ID
29associated with their name. Code using `People` would only interact with the
30public API we provide, such as a method to add a name string to the `People`
83c7162d
XL
31collection; that code wouldn’t need to know that we assign an `i32` ID to names
32internally. The newtype pattern is a lightweight way to achieve encapsulation
33to hide implementation details, which we discussed in the “Encapsulation that
34Hides Implementation Details” section of Chapter 17.
cc61c64b 35
94b46f34 36### Creating Type Synonyms with Type Aliases
cc61c64b 37
83c7162d 38Along with the newtype pattern, Rust provides the ability to declare a *type
2c00a5a8
XL
39alias* to give an existing type another name. For this we use the `type`
40keyword. For example, we can create the alias `Kilometers` to `i32` like so:
cc61c64b
XL
41
42```rust
43type Kilometers = i32;
44```
45
83c7162d
XL
46Now, the alias `Kilometers` is a *synonym* for `i32`; unlike the `Millimeters`
47and `Meters` types we created in Listing 19-23, `Kilometers` is not a separate,
48new type. Values that have the type `Kilometers` will be treated the same as
49values of type `i32`:
cc61c64b
XL
50
51```rust
52type Kilometers = i32;
53
54let x: i32 = 5;
55let y: Kilometers = 5;
56
57println!("x + y = {}", x + y);
58```
59
83c7162d
XL
60Because `Kilometers` and `i32` are the same type, we can add values of both
61types and we can pass `Kilometers` values to functions that take `i32`
62parameters. However, using this method, we don’t get the type checking benefits
63that we get from the newtype pattern discussed earlier.
cc61c64b
XL
64
65The main use case for type synonyms is to reduce repetition. For example, we
83c7162d 66might have a lengthy type like this:
cc61c64b
XL
67
68```rust,ignore
3b2f2976 69Box<Fn() + Send + 'static>
cc61c64b
XL
70```
71
83c7162d
XL
72Writing this lengthy type in function signatures and as type annotations all
73over the code can be tiresome and error prone. Imagine having a project full of
74code like that in Listing 19-32.
cc61c64b
XL
75
76```rust
3b2f2976 77let f: Box<Fn() + Send + 'static> = Box::new(|| println!("hi"));
cc61c64b 78
3b2f2976 79fn takes_long_type(f: Box<Fn() + Send + 'static>) {
ff7c6d11 80 // --snip--
cc61c64b
XL
81}
82
3b2f2976 83fn returns_long_type() -> Box<Fn() + Send + 'static> {
ff7c6d11 84 // --snip--
cc61c64b
XL
85# Box::new(|| ())
86}
87```
88
2c00a5a8 89<span class="caption">Listing 19-32: Using a long type in many places</span>
cc61c64b 90
83c7162d
XL
91A type alias makes this code more manageable by reducing the repetition. In
92Listing 19-33, we’ve introduced an alias named `Thunk` for the verbose type and
93can replace all uses of the type with the shorter alias `Thunk`.
cc61c64b
XL
94
95```rust
3b2f2976 96type Thunk = Box<Fn() + Send + 'static>;
cc61c64b
XL
97
98let f: Thunk = Box::new(|| println!("hi"));
99
100fn takes_long_type(f: Thunk) {
ff7c6d11 101 // --snip--
cc61c64b
XL
102}
103
104fn returns_long_type() -> Thunk {
ff7c6d11 105 // --snip--
cc61c64b
XL
106# Box::new(|| ())
107}
108```
109
2c00a5a8 110<span class="caption">Listing 19-33: Introducing a type alias `Thunk` to reduce
cc61c64b
XL
111repetition</span>
112
83c7162d
XL
113This code is much easier to read and write! Choosing a meaningful name for a
114type alias can help communicate your intent as well (*thunk* is a word for code
115to be evaluated at a later time, so it’s an appropriate name for a closure that
116gets stored).
cc61c64b 117
2c00a5a8
XL
118Type aliases are also commonly used with the `Result<T, E>` type for reducing
119repetition. Consider the `std::io` module in the standard library. I/O
120operations often return a `Result<T, E>` to handle situations when operations
121fail to work. This library has a `std::io::Error` struct that represents all
122possible I/O errors. Many of the functions in `std::io` will be returning
123`Result<T, E>` where the `E` is `std::io::Error`, such as these functions in
124the `Write` trait:
cc61c64b
XL
125
126```rust
127use std::io::Error;
3b2f2976 128use std::fmt;
cc61c64b
XL
129
130pub trait Write {
131 fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
132 fn flush(&mut self) -> Result<(), Error>;
133
134 fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>;
3b2f2976 135 fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Error>;
cc61c64b
XL
136}
137```
138
83c7162d 139The `Result<..., Error>` is repeated a lot. As such, `std::io` has this type of
cc61c64b
XL
140alias declaration:
141
142```rust,ignore
143type Result<T> = Result<T, std::io::Error>;
144```
145
83c7162d 146Because this declaration is in the `std::io` module, we can use the fully
94b46f34 147qualified alias `std::io::Result<T>`—that is, a `Result<T, E>` with the `E`
83c7162d
XL
148filled in as `std::io::Error`. The `Write` trait function signatures end up
149looking like this:
cc61c64b
XL
150
151```rust,ignore
152pub trait Write {
153 fn write(&mut self, buf: &[u8]) -> Result<usize>;
154 fn flush(&mut self) -> Result<()>;
155
156 fn write_all(&mut self, buf: &[u8]) -> Result<()>;
157 fn write_fmt(&mut self, fmt: Arguments) -> Result<()>;
158}
159```
160
83c7162d
XL
161The type alias helps in two ways: it makes code easier to write *and* it gives
162us a consistent interface across all of `std::io`. Because it’s an alias, it’s
163just another `Result<T, E>`, which means we can use any methods that work on
94b46f34 164`Result<T, E>` with it, as well as special syntax like the `?` operator.
cc61c64b 165
94b46f34 166### The Never Type that Never Returns
cc61c64b 167
2c00a5a8 168Rust has a special type named `!` that’s known in type theory lingo as the
83c7162d 169*empty type* because it has no values. We prefer to call it the *never type*
2c00a5a8 170because it stands in the place of the return type when a function will never
83c7162d 171return. Here is an example:
cc61c64b
XL
172
173```rust,ignore
174fn bar() -> ! {
ff7c6d11 175 // --snip--
3b2f2976 176}
cc61c64b
XL
177```
178
83c7162d
XL
179This code is read as “the function `bar` returns never.” Functions that return
180never are called *diverging functions*. We can’t create values of the type `!`
181so `bar` can never possibly return.
2c00a5a8 182
83c7162d 183But what use is a type you can never create values for? Recall the code from
94b46f34 184Listing 2-5; we’ve reproduced part of it here in Listing 19-34.
cc61c64b
XL
185
186```rust
187# let guess = "3";
188# loop {
189let guess: u32 = match guess.trim().parse() {
190 Ok(num) => num,
191 Err(_) => continue,
192};
193# break;
194# }
195```
196
2c00a5a8 197<span class="caption">Listing 19-34: A `match` with an arm that ends in
cc61c64b
XL
198`continue`</span>
199
2c00a5a8 200At the time, we skipped over some details in this code. In Chapter 6 in “The
83c7162d
XL
201`match` Control Flow Operator” section, we discussed that `match` arms must all
202return the same type. So, for example, the following code doesn’t work:
cc61c64b
XL
203
204```rust,ignore
83c7162d 205let guess = match guess.trim().parse() {
cc61c64b
XL
206 Ok(_) => 5,
207 Err(_) => "hello",
208}
209```
210
83c7162d 211The type of `guess` in this code would have to be an integer *and* a string,
94b46f34 212and Rust requires that `guess` have only one type. So what does `continue`
2c00a5a8
XL
213return? How were we allowed to return a `u32` from one arm and have another arm
214that ends with `continue` in Listing 19-34?
cc61c64b 215
83c7162d
XL
216As you might have guessed, `continue` has a `!` value. That is, when Rust
217computes the type of `guess`, it looks at both match arms, the former with a
218value of `u32` and the latter with a `!` value. Because `!` can never have a
219value, Rust decides that the type of `guess` is `u32`.
2c00a5a8
XL
220
221The formal way of describing this behavior is that expressions of type `!` can
222be coerced into any other type. We’re allowed to end this `match` arm with
83c7162d
XL
223`continue` because `continue` doesn’t return a value; instead, it moves control
224back to the top of the loop, so in the `Err` case, we never assign a value to
225`guess`.
226
227The never type is useful with the `panic!` macro as well. Remember the `unwrap`
228function that we call on `Option<T>` values to produce a value or panic? Here
229is its definition:
cc61c64b
XL
230
231```rust,ignore
232impl<T> Option<T> {
233 pub fn unwrap(self) -> T {
234 match self {
235 Some(val) => val,
236 None => panic!("called `Option::unwrap()` on a `None` value"),
237 }
238 }
239}
240```
241
83c7162d 242In this code, the same thing happens as in the `match` in Listing 19-34: Rust
94b46f34
XL
243sees that `val` has the type `T` and `panic!` has the type `!`, so the result
244of the overall `match` expression is `T`. This code works because `panic!`
245doesn’t produce a value; it ends the program. In the `None` case, we won’t be
246returning a value from `unwrap`, so this code is valid.
cc61c64b
XL
247
248One final expression that has the type `!` is a `loop`:
249
250```rust,ignore
251print!("forever ");
252
253loop {
254 print!("and ever ");
255}
256```
257
83c7162d
XL
258Here, the loop never ends, so `!` is the value of the expression. However, this
259wouldn’t be true if we included a `break`, because the loop would terminate
260when it got to the `break`.
cc61c64b 261
94b46f34 262### Dynamically Sized Types and the `Sized` Trait
cc61c64b 263
83c7162d
XL
264Due to Rust’s need to know certain details, such as how much space to allocate
265for a value of a particular type, there is a corner of its type system that can
266be confusing: the concept of *dynamically sized types*. Sometimes referred to
267as *DSTs* or *unsized types*, these types let us write code using values whose
94b46f34 268size we can know only at runtime.
cc61c64b 269
83c7162d
XL
270Let’s dig into the details of a dynamically sized type called `str`, which
271we’ve been using throughout the book. That’s right, not `&str`, but `str` on
272its own, is a DST. We can’t know how long the string is until runtime, meaning
273we can’t create a variable of type `str`, nor can we take an argument of type
274`str`. Consider the following code, which does not work:
cc61c64b
XL
275
276```rust,ignore
277let s1: str = "Hello there!";
278let s2: str = "How's it going?";
279```
280
2c00a5a8 281Rust needs to know how much memory to allocate for any value of a particular
83c7162d
XL
282type, and all values of a type must use the same amount of memory. If Rust
283allowed us to write this code, these two `str` values would need to take up the
284same amount of space. But they have different lengths: `s1` needs 12 bytes of
285storage and `s2` needs 15. This is why it’s not possible to create a variable
286holding a dynamically sized type.
287
288So what do we do? In this case, you already know the answer: we make the types
289of `s1` and `s2` a `&str` rather than a `str`. Recall that in the “String
94b46f34 290Slices” section of Chapter 4, we said the slice data structure stores the
2c00a5a8 291starting position and the length of the slice.
cc61c64b 292
83c7162d
XL
293So although a `&T` is a single value that stores the memory address of where
294the `T` is located, a `&str` is *two* values: the address of the `str` and its
295length. As such, we can know the size of a `&str` value at compile time: it’s
94b46f34
XL
296twice the length of a `usize`. That is, we always know the size of a `&str`, no
297matter how long the string it refers to is. In general, this is the way in
298which dynamically sized types are used in Rust: they have an extra bit of
299metadata that stores the size of the dynamic information. The golden rule of
300dynamically sized types is that we must always put values of dynamically sized
301types behind a pointer of some kind.
2c00a5a8 302
83c7162d
XL
303We can combine `str` with all kinds of pointers: for example, `Box<str>` or
304`Rc<str>`. In fact, you’ve seen this before but with a different dynamically
2c00a5a8
XL
305sized type: traits. Every trait is a dynamically sized type we can refer to by
306using the name of the trait. In Chapter 17 in the “Using Trait Objects that
83c7162d
XL
307Allow for Values of Different Types” section, we mentioned that to use traits
308as trait objects, we must put them behind a pointer, such as `&Trait` or
309`Box<Trait>` (`Rc<Trait>` would work too).
2c00a5a8 310
83c7162d
XL
311To work with DSTs, Rust has a particular trait called the `Sized` trait to
312determine whether or not a type’s size is known at compile time. This trait is
313automatically implemented for everything whose size is known at compile time.
314In addition, Rust implicitly adds a bound on `Sized` to every generic function.
315That is, a generic function definition like this:
cc61c64b
XL
316
317```rust,ignore
318fn generic<T>(t: T) {
ff7c6d11 319 // --snip--
3b2f2976 320}
cc61c64b
XL
321```
322
83c7162d 323is actually treated as though we had written this:
cc61c64b
XL
324
325```rust,ignore
326fn generic<T: Sized>(t: T) {
ff7c6d11 327 // --snip--
3b2f2976 328}
cc61c64b
XL
329```
330
94b46f34 331By default, generic functions will work only on types that have a known size at
83c7162d 332compile time. However, you can use the following special syntax to relax this
cc61c64b
XL
333restriction:
334
335```rust,ignore
336fn generic<T: ?Sized>(t: &T) {
ff7c6d11 337 // --snip--
3b2f2976 338}
cc61c64b
XL
339```
340
83c7162d
XL
341A trait bound on `?Sized` is the opposite of a trait bound on `Sized`: we would
342read this as “`T` may or may not be `Sized`.” This syntax is only available for
343`Sized`, not any other traits.
cc61c64b 344
83c7162d
XL
345Also note that we switched the type of the `t` parameter from `T` to `&T`.
346Because the type might not be `Sized`, we need to use it behind some kind of
347pointer. In this case, we’ve chosen a reference.
cc61c64b 348
83c7162d 349Next, we’ll talk about functions and closures!