Rust 中的所有权
Rust 语言的内存管理不同于其他语言常见的GC和手动内存释放, 是通过所有权这一机制来实现的.
所有权机制有如下特点
Rust中的变量在离开作用域之后就会直接被丢弃.- 一个变量只有一个所有者
一个变量只拥有一个所有者, 也就是说, 当这个位置上的指针指向的值有新的指针指向之后, 这个指针将会直接被丢弃.
上述代码在编译时将会遇到如下报错:
error[E0382]: borrow of moved value: `s1`
--> src/main.rs:19:15
|
17 | let s1 = String::from("value");
| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
18 | let s2 = s1;
| -- value moved here
19 | println!("{s1}");
| ^^^^ value borrowed here after move
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
|
18 | let s2 = s1.clone();
| ++++++++
For more information about this error, try `rustc --explain E0382`.
error: could not compile `test1` (bin "test1") due to 1 previous error
简单的let s2 = s1操作只是将s1的指针交由s2进行保管, 如果需要进行深拷贝, 则要使用let s2 = s1.clone()
那么为什么这段代码却不会报错呢
首先, 分配给程序的内存分为堆和栈两个不同的结构.
栈是一种LIFO(后进先出)结构, 每次都只能操作栈顶的元素进行push(压栈)或是pop(弹栈), 正因为结构简单, 栈上的操作相当迅速, 对于"str"这种简单的常量字符串直接复制一份也花不了太长时间.
而String::from("value")则是可变的String类型, 这种类型可能会随时变化内存的占用大小, 需要存储在堆区(因为栈区的操作限制, 不能动态分配内存). 在堆区操作相对较慢, 将所有数据复制出一份新的(深拷贝)将会浪费大量时间.
拷贝指针的方式固然很快, 但是又出现了一个问题, s1和s2指向同一片内存, 在释放内存的时候就会遇到二次释放的问题.
Rust的所有权问题就可以很好的处理这个问题. 因为这个特殊的机制, let a = b这种操作也被称为变量绑定.
既然这样, 如果我还是需要两份一样的字符串该怎么做呢?
可以新建一个指针引用, 这个指针指向s1, 再通过s1的指针指向实际的String(这种操作称为借用), 就像下面这样
如果需要修改指针指向的值, 需要给s1加上可变声明
虽然使用可变引用的方式很方便, 但是可变引用在同一作用域对单个数据智能存在一个. 所以下面的代码就会引发报错.
报错如下
error[E0499]: cannot borrow `s1` as mutable more than once at a time
在同一个作用域下使用多个可变引用可能会有数据污染的问题, 在一个可变引用中修改了值后另一个可变引用并不知道...
除此之外, Rust总是要求你使用的引用是有效的, 如果存在 c 语言下类似的悬空指针的问题将无法通过编译.
上面的代码会提示this function's return type contains a borrowed value, but there is no value for it to be borrowed from.
一个简单的解决办法就是直接返回一个String
(就算你声明为静态也是不允许的, 就像下面这样)
报错:
error[E0015]: cannot call non-const associated function `<String as From<&str>>::from` in statics
因为Rust需要一个 static 变量在编译时就能确定其值.
Rust的内存安全简直丧心病狂...