Rust中文翻译27

来源:互联网 发布:网络维护maxingit 编辑:程序博客网 时间:2024/05/29 15:08
5.8 所有权

本节是Rust三处描述所有权系统的其中之一.所有权是Rust最独特和引人注目的特性,这也是Rust程序员必须熟悉的一个特性.所有权使Rust得以实现它最大的设计目标,内存安全.这里有一些不同的概念,每一个都有自己的章节:
  • 所有权,你正在读的
  • 借用(borrowing, 5.9), 以及它的关联特性'引用'
  • 生命期(5.10),以及borrowing的高级特性
这三者是相关的,也是循序渐进的.你必须要完全理解这个三个部分.

5.8.1 元

在我们讨论细节之前,有两个关于所有权系统的重要说明.

Rust致力于安全和快速.它实现这个目标通过"零花费的抽象(zero-cost abstractions)",也就是说在Rust中,抽象花费最少的代价.我们在本章中讨论的所有分析都是在编译时完成的.你不需要为运行时开销费心.

然而,这个系统仍然有一些开销:学习曲线.很多Rust的初学者都会经历我们成为"与借用检测做斗争"的过程.也就是说Rust编译器拒绝编译程序员认为是可用的代码.因为程序员关于所有权如果运行的想法和真正的Rust规则经常发生冲突.你在开始的时候也会尽力类似的情况.然而这是好消息,更多有经验的Rust程序员反馈说一旦他们适应了这个规则一段时间以后,他们就会斗争的越来越少.

有了这个铺垫,我们来学习所有权系统.

5.8.2 所有权

变量绑定在Rust中有一个属性:他们对于他们绑定的对象具有所有权.也就是说当一个绑定离开了自己的作用域,那么他们绑定的资源就会被释放.例如:

fn foo() {
    let v = vec![1, 2, 3];
}

当v进入作用域的时候,一个新的Vec<T>对象被创建.在此例中,向量同时会在堆上分配内存给三个元素.当v离开foo()函数的时候,它就离开了自己的作用域,Rust会清理与这个向量有关的所有数据,甚至是堆上的空间.在作用域结束的时候这肯定会发生.

5.8.3 move 语义

然而,有一个微妙的东西:Rust保证任何资源只有一个明确的绑定对象.例如,如果你有一个向量,我们可以给它分配另一个绑定:

let v = vec![1, 2, 3];
let v2 = v;

但是,如果你在后面使用了v,我们会得到一个错误:

let v = vec![1, 2, 3];
let v2 = v;
println!("v[0] is: {}", v[0]);

他看起来显示这样的:




类似的情况发生在当你定义了一个函数,它获取了所有权,然后又把绑定对象当作参数传递给函数,之后使用了它:
fn take(v: Vec<i32>) {
     // what happens here isnʼt important.
}
let v = vec![1, 2, 3];
take(v);
println!("v[0] is: {}", v[0]);

同样的"use of moved value"错误发生了.当我们传递了所有权之后,我们说我们已经移动了我们指向的对象.你不必在此时特意声明什么,Rust代码会默认这样做.

细节:

我们在移动了所有权之后就无法使用了的原因很微妙,但是很重要.当我们这样写代码:

let v = vec![1, 2, 3];
let v2 = v;

第一行分配了一个向量的内存空间,v,以及它的所有数据.向量对象存放在栈上,它有一个指针,指向的内容在堆上.当我们把v赋值给v2,他创建了一个指针的拷贝,赋给v2.也就是说此时有2个指针都指向了向量在堆上的地址.这会导致Rust安全问题,引起数据竞争.因此,Rust禁止使用没移动过以后的v.

同样重要的是,优化会移除栈上的那次拷贝,取决于上下文环境.所以它不会像看起来的那样效率低下.

拷贝类型

我们已经解释了所有权在被转移到另一个绑定之后你就无法再使用原始的绑定了.然而,有一个特性(trait)可以改变这个行为,那就是拷贝.目前我们还没有讲过特性,你可以认为它就是一个特定类型的标注,提供了额外的行为.例如:

let v = 1;
let v2 = v;
println!("v is: {}", v);

此例中,v是一个i32,它实现了拷贝特性.也就是说,就像移动一样,我们将v赋值给v2时,这个数据的拷贝也生成了.但是,和移动不一样的是,我们仍然可以使用v.那是因为i32没有指向其他数据的指针,它的一份拷贝是全拷贝.

我们后面会讨论拷贝.

5.8.4 更多:

当然,如果我们必须要要交还所有权在使用函数的时候:

fn foo(v: Vec<i32>) -> Vec<i32> {
    // do stuff with v
    // hand back ownership
    v
}
这个看起来很荒谬.当我们使用更多所有权的时候会更糟:

fn foo(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) {
    // do stuff with v1 and v2
    // hand back ownership, and the result of our function
    (v1, v2, 42)
}

let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];
let (v1, v2, answer) = foo(v1, v2);

返回类型,返回行,函数调用都是的事情变得复杂.
幸运的是,Rust提供了一种特性,借用,来帮我们解决这个问题.那是下一节的主题!
1 0
原创粉丝点击