2015-04-07 21:16:02 -05:00
|
|
|
|
% `Deref` coercions
|
|
|
|
|
|
2015-04-23 10:35:37 -05:00
|
|
|
|
The standard library provides a special trait, [`Deref`][deref]. It’s normally
|
|
|
|
|
used to overload `*`, the dereference operator:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
|
|
|
|
|
struct DerefExample<T> {
|
|
|
|
|
value: T,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> Deref for DerefExample<T> {
|
|
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
|
|
fn deref(&self) -> &T {
|
|
|
|
|
&self.value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = DerefExample { value: 'a' };
|
|
|
|
|
assert_eq!('a', *x);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[deref]: ../std/ops/trait.Deref.html
|
|
|
|
|
|
|
|
|
|
This is useful for writing custom pointer types. However, there’s a language
|
|
|
|
|
feature related to `Deref`: ‘deref coercions’. Here’s the rule: If you have a
|
|
|
|
|
type `U`, and it implements `Deref<Target=T>`, values of `&U` will
|
|
|
|
|
automatically coerce to a `&T`. Here’s an example:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
fn foo(s: &str) {
|
|
|
|
|
// borrow a string for a second
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String implements Deref<Target=str>
|
|
|
|
|
let owned = "Hello".to_string();
|
|
|
|
|
|
|
|
|
|
// therefore, this works:
|
|
|
|
|
foo(&owned);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Using an ampersand in front of a value takes a reference to it. So `owned` is a
|
|
|
|
|
`String`, `&owned` is an `&String`, and since `impl Deref<Target=str> for
|
|
|
|
|
String`, `&String` will deref to `&str`, which `foo()` takes.
|
|
|
|
|
|
|
|
|
|
That’s it. This rule is one of the only places in which Rust does an automatic
|
|
|
|
|
conversion for you, but it adds a lot of flexibility. For example, the `Rc<T>`
|
|
|
|
|
type implements `Deref<Target=T>`, so this works:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
|
|
fn foo(s: &str) {
|
|
|
|
|
// borrow a string for a second
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String implements Deref<Target=str>
|
|
|
|
|
let owned = "Hello".to_string();
|
|
|
|
|
let counted = Rc::new(owned);
|
|
|
|
|
|
|
|
|
|
// therefore, this works:
|
|
|
|
|
foo(&counted);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
All we’ve done is wrap our `String` in an `Rc<T>`. But we can now pass the
|
|
|
|
|
`Rc<String>` around anywhere we’d have a `String`. The signature of `foo`
|
|
|
|
|
didn’t change, but works just as well with either type. This example has two
|
|
|
|
|
conversions: `Rc<String>` to `String` and then `String` to `&str`. Rust will do
|
|
|
|
|
this as many times as possible until the types match.
|
|
|
|
|
|
|
|
|
|
Another very common implementation provided by the standard library is:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
fn foo(s: &[i32]) {
|
|
|
|
|
// borrow a slice for a second
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vec<T> implements Deref<Target=[T]>
|
|
|
|
|
let owned = vec![1, 2, 3];
|
|
|
|
|
|
|
|
|
|
foo(&owned);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Vectors can `Deref` to a slice.
|
|
|
|
|
|
|
|
|
|
## Deref and method calls
|
|
|
|
|
|
|
|
|
|
`Deref` will also kick in when calling a method. In other words, these are
|
|
|
|
|
the same two things in Rust:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Foo;
|
|
|
|
|
|
|
|
|
|
impl Foo {
|
|
|
|
|
fn foo(&self) { println!("Foo"); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let f = Foo;
|
|
|
|
|
|
|
|
|
|
f.foo();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Even though `f` isn’t a reference, and `foo` takes `&self`, this works.
|
|
|
|
|
That’s because these things are the same:
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
f.foo();
|
|
|
|
|
(&f).foo();
|
|
|
|
|
(&&f).foo();
|
|
|
|
|
(&&&&&&&&f).foo();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A value of type `&&&&&&&&&&&&&&&&Foo` can still have methods defined on `Foo`
|
|
|
|
|
called, because the compiler will insert as many * operations as necessary to
|
|
|
|
|
get it right. And since it’s inserting `*`s, that uses `Deref`.
|