Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

lifetime/basic #180

giscus[bot] bot announced in Book Comments
Mar 28, 2022 · 19 comments · 9 replies
Discussion options

lifetime/basic

Learn Rust with Example, Exercise and real Practice, written with ❤️ by https://course.rs team

https://practice.rs/lifetime/basic.html

You must be logged in to vote

Replies: 19 comments 9 replies

Comment options

第10题的longest函数中参数y的生命周期是可以消除的吧

You must be logged in to vote
4 replies
Comment options

可以

Comment options

fn longest<'a>(x: &'a str, y: &str) -> &'a str

Comment options

聪明呀

Comment options

不过这时,在编译器的角度,y 被标注了什么生命周期呢?

Comment options

第1题

/* 为 `i` 和 `borrow2` 标注合适的生命周期范围 */
// `i` 拥有最长的生命周期,因为它的作用域完整的包含了 `borrow1` 和 `borrow2` 。
// 而 `borrow1` 和 `borrow2` 的生命周期并无关联,因为它们的作用域没有重叠
fn main() {
 let i = 3; // `i`生命周期开始 ─────────────────────────────┐ 
 {let borrow1 = &i; // `borrow1` 生命周期开始. ─────┐ │
 // │ │
 println!("borrow1: {}", borrow1); // │ │
 } // `borrow1` 生命周期结束. ─────────────────────────┘ │
 {let borrow2 = &i; // `borrow2`生命周期开始 ───┐ │
 │ │
 println!("borrow2: {}", borrow2); │ │
 } // `borrow2`生命周期结束─┘ │ 
} // `i`生命周期结束 ────────────────────────────────────────┘

第2题

/* 像上面的示例一样,为 `r` 和 `x` 标准生命周期,然后从生命周期的角度. */
fn main() { 
 {
 let r; // ---------+-- 'a
 // |
 { // |
 let x = 5; // -+-- 'b |
 r = &x; // | |
 } // -+ |
 // |
 println!("r: {}", r); // |
 } // ---------+
} // 引用的生命周期'a比被引用对象的生命周期'b长,因此编译错误。

第3题

/* 添加合适的生命周期标注,让下面的代码工作 */
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
 if x.len() > y.len() {
 x
 } else {
 y
 }
}
fn main() {}

第4题

4.1 方法1

/* 使用三种方法修复下面的错误 */
fn invalid_output() -> String { 
 String::from("foo")
}
fn main() {
}

4.2 方法2

/* 使用三种方法修复下面的错误 */
fn invalid_output() -> &'static str { 
 "foo"
}
fn main() {
}

4.3 方法3

/* 使用三种方法修复下面的错误 */
fn invalid_output<'a>() -> &'a str { 
 "foo"
}
fn main() {
}

第5题

// `print_refs` 有两个引用参数,它们的生命周期 `'a` 和 `'b` 至少得跟函数活得一样久
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
 println!("x is {} and y is {}", x, y);
}
/* 让下面的代码工作 */
fn failed_borrow() {
 let _x = 12;
 // ERROR: `_x` 活得不够久does not live long enough
 let y: &i32 = &_x;
 // 在函数内使用 `'a` 将会报错,原因是 `&_x` 的生命周期显然比 `'a` 要小
 // 你不能将一个小的生命周期强转成大的
}
fn main() {
 let (four, nine) = (4, 9);
 
 print_refs(&four, &nine);
 // 这里,four 和 nice 的生命周期必须要比函数 print_refs 长
 
 failed_borrow();
 // `failed_borrow` 没有传入任何引用去限制生命周期 `'a`,因此,此时的 `'a` 生命周期是没有任何限制的,它默认是 `'static`
}

第6题

/* 增加合适的生命周期标准,让代码工作 */
// `i32` 的引用必须比 `Borrowed` 活得更久
#[derive(Debug)]
struct Borrowed<'a>(&'a i32);
// 类似的,下面两个引用也必须比结构体 `NamedBorrowed` 活得更久
#[derive(Debug)]
struct NamedBorrowed<'a> {
 x: &'a i32,
 y: &'a i32,
}
#[derive(Debug)]
enum Either<'a> {
 Num(i32),
 Ref(&'a i32),
}
fn main() {
 let x = 18;
 let y = 15;
 let single = Borrowed(&x);
 let double = NamedBorrowed { x: &x, y: &y };
 let reference = Either::Ref(&x);
 let number = Either::Num(y);
 println!("x is borrowed in {:?}", single);
 println!("x and y are borrowed in {:?}", double);
 println!("x is borrowed in {:?}", reference);
 println!("y is *not* borrowed in {:?}", number);
}

第7题

/* 让代码工作 */
#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}
fn main()
{ 
 let var_a = 35;
 let example: Example;
 
 
 let var_b = NoCopyType {};
 
 /* 修复错误 */
 example = Example { a: &var_a, b: &var_b };
 
 
 println!("(Success!) {:?}", example);
}

第8题

#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
#[allow(dead_code)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}
/* 修复函数的签名 */
fn fix_me<'a: 'b, 'b>(foo: &'a Example) -> &'b NoCopyType
{ foo.b }
fn main()
{
 let no_copy = NoCopyType {};
 let example = Example { a: &1, b: &no_copy };
 fix_me(&example);
 println!("Success!")
}

第9题

/* 添加合适的生命周期让下面代码工作 */
struct ImportantExcerpt<'a> {
 part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
 fn level(&'a self) -> i32 {
 3
 }
}
fn main() {}

第10题

/* 移除所有可以消除的生命周期标注 */
fn nput(x: &i32) {
 println!("`annotated_input`: {}", x);
}
fn pass(x: &i32) -> &i32 { x }
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
 x
}
struct Owner(i32);
impl Owner {
 fn add_one(&mut self) { self.0 += 1; }
 fn print(&self) {
 println!("`print`: {}", self.0);
 }
}
struct Person<'a> {
 age: u8,
 name: &'a str,
}
enum Either<'a> {
 Num(i32),
 Ref(&'a i32),
}
fn main() {}
You must be logged in to vote
0 replies
Comment options

第八题这样为什么也能过编译?虽然与答案不同,如果这样可以的话应该可以应用生命周期消除。。然而不标注生命周期的话无法过编译

/* 修复函数的签名 */
fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType {
 foo.b
}
You must be logged in to vote
4 replies
Comment options

了解了,谢谢!

Comment options

#[derive(Debug)]
#[allow(dead_code)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}

