Extracting mut ref from non-mut ref

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?

2 Likes

You can't do that. Rust enforces that mutable references are always exclusive, and you can never go from &T to &mut T.

1 Like

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 :sweat_smile:

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;
    }
}
3 Likes

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.

1 Like

The other, uglier option is to start heading down the RefCell/Mutex path to get interior mutability.

1 Like

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;
    }
}

(Rust Explorer)

I haven’t tested running the code… but it compiles, so it ought to work :sweat_smile:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.