Lazily creating new value consuming old one

Hello,

I have some value of type A that cannot be cloned, and I have a function f(A) -> B that consumes said value to create a new value B. The A value is created during runtime and is not needed for anything else than creating the B value. But, I have multiple threads that need to see the B value, and the B value needs to be calculated lazily when it is first needed. More precisely, I will be having an Arc<B> that can be cloned to share the B value between threads.

How can I do this?

It seems like a use case for LazyLock or OnceLock, but I could not make it work.

I think I know a solution using RwLock (or Mutex, but I think RwLock would be more efficient) and some contained containing an enum, but I'm wondering if there is an easier solution.

Thanks!

Could you describe your struggles with LazyLock in more detail? Because the closure you pass to LazyLock::new can capture your A instance. You could then share the LazyLock with Arc between your threads. Example.

2 Likes

Interesting. I ran into an error message that told me that the closure cannot capture, but from your example, it looks like it can.

Be aware of the second generic parameter F on LazyLock. It defaults to fn() -> T, which can't capture. So if you don't let type inference figure out the type for F (i.e. if you were to let ll: LazyLock<B> = ...), then you get the error that function pointers can't capture their environment.

2 Likes

Yes, that was exactly my problem! Thanks for the explanation!

So what do I do if my LazyLock is part of a struct and I need to specify its type?

struct C<F>(Arc<LazyLock<B,F>>);

something like this?

You could propagate F as a generic parameter to the wrapping struct or use dynamic dispatch, i.e. with Box<dyn FnOnce() -> B>.

1 Like

Awesome, thanks!

1 Like