Share data using Rc<T>
In this section, we will start with a problem. We will try to implement the list in the diagram:
In the figure, listing A contains 5, followed by 10, listing B starts at 3, and listing C starts at 4. B and C will be joined to list A containing 5 and 10. In other words, the two lists try to share the 5 and 10 contained in list A.
Using Box
Let’s try using the Box type we’ve already learned:
enum List {
Cons(i32.Box<List>),
Nil,
}
// Create a list
let a = List::Cons(5.Box::new(List::Cons(10.Box::new(List::Nil))
)
);
// create list B and join list A
let b = List::Cons(3.Box::new(a));
// create list C and join list A
let c = List::Cons(4.Box::new(a)); // The ownership of a has been moved
Copy the code
Use references to solve the problem of transferring ownership of A
We can change the list type in Box to a reference type, so that b and C can both reference A:
#[derive(Debug)]
enum List<'a> {
Cons(i32.BoxThe < &'a List<'a>>),
Nil,
}
Copy the code
Reference Method 1:
let a = List::Cons(5.Box::new(&List::Cons(10.Box::new(&List::Nil)) // An error was reported, creating a temporary reference &nil, but it was destroyed when a was assigned));// &nil the closing statement here is destroyed before a has been created
println!("{:? }", a);
Copy the code
Reference Mode 2:
let a2 = &List::Nil;
let a1 = &List::Cons(10.Box::new(a2));
let a = &List::Cons(5.Box::new(a1));
let b = List::Cons(3.Box::new(a));
let c = List::Cons(4.Box::new(a));
println!("{:? }", b); // Cons(3, Cons(5, Cons(10, Nil)))
println!("{:? }", c); // Cons(4, Cons(5, Cons(10, Nil)))
Copy the code
Although it can be done, nesting relationships are not very intuitive.
Use Rc<T> to resolve
Rc<T> supports multiple ownership and the Rc in its name is short for Referencecounting. For those of you who know the concept of garbage collection, the Rc<T> type maintains an internal reference-count to verify that the value is still in use. A zero number of references to a value means that the value can be safely cleaned up without triggering reference invalidation:
use std::rc::Rc; / / into the Rc
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>), // replace Box with Rc
Nil
}
// Create an Rc instance
let a = Rc::new(List::Cons(5,
Rc::new(List::Cons(10,
Rc::new(List::Nil)
))
));
// Rc::clone is just an incremental reference count, although a. lone can also be used, but the data will be deeply copied
let b = List::Cons(3, Rc::clone(&a));
// Increase the number of references again
let c = List::Cons(4, Rc::clone(&a));
println!("{:? }", b); // Cons(3, Cons(5, Cons(10, Nil)))
println!("{:? }", c); // Cons(4, Cons(5, Cons(10, Nil)))
Copy the code
Observing reference counting
The current number of references can be obtained using the method provided by Rc<T> :
let a = Rc::new(List::Cons(5,
Rc::new(List::Cons(10,
Rc::new(List::Nil)
))
));
println!("Reference count after creation of a: {}", Rc::strong_count(&a));
// The reference count after creating a: 1
let b = List::Cons(3, Rc::clone(&a));
println!("Reference count after creation of b: {}", Rc::strong_count(&a));
// The reference count after b is created: 2
// Enter a new scope
{
let c = List::Cons(2, Rc::clone(&a));
println!("Reference count after c creation: {}", Rc::strong_count(&a));
// Reference count after c is created: 3
} // when c leaves scope, the reference count is automatically reduced by 1.
println!("Reference count after c destruction: {}", Rc::strong_count(&a));
// The reference count after c is destroyed: 2
Copy the code
Rc<T> makes it possible to share read-only data between different parts of a program through immutable references. If Rc<T> also allowed to hold multiple mutable references, it would violate the borrowing rule: multiple mutable borrows pointing to the same region would result in data contention and data inconsistencies.