八月中秋白露,路上行人凄凉。小桥明月桂花香,日夜千思万想。 心中万般宁静,青春好读文章。 十年苦读在书房,方见才学益广。
辛弃疾《西江月·夜行黄沙道中》
lazy_static 是rust中广泛使用的一个库,一直处于“Most Downloaded”下载列表的前十中,每天的下载量也是上万次。
它使用简单,切换方便,功能专一,同时支持std
和no-std
,也是一个我们学习rust开发的很好的例子。
lazy_static
可以帮助你实现延迟初始化static
常量的功能。
Rust 静态项是一种“全局变量”。它们类似于常量,但静态项不内联使用。这意味着每个值只对应一个实例, 并且在内存中只有一个固定的地址。
静态类型活在程序的整个生命周期,只有在程序退出的时候静态项才会调用drop。
静态类型是可变的, 你可以使用 mut
关键字声明可变性。
此外,任何存储在 static 的类型都必须是 Sync。
常量和静态常量都要求给他们一个值。并且他们可能只被赋予一个值,这个值是一个常数表达式。
很多情况下,我们希望延迟初始化静态量,只有在第一次访问的时候,或者在某个特定的时候才初始化它,那么久可以使用lazy_static
。
lazy_static
提供了一个宏lazy_static!
,使用这个宏把你的静态变量“包裹”起来就可以实现延迟初始化了,实际上这个宏会帮助你生成一个特定的struct
,这个struct
的deref
方法(trait Deref)提供了延迟初始化的能力,它也提供了initialize
方法,你也可以在代码中主动地调用它进行初始化。
首先我们看一段使用lazy_static
的代码,其中静态变量A
是延迟初始化的。
1 2 3 4 5 6 7 8 9
| use lazy_static::*; lazy_static! { static ref A: u8 = 42; } fn main() { println!("{}", *A); }
|
这段代码实际生成的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #![feature(prelude_import)] #![no_std] #[prelude_import] use ::std::prelude::v1::*; #[macro_use] extern crate std as std; use lazy_static::*; #[allow(missing_copy_implementations)] #[allow(non_camel_case_types)] #[allow(dead_code)] struct A { __private_field: (), } #[doc(hidden)] static A: A = A{__private_field: (),}; impl ::lazy_static::__Deref for A { type Target = u8; fn deref(&self) -> &u8 { #[inline(always)] fn __static_ref_initialize() -> u8 { 42 } #[inline(always)] fn __stability() -> &'static u8 { static LAZY: ::lazy_static::lazy::Lazy<u8> = ::lazy_static::lazy::Lazy::INIT; LAZY.get(__static_ref_initialize) } __stability() } } impl ::lazy_static::LazyStatic for A { fn initialize(lazy: &Self) { let _ = &**lazy; } } fn main() { { ::std::io::_print(::std::fmt::Arguments::new_v1(&["", "\n"], &match (&*A,) { (arg0,) => [::std::fmt::ArgumentV1::new(arg0, ::std::fmt::Display::fmt)], })); }; }
|
可以看到A
实际上是一个辅助struct, 实现了解引用的trait: impl ::lazy_static::__Deref for A
和主动初始化impl ::lazy_static::LazyStatic for A
。
重要的是deref
方法:
1 2 3 4 5 6 7 8 9 10 11
| fn deref(&self) -> &u8 { #[inline(always)] fn __static_ref_initialize() -> u8 { 42 } #[inline(always)] fn __stability() -> &'static u8 { static LAZY: ::lazy_static::lazy::Lazy<u8> = ::lazy_static::lazy::Lazy::INIT; LAZY.get(__static_ref_initialize) } __stability() }
|
__static_ref_initialize
是构建方法,使用你定义的表达式生成静态变量的值。
而__stability
是静态初始化的关键, 它使用Lazy
类型实现初次访问时的初始化。对于std
环境,它的定义如下:
1
| pub struct Lazy<T: Sync>(Cell<Option<T>>, Once);
|
而对于no-std
环境,它使用spin::Once
来实现,我们暂不关注no-std
环境。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| pub struct Lazy<T: Sync>(Cell<Option<T>>, Once); impl<T: Sync> Lazy<T> { #[allow(deprecated)] pub const INIT: Self = Lazy(Cell::new(None), ONCE_INIT); #[inline(always)] pub fn get<F>(&'static self, f: F) -> &T where F: FnOnce() -> T, { self.1.call_once(|| { self.0.set(Some(f())); }); unsafe { match *self.0.as_ptr() { Some(ref x) => x, None => { debug_assert!(false, "attempted to derefence an uninitialized lazy static. This is a bug"); unreachable_unchecked() }, } } } } unsafe impl<T: Sync> Sync for Lazy<T> {}
|
std::sync::Once
提供了只初始化一次的能力,它使用提供的构造器f
来实现初始化。
self.0.as_ptr()
返回*mut Option<T>
,而Some(ref x)
使用的是The ref pattern
, 因为我们要返回的是&T
类型。
这是lazy_static核心实现,为了更方便的切换静态变量的初始化,它又提供了lazy_static!
宏的实现。这个宏的定义有些长,就不再这里详细列出来了,
但是它却是一个很好的学习实现rust macro例子,建议对照代码仔细阅读。
它处理了静态变量定义的三种情况,主要是针对pub
的不同定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #[macro_export(local_inner_macros)] macro_rules! lazy_static { ($(#[$attr:meta])* static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { __lazy_static_internal!($(#[$attr])* () static ref $N : $T = $e; $($t)*); }; ($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { __lazy_static_internal!($(#[$attr])* (pub) static ref $N : $T = $e; $($t)*); }; ($(#[$attr:meta])* pub ($($vis:tt)+) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { __lazy_static_internal!($(#[$attr])* (pub ($($vis)+)) static ref $N : $T = $e; $($t)*); }; () => () }
|
实际调用的是__lazy_static_internal!
宏, 它通过Textual scope
将一个变量的定义拆成两部分@MAKE
和@TAIL
。
@MAKE
定义辅助struct,而@TAIL
为辅助struct实现特定的trait。 如果还有剩余的静态变量,则递归处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #[macro_export(local_inner_macros)] #[doc(hidden)] macro_rules! __lazy_static_internal { ($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { __lazy_static_internal!(@MAKE TY, $(#[$attr])*, ($($vis)*), $N); __lazy_static_internal!(@TAIL, $N : $T = $e); lazy_static!($($t)*); }; (@TAIL, $N:ident : $T:ty = $e:expr) => { impl $crate::__Deref for $N { ...... } impl $crate::LazyStatic for $N { fn initialize(lazy: &Self) { let _ = &**lazy; } } }; (@MAKE TY, $(#[$attr:meta])*, ($($vis:tt)*), $N:ident) => { #[allow(missing_copy_implementations)] #[allow(non_camel_case_types)] #[allow(dead_code)] $(#[$attr])* $($vis)* struct $N {__private_field: ()} #[doc(hidden)] $($vis)* static $N: $N = $N {__private_field: ()}; }; () => () }
|