I have a function that iterates over a linked-list data structure, but it does not compile:
use std::cell::RefCell;
use std::rc::{Rc, Weak};
struct Node {
pub data: i32,
pub next: Option<Rc<RefCell<Node>>>,
pub prev: Option<Weak<RefCell<Node>>>,
}
fn print_iterate_doesnt_work(node: &Rc<RefCell<Node>>) {
let mut current_node = node.clone();
loop {
current_node = {
let current_node_ref = current_node.borrow();
print!("{} ", current_node_ref.data);
if current_node_ref.next.is_none() {
return;
}
current_node.borrow().next.as_ref().unwrap().clone()
}
}
}
error[E0506]: cannot assign to `current_node` because it is borrowed
--> src/lib.rs:13:9
|
13 | current_node = {
| ^^^^^^^^^^^^ `current_node` is assigned to here but it was already borrowed
...
20 | current_node.borrow().next.as_ref().unwrap().clone()
| ---------------------
| |
| `current_node` is borrowed here
| a temporary with access to the borrow is created here ...
21 | }
22 | }
| - ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, Node>`
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
= note: borrow occurs due to deref coercion to `RefCell<Node>`
note: deref defined here
--> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/rc.rs:2124:5
|
2124 | type Target = T;
| ^^^^^^^^^^^
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
20 | let x = current_node.borrow().next.as_ref().unwrap().clone(); x
| +++++++ +++
For more information about this error, try `rustc --explain E0506`.
I understand current_node.borrow()
creates temporary Ref
. Why doesn't compiler see that I don't use current_node
and the temporary Ref
?
I also don't fully understand "and the borrow might be used here, when that temporary is dropped and runs the destructor for type Ref<'_, Node>
". As a result I don't understand why I should "save the expression's value in a new local variable x
and then make x
be the expression at the end of the block: let x = ...;
x`".
Changing the last line of the block to this makes it compile though:
current_node_ref.next.as_ref().unwrap().clone()
So what's going on here?
Your non working version is equivalent to this:
fn print_iterate_doesnt_work(node: &Rc<RefCell<Node>>) {
let mut current_node = node.clone();
loop {
let current_node_ref = current_node.borrow();
print!("{} ", current_node_ref.data);
if current_node_ref.next.is_none() {
return;
}
current_node = current_node.borrow().next.as_ref().unwrap().clone();
}
}
Now the problem is that the temporary current_node.borrow()
isn't dropped until the end of the statement. But at that time current_node
has already been replaced.
The working example works, because it stores the temporary in a block scoped variable, which is dropped before returning from the block, so also before reassigning current_node
so everything works out.