Often times, there are good ways to re-write the code in this case so that the current borrow checker accepts it as-well. Assuming that the code does exactly what you want to do, in this case, swapping the order of the if-statements does the job,
fn main() {
let mut head = Some(Box::new(ListNode::new(0)));
let mut tail = &mut head;
for i in 1..=5 {
if i == 1 {
if let Some(ref mut node) = tail {
node.next = Some(Box::new(ListNode::new(i)));
tail = &mut node.next;
}
}
}
}
however this is a very special-case kind of fix. To name a more general approach: What most often works for code that polonius accepts but stable compiler doesn’t is to re-trace your steps down to the value you’re assigning / returning / etc… conditionally. The value assigned to tail
in this case is &mut node.next
. It uses the node
variable and the borrow-checker isn’t happy. However, if you’re removing the extra step with the node
variable from the final connection and just assign something depending on tail
to tail
itself it turns out to be enough to make the borrow-checker happy. We can unwrap
because we know it’s an Option::Some
, so node
is tail.as_mut().unwrap()
, and we get
fn main() {
let mut head = Some(Box::new(ListNode::new(0)));
let mut tail = &mut head;
for i in 1..=5 {
if let Some(ref mut node) = tail {
if i == 1 {
node.next = Some(Box::new(ListNode::new(i)));
//tail = &mut node.next;
// re-trace our way here:
tail = &mut tail.as_mut().unwrap().next;
}
}
}
}
In relation to my previous explanation, this fix demonstrates quite clearly, that it was the lifetime of the reference node
in particular that confused the borrow-checker. In a more general setting, you’d often also have the condition itself depend on something like node
. The re-tracing approach keeps the lifetime of node
unconditionally short, the re-tracing can be done as soon as you’re gone past the innermost conditional. So you could’ve also done it before the node.next = …
line, the following code works, too:
fn main() {
let mut head = Some(Box::new(ListNode::new(0)));
let mut tail = &mut head;
for i in 1..=5 {
if let Some(ref mut node) = tail {
if i == 1 {
let node = tail.as_mut().unwrap();
node.next = Some(Box::new(ListNode::new(i)));
tail = &mut tail.as_mut().unwrap().next
}
}
}
}
note that the outer node
variable is completely unused now; the code further simplifies to
fn main() {
let mut head = Some(Box::new(ListNode::new(0)));
let mut tail = &mut head;
for i in 1..=5 {
if tail.is_some() {
if i == 1 {
let node = tail.as_mut().unwrap();
node.next = Some(Box::new(ListNode::new(i)));
tail = &mut node.next
}
}
}
}
and
fn main() {
let mut head = Some(Box::new(ListNode::new(0)));
let mut tail = &mut head;
for i in 1..=5 {
if tail.is_some() && i == 1 {
let node = tail.as_mut().unwrap();
node.next = Some(Box::new(ListNode::new(i)));
tail = &mut node.next
}
}
}
By the way, moving the assignment back out one level instead, i.e.
fn main() {
let mut head = Some(Box::new(ListNode::new(0)));
let mut tail = &mut head;
for i in 1..=5 {
if tail.is_some() { // compiler unhappy
let node = tail.as_mut().unwrap();
if i == 1 {
node.next = Some(Box::new(ListNode::new(i)));
tail = &mut node.next
}
}
}
}
fails, as expected. But the error message is a bit nicer than in the original code (because we don’t have all the hints pointing to essentially the same place); try it out for yourself.