thoughtpolice
thoughtpolice

Reputation: 499

A struct that captures variables in its function

Is it possible to build the struct

struct MyFn<A, B> {
    call: fn(A) -> B
}

such that it's able to capture immutable variables like in the following snippet?

let y: &f64 = &3.0
let my_fn: MyFn<f64, f64> {
    call: |x| {
        x + *y
    }
}

I'm aware that a closure is needed in this case, but what should the type of call be so that it can capture any immutable reference (e.g. a function reference)?

Upvotes: 2

Views: 404

Answers (1)

user4815162342
user4815162342

Reputation: 154916

Is it possible to build the struct[...] such that it's able to capture...?

No - capturing involves a closure, and the type of MyFn::call is a function. MyFn can store a closure in call by either making its type generic type or a trait object.

A generic is perfectly efficient, but makes MyFn have a different type for each closure:

struct MyFn<A, B, F: Fn(A) -> B> {
    call: F,
    _data: PhantomData<(A, B)>,
}

let y: f64 = 3.0;
let my_fn: MyFn<f64, f64, _> = MyFn {
    call: |x| x + y,
    _data: PhantomData,
};

Having separate types for each closure means that you won't be able to easily create a vector of MyFns with different closures, or store it in a place where you need to spell out the type.

Alternatively, you can make call dynamic, i.e. express it as a trait object. That will keep MyFn generic only over the input and output type, and will prevent different closures from affecting the type of MyFn. This flexibility comes at the cost of an allocation when constructing MyFn and a pointer indirection when invoking call. (That cost in advance is not nearly as large as it's sometimes made out to be, but it's good to be aware of it.) Such MyFn looks like this:

struct MyFn<'a, A, B> {
    call: Box<dyn Fn(A) -> B + 'a>,
}

let y: f64 = 3.0;
let my_fn: MyFn<f64, f64> = MyFn {
    call: Box::new(|x| x + y),
};

The dynamic version appears to have an extra lifetime compared to the generic one, but that's because the lifetime was part of the unnamed type of the closure in the previous version.

Upvotes: 5

Related Questions