在函数或模块的作用域内初始化一个静态变量

6

示例代码:

use std::sync::atomic::{AtomicU32, Ordering};

#[derive(Debug)]
struct Token(u32);

impl Token {
    fn new() -> Self {
        static COUNTER: AtomicU32 = AtomicU32::new(1);
        let inner = COUNTER.fetch_add(1, Ordering::Relaxed);
        Token(inner)
    }
}

fn main() {
    let t1 = Token::new();
    let t2 = Token::new();
    let t3 = Token::new();
    println!("{:?}\n{:?}\n{:?}", t1, t2, t3);
}

当我运行上面展示的代码片段时,它会打印出:
Token(1)
Token(2)
Token(3)

我在 Rust 参考文献中发现静态项的初始化是在编译时评估的。
当程序执行到初始化变量COUNTER的行时,我想知道运行时究竟发生了什么。编译后的代码看起来像什么,以便可以忽略初始化?
1个回答

6

static 变量的处理方式无论在模块级别还是函数级别定义,都是一样的。唯一的区别是名称解析的范围不同。

许多可执行文件的文件格式,包括 ELFPE,会将程序结构化到不同的部分中。通常,函数的代码位于一个部分中,可变全局变量位于另一个部分中,而常量(包括字符串字面值)位于另一个部分中。当操作系统将程序加载到内存中时,这些部分会以不同的内存保护选项映射到内存中(例如,常量是不可写的),如可执行文件中所指定的那样。

当文档说 static 项目在编译时初始化时,它意味着编译器确定该项目的初始值,然后将该值写入已编译二进制文件中的适当部分。当运行程序时,在程序运行任何指令之前,该值将已经在内存中存在了。

编译器能够评估表达式 AtomicU32::new(1),因为 AtomicU32::new 被定义为一个 const fn。将 const 添加到函数定义中意味着它可以用于在编译时评估的表达式中,但 const fn 在其能够执行的工作方面比常规函数受到更多限制。然而,在 AtomicU32::new 的情况下,该函数所需做的全部工作就是初始化 AtomicU32 结构体,这只是围绕一个 u32 的包装器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接