Skip to content

Instantly share code, notes, and snippets.

@bstrie
Last active September 1, 2019 21:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bstrie/e05b14f66017bd7560e3cfd2df8750d9 to your computer and use it in GitHub Desktop.
Save bstrie/e05b14f66017bd7560e3cfd2df8750d9 to your computer and use it in GitHub Desktop.
lazy static of the future
// This type is a replacement for the macro provided by the lazy_static crate
use lazy::Lazy;
use std::collections::HashMap;
static GLOBAL_MAP: Lazy<HashMap<u32, &str>> = Lazy::new(|| {
let mut m = HashMap::new();
m.insert(0, "foo");
m.insert(1, "bar");
m.insert(2, "baz");
m
});
fn main() {
assert_eq!(GLOBAL_MAP[&0], "foo");
assert_eq!(GLOBAL_MAP[&1], "bar");
assert_eq!(GLOBAL_MAP[&2], "baz");
}
mod lazy {
use std::{cell::UnsafeCell, mem::MaybeUninit, ops::Deref, sync::Once};
pub struct Lazy<T, F = fn()->T> {
once: Once,
value: UnsafeCell<MaybeUninit<T>>,
init: F,
}
impl<T, F> Lazy<T, F> {
pub const fn new(f: F) -> Self {
Lazy {
once: Once::new(),
value: UnsafeCell::new(MaybeUninit::uninit()),
init: f,
}
}
}
impl<T: Sync, F: Fn()->T> Deref for Lazy<T, F> {
type Target = T;
fn deref(&self) -> &T {
self.once.call_once(|| {
let p: *mut MaybeUninit<T> = self.value.get();
// Unsafe required to deref the raw pointer in `p`.
// We assert the following invariants:
// 1. This pointer is not accessible from safe code outside of
// this module; enforced by the privacy of the
// `value` field on the `Lazy` type, and the lack of any
// public item exported from this module that yields a
// reference by which `value` may be accessed.
// 2. No other code within this module attempts to perform
// any write that could race with this write operation;
// enforced in multi-threaded contexts via the `call_once`
// method on `std::sync::Once`.
// 3. The raw pointer represents a location in memory which
// this code has permission to access; enforced by the
// lack of any manipulation between where the pointer is
// originally received above and its usage below.
// NOTE: The unsafety here has no relation to the usage of
// `std::mem::MaybeUninit`; the only unsafe operation on that
// type is the `assume_init` method, which we do not have
// any need to ever call.
unsafe { *p = MaybeUninit::new((self.init)()) };
});
let inner_maybe: *mut MaybeUninit<T> = self.value.get();
// Unsafe required to deref the raw pointer in `inner_maybe`.
// We assert the following invariants:
// 1. At this point in execution, the innermost value contained
// within `Lazy` has infallibly been initialized; enforced by
// the above closure and the semantics of `Once::call_once`,
// which additionally guarantees that panics during the
// user-provided initialization are handled properly.
// Because of this, it is safe to constrain the data held within
// as being of type T.
// All invariants of the prior unsafe block still hold.
let inner_val: *const T = unsafe { (*inner_maybe).as_ptr() };
// Unsafe required to deref the raw pointer in `inner_val`.
// All invariants of the prior unsafe block still hold.
unsafe { &*inner_val }
}
}
// Unsafe required to implement the unsafe trait `Sync`.
// We assert the following invariants:
// 1. The internal mutability provided by `std::cell::UnsafeCell` as used
// by the implementation of the `Lazy` type is entirely thread-safe:
// enforced by the usage of `Once::call_once` in the impl of
// `Deref::deref` for `Lazy`, which is the one and only place that both
// has access to the UnsafeCell and mutates its contents.
unsafe impl<T: Sync + Send, F> Sync for Lazy<T, F> {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment