转载

Rust 学习笔记(九) — 字符串和生命周期

这个用的太多了,比如我们定义一个人:


struct Person { name: &str } impl Person { fn new(name: &str) -> Person { Person { name: name} } } fn main() { let nobody: Person = Person::new("nobody"); }

这么简单常用的代码,rust编译器也不放过,会报错:没有生命周期的参数。 Missing lifetime specifier。

为什么会这样呢? 其实这是rust编译器在和编程者讨价还价:如果你给Person这个结构体的属性name一个真正的String对象,Person的实例化对象就会拥有这个String的所有权,从而我可以保证这个String和Person的实例一起被销毁,在Person实例的生命周期结束之后。

但是现在,你给我的是一个引用,从而Person的实例nobody并不拥有这个引用的所有权,那么我怎么知道这个引用正好和Person一起被销毁呢?如果在引用之前就被销毁了,那么销毁之后Person结构体如果想用self.name 我找谁呢?

这就是&str不太方便的一个地方,因为所有权并不属于赋值的属性,所以需要手工的告诉编译器它的生命周期。下面这个式子,是可以通过编译的:

““

struct Person<‘a> {

name: &’a str

}

impl<‘a> Person<‘a> {

fn new(name: &’a str) -> Person<‘a> {

Person { name: name}

}

}

fn main() {

let nobody: Person = Person::new(“nobody”);println!(“{:?}”, nobody.name);

}

““

这个会正确的打印 “nobody” 到终端上。

‘生命周期的标识符, a是代号,你可以写任何东西,比如’person,  ‘name, 只要不是’static就可以,因为这个是系统保留的,用来表示全局不销毁的静态变量。Rust的惯例是a,如果有两个不同的周期就是a和b,以此类推。是不是挺有谭浩强的即视感?我也觉得有点,不过既然这是官方文档的惯例,我们还是遵从为好。

生命周期这么插入代表什么意思呢?其实生命周期的语法和泛型的语法蛮像,本质上是一个声明,告诉编译器,Person这个struct的实例将和name里面的&str 同周期,共命运(因为他们的生命周期都是’a, 所以是一样的)。编译器得到了这个 保证,就知道在何时可以销毁Person的实例了。如果有两个生命周期,那就 ‘a:’b 表示 a至少活得和b一样长。

注意,在Rust里面,生命周期只是一个annotation!!具体细节以后再说

下面我们玩个花活,结束今天的笔记,注释都在代码里:

“““`

struct Person<‘a> {

name: &’a str // 表示name这个属性的内容和Person同始终。

}

struct Student<‘a, ‘b:’a> { // 表示student里面有俩字符串,name和grade,name这个变量至少活得和grade一样长。

name: &’b str,

grade: &’a str

}

impl<‘a, ‘b> Student <‘a, ‘b> { // 来给student这个结构添加函数,函数里面有两个生命周期的参数。fn new(person: &’b Person, grade: &’a str) -> Student<‘a, ‘b> {

/ 用person的实例来创建student,因为是引用,所以也要告诉编译器生命周期的事情,并且我们一般先创建person,然后再添加grade,所以grade的生命周期不大于person的。 /

Student { //返回一个student实例

name: person.name,

grade: grade

}

}

}

impl<‘a, ‘b: ‘a> Person<‘a> { // 给person结构体添加函数

fn new(name: &’a str) -> Person<‘a> {

Person { name: name}

}

fn change_name(&’a mut self, new_name: &’b str) { //注意这里!&’a mut 是可变借用self.name = new_name

}

}

fn main() {

let mut nobody: Person = Person::new(“nobody”);{ //去掉这两个括号,马上报错!

let student: Student = Student::new(&nobody, “A”);

println!(“{:?}”, (student.name, student.grade));

} //去掉这两个括号,马上报错!nobody.change_name(“somebody”);

/* 去掉大括号之后,编译器会说,student已经不可变的借用了nobody的name,没法可变借用,因为我们的student实例一直牢牢的借着 nobody的name不还,生命周期还没有完成但是,加了大括号,student的生命周期已经完成,已经被销毁,但是因为name的生命周期并没有结束,所以还可以change_name.

*/

}

““““““`

原文  https://blog.scislab.com/zh/2016/04/rust-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%88%e5%85%ab%ef%bc%89-struct-%e9%87%8c%e9%9d%a2%e7%9a%84%e5%ad%97%e7%ac%a6%e4%b8%b2/
正文到此结束
Loading...