Reputation: 3386
I have a struct that contains a field that is rather expensive to initialize, so I want to be able to do so lazily. However, this may be necessary in a method that takes &self
. The field also needs to be able to modified once it is initialized, but this will only occur in methods that take &mut self
.
What is the correct (as in idiomatic, as well as in thread-safe) way to do this in Rust? It seems to me that it would be trivial with either of the two constraints:
lazy-init
's Lazy<T>
type.However, I'm not quite sure what to do with both in place. RwLock
seems relevant, but it appears that there is considerable trickiness to thread-safe lazy initialization given what I've seen of lazy-init
's source, so I am hesitant to roll my own solution based on it.
Upvotes: 4
Views: 2092
Reputation: 300149
The simplest solution is RwLock<Option<T>>
.
However, I'm not quite sure what to do with both in place.
RwLock
seems relevant, but it appears that there is considerable trickiness to thread-safe lazy initialization given what I've seen oflazy-init
's source, so I am hesitant to roll my own solution based on it.
lazy-init
uses tricky code because it guarantees lock-free access after creation. Lock-free is always a bit trickier.
Note that in Rust it's easy to tell whether something is tricky or not: tricky means using an unsafe
block. Since you can use RwLock<Option<T>>
without any unsafe block there is nothing for you to worry about.
A variant to RwLock<Option<T>>
may be necessary if you want to capture a closure for initialization once, rather than have to pass it at each potential initialization call-site.
In this case, you'll need something like RwLock<SimpleLazy<T>>
where:
enum SimpleLazy<T> {
Initialized(T),
Uninitialized(Box<FnOnce() -> T>),
}
You don't have to worry about making SimpleLazy<T>
Sync
as RwLock
will take care of that for you.
Upvotes: 5