I am subjecting myself to some torture in the form of implementing safe data structures to better learn the language, but I think I'm either pushing the limits of safe rust or my understanding of the language.
I am experimenting with RefCell
, OnceCell
, Rc
and Weak
by implementing linked lists with head access that only support pushing to the tail, that is, lists that must find their tails in order to grow them. Both with iterative and recursive approaches.
Consider this list made with OnceCell
and a node that only contains a reference to the next element:
// SINGLY LIST MADE WITH ONCECELL ----------------------------------------------
struct SinglyOnceNode {
next: Rc<OnceCell<SinglyOnceNode>>,
}
impl SinglyOnceNode {
fn new() -> Self {
Self { next: Rc::new(OnceCell::new()) }
}
}
pub struct SinglyOnceList {
head: Rc<OnceCell<SinglyOnceNode>>,
}
impl SinglyOnceList {
pub fn new() -> Self {
Self { head: Rc::new(OnceCell::new()) }
}
pub fn push_iterative(&self) {
let mut link = &self.head;
while let Some(node) = link.get() {
link = &node.next;
}
let _ = link.set(SinglyOnceNode::new());
}
pub fn push_recursive(&self) {
Self::rec(&self.head);
}
fn rec(link: &Rc<OnceCell<SinglyOnceNode>>) {
match link.get() {
None => { let _ = link.set(SinglyOnceNode::new()); },
Some(node) => Self::rec(&node.next),
}
}
}
This works exactly as expected, a &
shared reference is returned to the node and everything is fine.
Now consider the same list but implemented with RefCell
:
// SINGLY LIST MADE WITH REFCELL -----------------------------------------------
struct SinglyRefNode {
next: Option<Rc<RefCell<SinglyRefNode>>>,
}
impl SinglyRefNode {
fn new() -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self { next: None }))
}
}
pub struct SinglyRefList {
head: Option<Rc<RefCell<SinglyRefNode>>>,
}
impl SinglyRefList {
pub fn new() -> Self {
Self { head: None }
}
pub fn push_iterative(&mut self) {
let mut link_opt = &mut self.head;
while let Some(link) = link_opt {
link_opt = &mut link.borrow_mut().next; // <------------ DOESN'T WORK
}
link_opt.replace(SinglyRefNode::new());
}
pub fn push_recursive(&mut self) {
Self::rec(&mut self.head);
}
fn rec(link: &mut Option<Rc<RefCell<SinglyRefNode>>>) {
match link {
None => { link.replace(SinglyRefNode::new()); },
Some(link) => Self::rec(&mut link.borrow_mut().next),
}
}
}
The recursive version works perfectly fine, but the iterative version either doesn't work or I am incapable of making it work.
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:70:29
|
69 | while let Some(link) = link_opt {
| -------- borrow later used here
70 | link_opt = &mut link.borrow_mut().next; // DOESN'T WORK
| ^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
|
= note: consider using a `let` binding to create a longer lived value
I have tried to solve it with the advice but I've been completely incapable. What is the problem here? My understanding by reading "Too Many Linked Lists" Is that .borrow()
and .borrow_mut()
create a "tunnel" of references and if the tunnel is cut none of the references down the line will work. Am I wrong?.
Also, the recursive version works because it's not tail recursive and therefore all references live in their own stack frames?.
Many thanks!!