0

I've came across this case where I would like to create a struct, that can be modified by a closure that is owned by the struct.

I have never been too deep into closures and capturing environement, nor lifetimes, but I'm wondering why the borrow checker is mad at me. This is what I was hoping for :


struct A {
    pub a: usize,
    pub b: String,
    pub c: C,
}

struct C {
    pub modifier: Box<dyn FnMut()>
}

fn main() {
    let a = 3;
    let b = "Hello, World !".to_string();
    let c = C {
        modifier: Box::new(|| {
            a += 1;
            b.push('c');
        })
    };
    
    let mut my_struct = A {
        a, b, c
    };
    
    println!("a, b : {}, {}", my_struct.a, my_struct.b);
    (my_struct.c.modifier)();
    println!("a, b : {}, {}", my_struct.a, my_struct.b);
    
}

Here, my closure in my C struct does captures my variables a and b, so a and b must live longer than the closure. However, I am storing all of these in the same struct, so they will be dropped at the same time ? meaning that if a or b gets dropped, so does C ?

Here is a playground link.

This does not compile, and it complains that a and b does not live lng enough. We is this so ?

Is there a known pattern that would allow one to achieve such a thing ? (the goal would be that the closure is user-defined, and can modify the struct it is in.)

  • 1
    A closure that modifies a struct necesarily has a reference to the struct, so it can't also be stored in the struct. See [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct) – cafce25 Jul 18 '23 at 14:00
  • Additionaly your model of when something stops being is a little askew, `b` becomes invalid as soon as you move it into `my_struct`, any references to `b` stop being valid at that point, too. It is after all not at the same memory location any longer. For a value implementing `Copy` such as `a` the story is a little different but any references to it cannot leave the function either. – cafce25 Jul 18 '23 at 14:04
  • 4
    On the other hand, storing a closures environment with it is as simple as putting `move` in front of the `||`. – cafce25 Jul 18 '23 at 14:08
  • If you don't want to use `move`, you can pass them as parameters to the closure. – Chayim Friedman Jul 18 '23 at 14:15

1 Answers1

0

You could add a method that calls the modifier with the right parameters from self to A, that way you don't have to store references to the struct in itself:

struct A {
    pub a: usize,
    pub b: String,
    pub c: C,
}

struct C {
    pub modifier: Box<dyn Fn(&mut usize, &mut String)>
}

impl A {
    fn call_modifier(&mut self) {
        (self.c.modifier)(&mut self.a, &mut self.b);
    }
}

fn main() {
    let a = 3;
    let b = "Hello, World !".to_string();
    let c = C {
        modifier: Box::new(|a, b| {
            *a += 1;
            b.push('c');
        })
    };
    
    let mut my_struct = A {
        a, b, c
    };
    
    println!("a, b : {}, {}", my_struct.a, my_struct.b);
    my_struct.call_modifier();
    println!("a, b : {}, {}", my_struct.a, my_struct.b);
    
}
cafce25
  • 15,907
  • 4
  • 25
  • 31