Rust中文翻译27

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提供了一种特性,借用,来帮我们解决这个问题.那是下一节的主题!

时间: 2024-08-30 00:58:26

Rust中文翻译27的相关文章

Rust中文翻译7

去台湾玩儿了一个礼拜,赶紧回来继续翻译吧! 3.1.5 循环 Page 38 loop关键字可以实现一个无限循环.让我们来加入一个循环: extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); prin

Rust中文翻译29

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

Rust中文翻译21

4.7 错误处理 有时候程序会发生错误.对于不可避免的事情发生时最好有一个计划来处理.Rust有丰富的处理错误的方法. 你的程序会出现两种类型的错误:失败和崩溃.我们先讨论两者的区别,然后讨论如何处理他们.然后,我们讨论把错误升级为崩溃. Page 107 4.7.1 失败和崩溃 Rust使用两种错误的类型:失败和崩溃.失败是一种可以被挽回的错误.崩溃却不行. "挽回"是什么意思呢?在多数情况下,很有可能会发生错误.例如,一个parse函数: "5".parse()

Rust中文翻译12

Page 63 我们已经学习了如何写一些Rust代码了.但是能写Rust代码和能写好的Rust代码又很大区别. 这一章讲述一些相对独立的指南,告诉你如何把的Rust代码更进一步.一些常用的模式和标准库会被提到.你可以随意安排阅读本章的顺序. 4.1 栈和堆 作为一个系统级别的语言,Rust可以在底层进行操作.如果你来自于高层语言,有一些系统编程概念你可能不太熟悉.最重要的就是内存中的栈和堆是如何工作的.如果你了解类C语言中栈的分配,这一章就是一个回顾.如果你不了解,你可以在本章学习这些概念了,但

Rust中文翻译28

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

Rust中文翻译17

4.2.4 文档测试 没有什么比带有示例的文档更好的了.也没有什么比不能工作的例子更糟的了,因为有可能文档中的代码已经修改了.为此,Rust支持自动测试我们示例代码.让我们看一个富有血肉的src/lib.rs的例子: //! The àdder` crate provides functions that add numbers to other numbers. //! //! # Examples //! //! ``` //! assert_eq!(4, adder::add_two(2)

Rust中文翻译15

大多数使用垃圾回收的语言都默认在堆上分配内存.这就意味着每个值都要装箱.有很多原因导致他们这样设计,但是这超出了本书的范围.同样,也有很多优化设计导致它并不是100%这样工作的.垃圾回收器宁愿选择在堆上分配内存,也不使用栈和Drop操作来释放内存. 4.1.7 该用哪一种呢? 所以当栈又快又好用的时候,为什么我们还需要使用堆呢?一个重要的原因是,栈只给你提供了LIFO语义来管理内存.堆分配却可以提供更加通用的和任意的顺序来使用内存,同时附带一些开销. 通常情况下,你应当使用栈,Rust默认就是如

Rust中文翻译20

Page 100 并发和并行在计算机科学中是非常重要的主题.在工业领域也很火.计算机如今有越来越多的核心,然而很多程序员还没有准备好使用它们. Rust安全的内存特性同样适用于并发存储.并发的Rust代码也是内存安全的,没有数据竞争.Rust的类型系统会保证这一点,给你提供了有利的帮助在编译时实现并发代码. 在我们开始讨论Rust的并发之前,我们需要理解一个很重要的事情:Rust是一个低级别语言,它的所有功能都是通过库来提供的,而不是语言自身.也就是说你不喜欢Rust处理并发的方式,你完全可以实

Rust中文翻译34

5.14 Match 经常,一个简单的if/else还不够,因为你可能会有多于两种情况.而且,条件会变得更加复杂.Rust有一个关键字,match,允许你替代复杂的if/else组合,来实现一些更强大的功能.看一下: let x = 5; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), 4 => println!(&