Rust Deref Trait
Deref< T >
The dereference operator (*) can have its behavior customized using the Deref<T> trait.
The smart pointer can be used as a reference if the Deref<T> trait is implemented. As a result, smart pointers can also be utilized with code designed for references.
Regular References
A regular reference is a type of pointer that points to a value that is kept in a different location. Let’s look at a quick example of creating an i32 type value reference and using it with the dereference operator.
fn main()
{
let a = 20;
let b = &a;
if a==*b
{
println!("a and *b are equal");
}
else
{
println!("they are not equal");
}
}
Output:
a and *b are equal
In the example above, a has the reference to the ‘a’ variable, whereas b holds the i32 type value, 20. The value 20 is represented if we use *b. The true value will be returned when we compare the variables a and *b. When we utilize &b rather than *b, the compiler generates an error.
Box< T > as a Reference
The Box<T> pointer can be used as a reference.
Example:
fn main()
{
let a = 11;
let b = Box::new(a);
print!("Value of *b is {}",*b);
}
Output:
Value of *b is 11
Box<T> functions in the same way as the ordinary references in the example above. The box in b points to the data instead of the value by using the ‘&’ operator, which is the only difference between the two.
Smart Pointer as References
We now construct a smart pointer that resembles the Box<T> type, and we will observe how it behaves in contrast to conventional references.
The tuple struct containing a single element, such as MyBox<T>, can be defined as the Box<T>.
We define the method on type MyBox<T> after constructing the tuple struct.
Example:
struct MyBox(T);
impl MyBox
{
fn example(y : T)->MyBox
{
MyBox(y)
}
}
fn main()
{
let a = 8;
let b = MyBox::example(a);
print!("Value of *b is {}",*b);
}
Output:
The smart pointer, b, is created in the example above, but it is not dereferenceable. Consequently, we draw the conclusion that dereferenceing customized pointers that resemble the Box<T> type is not possible.
Implementing a Deref Trait
- The standard library, which is used to implement the deref method, defines the Deref trait.
- By borrowing the self, the deref method yields a reference to the inner data.
Example:
struct MyBox
{
a : T,
}
use :: std::ops::Deref;
impl Deref for MyBox
{
type Target = T;
fn deref(&self) ->&T
{
&self.a
}
}
fn main()
{
let b = MyBox{a : 10};
print!("{}",*(b.deref()));
}
Output:
10
Program Explanation
The MyBox type implements the Deref trait.
The deref() method, which returns the reference to the ‘a’ variable, is implemented by the Deref trait.
An related type for a Deref trait is Target = T;. The generic type argument is declared using associated type.
We establish the MyBox type, b, instance.
Dereference of the reference returned by the deref() method is done after the deref() function is invoked using an instance of the MyBox type, b.deref().
Deref Coercion
Deref Coercion is the process of changing the reference into which Deref can convert the original type from one that implements the Deref trait to another.
Deref Coercion is applied to the function and method arguments.
When we give a reference of a certain type to a function that does not match the type of an argument in the function declaration, deref coercion occurs automatically.
Example:
struct MyBox(T);
use :: std::ops::Deref;
impl MyBox
{
fn hello(x:T)->MyBox
{
MyBox(x)
}
}
impl Deref for MyBox
{
type Target = T;
fn deref(&self) ->&T
{
&self.0
}
}
fn print(m : &i32)
{
print!("{}",m);
}
fn main()
{
let b = MyBox::hello(5);
print(&b);
}
Output:
5
The print(&b) function is being called in the example above with the argument &b, which is the reference of &Box<i32>. In this instance, we apply the Deref trait, which uses Deref Coercion to change the &Box<i32> into &i32.
Interaction of Derif Coercion with mutability
Up until now, we have been able to override the * operator on immutable references using the Deref Trait, and we can now override the * operator on mutable references using the DerefMut trait.
Rust performs Deref coercion in the following three cases:
- &T is changed to &U type when T: Deref<Target = U>, where T and U are the immutable references.
- &mut T is changed to &mut U when T: DerefMut<Target = U>, where T and U are the mutable references.
- &mut T is changed to &U when T: Deref<Target = U>, where T is a mutable reference and U is an immutable reference.
Note: Rust can coerce the mutable reference into the immutable reference, but it cannot coerce the immutable reference into the mutable reference because of the borrowing rules.