πDeref Trait
What is Deref
?
Deref
?π‘ Deref
is a trait that defines how to dereference a type. Dereferencing means accessing the underlying value that a reference points to.
π‘ The Deref
trait allows an instance of smart pointer to behave like a regular reference to the value it contains. By implementing the Deref
trait, you can customize the behavior of the dereference operator to work with your custom types, enabling them to be used in contexts where references are expected.
The Deref
trait is part of Rust's standard library and is defined as follows:
It contains one property and one required method:
type Target: ?Sized
: This defines an associated type namedTarget
within the trait which represents the type that we want to dereference to. TheTarget
type can be any type, and it can also be a trait itself. The?Sized
part indicates that the size of the target type might not be known at compile time.fn deref(&self) -> &Self::Target
: This defines the required method of the trait namedderef
. It takesself
(a reference to the implementing type) as an argument and returns a reference (&
) to the associated typeTarget
.
How Deref
Is Used?
Deref
Is Used?Implicit Dereferencing: The compiler often performs implicit dereferencing when using references. For example, if you have a reference to a variable (
&x
), you can directly usex
in many situations, and the compiler will automatically dereference it to access the actual value.Explicit Dereferencing: You can also explicitly dereference a reference using the dereference operator (
*
). This is useful when the compiler can't perform implicit dereferencing, or when you want to be more explicit about your code.
How Can We Use Deref
?
Deref
?The Deref
trait solves the issue of working with different types of references in a consistent way.
Unifying Reference Types: Rust has different types of references like
&T
(immutable reference),&mut T
(mutable reference), andBox<T>
(heap-allocated value). TheDeref
trait allows you to define a common way to access the underlying value for all these reference types. This simplifies code that needs to work with various reference types.Custom Dereferencing Behavior: You can implement the
Deref
trait for custom types. This allows you to define how your custom type should be dereferenced.
Implicit Dereferencing
This is the most common way Deref
is used. The compiler automatically dereferences a reference when it's used in certain contexts.
In this example, the print_value
function takes a reference to an i32
(&i32
).
Inside the function, we can directly use x
within the println!
macro because the compiler implicitly dereferences x
to access the underlying value.
Explicit Dereferencing
You can also explicitly dereference a reference using the dereference operator (*
). This is useful when implicit dereferencing isn't allowed or for clarity:
In this example, The try_swap
function takes two mutable references (&mut i32
) as arguments, indicating it borrows both values for modification. This function will try to swap the value of x
and y
.
We create a temporary variable temp
to hold the value of x
before overwriting it.
Since we have mutable references (&mut i32
), we need to explicitly dereference them using *
to access and modify the underlying values.
This let temp = *x
line, we dereference x
to get its value and stores it in temp
.
After that we assign (x = *y
) the dereferenced value of y
(using y
) to the location pointed to by x
(achieved through dereferencing x
).
And the last line, we assign (y = temp
) the value stored in temp
(which is the original value of x
) to the location pointed to by y
(again, dereferencing y
).
The main function would return swapped value between x
and y
.
Without explicit dereferencing, attempting to assign values directly to x
and y
within the function would result in a compile error because Rust's borrowing rules require explicit access to the underlying mutable values.
Using Deref
with Box<T>
Deref
with Box<T>
The Box<T>
type implements Deref
, allowing it to be used as a reference to T
:
In this example, we declare a variable x
which holds a Box<i32>
of value 5
.
We print the value of x
twice, one is x
itself, and the other is a *x
(dereference operator) which calls the deref
method on the Box
.
We can see that both outputs return the same value 5
, however the first line returns a reference to the value 5
instead of actual value. And, the second line returns the actual value. Because the dereference operator allows us to access the underlying value.
For clarifying the value of x
, we use 2 assertions. The first one asserts that x
is equal 5
. This will cause an ERROR, because 5
is an i32
type and the x
is a reference of i32
. Rust doesnβt allow to compare a number and a reference to a number because theyβre different types.
To compare those values, we must use the dereference operator to follow the reference to the value itβs pointing to. In the second assertion, we have to use *x
to follow the reference to the value of x
(5), so the compiler can compare the actual value.
Using Deref
with Custom Smart Pointer
Deref
with Custom Smart PointerIn this example, weβre going to create a our own smart pointer which is called MySmartPointer<T>
that implements Deref
to allow dereferencing to the inner value T
.
We create a MySmartPointer
struct which wraps a value of type T
.
After that we implement a new
function for MySmartPointer
instance. Itβs taking an argument x
of type T
and storing it in the internal field.
To allow dereference for our smart point, weβre going to implement Deref
trait for MySmartPointer
. The deref
method returns a reference (&
) to the inner value (&self.0
- the .0
accesses the first value in a tuple struct). This allows dereferencing the smart pointer to access the underlying data.
In the main function, we create a MySmartPointer
instance with value 5.
In the first line, we directly print the MySmartPointer
instance which returns the instance of MySmartPointer
with the value 5 (x = MySmartPointer(5)
).
To print the actual value, in the second line, we explicitly dereference the smart point using dereference operator (*x
) to access the underlying value.
When we entered the *x
, behind the scenes Rust actually ran this code:
Last updated