How to encapsulate an opaque closure type within an enclosing struct

I'm able to express the variance of a LazyCell that's initialized via a capturing closure like so:

use std::cell::LazyCell;

#[derive(Clone, Copy)]
struct ComplexType {
    // ...
}

impl ComplexType {
    pub fn expensive(self) -> String {
        // ...
    }
}

impl Default for ComplexType {
    fn default() -> Self {
        // ...
    }
}

fn main() {
    let foo = ComplexType::default();
    // captures foo with a move, so second type param implements Fn() -> String
    let bar: LazyCell<String, _> = LazyCell::new(move || foo.expensive());
    
    // do something with the lazily initialized value
}

I can't figure out the way to express this if I want to encapsulate a bunch of lazily evaluated values though:

use std::cell::LazyCell;

#[derive(Clone, Copy)]
struct ComplexType {
    // ...
}

impl ComplexType {
    pub fn expensive(self) -> String {
        // ...
    }
}

impl Default for ComplexType {
    fn default() -> Self {
        // ...
    }
}

struct LazyInit {
     field1: LazyCell<ComplexType>,
     // ...
}

fn main() {
    let foo = ComplexType::default();

    // rustc error: closures can't be coerced into fn ptrs if they capture a value
    // I'd have to make `LazyInit` generic over every single `LazyCell` instance that it encapsulates
    let bar: LazyCell<String, _> = LazyCell::new(move || foo.expensive());

    let lazy_values = LazyInit { field1: bar };
    
    // do something with the lazily initialized value
}

Is there any other way to express this sort of lazy field initialization without making the containing type generic over every single LazyCell field?

You can type-erase the closures by storing them as Box<dyn FnOnce() -> T>.

1 Like

I thought I had tried this, but all I needed to do a trait cast for the boxed type in the call to LazyCell::new

I was able to write this shorthanded like this:

use std::cell;

type LazyCell<T> = cell::LazyCell<T, Box<dyn FnOnce() -> T>>;

struct LazyInit {
    field1: LazyCell<()>,
    // ...
}