因为这个struct声明的隐含条件实际上已经包括了`b的生命周期应该至少不小于结构体对象的生命周期长度

fn fix_me(foo: &Example) -> &NoCopyType
{ foo.b }

这个调用虽然看起来符合消除的第一个原则
但实际上foo自己有一个隐含的'a生命周期(应该和结构体里&'a u32'不同)(为了不混淆其实自己起个别的名字更好比如'z)
所以foo.b的生命周期其实和结构体的'a并不相同

因此下面这样写之所以能够成功,是因为在告诉编译器返回值的生命周期和结构体的生命周期一样,但像上面所描述的原因,它是不能被省略消除的。

/* 修复函数的签名 */
fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType {
 foo.b
}
Comment options

我是这样理解的:

答案的生命周期标注 fn fix_me<'a: 'b, 'b>(foo: &'a Example) -> &'b NoCopyType; 表示 'b 小于等于 'a 即可。

而采用相等生命周期的标注 fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType; 则是答案的一种特化,即此时 'b'a 相等。

正如文中所说

消除规则不是万能的,若编译器不能确定某件事是正确时,会直接判为不正确,那么你还是需要手动标注生命周期

编译器无法确定你需要前者的通用标注还是后者的特化标注,因此编译器会直接判为不正确。

为啥是 'b 小于等于 'a 即可?我不是疑惑 'a: 'b 的含义,而是疑惑结构体里的成员不应该要比结构体死得更晚吗?'b是结构体内成员的生命周期,应该要比结构体 'a 更大吧

Comment options

我是这样理解的:

答案的生命周期标注 fn fix_me<'a: 'b, 'b>(foo: &'a Example) -> &'b NoCopyType; 表示 'b 小于等于 'a 即可。

而采用相等生命周期的标注 fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType; 则是答案的一种特化,即此时 'b'a 相等。

为啥是 'b 小于等于 'a 即可?我不是疑惑 'a: 'b 的含义,而是疑惑结构体里的成员不应该要比结构体死得更晚吗?'b是结构体内成员的生命周期,应该要比结构体 'a 更大吧

哈哈,是的,我写错了,当时脑子懵逼了😂我那样理解也是不对的,我把它删了,省得误导别人。

我现在理解的是,不符合第一个原则,因为 foo: &Example 这个参数包含3个生命周期,即 foo: &'c Example<'a, 'b>

Comment options

done.

You must be logged in to vote
0 replies
Comment options

第8题我是这样写的,通过了:

#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
#[allow(dead_code)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}
/* 修复函数的签名 */
fn fix_me<'a, 'b>(foo: &Example<'a, 'b>) -> &'b NoCopyType
{ foo.b }
fn main()
{
 let no_copy = NoCopyType {};
 let example = Example { a: &1, b: &no_copy };
 fix_me(&example);
 println!("Success!")
}
You must be logged in to vote
0 replies
Comment options

You must be logged in to vote
0 replies
Comment options

第8题这样改也不错的:

#[derive(Debug)]
struct NoCopyType {
}
#[derive(Debug)]
#[allow(dead_code)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}
/* 修复函数的签名 */
fn fix_me<'b>(foo: &Example<'_, 'b>) -> &'b NoCopyType { 
 foo.b 
}
fn main()
{
 let no_copy = NoCopyType {};
 let example = Example { a: &1, b: &no_copy };
 fix_me(&example);
 println!("Success!")
}
You must be logged in to vote
0 replies
Comment options

第四题,可以简写,为了练习没有简写。应该是对的吧

/* 使用三种方法修复下面的错误 */
fn invalid_output1<'a>() -> String { 
 String::from("foo") 
}
fn invalid_output2<'a>(s: &'a str) -> &'a str {
 s
}
fn invalid_output3<'a>() -> &'static str { 
 "foo"
}
fn main() {
}
You must be logged in to vote
0 replies
Comment options

lala

You must be logged in to vote
0 replies
Comment options

done

You must be logged in to vote
0 replies
Comment options

第4题的另一种解法

#![feature(string_leak)]
fn invalid_output() ->&'static str { 
 String::from("foo").leak()
}
You must be logged in to vote
1 reply
Comment options

这个解法有点偷懒的感觉,因为可能会造成内存泄露。不过跑过了,就分享一下,哈哈哈哈

Comment options

1、讲真,我本来以为标注是在代码中插入'a这个符号,结果发现编译器让我删掉......看半天没写出来,发现参考答案的意思是在代码中用注释"画"出来,类似这种:

fn main() {
 let i = 3; // Lifetime for `i` starts. ────────────────┐
 // │
 { // │
 let borrow1 = &i; // `borrow1` lifetime starts. ──┐│
 // ││
 println!("borrow1: {}", borrow1); // ││
 } // `borrow1 ends. ──────────────────────────────────┘│
 // │
 // │
 { // │
 let borrow2 = &i; // `borrow2` lifetime starts. ──┐│
 // ││
 println!("borrow2: {}", borrow2); // ││
 } // `borrow2` ends. ─────────────────────────────────┘│
 // │
} // Lifetime ends. ─────────────────────────────────────┘

