]>
Commit | Line | Data |
---|---|---|
9346a6ac AL |
1 | % `Deref` coercions |
2 | ||
bd371182 AL |
3 | The standard library provides a special trait, [`Deref`][deref]. It’s normally |
4 | used to overload `*`, the dereference operator: | |
5 | ||
6 | ```rust | |
7 | use std::ops::Deref; | |
8 | ||
9 | struct DerefExample<T> { | |
10 | value: T, | |
11 | } | |
12 | ||
13 | impl<T> Deref for DerefExample<T> { | |
14 | type Target = T; | |
15 | ||
16 | fn deref(&self) -> &T { | |
17 | &self.value | |
18 | } | |
19 | } | |
20 | ||
21 | fn main() { | |
22 | let x = DerefExample { value: 'a' }; | |
23 | assert_eq!('a', *x); | |
24 | } | |
25 | ``` | |
26 | ||
27 | [deref]: ../std/ops/trait.Deref.html | |
28 | ||
29 | This is useful for writing custom pointer types. However, there’s a language | |
30 | feature related to `Deref`: ‘deref coercions’. Here’s the rule: If you have a | |
31 | type `U`, and it implements `Deref<Target=T>`, values of `&U` will | |
32 | automatically coerce to a `&T`. Here’s an example: | |
33 | ||
34 | ```rust | |
35 | fn foo(s: &str) { | |
476ff2be | 36 | // Borrow a string for a second. |
bd371182 AL |
37 | } |
38 | ||
476ff2be | 39 | // String implements Deref<Target=str>. |
bd371182 AL |
40 | let owned = "Hello".to_string(); |
41 | ||
476ff2be | 42 | // Therefore, this works: |
bd371182 AL |
43 | foo(&owned); |
44 | ``` | |
45 | ||
46 | Using an ampersand in front of a value takes a reference to it. So `owned` is a | |
47 | `String`, `&owned` is an `&String`, and since `impl Deref<Target=str> for | |
48 | String`, `&String` will deref to `&str`, which `foo()` takes. | |
49 | ||
50 | That’s it. This rule is one of the only places in which Rust does an automatic | |
51 | conversion for you, but it adds a lot of flexibility. For example, the `Rc<T>` | |
52 | type implements `Deref<Target=T>`, so this works: | |
53 | ||
54 | ```rust | |
55 | use std::rc::Rc; | |
56 | ||
57 | fn foo(s: &str) { | |
476ff2be | 58 | // Borrow a string for a second. |
bd371182 AL |
59 | } |
60 | ||
476ff2be | 61 | // String implements Deref<Target=str>. |
bd371182 AL |
62 | let owned = "Hello".to_string(); |
63 | let counted = Rc::new(owned); | |
64 | ||
476ff2be | 65 | // Therefore, this works: |
bd371182 AL |
66 | foo(&counted); |
67 | ``` | |
68 | ||
69 | All we’ve done is wrap our `String` in an `Rc<T>`. But we can now pass the | |
70 | `Rc<String>` around anywhere we’d have a `String`. The signature of `foo` | |
71 | didn’t change, but works just as well with either type. This example has two | |
c30ab7b3 | 72 | conversions: `&Rc<String>` to `&String` and then `&String` to `&str`. Rust will do |
bd371182 AL |
73 | this as many times as possible until the types match. |
74 | ||
75 | Another very common implementation provided by the standard library is: | |
76 | ||
77 | ```rust | |
78 | fn foo(s: &[i32]) { | |
476ff2be | 79 | // Borrow a slice for a second. |
bd371182 AL |
80 | } |
81 | ||
476ff2be | 82 | // Vec<T> implements Deref<Target=[T]>. |
bd371182 AL |
83 | let owned = vec![1, 2, 3]; |
84 | ||
85 | foo(&owned); | |
86 | ``` | |
87 | ||
88 | Vectors can `Deref` to a slice. | |
89 | ||
90 | ## Deref and method calls | |
91 | ||
e9174d1e SL |
92 | `Deref` will also kick in when calling a method. Consider the following |
93 | example. | |
bd371182 AL |
94 | |
95 | ```rust | |
96 | struct Foo; | |
97 | ||
98 | impl Foo { | |
99 | fn foo(&self) { println!("Foo"); } | |
100 | } | |
101 | ||
e9174d1e | 102 | let f = &&Foo; |
bd371182 AL |
103 | |
104 | f.foo(); | |
105 | ``` | |
106 | ||
e9174d1e SL |
107 | Even though `f` is a `&&Foo` and `foo` takes `&self`, this works. That’s |
108 | because these things are the same: | |
bd371182 AL |
109 | |
110 | ```rust,ignore | |
111 | f.foo(); | |
112 | (&f).foo(); | |
113 | (&&f).foo(); | |
114 | (&&&&&&&&f).foo(); | |
115 | ``` | |
116 | ||
117 | A value of type `&&&&&&&&&&&&&&&&Foo` can still have methods defined on `Foo` | |
118 | called, because the compiler will insert as many * operations as necessary to | |
119 | get it right. And since it’s inserting `*`s, that uses `Deref`. |