惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
AI
AI
月光博客
月光博客
爱范儿
爱范儿
博客园 - 司徒正美
Last Week in AI
Last Week in AI
博客园 - 三生石上(FineUI控件)
S
Security @ Cisco Blogs
腾讯CDC
W
WeLiveSecurity
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Help Net Security
Help Net Security
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学
Cyberwarzone
Cyberwarzone
K
Kaspersky official blog
Security Latest
Security Latest
博客园 - 叶小钗
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
A
Arctic Wolf
C
Cisco Blogs
H
Heimdal Security Blog
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
Google DeepMind News
Google DeepMind News
小众软件
小众软件
T
Tenable Blog
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
The Last Watchdog
The Last Watchdog
V2EX - 技术
V2EX - 技术
Simon Willison's Weblog
Simon Willison's Weblog
Vercel News
Vercel News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Vulnerabilities – Threatpost
L
LangChain Blog
Y
Y Combinator Blog
V
V2EX
Hacker News - Newest:
Hacker News - Newest: "LLM"
Latest news
Latest news
D
Docker
AWS News Blog
AWS News Blog
Google Online Security Blog
Google Online Security Blog
H
Help Net Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Troy Hunt's Blog
TaoSecurity Blog
TaoSecurity Blog
Cloudbric
Cloudbric
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

博客园 - PKICA

rust类型系统标记 编译配置解答 git实用命令 rust底层设计理念值得注意的几个地方总结 rust可变引用作为函数参数的机理详解 Rust内存重解释transmute C与Rust类型映射 Rust FFI 安全抽象范式 rust重借用机制与原理 rust参数传递模型 汇编语言语法详解 gdb汇编调试 gdb-pwndbg的安装与使用指南 gdb调试插件gef C语言thread_local linux系统readelf命令使用指南 gcore转储进程内存 gdb查看命令 RGB与YUV颜色编码的区别 Rust原子类型 C++ STL求两个集合交集差集 gdb调试集锦 ubuntu24.0.4使用root用户登录 ubuntu24.0.4输入密码后跳回登录界面 AI内存压缩技术TurboQuant及存疑 ubuntu切换到指定内核版本 在没有顶级科技大佬直接背书的情况下deepseek为啥能够异军突起? HuggingFace和deepseek的关系 当前主流AI大模型 Rust写时克隆Cow系列2
rust延迟初始化原语
PKICA · 2026-05-28 · via 博客园 - PKICA

std::sync::OnceLockstd::sync::LazyLock 是 Rust 标准库中用于线程安全延迟初始化(Lazy Initialization)的核心同步原语。它们在 Rust 1.70 和 1.80 中先后被正式稳定,旨在完美替代社区常用的第三方库 once_celllazy_static


一、 核心区别速查

特性std::sync::LazyLock<T, F>std::sync::OnceLock<T>
初始化时机 创建时绑定闭包,首次解引用(Deref)时触发 创建时留空,后续显式调用 .get_or_init() 时触发
类型签名 包含闭包类型 F,写法较冗长 仅包含包裹的数据类型 T
灵活性 只能使用创建时指定的全局唯一闭包 可以在不同地方传入不同的闭包或动态参数
典型场景 静态全局配置、正则表达式编译、数据库连接池 运行时动态确定的单例、带运行时入参的延迟初始化

二、 std::sync::LazyLock 详解

LazyLock 采用结构一体化的设计思想,它将数据与其初始化闭包(生成函数)强绑定在一起。

1. 基本语法与示例

当你声明一个 LazyLock 时,必须立即提供一个初始化函数。当代码第一次通过 * 运算符对其进行解引用时,该函数才会被执行:

use std::sync::LazyLock;
use std::collections::HashMap;

// 声明全局静态变量,自动推导闭包类型
static GLOBAL_MAP: LazyLock<HashMap<&'static str, i32>> = LazyLock::new(|| {
    println!("Initializing GLOBAL_MAP..."); // 仅会打印一次
    let mut m = HashMap::new();
    m.insert("apple", 1);
    m.insert("banana", 2);
    m.into()
});

