Rust每周一库: lazy_static

八月中秋白露,路上行人凄凉。小桥明月桂花香,日夜千思万想。 心中万般宁静,青春好读文章。 十年苦读在书房,方见才学益广。

辛弃疾《西江月·夜行黄沙道中》

lazy_static 是rust中广泛使用的一个库,一直处于“Most Downloaded”下载列表的前十中,每天的下载量也是上万次。

它使用简单,切换方便,功能专一,同时支持stdno-std,也是一个我们学习rust开发的很好的例子。

lazy_static可以帮助你实现延迟初始化static常量的功能。

Rust 静态项是一种“全局变量”。它们类似于常量,但静态项不内联使用。这意味着每个值只对应一个实例, 并且在内存中只有一个固定的地址。

静态类型活在程序的整个生命周期,只有在程序退出的时候静态项才会调用drop。

静态类型是可变的, 你可以使用 mut 关键字声明可变性。

此外,任何存储在 static 的类型都必须是 Sync。

常量和静态常量都要求给他们一个值。并且他们可能只被赋予一个值,这个值是一个常数表达式。

很多情况下,我们希望延迟初始化静态量,只有在第一次访问的时候,或者在某个特定的时候才初始化它,那么久可以使用lazy_static

lazy_static提供了一个宏lazy_static!,使用这个宏把你的静态变量“包裹”起来就可以实现延迟初始化了,实际上这个宏会帮助你生成一个特定的struct,这个structderef方法(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()));
});
// `self.0` is guaranteed to be `Some` by this point
// The `Once` will catch and propagate panics
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)*) => {
// use `()` to explicitly forward the information about private items
__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 {
// optional visibility restrictions are wrapped in `()` to allow for
// explicitly passing otherwise implicit information about private items
($(#[$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;
}
}
};
// `vis` is wrapped in `()` to prevent parsing ambiguity
(@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: ()};
};
() => ()
}