Rust所有权


程序的内存可以在以下位置分配:

  • 栈(Stack)

  • 堆(Heap)

栈遵循后进先出的顺序,栈存储在编译时已知大小的数据值,例如,固定大小为i32的变量是栈存储的候选对象,因为它的大小在编译时是已知的。固定大小后,所有标量类型都可以存储在栈中。

考虑一个字符串示例,该字符串在运行时分配了一个值,其大小不能在编译时确定,因此它不是栈存储的候选对象,而是堆存储的候选对象。

堆存储在编译时大小未知的数据值,用于存储动态数据,简而言之,将堆内存分配给在程序的整个生命周期中可能发生变化的数据值。

在内存中,堆与栈相比组织较少的区域。

什么是所有权?


Rust中的每个值都有一个变量,称为owner值。,Rust中存储的每个数据都会有一个与之关联的所有者。例如,在语法中: age = 30,age是值30的所有者。

  • 每个数据一次只能拥有一个所有者;

  • 两个变量不能指向相同的存储位置,变量将始终指向不同的存储位置。

所有权转移


值的所有权可以通过以下方式转移:

  • 将一个变量的值分配给另一个变量;

  • 将值传递给函数;

  • 从函数返回值。

将一个变量的值分配给另一个变量

Rust的主要卖点是其内存安全性,通过严格控制谁可以在什么时候使用变量来实现内存安全。

请考虑以下代码段:

fn main(){
    let v = vec![1,2,3];
    //向量v在堆中拥有对象

    //在任何给定时间只有一个变量拥有堆内存
    let v2 = v;
    //这里有两个变量拥有堆值,rust中不允许两个指向相同内容的指针

    //Rust在内存访问方面非常聪明,因此可以检测到竞争情况,因为两个变量指向同一个堆

    println!("{:?}",v);
}

所有权的思想是,只有一个变量绑定到资源,或者v绑定到资源或v2绑定到资源。

上面的例子抛出一个错误: borrow of moved value: `v`,这是因为资源的所有权已转移到v2,这意味着所有权从v移到v2(v2 = v),并且在移动后v无效。

将值传递给函数

当我们将堆中的对象传递给闭包或函数时,值的所有权也会发生变化。

fn main(){
    let v = vec![1,2,3];     //向量v在堆中拥有对象
    let v2 = v;              //将所有权移至v2
    display(v2);             // v2移至显示状态,并且v2无效
    println!("In main {:?}",v2);    // v2在这里不再可用
}
fn display(v:Vec<i32>){
    println!("inside display {:?}",v);
}

从函数返回值

传递给函数的所有权将在函数执行完成时失效,一种解决方法是让函数将拥有的对象返回给调用者。

fn main(){
    let v = vec![1,2,3];       //向量v在堆中拥有对象
    let v2 = v;                //将所有权移至v2
    let v2_return = display(v2);
    println!("In main {:?}",v2_return);
}
fn display(v:Vec<i32>)->Vec<i32> { 
    //返回相同的向量
    println!("inside display {:?}",v);
}

所有权和原始类型


如果将一个基本类型变量中的内容复制到另一个变量中,所有权没有发生转移,这是因为原始变量比对象需要更少的资源。请考虑以下示例:

fn main(){
    let u1 = 10;
    let u2 = u1;  //将u1值复制(不移动)到u2

    println!("u1 = {}",u1);
}

输出为10。