I have a usual list (sigh...) and I want to save a mutable ref to one node for later.
I have this walking cycle:
while let Some(ref node) = cur {
if looks_interesting(){
target = cur;
}
cur = &node.next;
}
After cycle is done, I want to change content of the target. As far as I understand Rust safety this is safe: cur is gone and if only I have had a mutable reference in target, that's would be easy.
But target is shared, and I can't make it mut, as cur need to become mut, and I can't do it.
So, whilst I see that having mut target is totally safe, how can I pursue compiler to allow me do so?
You can share mutable references (in a limited manner) via re-borrowing. So if your loop operates via &mut references, and target, if set, gets reborrowed to create the next cur, the whole thing becomes just about possible. In theory. If it wasn’t for limitations in the current borrow checker
I.e. with -Z polonius (unstable flag for a new experimental improved borrow checker) the following works
struct Node {
contents: i32,
next: List,
}
type List = Option<Box<Node>>;
fn looks_interesting(_: i32) -> bool {
todo!();
}
fn foo(mut list: List) {
let mut cur = &mut list;
let mut target = &mut None;
while let Some(mut node) = cur.as_mut() {
if looks_interesting(node.contents) {
target = cur;
node = target.as_mut().unwrap(); // re-create `node`, borrowing from `target`
}
cur = &mut node.next;
}
// iteration done, now use target
if let Some(target_node) = target.as_mut() {
target_node.contents += 1;
}
}
On stable rustc, the most straightforward approach might simply be to count the items, save the desired index in the list as a number, and in the end re-walk the list down to that index. Asymptotically, that’s likely not even less efficient.
I just took the challenge of trying to make this work on stable rust using @Yandros's crate polonius_the_crab - Rust. Looks like it’s possible; and I even got rid of the unwrap:
/*
[dependencies]
polonius-the-crab = "0.2"
*/
use polonius_the_crab::prelude::*;
struct Node {
contents: i32,
next: List,
}
type List = Option<Box<Node>>;
fn looks_interesting(_: i32) -> bool {
todo!();
}
fn foo(mut list: List) {
fn find_next_interesting_target(mut cur: &mut List) -> Option<&mut Node> {
while let Some(mut node) = cur.as_deref_mut() {
polonius! {
|node| -> Option<&'polonius mut Node> {
if looks_interesting(node.contents) {
polonius_return!(Some(node));
}
}
}
cur = &mut node.next;
}
None
}
if let Some(mut target) = find_next_interesting_target(&mut list) {
while let Some(new_target) = find_next_interesting_target(&mut target.next) {
target = new_target;
}
// iteration done, now use target
target.contents += 1;
}
}