在软件开发过程中,如果一个变量总是保持不变,我们可以声明为常量,如果一个变量全局唯一,可以使用静态变量,如果既是常量又是全局变量,则可以同时声明这2种特性,例如java可以这样 public static final int ID = 123。
Rust语言中使用const, static来实现这2个场景,但与其他语言稍有不同,rust中的const 和 static不能一起使用。
首先,我们先看下const, const有以下特点
每个使用const常量的地方,相当于拷贝了一份const常量,所以如果有 3 个地方用到常量,则会调用3次该类型的drop函数, 但如果没有地方用到
如果是定义常量引用,则不会有drop函数调用,因为使用他的地方是拷贝引用,没有生成对象
常量自定义结构体裸指针,不能实现Drop trait,否则编译不过
我们先看下使用常量对象
: FOO = FOO {}; {} () { test_const(); test_const(); test_const_ref(&); } (foo:FOO){} (foo:&FOO){} FOO { (&) { (); } }
输出如下,我们调用了3次函数,drop也被调用了次,说明 每个函数调用的地方,都生成了一个FOO对象
Finished dev [unoptimized + debuginfo] target(s) in 0.87s
Running `target\debug\rtest.exe`
FOO drop
FOO drop
FOO drop
再看下使用常量引用
: &FOO = &FOO {}; {} () { test_const_ref(); () } (foo:&FOO){} FOO { (&) { (); } }
运行结果如下,可以看到,使用常量引用,没有drop函数调用
() 0 `\\`
再看下常量裸指针
: *FOO = &FOO {} *FOO; {} () { () } FOO { (&) { (); } }
运行会编译出错
error: untyped pointers are not allowed constant --> src\main.rs:: | | FOO_PTR: * FOO = &FOO {} * FOO; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; warning emitted
去掉Drop impl后
: *FOO = &FOO {} *FOO; {} () { () } 编译成功 Finished dev [unoptimized + debuginfo] target(s) s Running `target\debug\rtest.exe` ok
下面再看看static, static有以下特点
static变量全局唯一,生命周期在整个应用中,所有权不能转移,drop函数不会被调用
不可变static变量必须实现Sync trait,以实现跨线程访问
可变static 变量可以不实现 sync特质,对可变static的访问需要unsafe块或unsafe函数中使用。该类型变量在多线程访问时,必须确保安全性,比如加锁, 可变static 必须在unsafe中访问
先看下 1,所有权不能转移
:FOO = FOO {} ; {} () { test(); () } (foo:FOO) { }
test函数需要FOO的所有权,可以看到编译会出错
error[E0507]: cannot move out of item --> src\main.rs:: | | test(FOO); | ^^^ move occurs because has , which does not implement the trait error: aborting due to previous error; warning emitted
再看看析构函数方面
:FOO = FOO {} ; {} () { test_ref(&); test_ref(&); () } (foo:&FOO) { } FOO { (&) { (); } } 输出,没有调用析构函数 Finished dev [unoptimized + debuginfo] target(s) s Running `target\debug\rtest.exe` ok
对于2,跨线程访问,需要实现Sync trait. 由于 Rust中,只要Struct/Enum中的所有成员是Sync的,则该Struct/Enum也是Sync
以下FOO,i32自动是Sync,所以编译成功
:FOO = FOO{: &} ; {: &, } () {}
改成下面
:FOO = FOO{ : &*} ; { : *, } () {}
编译出错,原因是 裸指针不是Sync的,所以报错
error[E0277]: `* ` cannot be shared between threads safely --> src\main.rs:: | | / FOO:FOO = FOO{ | | id: & * | | } ; | |___^ `* ` cannot be shared between threads safely
对于3,可变static变量, 非Sync的类型也可以声明,如以下例子
:FOO = FOO{ : &*} ; { : *, } () {}
最后,需要注意的是,unsafe是程序员自己保证安全性。以下例子演示 static mut在多线程下,数据不一致的问题
use std::collections::HashSet;
static mut clock:i32 = 0;
fn main() {
let mut res = HashSet::new();
for i in 0 .. 200 { //测试200次,次数够多,出现数据不一致的几率才会大
res.insert(test_mut_static_in_threads());//test_mut_static_in_threads函数,多线程对全局变量clock递增,返回递增后的结果,存入set中去重
unsafe {
clock = 0;//将全局变量清零
}
}
for i in res {
println!("num is .... {}", i);
}
}
fn test_mut_static_in_threads() -> i32 {
let mut vv = vec![];
for i in 0 .. 1000 {
let v = std::thread::spawn(||{
unsafe {
clock = clock + 1;
}
});
vv.push(v);
}
for v in vv {
v.join().unwrap();
}
unsafe {
clock
}
}
看下运行结果,可以看到多线程下,全局变量自增不一致,如果需要保证一致性,需要加锁或者使用并发原语
: 3 warnings emitted