跳过正文

常量:const/static/lazy_static!

··866 字
Rust
rust-lang - 这篇文章属于一个选集。
§ 5: 本文

Rust 不支持用 let 定义全局变量,但支持使用 conststatic 定义的全局常量。全局常量需要使用全大写名称,否则编译器警告:

  1. const :不可变值;
  2. 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 常量的区别:

  1. const 常量 :编译器不分配内存,故没有内存地址,编译时做 inline 替换;
  2. 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;
}
rust-lang - 这篇文章属于一个选集。
§ 5: 本文

相关文章

不安全:unsafe
··824 字
Rust
Rust
借用:refer/borrow
··3127 字
Rust
Rust 引用类型和借用
函数、方法和闭包:function/method/closure
··7032 字
Rust
Rust 函数、方法和闭包
包和模块:package/crate/module
··2066 字
Rust
Rust 项目的包和模块组织结构