Industrial Training




Rust RefCell(T)

Interior mutability pattern is a pattern is used to mutate the reference if we have an immutable reference. RefCell< T> can be used to achieve the interior mutability.


Important Points to remember:
    RefCell< T> represents the single ownership over the data that it holds.
    If we use RefCell< T>, then the invariants are enforced at the runtime.
    RefCell< T> is mainly used in the single-threaded scenario and will give an error if we use in a multithreaded case.
    RefCell< T> checks the mutable borrows at the runtime. Therefore, we can say that we can mutate the value even when the RefCell< T> value is immutable.

Interior Mutability


According to the borrowing rules, if we have an immutable value, then we cannot borrow mutably.


Let's see a simple example:
 fn main()  
{  
  let a = 15;  
  let b = &mut a;  
}  

Output:

In the above example, we have seen that the immutable value cannot be borrowed mutably. But, RefCell is the one way to achieve the interior mutability.


Keeping Track of Borrows at Runtime with RefCell< T>


RefCell< T> consists of two methods that keep track of borrows at runtime:


    borrow(): The borrow() method returns the smart pointer of type Ref< T>.
    borrow_mut(): The borrow_mut() method returns the smart pointer of type RefMut< < T>.

Note: Both the types Ref< T> and RefMut< T> implements Deref trait. Therefore, they can be treated as regular references.


Some Important Points:
    The RefCell< T> keeps a record of how many Ref< T> and Refmut< T> smart pointers are currently active.
    Whenever the borrow() method is called, then the RefCell< T> increases the count of how many immutable borrows are active. When the Rc< T> goes out of the scope, then RefCell< T> decreases the count by one.
    The RefCell< T> lets us have many immutable borrows but one mutable borrow at a time, just as compile-time borrowing rules. If we violate this rule, then the RefCell< T> will panic at runtime.

borrow() method


The borrow() method borrows the immutable value. Multiple immutable borrows can be taken at the same time.


Syntax:
pub fn borrow(&self) -> Ref< T>  

Let's see a simple example when multiple immutable borrow occurs:
 use std::cell::RefCell;  
fn main()  
{  
  let a = RefCell::new(15);  
  let b = a.borrow();  
  let c = a.borrow();  
  println!("Value of b is : {}",b);  
  println!("Value of c is : {}",c);  
}  

Output:

Let's see a simple example of panic condition:
use std::cell::RefCell;  
fn main()  
{  
  let a = RefCell::new(10);  
  let b = a.borrow();  
  let c = a.borrow_mut(); // cause panic.  
  println!("Value of b is : {}",b);  
  println!("Value of c is : {}",c);  
}  

Output:

In the above example, program panics at runtime as immutable borrows and mutable borrows cannot occur at the same time.


borrow_mut() method


The borrow_mut() method borrows the mutable value. Mutable borrows can occur once.


Syntax:
pub fn borrow_mut(&self) -> RefMut< T>;

Let's see a simple example:
 use std::cell::RefCell;  
fn main()  
{  
  let a = RefCell::new(15);  
  let b = a.borrow_mut();  
  println!("Now, value of b is {}",b);  
}  


Let's see a simple example of panic condition:
 use std::cell::RefCell;  
fn main()  
{  
  let a = RefCell::new(15);  
  let b = a.borrow_mut();  
  let c = a.borrow_mut();  
}  

Output:

In the above example, mutable borrow occurs twice. Therefore, the program panics at the runtime and throws an error 'already borrowed: BorrowMutError'.


Multiple owners of Mutable Data By combining Rc< T> and RefCell< T>


We can combine Rc< T> and RefCell< T> so that we can have multiple owners of mutable data. The Rc< T> lets you have multiple owners of a data, but it provides only immutable access to the data. The RefCell< T> lets you to mutate the data. Therefore, we can say that the combination of Rc< T> and RefCell< T> provides the flexibility of having multiple owners with mutable data.


Let's see a simple example:
 #[derive(Debug)]  
enum List  
{  
 Cons(Rc< RefCell< String>>,Rc< List>),  
 Nil,  
}  
use List:: {Cons,Nil};  
use std::rc::Rc;  
use std::cell::RefCell;  
fn main()  
{  
  let val = Rc::new(RefCell::new(String::from("java")));  
  let a = Rc::new(Cons(Rc::clone(&val),Rc::new(Nil)));  
  let b = Cons(Rc::new(RefCell::new(String::from("C"))),Rc::clone(&a));  
  let c = Cons(Rc::new(RefCell::new(String::from("C++"))),Rc::clone(&a));  
  *val.borrow_mut() = String::from("C# language");  
  println!("value of a is : {:?}",a);  
  println!("value of b is : {:?}",b);  
  println!("value of c is : {:?}",c);  
}  

Output:

In the above example, we create a variable 'val' and store the value "java" to the variable 'val'. Then, we create the list 'a' and we clone the 'val' variable so that both the variable 'a' and 'val' have the ownership of 'java' value rather than transferring the ownership from 'val' to 'a' variable. After creating 'a' list, we create 'b' and 'c' list and clones the 'a' list. After creating the lists, we replace the value of 'val' variable with "C#" language" by using the borrow_mut() method.




Hi I am Pluto.