Rust 不支持用 let 定义全局变量,但支持使用 const
或 static
定义的全局常量。全局常量需要使用全大写名称,否则编译器警告:
const
:不可变值;static
:不可变或可变的值(static mut
),需要在 unsafe 中读写 static mut 值;
const 和 static 常量默认具有 'static lifetime
,即它们在程序运行过程中一直存在。
const 是编译时全局常量,声明时必须指定值类型,同时用常量值进行初始化,编译器不会自动推导常量类型:
// 字面量常量初始化
const THING: u32 = 0xABAD1DEA;
// const 借用默认是 'static
const WORDS: &'static str = "hello rust!";
// 等效于
const WORDS: &str = "hello rust!";
const 和 static 常量的区别:
const 常量
:编译器不分配内存,故没有内存地址,编译时做 inline 替换;static 常量
:有内存地址,故支持借用操作,而且 main 函数返回时并不会 drop static 常量值;
// 全局常量:在所有 scope 外声明和初始化
const THRESHOLD: i32 = 10; // 全局常量
static LANGUAGE: &str = "Rust"; // 全局常量,默认为 'static lifetime
// 全局 static 可变变量,需要在 unsafe 代码中访问
static mut stat_mut = "abc";
fn is_big(n: i32) -> bool {
n > THRESHOLD
}
fn main() {
let n = 16;
println!("This is {}", LANGUAGE);
println!("The threshold is {}", THRESHOLD);
println!("{} is {}", n, if is_big(n) { "big" } else { "small" }); // if-else 可以返回值
// 错误:不可以修改 const 值。
THRESHOLD = 5;
}
fn computation() -> &'static DeepThought {
// 在函数中声明 static 常量,和 C 的 static 变量类似,在函数返回时常量仍有效,在程序整个生命周
// 期均有效。
static COMPUTATION: OnceLock<DeepThought> = OnceLock::new();
COMPUTATION.get_or_init(|| DeepThought::new())
}
对 const/static 常量的初始化, 只能使用 const 类型函数或编译时可以确定结果的常量表达式
。为了克服该限制,可以使用 lazy_static!
宏定义静态变量,初始化表达式会在变量第一次被解引用时自动运行,表达式值会被存储在常量中供后续使用:
use std::sync::Mutex;
lazy_static! {
// 任意初始化表达式,而不局限于 const 类型函数和值。
static ref HOSTNAME: Mutex<String> = Mutex::new(String::new());
}
const/static 常量不可修改,所以一般使用支持内部可变性的 Mutex/AtomicXX
来作为全局变量类型:
// static 常量不能被修改,也不能被 move:
static VEC: Vec<u32> = vec![];
fn move_vec(v: Vec<u32>) -> Vec<u32> {
v
}
// move_vec(VEC); // 错误
const BIT1: u32 = 1 << 0;
const BIT2: u32 = 1 << 1;
const BITS: [u32; 2] = [BIT1, BIT2];
const STRING: &'static str = "bitstring";
struct BitsNStrings<'a> {
mybits: [u32; 2],
mystring: &'a str,
}
const BITS_N_STRINGS: BitsNStrings<'static> = BitsNStrings {
mybits: BITS,
mystring: STRING,
};
use std::sync::Mutex;
use std::sync::atomic::AtomicUsize;
static MY_GLOBAL: Vec<usize> = Vec::new(); // OK, 但是不可修改。
static PACKETS_SERVED: AtomicUsize = AtomicUsize::new(0); // ok, 可以修改
static HOSTNAME: Mutex<String> = Mutex::new(String::new()); // ok, 可以修改
fn main() {
let mut name = HOSTNAME.lock().unwrap();
name.push_str("localhost");
println!("Results: {name}");
}
// use `once_cell` crate if you need lazy initialization of a constant
use once_cell::sync::OnceCell;
const HOME_DIR: OnceCell<String> = OnceCell::new();
// use .set to set the value (can only be done once)
HOME_DIR.set(std::env::var("HOME").expect("HOME not set"));
// use .get to retrieve the value
HOME_DIR.get().unwrap();
static mut
是可以修改的全局变量,但需要在 unsafe block
中修改它。static mut 类型变量也广泛应用于 C 库的 extern block 中:
extern "C" {
// C 库全局变量
static mut ERROR_MESSAGE: *mut std::os::raw::c_char;
}