pub struct Lazy<T> { /* private fields */ }
Expand description
We want to have a static value that’s set at runtime and this executor will
only use libstd. As of 10/26/21, the lazy types in std are still only on
nightly and we can’t use another crate, so crates like once_cell
and
lazy_static
are also out. Thus, we create our own Lazy type so that it will
calculate the value only once and only when we need it.
Implementations§
Source§impl<T> Lazy<T>
impl<T> Lazy<T>
Sourcepub const fn new() -> Self
pub const fn new() -> Self
We must construct the type using a const fn so that it can be used in
static
contexts. The nice thing is that all of the function calls we
make here are also const and so this will just work. The compiler will
figure it all out and make sure the Lazy
static value exists in our
final binary.
Sourcepub fn get_or_init(&self, func: fn() -> T) -> &T
pub fn get_or_init(&self, func: fn() -> T) -> &T
This function will either grab a reference to the type or creates it with a given function
Trait Implementations§
Source§impl<T> Drop for Lazy<T>
We now need to implement Drop
by hand specifically because MaybeUninit
will need us to drop the value it holds by ourselves only if it exists. We
check if the value exists, swap it out with an uninitialized value and then
change MaybeUninit<T>
into just a T
with a call to assume_init
and
then call drop
on T
itself
impl<T> Drop for Lazy<T>
We now need to implement Drop
by hand specifically because MaybeUninit
will need us to drop the value it holds by ourselves only if it exists. We
check if the value exists, swap it out with an uninitialized value and then
change MaybeUninit<T>
into just a T
with a call to assume_init
and
then call drop
on T
itself
impl<T: Send> Send for Lazy<T>
Now you might be asking yourself why we are implementing these traits by
hand and also why it’s unsafe to do so. UnsafeCell
is the big reason here
and you can see this by commenting these two lines and trying to compile the
code. Because of how auto traits work then if any part is not Send
and
Sync
then we can’t use Lazy
for a static. Note that auto traits are a
compiler specific thing where if everything in a type implements a trait
then that type also implements it. Send
and Sync
are great examples of
this where any type becomes Send
and/or Sync
if all its types implement
them too! UnsafeCell
specifically implements !Sync and since it is not
Sync
then it can’t be used in a static
. We can override this behavior
though by implementing these traits for Lazy
here though. We’re saying
that this is okay and that we uphold the invariants to be Send + Sync
. We
restrict it though and say that this is only the case if the type T
inside Lazy
is Sync
only if T
is Send + Sync
. We know then that
this is okay because the type in UnsafeCell
can be safely referenced
through an &'static
and that the type it holds is also safe to use across
threads. This means we can set Lazy
as Send + Sync
even though the
internal UnsafeCell
is !Sync in a safe way since we upheld the invariants
for these traits.