`
zcmit
  • 浏览: 17972 次
文章分类
社区版块
存档分类
最新评论

Rust中文翻译29

 
阅读更多
5.10 生命期

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

5.10.1 元

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

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

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

有了这个铺垫,我们来学习生命期.

5.10.2 生命期

把一个正在被使用的资源的引用借给别人会引起会乱.例如,想一下这个操作:
  • 我请求一个某种类型资源的句柄
  • 我借给你这个资源的一个引用
  • 我决定我使用完了这个资源,释放了它,而你还在使用这个引用
  • 你决定使用这个资源
Oh!你的引用指向了一个无效的资源.这被称为悬浮指针,或"释放后使用",当资源就是内存的时候.
解决这个问题,我们需要确保第四步不会在第三步之后发生.Rust的所有权系统通过生命期来实现,它描述了引用适用的作用域.

当我们有一个带有一个引用参数的函数时,我们就会隐式的或显式的成为引用的生命期:

// implicit
fn foo(x: &i32) {
}

// explicit
fn bar<'a>(x: &'a i32) {
}

'a读作生命期a.技术上讲,每一个引用都有生命期,但是编译器允许你忽略他们.我们先看一下显示的例子:

fn bar<'a>(...)

在我们的参数列表中,我们使用我们起的名字:

...(x: &'a i32)

如果我们使用了一个&mut 引用,我们就这样:

...(x: &'a mut i32)

如果比较&mut i32和&'a mut i32,他们是一样的,仅是'a插入到了&
和mute i32之间而已.我们把&mut i32读作"一个指向i32的可变引用",把&'a mut i32读作"一个指向i32的可变引用,生命期是'a".

当你使用struct时你同样需要显式生命期:
struct Foo<'a> }
x: &'a i32,
}

fn main() {
let y = &5; // 和'let _y = 5; let y = &_y;'一样
let f = Foo {x: y};
println!("{}", f.x);
}
正如你看到的,structs也有生命期.可函数具有同样的形式,

struct Foo<'a> {
声明了一个生命期'a,并且

x: &'a i32,

使用了它.为什么我们需要生命期呢?我们需要保证任何一个Foo的引用不能生存的长于它包含的一个i32引用.

考虑作用域:

思考生命期的一种方法就是虚拟一个引用的可用的作用域.例如:

fn main() {
let y = &5;// -+ y 进入作用域
// |
// stuff // |
// |
} // -+ y 离开作用域

加入Foo:

struct Foo<'a> {
x: &'a i32,
}

fn main() {
let y = &5; // -+ y 进入作用域
let f = Foo {x: y}; // -+ f 进入作用域
// stuff // |
// |
} // -+ y和f都离开作用域

f生活在y的作用域中,所以一切正常.如果不是呢?这个代码没法工作:

struct Foo<'a> {
x: &'a i32,
}

fn main() {
let x; // -+ x goes into scope
// |
{ // |
let y = &5; // ---+ y goes into scope
let f = Foo { x: y };// ---+ f goes into scope
x = &f.x; // | | error here
} // ---+ f and y go out of scope
// |
println!("{}", x); // |
} // -+ x goes out of scope

WOW!正如你看到的,f和y小于x的作用域.当我们x = &f.x时,我们创建了一个引用指向超出了其作用域的对象上.

命名的生命期就是把这个作用域起来一个名字.这是第一步.

'static

static生命期是一种特殊的生命期.它表示某些东西具有整个程序的生命期.多数Rust程序员第一次使用'static处理字符串:

let x: &'static str = "Hello, world.";

字符串字面常量具有&'static str类型是因为引用总是存在的:他们会存在于最后的二进制文件的数据段中.另一个例子是全局变量:

static FOO: i32 = 5;
let x: &'static i32 = &FOO;

把一个i32添加到二进制文件的数据段中,然后x是一个指向它的引用.

生命期省略:

Rust提供函数体内的强有力的局部类型引用.但是这不能使用在签名中的类型推导.然而,基于人体工学的原因,一个非常严格的次要的推导算法"生命期省略"可以应用于函数签名.它只作用在函数签名中,而不是函数体中,只作用在生命期参数,有三个值得纪念的明确的规则.这使得生命期省略成为了书写函数签名的短板,不隐藏真实类型可以导致完整的本地类型推导.

当讨论生命期省略的时候,我们使用术语输入生命期和输出生命期.输入生命期是和函数参数有关的生命期,输出生命期是和返回值有关的生命期.例如,这个函数有一个输入生命期:

fn foo<'a>(bar: &'a str)

这个有一个输出生命期:

fn foo<'a> -> &'a str

这个有输出和输入生命期:

fn foo<'a>(bar: &'a str) -> &'a str

这里是三条规则:
  • 每一个函数参数中省略的生命期都会成为一个明确的生命期参数
  • 如果有一个输入生命期,不管是否省略,他的生命期会被分配给函数返回值中所有被省略的生命期
  • 如果有多个输入生命期,其中一个是&self或者&mut self,self的生命期就会被分配给所有省略的输出生命期
否则,就会有一个错误指向一个省略的输出生命期

例子: 这里有一些关于省略生命期的例子.我们把每个例子配对来显示生路的版本和展开的版本

fn print(s: &str); // elided
fn print<'a>(s: &'a str); // expanded

fn debug(lvl: u32, s: &str); // elided
fn debug<'a>(lvl: u32, s: &'a str); // expanded

// In the preceding example, `lvl` doesnʼt need a lifetime because itʼs not a
// reference (`&`). Only things relating to references (such as a `struct`
// which contains a reference) need lifetimes.

fn substr(s: &str, until: u32) -> &str; // elided
fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded

fn get_str() -> &str; // ILLEGAL, no inputs

fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs
fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is unclear

fn get_mut(&mut self) -> &mut T; // elided
fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded

fn args<T:ToCStr>(&mut self, args: &[T]) -> &mut Command // elided
fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded

fn new(buf: &mut [u8]) -> BufWriter; // elided
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics