Last active
September 1, 2019 21:49
-
-
Save bstrie/e05b14f66017bd7560e3cfd2df8750d9 to your computer and use it in GitHub Desktop.
lazy static of the future
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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