发现是示例没好好看......2333

2、略

3、生命周期使用:

/* 添加合适的生命周期标注,让下面的代码工作 */
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
 if x.len() > y.len() {
 x
 } else {
 y
 }
}
fn main() {}

4、

第一种,不要考虑生命周期

/* 使用三种方法修复下面的错误 */
fn invalid_output() -> String { 
 String::from("foo") 
}
fn main() {
}

第二种,引入入参

/* 使用三种方法修复下面的错误 */
fn invalid_output<'a>(input:&'a mut String) -> &'a String { 
 (*input)=String::from("foo");
 input
}
fn main() {
}

第三种,考虑使用'static

fn invalid_output() -> &'static str { 
 "foo"
}
fn main() {
}

5、函数内使用``a会报错,函数内变量的生命周期显然小于'a`

// `print_refs` 有两个引用参数,它们的生命周期 `'a` 和 `'b` 至少得跟函数活得一样久
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
 println!("x is {} and y is {}", x, y);
}
/* 让下面的代码工作 */
fn failed_borrow<'a>() {
 let _x = 12;
 // ERROR: `_x` 活得不够久does not live long enough
 let y = &_x;//删除就好
 // 在函数内使用 `'a` 将会报错,原因是 `&_x` 的生命周期显然比 `'a` 要小
 // 你不能将一个小的生命周期强转成大的
}
fn main() {
 let (four, nine) = (4, 9);
 
 print_refs(&four, &nine);
 // 这里,four 和 nice 的生命周期必须要比函数 print_refs 长
 
 failed_borrow();
 // `failed_borrow` 没有传入任何引用去限制生命周期 `'a`,因此,此时的 `'a` 生命周期是没有任何限制的,它默认是 `'static`
}

6、结构体中的引用

/* 增加合适的生命周期标准,让代码工作 */
// `i32` 的引用必须比 `Borrowed` 活得更久
#[derive(Debug)]
struct Borrowed<'a>(&'a i32);
// 类似的,下面两个引用也必须比结构体 `NamedBorrowed` 活得更久
#[derive(Debug)]
struct NamedBorrowed<'a>{
 x: &'a i32,
 y: &'a i32,
}
#[derive(Debug)]
enum Either<'a> {
 Num(i32),
 Ref(&'a i32),
}
fn main() {
 let x = 18;
 let y = 15;
 let single = Borrowed(&x);
 let double = NamedBorrowed { x: &x, y: &y };
 let reference = Either::Ref(&x);
 let number = Either::Num(y);
 println!("x is borrowed in {:?}", single);
 println!("x and y are borrowed in {:?}", double);
 println!("x is borrowed in {:?}", reference);
 println!("y is *not* borrowed in {:?}", number);
}

7、结构体的引用需要比结构体活得更久或者等同

/* 让代码工作 */
#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}
fn main()
{ 
 let var_a = 35;
 let example: Example;
 
 {
 let var_b = NoCopyType {};
 
 /* 修复错误 */
 example = Example { a: &var_a, b: &var_b };
 println!("(Success!) {:?}", example);
 } 
 //不可以在外面 println!("(Success!) {:?}", example);因为结构体引用的var_b在上一个}被消除了
}

8、

#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
#[allow(dead_code)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}
/* 修复函数的签名 */
fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType
{ foo.b }
fn main()
{
 let no_copy = NoCopyType {};
 let example = Example { a: &1, b: &no_copy };
 fix_me(&example);
 println!("Success!")
}

9、带引用的结构体实现方法的写法

/* 添加合适的生命周期让下面代码工作 */
struct ImportantExcerpt<'a> {
 part: &'a str,
}
impl <'a>ImportantExcerpt<'a> {
 fn level(&self) -> i32 {
 3
 }
}
fn main() {}

10、