fn main() {
    // 首次访问:触发初始化逻辑
    println!("Apple price: {:?}", GLOBAL_MAP.get("apple"));
    
    // 再次访问:直接读取缓存数据,不再执行闭包
    println!("Banana price: {:?}", GLOBAL_MAP.get("banana"));
}

2. 核心原理与设计

  • 自动解引用(Deref):LazyLock 实现了 Deref trait。你不需要调用任何初始化方法,像普通变量一样使用它(或使用 *GLOBAL_MAP),Rust 编译器就会在底层悄悄地帮你安全地调用初始化逻辑。
  • 类型签名的代价:LazyLock 的完整类型其实是 LazyLock<T, F>,其中 F 是闭包的类型。由于在 static 变量中无法使用闭包类型推导,我们往往依赖 Rust 对标准库 LazyLock 的特殊简化支持,或者将初始化抽离为具体函数。

三、 std::sync::OnceLock 详解

OnceLock 采用容器化的设计思想。它初始化时是一个“空盒子”,你需要在稍后的代码中显式地往盒子里填入数据。

1. 基本语法与示例

OnceLock 不在声明时绑定闭包,而是在运行时通过 .get_or_init() 方法传入初始化闭包:

use std::sync::OnceLock;

static RUNTIME_CONFIG: OnceLock<String> = OnceLock::new();

fn init_config(env: &str) {
    // 根据运行时的入参来初始化全局变量
    RUNTIME_CONFIG.get_or_init(|| {
        format!("Config loaded for environment: {}", env)
    });
}

fn main() {
    // 模拟运行时的某种配置行为
    init_config("production");

    // 获取已经初始化好的值(若未初始化则会返回 None)
    if let Some(config) = RUNTIME_CONFIG.get() {
        println!("{}", config);
    }
    
    // 再次尝试用不同的闭包进行初始化,此处的闭包会被安全忽略,并返回已有的值
    let value = RUNTIME_CONFIG.get_or_init(|| "fallback".to_string());
    println!("Value: {}", value); // 依然输出 production 相关的配置
}

2. 核心方法

  • set(value):尝试直接填入值。如果盒子已经是满的,会返回 Err(value)
  • get():非阻塞地获取引用,如果没初始化则返回 None
  • get_or_init(|| ...):如果盒子空,执行闭包并填入;如果盒子满,直接返回已有值引用。
  • get_or_try_init(|| ...):允许闭包返回一个 Result。如果闭包返回 ErrOnceLock 会保持未初始化状态,允许下一次重新尝试初始化。

四、 核心技术内幕:它们如何保证线程安全?

无论是 LazyLock 还是 OnceLock,其底层都建立在 std::sync::Once 原语之上。

  • 双重检查锁(Double-Checked Locking):在高并发环境下,当多个线程同时第一次访问该变量时,底层会通过原子操作(Atomic)检查初始化状态。
  • 阻塞等待:率先到达的线程会成功获取内部锁并执行初始化闭包,而其他慢了一步的线程会安全地阻塞等待,直到第一个线程初始化完毕。
  • 零运行时开销:一旦初始化完成,后续的所有访问都会通过原子操作(Ordering::Acquire)直接读取数据,不再有任何锁竞争或阻塞开销。

五、 选型指南:

  • 无脑选择 LazyLock 的场景:如果你要定义一个全局静态常量(如正则表达式 Regex、固定配置项),且它的初始化逻辑是绝对固定、不依赖运行时动态入参的。它的人体工程学体验最好,直接当成普通变量用即可。
  • 必须选择 OnceLock 的场景:
    • 变量的初始化依赖于运行时才能获取到的参数(例如从命令行解析出的 port,或者读取出来的 config_path)。
    • 变量的初始化可能会失败(需要用到 get_or_try_init 捕获错误并支持重试)。
    • 需要在复杂的结构体内部实现某个字段的懒加载(OnceLock 的类型签名没有闭包,对结构体更友好)。

如果需要了解上述锁机制在单线程环境下的非线程安全高效替代品,可以随时询问标准库的 std::cell::LazyCellstd::cell::OnceCell。 

参考资料:

Struct std::sync::OnceLock

Rust: std::sync::OnceLock —— 全局变量的标准化方案