I am using rust in prod for more than 6 months already and I feel like I got the most parts yet I still cannot replicate the behaviour of lazy_static
by myself and I feel awful about it.
I just want to learn about your experiences. How did you get the details of such magic things? It sounds extremely simple just initialize a global pointer once and use it. I just have no clue what to do about it still.
Basically, there are two things to know here:
-
there is a lazy initialization pattern (
Lazy
)Instead of holding a
T
, you hold anOption<T>
(and maybe animpl FnOnce() -> T
factory, unless you expect the factory to be explicitely given later). So the value is internally init withNone
, and the first time it is queried (through a function / method call), it is replaced withSome(factory())
.Now, since we will want to have such thing in a
static
, which requiresSync
-ness and does not provide&mut _
access (only&_
), thenSync
-hronised interior mutability is required. So it's rather something likeRwLock<Option<T>>
- the actual implementation is an optimized version of it, since mutation only happens once, so they instead have a raw
UnsafeCell<MaybeUninit<T>>
* coupled to aOnce
(anAtomicUsize
status (uninit, being init, poisoned, already init))
- the actual implementation is an optimized version of it, since mutation only happens once, so they instead have a raw
-
there is a
static
variable (Deref
trick, c.f., link below)now the
Lazy
value can be used in astatic
context, but to give an API to users where the method call is "invisible", a trick / hack is used:- a proxy zero-sized struct occupies the name of our
lazy_static
, but has aDeref
implementation which dereferences to an internal / hiddenLazy
which is the thing that actually contains our (lazy) expression. It is within thatderef()
method that theLazy
"get or init" pattern is called each time ourlazy_static
is referred to.
- a proxy zero-sized struct occupies the name of our
* it turns out they use a Cell<Option<T>>
instead of an UnsafeCell<Option<T>>
since it offers the ergonomic Cell::set
, but it is still an unsafe
construct in a Sync
context, hence the need for a manual Sync
-hronization based on Once
. They have a FIXME to replace Option<T>
with MaybeUninit<T>
, but since they use the unreachable_unchecked
hint on the None
case, the program behavior should be the same, it may just waste one byte, which is not a big deal for a global variable.
I think i understand the thing a bit i will today try some stuff with unsafe dereferencing the pointer and stuff lets see if i will figure the thing out thnak you so much for the answer
You can look at once_cell
's implementation of Lazy
to get a macro-free version of lazy_static
. It maybe easier to follow.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.