/* 移除所有可以消除的生命周期标注 */
fn nput(x: & i32) {
 println!("`annotated_input`: {}", x);
}
fn pass(x: & i32) -> &i32 { x }
fn longest<'a,'b>(x: &'a str, y: &'b str) -> &'a str {
 x
}
struct Owner(i32);
impl Owner {
 fn add_one(& mut self) { self.0 += 1; }
 fn print(& self) {
 println!("`print`: {}", self.0);
 }
}
struct Person<'a> {
 age: u8,
 name: &'a str,
}
enum Either<'a> {
 Num(i32),
 Ref(&'a i32),
}
fn main() {}
You must be logged in to vote
0 replies
Comment options

/* 使用三种方法修复下面的错误 */
fn invalid_output<'a>() -> &'a str { 
 "foo" 
}
fn main() {
}

上面能够通过编译,是因为"foo": &str具有'static

You must be logged in to vote
0 replies
Comment options

done~~~

You must be logged in to vote
0 replies
Comment options

有的题就和脑筋急转弯一样,不看答案是真难想啊

You must be logged in to vote
0 replies
Comment options

第三题

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
 if x.len() > y.len() { x } else { y }
}

第四题

fn invalid_output1() -> String {
 String::from("foo")
}
fn invalid_output2() -> &'static str {
 "foo"
}
fn invalid_output3<'a>(s: &'a mut String) -> &'a String {
 *s = String::from("foo");
 s
}

第五题

fn failed_borrow() {
 let _x = 12;
 let y: &i32 = &_x;
}

第六题

/* 增加合适的生命周期标准,让代码工作 */
// `i32` 的引用必须比 `Borrowed` 活得更久
#[derive(Debug)]
struct Borrowed<'a>(&'a i32);
// 类似的,下面两个引用也必须比结构体 `NamedBorrowed` 活得更久
#[derive(Debug)]
struct NamedBorrowed<'a> {
 x: &'a i32,
 y: &'a i32,
}
#[derive(Debug)]
enum Either<'a> {
 Num(i32),
 Ref(&'a i32),
}
fn main() {
 let x = 18;
 let y = 15;
 let single = Borrowed(&x);
 let double = NamedBorrowed { x: &x, y: &y };
 let reference = Either::Ref(&x);
 let number = Either::Num(y);
 println!("x is borrowed in {:?}", single);
 println!("x and y are borrowed in {:?}", double);
 println!("x is borrowed in {:?}", reference);
 println!("y is *not* borrowed in {:?}", number);
}

第七题

/* 让代码工作 */
#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
struct Example<'a, 'b> {
 a: &'a u32,
 b: &'b NoCopyType
}
fn main()
{
 let var_a = 35;
 let example: Example;
 {
 let var_b = NoCopyType {};
 /* 修复错误 */
 example = Example { a: &var_a, b: &var_b };
 println!("(Success!) {:?}", example);
 }
}

第八题

fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType

第九题

/* 添加合适的生命周期让下面代码工作 */
struct ImportantExcerpt<'a> {
 part: &'a str,
}
impl <'a>ImportantExcerpt<'a> {
 fn level(&'a self) -> i32 {
 3
 }
}
fn main() {}

第10题

/* 移除所有可以消除的生命周期标注 */
fn nput(x: &i32) {
 println!("`annotated_input`: {}", x);
}
fn pass(x: &i32) -> &i32 { x }
fn longest<'a, 'b>(x: &'a str, y: &str) -> &'a str {
 x
}
struct Owner(i32);
impl Owner {
 fn add_one(&mut self) { self.0 += 1; }
 fn print(&self) {
 println!("`print`: {}", self.0);
 }
}
struct Person<'a> {
 age: u8,
 name: &'a str,
}
enum Either<'a> {
 Num(i32),
 Ref(&'a i32),
}
fn main() {}
You must be logged in to vote
0 replies
Comment options

done

You must be logged in to vote
0 replies
Comment options

done

You must be logged in to vote
0 replies
Comment options

done.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /