Theoretical Paper
- Computer Organization
- Data Structure
- Digital Electronics
- Object Oriented Programming
- Discrete Mathematics
- Graph Theory
- Operating Systems
- Software Engineering
- Computer Graphics
- Database Management System
- Operation Research
- Computer Networking
- Image Processing
- Internet Technologies
- Micro Processor
- E-Commerce & ERP
- Dart Programming
- Flutter Tutorial
- Numerical Methods Tutorials
- VueJS
- Go
Practical Paper
Industrial Training
Deref< T>
-
Deref< T> trait is used to customize the behavior of dereference operator (*).
If we implement the Deref< T> trait, then the smart pointer can be treated as a reference. Therefore, the code that works on the references can also be used on the smart pointers too.
Regular References
Regular reference is a kind of pointer that points to some value which is stored somewhere else. Let's see a simple example to create the reference of i32 type value and then we use the dereference operator with this reference.
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 above example, a holds the i32 type value, 20 while b contains the reference of 'a' variable. If we use *b, then it represents the value, 20. Therefore, we can compare the variable a and *b, and it will return the true value. If we use &b instead of *b, then the compiler throws an error "cannot compare {integer} with {& integer}".
Box< T> as a Reference
The Box< T> pointer can be used as a reference.
Let's see a simple example:
fn main() { let a = 11; let b = Box::new(a); print!("Value of *b is {}",*b); }
Output:
Value of *b is 11
In the above example, Box< T> behaves similarly as the regular references. The only difference between them is that b contains the box pointing to the data rather than referring to the value by using the '&' operator.
Smart Pointer as References
Now, we create the smart pointer similar to the Box< T> type, and we will see how they behave differently from the regular references.
/
-
The Box< T> can be defined as the tuple struct with one element for example, MyBox< T>.
After creating the tuple struct, we define the function on type MyBox< T>.
Let's see a simple example:
struct MyBox< T>(T); impl< T> MyBox< T> { fn example(y : T)->MyBox< T> { MyBox(y) } } fn main() { let a = 8; let b = MyBox::example(a); print!("Value of *b is {}",*b); }
Output:
In the above example, we create the smart pointer, b, but it cannot be dereferenced. Therefore, we conclude that the customized pointers which are similar to the Box< T> type cannot be dereferenced.
Implementing a Deref Trait
-
The Deref trait is defined in the standard library which is used to implement the method named deref.
The deref method borrows the self and returns a reference to the inner data.
Let's see a simple example:
struct MyBox< T> { a : T, } use :: std::ops::Deref; impl< T> Deref for MyBox< T> { type Target = T; fn deref(&self) ->&T { &self.a } } fn main() { let b = MyBox{a : 10}; print!("{}",*(b.deref())); }
Output:
10
Program Explanation
-
The Deref trait is implemented on the MyBox type.
The Deref trait implements the deref() method, and the deref() method returns the reference of 'a' variable.
The type Target = T; is an associated type for a Deref trait. Associated type is used to declare the generic type parameter.
We create the instance of MyBox type, b.
The deref() method is called by using the instance of MyBox type, b.deref() and then the reference which is returned from the deref() method is dereferenced.
Deref Coercion
-
Deref Coercion is a process of converting the reference that implements the Deref trait into the reference that Deref can convert the original type into.
Deref Coercion is performed on the arguments of the functions and methods.
Deref Coercion happens automatically when we pass the reference of a particular type to a function that does not match with the type of an argument in the function definition.
Let's see a simple example:
struct MyBox(T); use :: std::ops::Deref; impl< T> MyBox< T> { fn hello(x:T)->MyBox< T> { MyBox(x) } } impl< T> Deref for MyBox< T> { 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
In the above example, we are calling the print(&b) function with an argument &b, which is the reference of &Box
Interaction of Derif Coercion with mutability
Till now, we use the Deref Trait to override the * operator on immutable references, and we can use the DerefMut trait to override the * operator on mutable references.
Rust performs Deref coercion in the following three cases:
-
When T: Deref< Target = U> where T and U are the immutable references , then &T is converted into &U type.
When T: DerefMut< Target = U> where T and U are the mutable references, then &mut T is converted into &mut U.
When T: Deref< Target = U> where T is a mutable reference and U is an immutable reference, then &mut T is converted into &U.
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.