15.2.1 What Is the Deref Trait
Deref is short for Dereference.
If a type implements the Deref trait, it allows us to customize the behavior of the dereference operator *. By implementing Deref, a smart pointer can be handled like a regular reference.
15.2.2 The Dereference Operator
First, let’s emphasize that a regular reference is also a kind of pointer. Take a look at this example:
fn main(){
let x = 5;
let y = &x;
assert_eq!(x, 5);
assert_eq!(*y, 5);
}
-
xis of typei32and holds the value5;ystores a reference that points to the memory address ofx. Its type is&i32, which meansyis a reference tox. - The first assertion compares
xwith5. Since the value stored inxis5, the two are equal, so the assertion passes. - The second assertion compares
*ywith5.yis a pointer, and if you want to extract the value it points to, you add the dereference symbol*in front of the variable name. In other words, the type ofyis&i32, the type of*yisi32, and because5is also of typei32,*ycan be compared with5, whileycannot.
15.2.3 Using Box<T> as a Reference
Box<T> can replace the reference in the previous example. Take a look:
fn main(){
let x = 5;
let y = Box::new(x);
assert_eq!(x, 5);
assert_eq!(*y, 5);
}
It is worth noting that the logic in the previous code and this code is slightly different:
-
y = &xin the previous example assigns a pointer toxtoy, which is a pointer to stack memory, becausei32is stored on the stack. -
y = Box::new(x)here copies the value ofxto the heap and then passes the pointer to that heap value toy.
15.2.4 Defining Your Own Smart Pointer
Box<T> is defined as a tuple struct with one element (for tuple structs, see 5.1. Defining and Instantiating Structs). Let’s define a MyBox<T>, which is also a tuple struct:
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
- First we define a tuple struct
MyBox, using the generic parameterTto stand in for the concrete type, and store a value of typeTin this tuple struct. - Then, through an
implblock, we define anewfunction to create a newMyBoxinstance.
Now let’s write the main function and see whether it works in practice:
fn main(){
let x = 5;
let y = MyBox::new(x);
assert_eq!(x, 5);
assert_eq!(*y, 5);
}
The final assertion, assert_eq!(*y, 5), produces an error at *y. The error message is:
error[E0614]: type `MyBox<{integer}>` cannot be dereferenced
--> src/main.rs:14:13
|
14 | assert_eq!(*y, 5);
| ^^ can't be dereferenced
For more information about this error, try `rustc --explain E0614`.
error: could not compile `mybox` (bin "mybox") due to 1 previous error
That means MyBox cannot be dereferenced.
This is because we have not implemented the Deref trait for MyBox.
15.2.5 Implementing the Deref Trait
The Deref trait in the standard library requires us to implement a deref method: this method borrows self and returns a reference to the internal data.
Using the code above as an example, if we want to implement the Deref trait for MyBox—that is, implement the deref method—we can write:
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
}
}
-
use std::ops::Deref;brings theDereftrait into the current scope. - To implement
DerefforMyBox, we writeimpl<T> Deref for MyBox<T>and then implement thederefmethod in thatimplblock. -
type Target = T;defines the associated type of theDereftrait. Associated types are a slightly different way to define generic parameters, and we will talk about them later. - The
derefmethod borrowsself, that is,&self, and returns a reference of type&T, specifically&self.0: it returns the element at index0in the tuple struct by reference (in this case, there is only one element). Since the return value is a reference, we can use the*dereference operator to access the value.
Let’s run the main function again and see whether there is any problem:
fn main(){
let x = 5;
let y = MyBox::new(x);
assert_eq!(x, 5);
assert_eq!(*y, 5);
}
It compiles successfully, so there is no problem.
In fact, the Rust compiler implicitly expands *y in main into:
*(y.deref())
It first calls the deref method on MyBox to return a reference, and then uses the dereference operator * for an ordinary dereference operation.
Top comments (0)