0

Im trying to create a struct that stores 2 closures, each with read references to an parent scope variable where they were created.

After I have closed over the references in the parent scope, how do I make sure they live for the same amount of time as the closures?

For example:

struct S {
    x: bool,
}

type Closure = Box<dyn Fn()>;

struct Env {
    fn_a: Closure,
    fn_b: Closure,
}

fn get_env() -> Env {
    let s = S { x: true };

    let fn_a = || {
        &s.x;
    };
    let fn_b = || {
        &s.x;
    };

    Env {
        fn_a: Box::new(fn_a),
        fn_b: Box::new(fn_b),
    }
}

fn main() {
    let env = get_env();
}

playground

Results in:

error[E0597]: `s` does not live long enough
  --> src/main.rs:16:10
   |
15 |     let fn_a = || {
   |                -- value captured here
16 |         &s.x;
   |          ^ borrowed value does not live long enough
...
23 |         fn_a: Box::new(fn_a),
   |               -------------- cast requires that `s` is borrowed for `'static`
...
26 | }
   | - `s` dropped here while still borrowed```
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
zino
  • 1,222
  • 2
  • 17
  • 47
  • 1
    It looks like your question might be answered by the answers of [Is there any way to return a reference to a variable created in a function?](https://stackoverflow.com/q/32682876/155423); [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Aug 27 '19 at 14:57
  • @trentcl I realised I might have to do it that way. The reason for the closures is that the parent scope has many more variables, and I would prefer to be able to just close over the ones I use inside the closure bodies rather than define a new struct that contains the set of closed over variables. – zino Aug 27 '19 at 15:43
  • @Shepmaster thanks will investigate the links. I think although the underlying principles are the same, this question specifically asks about the semantics of closure syntax. – zino Aug 27 '19 at 16:10
  • 1
    I mean, you certainly can do it that way. But you have to move the variables into the closures -- SOFe's answer shows how with data that needs to be shared between them. If you have a lot of variables it seems like this solution would be far more unwieldy than just wrapping them in a struct. – trent Aug 27 '19 at 16:12

1 Answers1

2

It depends on your scenario. How long would the returned closures live?

If it lives as long as the Env value, what about moving s into the Env value and accessing s from the Env value?

If you don't know (or don't want to spend the trouble to figure out) the lifetime for the closures, a convenient (although slightly less efficient) way is to use Rc, then move a clone of the Rc from the results:

Playground

use std::rc::Rc;

pub struct S {
    x: bool,
}

pub type Closure = Box<dyn Fn()>;

#[allow(unused)]
pub struct Env {
    fn_a: Closure,
    fn_b: Closure,
}

fn get_env() -> Env {
    let s = Rc::new(S { x: true });

    let cloned = Rc::clone(&s);
    let fn_a = move || {
        s.x;
    };
    let fn_b = move || {
        cloned.x;
    };

    Env {
        fn_a: Box::new(fn_a),
        fn_b: Box::new(fn_b),
    }
}

fn main() {
    let _env = get_env();
}
SOFe
  • 7,867
  • 4
  • 33
  • 61
  • 2
    A trait object without lifetime gets `'static` as rule. So `Closure` type is equivalent to `pub type Closure = Box;`. Another reason must using `move` closure here to own the captured variables. – edwardw Aug 27 '19 at 15:22