Rust 所有权
程序的内存可以分配在以下几个地方:
- 栈(Stack)
- 堆(Heap)
栈
栈遵循先进后出的顺序。栈用于存储在编译时大小已知的数据值。例如,大小固定的i32类型的变量可以在栈上分配,因为其大小在编译时已知。所有标量类型都可以存储在栈上,因为大小是固定的。
考虑一个字符串的示例,在运行时给它赋值。这样的字符串的确切大小在编译时无法确定。因此,它不适合在栈上分配,而适合在堆上分配。
堆
堆存储在编译时大小未知的数据值。它用于存储动态数据。简单地说,堆内存分配给可能在程序生命周期内发生变化的数据值。与栈相比,堆的组织结构较不规整。
什么是所有权
Rust中的每个值都有一个称为“所有者”的变量。Rust中存储的每个数据都有一个与之关联的所有者。例如,在以下语法中:
let age = 30,age是值30的所有者。
- 每个数据一次只能有一个所有者。
- 两个变量不能指向同一个内存位置。这些变量将始终指向不同的内存位置。
转移所有权
可以通过以下方式转移值的所有权:
- 将一个变量的值赋给另一个变量。
- 将值传递给函数。
- 从函数中返回值。
将一个变量的值赋给另一个变量
Rust作为一种语言的一个主要卖点是其内存安全性。通过对谁可以在什么时候使用限制的严格控制来实现内存安全。
考虑以下代码片段:
fn main(){
let v = vec
![1,2,3];
// vector v owns the object in heap
//only a single variable owns the heap memory at any given time
let v2 = v;
// here two variables owns heap value,
//two pointers to the same content is not allowed in rust
//Rust is very smart in terms of memory access ,so it detects a race condition
//as two variables point to same heap
println!("{:?}",v);
}
上面的示例声明了一个向量v。所有权的概念是只有一个变量绑定到资源,要么是v绑定到资源,要么是v2绑定到资源。上面的示例抛出了一个错误 – 使用了转移的值:v
。这是因为资源的所有权转移到了v2。这意味着所有权从v转移到了v2(v2=v),并且在转移之后,v将无效。
将值传递给函数
当我们将堆中的一个对象传递给闭包或函数时,值的所有权也会发生变化。
fn main(){
let v = vec
![1,2,3]; // vector v owns the object in heap
let v2 = v; // moves ownership to v2
display(v2); // v2 is moved to display and v2 is invalidated
println!("In main {:?}",v2); //v2 is No longer usable here
}
fn display(v:Vec<i32>){
println!("inside display {:?}",v);
}
从函数返回值
传递给函数的所有权在函数执行结束时将失效。其中一个解决方法是让函数将所有权的对象返回给调用者。
fn main(){
let v = vec
![1,2,3]; // vector v owns the object in heap
let v2 = v; // moves ownership to v2
let v2_return = display(v2);
println!("In main {:?}",v2_return);
}
fn display(v:Vec<i32>)->Vec<i32> {
// returning same vector
println!("inside display {:?}",v);
}
所有权和基本类型
对于基本类型,一个变量的内容被复制到另一个变量中。所以,没有发生所有权转移。这是因为一个基本类型变量需要比一个对象更少的资源。考虑以下示例 –
fn main(){
let u1 = 10;
let u2 = u1; // u1 value copied(not moved) to u2
println!("u1 = {}",u1);
}
输出将为 -10。