This Gist now has its own repository on GitHub: https://github.com/Kimundi/lazy-static.rs
-
-
Save Kimundi/8782487 to your computer and use it in GitHub Desktop.
| #![feature(macro_rules)] | |
| use std::collections::HashMap; | |
| macro_rules! lazy_init { | |
| ($(static ref $N:ident : $T:ty = $e:expr;)*) => { | |
| $( | |
| #[allow(non_camel_case_types)] | |
| struct $N {__unit__: ()} | |
| static $N: $N = $N {__unit__: ()}; | |
| impl Deref<$T> for $N { | |
| #[allow(dead_code)] | |
| fn deref<'a>(&'a self) -> &'a $T { | |
| use std::sync::{Once, ONCE_INIT}; | |
| use std::mem::transmute; | |
| #[inline(always)] | |
| fn require_share<T: Share>(_: &T) { } | |
| unsafe { | |
| static mut s: *$T = 0 as *$T; | |
| static mut ONCE: Once = ONCE_INIT; | |
| ONCE.doit(|| { | |
| s = transmute::<Box<$T>, *$T>(box() ($e)); | |
| }); | |
| let static_ref = &*s; | |
| require_share(static_ref); | |
| static_ref | |
| } | |
| } | |
| } | |
| )* | |
| } | |
| } | |
| lazy_init! { | |
| static ref NUMBER: uint = times_two(3); | |
| static ref VEC: [Box<uint>, ..3] = [box 1, box 2, box 3]; | |
| static ref OWNED_STRING: String = "hello".to_string(); | |
| static ref HASHMAP: HashMap<uint, &'static str> = { | |
| let mut m = HashMap::new(); | |
| m.insert(0u, "abc"); | |
| m.insert(1, "def"); | |
| m.insert(2, "ghi"); | |
| m | |
| }; | |
| } | |
| fn times_two(n: uint) -> uint { | |
| n * 2 | |
| } | |
| #[test] | |
| fn test_basic() { | |
| assert_eq!(OWNED_STRING.as_slice(), "hello"); | |
| assert_eq!(*NUMBER, 6); | |
| assert!(HASHMAP.find(&1).is_some()); | |
| assert!(HASHMAP.find(&3).is_none()); | |
| assert_eq!(VEC.as_slice(), &[box 1, box 2, box 3]); | |
| } | |
| #[test] | |
| fn test_repeat() { | |
| assert_eq!(*NUMBER, 6); | |
| assert_eq!(*NUMBER, 6); | |
| assert_eq!(*NUMBER, 6); | |
| } |
Answered on IRC already, but for future reference: struct $N {__unit__: ()}; only lives in the type namespace, allowing a static $N; to coexist.
struct $N; would also live in the value namespace, as its constructuor, and thus prevent a static of the same name
This is only memory-safe if $T satisfy the Share kind, right? Does/could/should this macro enforce Share? (I.e. fail at compile time when it would be unsafe.)
You're right, I'm currently investigating how to make this safe.
I updated the current code to show how you can do a unsafe mutation of a static, which needs to be prevented.
I think with the planned-to-be-re-added bounds on structs a phantom type parameter could work:
struct $N<T: Share> { unit: () }
static $N: $N<$T>= $N { unit: () }
EDIT: Adding a fn require_share<T: Share>() works just as well.
I removed the #[allow(dead_code)] line when integrating this in Servo. I found the dead code warning to be relevant, only showing up when a given static was actually unused.
This now officially lives in https://github.com/Kimundi/lazy-static.rs
Why
$N {__unit__: ()};rather than a$N;empty struct?