Why does moved function does not change captured mutable value?

Consider following code

fn main(){
    let mut value: Option<usize> = None;
    let mut f = move || {
        value = Some(1234);
    };
    f();
    println!("{:?}", value);
}

I don't quite understand why value in this case is still None but I guess this is because of move. Still this seems weird for me, can someone ELI5 me this?

This was easy example, but what I want to do really is to first use filter() on Lines but capture when first match was found (function first returned false) and then using another loop with for_each() to insert new content to that line.

Of course I can use just single for_each here, but is it possible to use both?

Without move I'm getting error that value was borrowed as mutable, and then as non mutable.

In the code above, value is being copied, so the variable named value inside the closure isn't the same as the value you're printing at the end.

Can you show the code that produced the error?

Sure, something like this (playground)

fn main(){
    let test = "lorem\nipsum\ndolor";
    let replacement = "something";
    let lines = test.lines();
    let mut index_found: Option<usize> = None;
    let lines_filtered = lines.enumerate().filter(|(i, line)| {
        if line == &"ipsum" {
            index_found = Some(i.clone());
            return false;
        }
        true
    });
    let mut buf = String::new();
    lines_filtered.for_each(|(i, line)| {
        match index_found {
            None => {},
            Some(index_found) => {
                buf += replacement;
                buf += "\n";
            }
        }
        buf += line;
    })
}

error[E0502]: cannot borrow index_found as immutable because it is also borrowed as mutable
--> src/main.rs:14:29

To observe the change you have to use a reference.

&mut references are strictly exclusive, so you have to drop f before you can access variables it has exclusive access to.

Alternatively, use &Cell<usize> that can be shared and mutable. Arc<AtomicUsize> if it needs to be thread safe and live for any lifetime (Arc is a kind of reference too).

1 Like

In the original code example, you could create a temporary mutable reference and move the reference into the closure (playground).

fn main(){
    let mut value: Option<usize> = None;
    let mut f = {
        let value_ref = &mut value;
        move || 
        {
            *value_ref = Some(1234);
        }
    };
    f();
    println!("{:?}", value);
}
1 Like

In this example, you could split the logic of finding the index of the target value and replacing the value (noting that your code replaces the last match). Playground

fn main() {
    let test = "lorem\nipsum\ndolor\nipsum";
    let replacement = "something";
    let lines = test.lines();
    let mut buf = String::new();

    let index_found = lines
        .enumerate()
        .filter_map(|(i, line)| if line == "ipsum" { Some(i) } else { None })
        .last();

    test.lines().enumerate().for_each(|(i, line)| {
        if Some(i) == index_found {
            buf += replacement;
            buf += "\n";
        } else {
            buf += line;
            buf += "\n";
        }
    });

    println!("{}", buf);
}
1 Like

Is this actually what you want to do? Assuming this compiled, once "ipsum" is found, this would insert the replacement line before all the remaining lines.