一、说明
类型 | 含义 | 样例 |
---|---|---|
T | 1、所有可能的类型集合或其中一个具体类型 | T,&T,&&T;i32,&i32,&&i32 |
&T | T类型的引用(共享不可变的引用) | &i32, &&i32 |
&mut T | T类型的可变引用(独占可变引用) | &mut i32 |
T类型是&T和&mut T的超集;
“可变不共享,共享不可变”;
在Rust中一个变量的生命期是指其所指向的内存地址中数据有效的“一段时期”;而这段时期是有编译器静态分析得出的,有效性是由编译器保证的
二、样例
1、T 只包含所有权类型
类型 | T | &T | &mut T |
---|---|---|---|
样例 | i32 | &i32 | &mut i32 |
其中T包含全体所有权类型;&T包括全体不可变引用;&mut T包括全体可变引用;
其中T、&T、&mut T是不相交的有限集合
实际情况如下
类型 | T | &T | &mut T |
---|---|---|---|
例子 | i32,&i32,&muti32,&&i32,&mut &mut i32,... | &i32,&&i32,&&mut i32,... | &mut i32, &mut &mut i32, &mut &i32,... |
T, &T, &mut T都是无限集合; T是&T和&mut T的超集(由于可引用类型T自身无限次),且&T和&mut T是不相交的集合
trait TestTrait {}
impl<T> TestTrait for T {} // target-1
impl<T> TestTrait for &T {} // target-2
impl<T> TestTrait for &mut T {} // target-3
上述代码是不能编译通过的:
error[E0119]: conflicting implementations of trait `TestTrait` for type `&_`
--> src/bin/trait_generic_demo.rs:6:1
|
4 | impl <T> TestTrait for T{}
| ------------------------ first implementation here
5 |
6 | impl<T> TestTrait for &T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&_`
所以验证了“T是&T和&mut T的超集”,也就意味着“target-1”的代码包括了“target-2”和“target-3”的实现;
而若是注释掉“target-1”的代码,其他不变,则编译也是OK的,说明了“&T和&mut T是不相交的集合”
2、若是 T:'static,那么T的生命周期就会直到程序结束为止都一定是有效的
- T:'static 应视为T有着'static的生命周期 ???
- &'static T和T:'static 是一回事 ???
- 若T:'static 则T一定不可变的 ???
- 若T: 'static 则只能编译期创建 ???
当首次接触'static生命周期,更多是如下的例子
let str_literal: &'static str = "字符串字面量";
这里的“字符串字面量”会被硬编码到编译出来的二进制文件中,并在运行时被加载到只读内存中,在整个运行期间有效且不可变的,使其生命周期为'static。 ???
举个例子:
static strs: [&str; 3] = ["hello", "world", "yes"];
static mut mut_strs: [&str; 3] = ["hello", "world", "yes"];
fn main() {
// 错误信息
// error[E0594]: cannot assign to `strs[_]`, as `strs` is an immutable static item
// --> src/bin/static_var_demo.rs:7:5
// |
// 7 | strs[0] = "come_here";
// | ^^^^^^^^^^^^^^^^^^^^^ cannot assign
// strs[0] = "come_here";
unsafe {
mut_strs[0] = "come_here";
assert_eq!("come_here", mut_strs[0]);
}
}
从上面的示例中,得出关于静态变量
- 只能在编译期创建
- 默认情况下它们是不可变的,修改静态变量可通过unsafe
- 在整个程序运行期间都有效
那么静态变量的生命周期是不是就是'static? 具有'static生命周期的变量是否要遵守上述同样的规则?
需要注意的的是带有'static生命周期注解的类型和一个被'static约束的类型是不一样的,后者是可以在运行时被动态分配的,能被安全的修改的,也可以被drop,其还能存活任意的时长;
区分&'static T和 T: 'static:
- &'static T是一个指向T的不可变引用,其中T可被安全的无限期地持有,甚至可直到程序结束;要求T自身不可变且保证在引用创建后不会被move; T也并不需要在编译时创建;
- T:'static 是指T可以被安全地无限期地持有,甚至可以直到程序结束;T:'static 在包括了全部的&'static T的同时,还包括了所有权类型; 数据的所有者保证,只要自身还持有数据的所有权,数据就不会失效,这样所有者能够安全的无限期的持有数据,直至程序结束;此时T满足'static生命周期的约束而非T有着'static 生命周期;
use rand;
/// &'static T 样例
fn rand_str_generator() -> &'static str {
let rand_str = rand::random::<u64>().to_string();
Box::leak(rand_str.into_boxed_str()); // 存在“内存泄漏”
}
/// T: 'static样例
fn drop_static<T: 'static>(t: T) {
std::mem::drop(t);
}
fn drop_static_demo() {
let mut strings: Vec<String> = Vec::new();
for _ in 0..10 {
if rand::random() {
let string = rand::random::<u64>().to_string();
strings.push(string);
}
}
for mut string in strings {
string.push_str(" a mutation");
println!("{:?}", string);
drop_static(string);
}
println!("i am the end of the program");
}
fn main() {
// &'static T
let rand_str = rand_str_generator();
println!("{:?}", rand_str);
// T: 'static 样例
drop_static_demo();
}
关键点:
- T: 'static 应当视为 “T 满足 'static 生命周期约束”
- 若 T: 'static 则 T 可以是一个有 'static 生命周期的引用类型 或 是一个所有权类型
- 因为 T: 'static 包括了所有权类型,所以 T
- 可以在运行时动态分配
- 不需要在整个程序运行期间都有效
- 可以安全,自由地修改
- 可以在运行时被动态的 drop
- 可以有不同长度的生命周期
(待续)