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 是延迟初始化的。

use lazy_static::*;

lazy_static! {
    static ref A: u8 = 42;
}

fn main() {
    println!("{}", *A);
}

这段代码实际生成的代码如下:

#![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 方法:

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 环境,它的定义如下:

pub struct Lazy<T: Sync>(Cell<Option<T>>, Once);

而对于 no-std 环境,它使用 spin::Once 来实现,我们暂不关注 no-std 环境。

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 的不同定义。

#[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。 如果还有剩余的静态变量,则递归处理。

#[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: ()};
    };
    () => ()
}
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章