Closure/anonymous function that mutates data outside itself

I have following code in rust and this one i working. Here i define fuinction that mutates the argument passed by reference.

fn inc (v: &mut i32){
    *v+=1;
}

fn main() {
    let mut n = 0;
    // let mut inc = || n += 1;

    inc(&mut n); // call the closure to increment n
    println!("n = {}", n); // prints "n = 1"

    inc(&mut n); // call the closure again to increment n
    println!("n = {}", n); // prints "n = 2"

    inc(&mut n); // call the closure again to increment n
    println!("n = {}", n); // prints "n = 3"
}

I tried using a named function instead of an anonymous one (the first example in my question)

in the code example above I have function inc than at takes argument by reference and increments it. Is it somehow possible to define anonymous lambda instead that will do the same task? Ideally it would to have something similiar to the following code to be working:

fn main() {
    let mut n = 0;    let mut inc = || n += 1;

    inc(); // call the closure to increment n
    println!("n = {}", n); // prints "n = 1"

    inc(); // call the closure again to increment n
    println!("n = {}", n); // prints "n = 2"

    inc(); // call the closure again to increment n
    println!("n = {}", n); // prints "n = 3"
}

The problem is that the lambda (continuously) borrows n for basically its entire lifetime, and while there is a mutable borrow to a value, no other borrows can co-exist. So this pattern is not really expressible in safe Rust (exactly because it's error-prone).

Here's a similar question that was recently asked.

You can either use some form of interior mutability (e.g., Cell, RefCell, Mutex, etc.), but for such a simple case, it's not really idiomatic. If you can realize this by passing a borrow of the value to a function, you should just do that. (It's more readable anyway, due to the reader knowing exactly what state the function will modify.)

2 Likes

For this simple case, you can also return the incremented value from the closure.

1 Like

Thanks for answering. I hope I can ask several questions about borrowing and Rust ideology here. Do I correctly understand that once i pas non-primitive data ass argument to function it is moved and I cannot read it anymore?
like this

let var = SomeStruct{};
modify_data(var); // mutable referedce
modify_data(var);  // second mutable reference

Do I also correctly understand that the only solution to pass mutable reference is to return the reference back from function like this.

let modified_value=func_call(val):
let modified_value_2=func_call(modified_value):

or even like this:

val=func_call(val):
val=func_call(val):

Why are other options considered unsafe?

Also can I achive this effect using data stored on heap to achieve this task, for example store several references to one boxed value?

Thanks in advance

Passing a value to a function moves it, just like assigning it to a variable does. This is true of all types, not just non-primitives.

You might be confusing "primitive" with "Copy". Values of a type that implements Copy will not be invalidated when moved from, they will be copied instead.

I don't understand how this snippet is relevant; you are passing var by-value to the function, there are no mutable references at all.

No, a mutable reference is passed by taking the address, like this:

func_call(&mut val);

But for basic stuff like this, please read The Book.

I'm not sure what specific "other options" you are referring to. If you don't know why sharing-xor-mutability must be upheld, read this.

You can create references to values on the stack or on the heap, or anywhere else, really. Whether a value is on the stack or the heap does not affect borrowing rules. References don't have different types based on whether they point to the stack or the heap or any other memory region. The borrowing rules are the same: you can borrow a value immutably as many times as you like, or borrow it immutably exactly once at a time.

1 Like

this comment is just for fun:

you cannot do this using compiler generated closures, but you can define your own "closure"-like type in nightly rust using unstable features. just note, you exclusively borrows the variable, so you can't access the original variable directly, but you can define a "accessor" on the borrowing type.

fn main() {
    let mut x = 42;
    let mut inc = Inc::new(&mut x);
    inc();
    dbg!(inc.get());
    inc();
    dbg!(inc.get());
}

full code:

i kind of came up with a similiar idea like this. It is not state but ;Adder; but the idea is the same, it requires to be called to react data and if you call it with arg 0 you get data itself, it can be modified to be called without an argument i know

use std::ops::*;

struct adder{
    f:Box<dyn Fn(i32)->i32>
}

impl Deref for adder{
    type Target = dyn std::ops::Fn(i32) -> i32 + 'static;//dyn Fn(i32)->i32;
    fn deref(&self)->&Self::Target {
        &self.f
    }
}

trait updatable{
    fn update(&mut self, v:i32);
}

impl adder{
    fn update(&mut self, v:i32){
        self.f = Box::new(move |d:i32|v+d);
    }
    fn new(v:i32 )->Self{
        Self{
            f:Box::new(move |d:i32|v+d)
        }
    }
}


struct Adder {
    value:i32,
    f:Box<dyn Fn(i32)->i32>
}

impl Deref for Adder{
    type Target = dyn std::ops::Fn(i32) -> i32 + 'static;//dyn Fn(i32)->i32;
    fn deref(&self)->&Self::Target {
        &self.f
    }
}
impl Adder {
    fn new(v:i32 )->Self{
        Self{
            value:v,
            f:Box::new(move |d:i32|v+d)
        }
    }

    fn shallow_copy(self) -> Box<Adder> {
        // Box::new(Adder { f: self.f })
        Box::new(Self{
            value:self.value,
            f: Box::new(move |d:i32|self.value+d),
        })
    }

    fn update(&mut self, v: i32) {
        self.value = v;
    }
}

fn main() {
    let mut k2 = adder::new(2004);
    println!("Hello, world! {} {}", k2(0), k2(3));
    k2.update(100);
    println!("Hello, world! {} {}", k2(6), k2(0));
}

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.