zino
zino

Reputation: 1612

Two closures that use references to parent scope

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```

Upvotes: 0

Views: 433

Answers (1)

SOFe
SOFe

Reputation: 8214

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

Upvotes: 2

Related Questions