15.3.1 Implicit Deref Coercion for Functions and Methods
Implicit deref coercion is a convenience feature for functions and methods.
Its principle is this: *if type T implements the Deref trait, then deref coercion can convert a reference to T into the reference produced after applying Deref to T.
When a reference of some type is passed to a function or method and its type does not match the declared parameter type, deref coercion happens automatically. The compiler makes a series of calls to deref to convert it to the required parameter type. This happens at compile time, so there is no additional performance overhead.
That sounds a bit abstract, so let’s look at an example. We will continue from the code in the previous article:
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
This is the code from the previous article. It defines the MyBox tuple struct (see 5.1. Defining and Instantiating Structs for an introduction to tuple structs), creates the new function, and implements the Deref trait for it, so we can use ordinary dereference operations on MyBox.
Here is the additional code:
fn hello(name: &str) {
println!("Hello, {}", name);
}
The hello function takes &str, that is, a string slice, and prints it.
Now let’s look at the main function:
fn main(){
let m = MyBox::new(String::from("Rust"));
hello(&m);
}
m is of type MyBox<String>, and &m is &MyBox<String>. However, hello expects &str, and this code does not cause an error. Why?
First, MyBox already implements the Deref trait, so Rust can call deref to convert &MyBox<String> into &String. That is the somewhat abstract rule we just discussed.
That is still not the end of the conversion. &String and &str are different types, so how does that conversion happen? Because String also implements the Deref trait, and its deref implementation returns a string slice of type &str, Rust uses deref on &String to convert &String into &str. The type finally matches.
If Rust did not have deref coercion, the code would look like this:
hello(&(*m)[..]);
- First, use the dereference operator
*to convertmfromMyBox<String>intoString. - Then add the reference symbol
&to convertStringinto&String. - By using the slice syntax
[..], you can get a reference to the full contents of theStringand convert its value from&Stringinto&str.
15.3.2 Deref and Mutability
You can use the DerefMut trait to overload the * operator for mutable references. Compared with Deref, DerefMut adds Mut, which means that DerefMut returns a mutable reference &mut T, whereas Deref returns an immutable reference &T.
When a type and trait satisfy the following three cases, Rust performs deref coercion:
When
T: Deref<Target = U>,&Tcan be converted to&U:
Timplements theDereftrait, and the return type ofderefunderDerefis&U, so&Tcan be converted to&U.
For example, theMyBoxtype in the code above implementsDeref, and itsderefmethod returns&T, so&MyBoxcan be converted to&T.When
T: DerefMut<Target = U>,&mut Tcan be converted to&mut U.
Timplements theDerefMuttrait (DerefMutreturns a mutable reference&mut T), and the return type ofderefunderDerefMutis&mut U, so&mut Tcan be converted to&mut U.When
T: Deref<Target = U>,&mut Tcan be converted to&U.
Rust can automatically convert a mutable reference into an immutable reference, but the reverse is definitely not allowed. Converting an immutable reference into a mutable reference requires the reference to be unique (this was discussed in the borrow rules, see 4.4. Reference and Borrowing).
Top comments (0)