Rust Smart Pointers

Rust RefCell<T>

RefCell

Interior mutability In the event that the reference is immutable, a pattern is utilized to change it. It is possible to accomplish the interior mutability using RefCell<T>.

Important Points to remember:

  • RefCell<T> stands for the exclusive ownership of the information it contains.
  • The invariants are enforced at runtime if we utilize RefCell<T>.
  • RefCell<T> is primarily used in single-threaded scenarios; using it in a multithreaded scenario will result in an error.
  • At runtime, RefCell<T> verifies the mutable borrows. As a result, even in cases where the RefCell<T> value is immutable, we can still change the value.

Interior Mutability

We cannot borrow mutably if we have an immutable value, as per the borrowing rules.

Example:

				
					 fn main()  
{  
  let a = 15;  
  let b = &mut a;  
}  
				
			

Output:

Rust RefCell<T> -

We have seen in the example above that an immutable value cannot be borrowed in a modified manner. However, RefCell is the only method that allows for interior mutability.

Keeping Track of Borrows at Runtime with RefCell

RefCell<T> has two ways to track borrows during runtime:

  • borrow(): The smart pointer of type Ref<T> is returned by the borrow() method.
  • borrow_mut(): The smart pointer of type RefMut\<T> is returned by the borrow_mut() function.

Some Important Points:

  • The ReffCell<T> maintains track of the number of active Ref<T> and Refmut<T> smart pointers.
  • The RefCell<T> increments the count of active immutable borrows each time the borrow() method is called. RefCell<T> reduces the count by one when the Rc<T> leaves the scope.
  • Similar to compile-time borrowing restrictions, the RefCell<T> allows us to have several immutable borrows but only one mutable borrow at a time. The RefCell<T> will panic at runtime if we break this rule.

borrow() method

The immutable value is borrowed by the borrow() method. It is possible to accept more than one immutable borrow at once.

Syntax:

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

Example:

				
					 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:

Rust RefCell<T> -

Example:

				
					 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:

Rust RefCell<T> -

The program in the aforementioned example panics during runtime because mutable and immutable borrows cannot happen simultaneously.

borrow_mut() method

The borrow_mut() method borrows a mutable value. Mutable borrowing can only occur once.

Syntax:

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

Example:

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

Output:

Rust RefCell<T> -

Example:

				
					 use std::cell::RefCell;  
fn main()  
{  
  let a = RefCell::new(15);  
  let b = a.borrow_mut();  
  let c = a.borrow_mut();  
} 
				
			

Output:

Rust RefCell<T> -

In the preceding example, mutable borrow appears twice. As a result, the program panics during runtime and returns the error ‘already borrowed: BorrowMutError’.

Multiple owners of Mutable Data By combining Rc and RefCell

We can combine Rc<T> and RefCell<T> to allow multiple owners of mutable data. Although Rc<T> allows for numerous data owners, it only gives immutable access to the data. The RefCell<T> allows you to modify the data. Combining Rc<T> and RefCell<T> allows for many owners with changeable data.

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:

Rust RefCell<T> -

In the example above, we define a variable ‘val’ and assign the value “java” to it. Then, we construct the list ‘a’ and clone the ‘val’ variable such that both ‘a’ and ‘val’ own the ‘java’ value rather than moving ownership from ‘val’ to the ‘a’ variable. After constructing the ‘a’ list, we generate the ‘b’ and ‘c’ lists before cloning the ‘a’ list. After constructing the lists, we use the borrow_mut() method to replace the value of the ‘val’ variable with “C#” language.

Share this Doc

Rust RefCell<T>

Or copy link

Explore Topic