Rust语言Ownership,Reference和Lifetime详解

1. Ownership

rust的ownership系统是它区别与其它语言的最主要的特征。只有理解了ownership系统,才能真正算是入门。

Rust的绑定变量有一个属性:获得它所绑定资源的所有权。这意味着当绑定变量超出作用域时,它所绑定资源的资源就会释放。

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

绑定变量v的作用域是函数foo的函数体,创建v时,先会在栈上分配空间来保存v这个变量 ,然后会在堆上分配空间以保存它的3个元素。当v超出作用域时,Rust会清除栈和堆上这些资源。

有一点要注意:Rust确保有且只有一个变量绑定到给定的资源。

let v = vec![1, 2, 3]; //创建一个vector,并绑定到一个变量let v2 = v; //把它赋给另一个变量。println!(“v[0] is: {}”, v[0]); //使用原来的那个绑定变量。

运行上面的代码会报错。

: `v`: {}”, v[0]);let v2= v;

这行代码是把v赋给v2,它们都指向同一个vector,这违反了Rust安全承诺。所以在这个赋值后Rust不允许再使用变量v。在编译器优化时,可以会把它释放掉。看起来就像v的所有都转移(move)到v2了。

下面我们再看一个例子,这回我们把类型从vector换成i32.

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

这个代码就可以运行,为什么?因为这个例子里v的类型是i32,它实现了Copy trait,所以let v2 = v;这行代码执行时,rust会把v的值深度copy一份,然后给v2,所以v在赋值后可以用的。 v2和v拥有不同的资源,分别是各自资源的owner。

move还是copy ?

当一个局部变量用做右值时,它可能会被move或copy,取决于它的类型,如果它实现了Copy这个trait,那它就会被copied,否则就会被moved.

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

vector没有实现Copy trait,所以在赋值后v就不可以用了。

如果我们写了一个函数,以vector为参数,为了能让函数调用后原来的变量能正常使用,我们必须手动归还这个ownership。

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引入了borrowing 来解决这个问题。

2. References and Borrowing

在Ownership一节,我们给出了一个手动归还Ownership例子,手动归还实在太不方便。

Rust使用reference 来解决这个问题。这是reference版本的。

fn foo(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {}let v1 = vec![1, 2, 3];let v2 = vec![1, 2, 3];let answer = foo(&v1, &v2);// we can use v1 and v2 here!

reference是什么?官方文档是这样解释的。

We call the &T type a ‘reference’, and rather than owning the resource, it borrows ownership.

borrow,借,也就是所有权是没变的。我借你的书看,书还是你的(所有权归你),但是我现在在用它。 引用也是这个意思,引用可以使用资源,但是不拥有所有权。

默认的References不可变的,跟绑定一样.

fn foo(v: &Vec<i32>) {v.push(5);}let v = vec![];foo(&v);

会报错:

error: cannot borrow immutable borrowed content `*v` as mutablev.push(5);^

不可变的引用,不能修改资源的内容。如果要修改资源的内容,我们先取得可变引用。

let mut x = 5;{let y = &mut x;*y += 1;}println!(“{}”, x);

x的值被修改了。你会奇怪,,我们为什么要把修改的代码放在{}块里。如果我们把这两个花括号去掉会报错。

error: cannot borrow `x` as immutable because it is also borrowed as mutableprintln!(“{}”, x);^note: previous borrow of `x` occurs here; the mutable borrow preventssubsequent moves, borrows, or modification of `x` until the borrow endslet y = &mut x;^note: previous borrow ends herefn main() {}

为什么? 我们先来说说Rust对references规定吧。

我们再来看上边的例子:

let mut x = 5;let y = &mut x; // -+ 可变引用 y 开始生效// |*y += 1;// |// |println!(“{}”, x); // -+ – 试图使用原来的可变绑定// -+ 可变引用 y 离开作用域

我们无法在可变引用y的作用域里使用x. 因为它违反了同时只能有一个可变引用的这条规则。

了解了引用我们下面再来学习Liftetime.

3. Lifetime

Lifetime是刚接触rust时特别容易产生迷惑的一个概念,所以我在这里花了比较大的篇章来说明它,基于我自己的理解,希望能给大家讲明白。

在上一节里我们讲了引用和借用 把资源的引用借给他人使用其结果可能会很复杂。假如:

第4步,当你使用引用时,它所指向的资源已经不在了!这将导致不可预知的问题。

如何避免上述情况的发生?

一种解决方案就是: 当还有一个指向资源的引用存在时,资源就不能被释放。 没有指向资源的引用时,才能释放它。

这个方案下第3步就不会释放资源,第4步就是安全的。

在这种情况下资源怎么释放呢?有两种方案,引用计数(ARC)和垃圾回收器GC. Objective-C和Swift使用的是ARC,java和.net使用的是GC.

rust没有使用ARC也没有使用GC. onwer使用完了资源(通常是owner超出作用范围自动销毁)就会释放资源。

那第4步怎么办?

Rust使用某种机制来保证第4步不会发生!

人格的完善是本,财富的确立是末。

Rust语言Ownership,Reference和Lifetime详解

相关文章:

你感兴趣的文章:

标签云: