POINTERS
- Stack is LIFO.
- When your functions returns to the base function, the value in the stack is not deleted, the stack just moves its pointer to a higher point.
- Heap is random access.
- A pointer is a general concept for a variable that contains an address in memory. This address refers to some other data.
- Smart pointers are data structure that acts like a pointer but also have additional metadata and capabilities.
- Unlike normal reference, smart pointer themselves own the data they point to.
Box<T>
- Box <T> is one of those smart pointers in rust, which is used allocating values in heap. i.e it can be used to store even integer in heap.
fn main(){
let age:Box<i32> = Box::new(10);
let double_age: i32 = *age * 10;
println!("{}", age);
println!("{}", double_age);
}
- The above code block shows a basic example of box usage.
- As you can see here the integer is stored in heap with * dereferences unary operator. It extracts the value of the variable, omiitting the pointer or reference.
- Deref is a trait that a rust can use to dereference the box.
use::std::ops::Deref;
struct BoxedValue<T>{
value: T
}
impl<T> Deref for BoxedValue<T>{
type Target = T;
fn deref(&self) -> &self::Target{
&self.value
}
}
fn main(){
let age:Box<i32> = BoxedValue::new(10);
let double_age: i32 = *age * 10;
println!("{}", age);
println!("{}", double_age);
}
- The above block shows the implementation of Box on our own.
fn main(){
let age:Box<i32> = BoxedValue::new(10);
let double_age: &i32 = age.deref();
println!("{}", double_age);
}
- Although deref method and * may look like it is providing the same functionality, it's really not.
- deref method gives a reference to the boxed value whereas * provides the actual value.
fn print_integer(value: &i32){
println!("{}", value);
}
fn main(){
let age: Box<i32> = BoxedValue::new(10);
let test: &Box<i32> = &age;
print_integer(&age);
}
- In the above example you can see that when you are passing a reference of a boxed value, the code is working perfectly without any issue it is called implicit deref coercion.
Rc<T> (Reference counted)
- Used to enable multiple ownership explicitly by using Rust Type Rc<T>. It keeps track of the number of references to a value to determine whether or not the value is still in use.
- Rc is single threaded. i.e, it cannot be passed from one thread to another.
- It disallows mutation of wrapped value.
- You call also create a weak reference, as long the objects exist, the reference is also available but when the objects is removed the weak reference will throw an error.
use ::std::rc::Rc;
fn main(){
let array: Vec<String> = vec!["Sanu Shilshad".to_string()];
let rc: Rc<Vec<String>> = Rc::new(array);
let weak: Weak<Vec<String>> = Rc::downgrade(this: &rc);
drop(rc);
let value: Rc<Vec<String>> = weak.upgrade().unwrap();
println!("{:?}", value);
}
- The above example shows an example of the working on of weak reference, and the result would be an error as the rc is already removed.
- In the above example upgrade method returns a option and you can use a match on it like below:
use ::std::rc::Rc;
fn main(){
let array: Vec<String> = vec!["Sanu Shilshad".to_string()];
let rc: Rc<Vec<String>> = Rc::new(array);
let weak: Weak<Vec<String>> = Rc::downgrade(this: &rc);
drop(rc);
match weak.upgrade(){
some(rc) => println!("{:?}", rc),
None => println!("None")
}
}
- In the above example rc2 will have have reference to the array even if the rc is dropped unlike weak reference.
- You can't mutate value stored inside an Rc, it is not possible in Rc, it there anther type called Cell which allow mutability.
Cell<T>
- This type provides interior mutability, i.e, it allows you to mutate data even when there are immutable reference to that data.
- This type holds single ownership over the data it holds.
- This is highly unsafe.
use ::std::cell::Cell;
struct Person{
name: String,
age: Cell<u8>
}
impl Person {
fn increment_age(&self) -> u8{
self.age.set(val:self.age.get() + 1);
self.age.get()
}
}
fn main(){
let person: Person = Person{
name: "Sanu Shilshad".to_string(),
age: Cell::new(20)
}
let new_age: u8 = person.increment_age();
println!("{}, new_age);
println!("{}, person.age.get());
}
- The above example shows a basic implementation of Cell.
- RefCell a much safer type which allows multiple immutable borrows or one mutable borrow.
- With type box, the borrowing rules are enforced during compile time. While with type Refcell it is enforced during runtime.
- Refcell is only allowed in single threaded environment.
use ::std::cell::RefCell;
fn main(){
let ref_cell: RefCell<Vec<i32>> = RefCell::new(vec![1, 2, 4]);
let mut mut_ref: RefMut<Vec<i32>> = ref_cell,borrow_mut();
let len: usize = ref_cell.borrow().len();
println("{}", len);
mut_ref.push(100);
}
- The above example will throw a panic at run time well as we have called it twice, a mutable and an immutable one, thus breaking RefCell's rule.
Comments
Post a Comment