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?