【Rust 日报】2023-11-26 Rust全局变量,两年过去了

2023-11-28 15:10:02 浏览数 (1)

Rust全局变量,两年过去了

本文是一篇关于Rust全局变量的翻译文章。

2021 年 11 月,我写了一篇博文,研究了 Rust 与全局变量的奇怪关系。它旨在解释为什么这种无处不在的语言特性需要外部 crates,并以关于在新代码中使用全局变量的个人建议结束。两年过去了,Rust 已经发生了很大的变化,是时候重新审视一下了。本文的其余部分假定您已经阅读了上一篇文章或熟悉该主题。

Const Mutex 和 RwLock 构造函数

第一个变化是 Mutex::new() 从 Rust 1.63 开始是 const,所以上一篇文章中的这个例子现在可以按预期编译和工作:

代码语言:javascript复制
// 2年前不能编译,现在可以了
static LOG_FILE: Mutex<String> = Mutex::new(String::new());

1.62 为这一改进奠定了基础,它在 Linux 上用轻量级、非分配实现取代了 Mutex、RwLock 和 CondVar,并且 1.63 扩展为在所有平台上提供这些类型的const构造。结果是,对于简单类型的互斥保护全局变量,无需任何特殊处理就能工作。

尽管我们不再需要将每个静态 Mutex 封装在 OnceCell 或等效物中,但我们仍然需要一个类似cell的包装器,用于仅在首次使用时完成锁定写入以初始化值的情况。在这种情况下,对全局的后续访问是只读的,不应该需要锁定,只需要原子检查。这是全局变量的一种非常常见的用法,一个很好的例子是全局变量持有一个延迟编译的正则表达式。

这给我们带来了下一个更重要的消息。

OnceCell 已进入标准库

从 Rust 1.70 开始,once_cell crate 的 once_cell::sync::OnceCell 集成到标准库中,成为 std::sync::OnceLock。在 Rust 存在以来,这是第一次,你不需要编写不安全的代码,也不需要引入封装它的外部 crate,就能够创建在首次使用时初始化的全局/静态变量。用法基本与once_cell相同:

代码语言:javascript复制
use std::sync::OnceLock;
use regex::Regex;

pub fn log_file_regex() -> &'static Regex {
    static LOG_FILE_REGEX: OnceLock<Regex> = OnceLock::new();
    LOG_FILE_REGEX.get_or_init(|| Regex::new(r#"^d -[[:xdigit:]]{8}$"#).unwrap())
}

// use log_file_regex().is_match(some_name) anywhere in your program

这个新增功能乍看起来可能并不像个大事件,因为once_cell 多年来一直提供了相同的功能。然而,将其加入到标准库中在几个方面极大地有益于该语言。首先,应用程序和库都广泛使用 initialize-on-first-use 的全局变量,现在两者都可以从它们的依赖项中淘汰像once_celllazy_static这样的 crate。其次,现在可以通过宏生成的代码创建全局变量,而不会出现笨拙的 once_cell 再导出和其他逻辑问题。第三,它使得教授这门语言变得更容易,教材不再需要决定是否涵盖once_celllazy_static,也不需要解释为什么一开始就需要外部 crate 来处理全局变量。这个煞费苦心的长篇 StackOverflow 回答就是一个深陷泥潭的好例子,我先前关于这个主题的博客文章也是如此。后者中的整个stdlib/unsafe部分现在已经变得过时,因为使用OnceLock可以在不损失性能的情况下安全地实现相同的效果。

然而,工作还没有完成。请注意静态变量如何被放置在包含对OnceLock::get_or_init()进行唯一调用的函数内部。这种模式确保对静态OnceLock的每次访问都通过一个位置,该地方还对其初始化。once_cell通过once_cell::sync::Lazy减少了这种冗长性,但等效的stdlib类型尚未稳定,卡在一些技术问题上。将全局变量放置在函数内的解决方法并不是一个重大障碍,但值得一提。当比较OnceLock的使用便捷性与lazy_static::lazy_static!once_cell::sync::Lazy相比时,这一点尤为重要,后两者都提供了在单一位置初始化而无需额外工作的便利性。

2024年使用什么

两年前,我建议的TL;DR是“根据你更喜欢的语法选择使用once_celllazy_static”。现在,建议转变为:在几乎所有情况下使用标准库设施,比如OnceLock或原子操作,当你需要的便利性尚未被标准库覆盖时,再使用once_cell

特别是:

  • 与以前一样,当你想在static中使用的类型支持线程安全的内部可变性并具有const构造函数时,可以直接将其声明为静态。 (编译器会为你检查所有这些,只需查看它是否能编译。) 这以前仅包括原子类型,但现在还包括互斥锁和读写锁。 因此,如果像static CURRENT_CONFIG: Mutex<Option<Config>> = Mutex::new(None)static SHOULD_LOG: AtomicBool = AtomicBool::new(true) 对你有用,可以直接使用。
  • 当这种方法不起作用,或者需要在首次使用时进行初始化,请使用std::sync::OnceLock,最好封装在如上所示的函数中。
  • 如果你创建了大量的全局变量,并希望避免每个变量都封装在一个函数中的样板代码,可以使用once_cell::sync::Lazy。该类型很可能以某种形式稳定下来,这使其优于lazy_static。在新代码中使用lazy_static没有好的理由。

请注意,使用once_celllazy_static的现有代码并不需要立即处理。这些 crate 将无限期保持可用,并且它们生成的汇编代码几乎与标准库的OnceLock相同。上述建议旨在指导你对新代码或你正在重构的代码的决策。

文章: https://morestina.net/blog/2055/rust-global-variables-two-years-on

minus 5.5.0发布

minus 是一个用 Rust 编写的异步终端分页库。这个版本有很多新功能:

  • 增量搜索。
  • 能够禁用提示的显示。
  • 能够控制搜索高亮显示的处理方。
  • 一些新功能,用于在搜索处于活动状态时应用条件以运行增量搜索。

GitHub: https://github.com/arijit79/minus

hysp:CTF玩家的包管理器

项目作者是一名 CTF 玩家,在玩 CTF(夺旗)时需要大量的工具。大多数渗透测试 Linux 发行版不提供软件包,有些发行包很旧,例如 kali 或 parrot。

为了解决这个问题,作者写了一个包管理器 hysp,它有简单的 pkgs toml 配置,它允许二进制安装,目前使用 sha 进行二进制验证,(计划在下一个版本里程碑中使用 blake3)。

刚刚发布了一个新版本,其中包含定义自定义配置文件的选项,例如定义 home、bin、data directory 和 repository 以查找包。

GitHub: https://github.com/pwnwriter/hysp


From 日报小组 长琴

社区学习交流平台订阅:

  • Rustcc 论坛:支持 rss
  • 微信公众号:Rust 语言中文社区

0 人点赞