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:
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
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:
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:
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;
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:
Example:
use std::cell::RefCell;
fn main()
{
let a = RefCell::new(15);
let b = a.borrow_mut();
let c = a.borrow_mut();
}
Output:
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>,Rc),
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 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.