【Rust练习】23.生命周期

news/2024/11/26 7:22:28/

练习题来自https://practice-zh.course.rs/lifetime/basic.html

1

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

我就不标注了,i的生命周期从main函数开始到结束。

2

rust">/* 像上面的示例一样,为 `r` 和 `x` 标准生命周期,然后从生命周期的角度. */fn main() {  {let r;                // ---------+-- 'a//          |{                     //          |let x = 5;        // -+-- 'b  |r = &x;           //  |       |}                     // -+       |//          |println!("r: {}", r); //          |}                         // ---------+
}

和上面的代码一样,这段代码是无法通过编译的,r的生命周期大于其引用的x。这意味着一旦x被释放,r将成为一个悬垂引用。

3

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

答案:

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

原则1:每一个引用参数都会获得独特的生命周期
然后没有了,此时编译器无法推导出返回值引用的生命周期,这就需要我们手动标注。因为我们将返回值的生命周期标注为a,就必须让返回值的生命周期小于入参的(否则入参被释放了,就出现悬垂引用的问题了)

另外,类似的生命周期问题在C++中也是存在的:

int &larger(int &a, int &b)
{return a > b ? a : b;
}int main()
{int a = 1;int& res = a;{int b = 2;res = larger(a, b);}cout << res << endl;return 0;
}

注意,C++是强类型语言,因此res的类型必须提前声明(即使用auto,那也得是auto&)。另外,C++中的引用声明时就必须初始化。

当b为更大值时,这里就会出现悬垂引用的问题。但在我编译这段代码并反复运行时,我没有看到异常输出。我的C++水平还没法解释为什么。

4

使用三种方法修复下面的错误

rust">fn invalid_output<'a>() -> &'a String { &String::from("foo") 
}fn main() {
}

第一种当然是最原始的方法,转移所有权:

rust">fn invalid_output() ->  String { String::from("foo") 
}

新增一个入参也是不错的选择,甚至可以省略生命周期标注符号:

rust">fn invalid_output(s: &String) -> &String {s
}

第三种我倒是没想到,看了答案才意识到,我也可以直接返回硬编码的&str

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

5

rust">// `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 enoughlet y: &'a 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`
}

Rust处理这类问题的一个基本原则是:调用者的生命周期一定要比被调用的短,否则就会出现空的调用。

rust">fn failed_borrow<'a>() {let _x = 12;// ERROR: `_x` 活得不够久does not live long enoughlet y: & i32 = &_x;// 在函数内使用 `'a` 将会报错,原因是 `&_x` 的生命周期显然比 `'a` 要小// 你不能将一个小的生命周期强转成大的
}

6

rust">/* 增加合适的生命周期标准,让代码工作 */// `i32` 的引用必须比 `Borrowed` 活得更久
#[derive(Debug)]
struct Borrowed(&i32);// 类似的,下面两个引用也必须比结构体 `NamedBorrowed` 活得更久
#[derive(Debug)]
struct NamedBorrowed {x: &i32,y: &i32,
}#[derive(Debug)]
enum Either {Num(i32),Ref(&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);
}

我得去查查Rust有没有什么编码规范,规定了生命周期的命名。

rust">#[derive(Debug)]
struct Borrowed<'a>(&'a i32);// 类似的,下面两个引用也必须比结构体 `NamedBorrowed` 活得更久
#[derive(Debug)]
struct NamedBorrowed<'b> {x: &'b i32,y: &'b i32,
}#[derive(Debug)]
enum Either <'c>{Num(i32),Ref(&'c i32),
}

7

rust">/* 让代码工作 */#[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);
}

b的生命周期太短了,打印exampleb已经被释放了。

rust">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

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

加个生命周期标识符就行了,至于是否真能活到那时候,谁知道。

rust">fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType
{ foo.b }

9

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

答案

rust">struct ImportantExcerpt<'a> {part: &'a str,
}impl<'a> ImportantExcerpt<'a> {fn level(&'a self) -> i32 {3}
}

10

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

由于入参只有一个引用,引用只有一个生命周期,第一个、第二个消除:

rust">fn nput(x: &i32) {println!("`annotated_input`: {}", x);
}fn pass(x: &i32) -> &i32 { x }

参数含有self时(即方法),self的生命周期会赋给全部输出,这里也消除:

rust">struct Owner(i32);impl Owner {fn add_one(&mut self) { self.0 += 1; }fn print(&self) {println!("`print`: {}", self.0);}
}

http://www.ppmy.cn/news/1550007.html

相关文章

选择使用whisper.cpp进行语音转文字

需要将一些wav格式的语音文件转成文字&#xff08;ASR&#xff0c;STT&#xff09;&#xff0c;接到这个任务后&#xff0c;首先上网搜索有没有现成免费的工具或服务可以使用。常用的关键字如“语音转文字 免费 在线”。 搜到的很多野鸡网站&#xff0c;都可以免注册免费提供短…

代码随想录算法训练营第五十七天|Day57 图论

prim算法精讲 https://www.programmercarl.com/kamacoder/0053.%E5%AF%BB%E5%AE%9D-prim.html prim算法核心就是三步&#xff0c;熟悉这三步&#xff0c;代码相对会好些很多&#xff1a; 第一步&#xff0c;选距离生成树最近节点第二步&#xff0c;最近节点加入生成树第三步&a…

【AI技术赋能有限元分析应用实践】pycharm终端与界面设置导入Abaqus2024自带python开发环境

目录 一、具体说明1. **如何在 Windows 环境中执行 Abaqus Python 脚本**2. **如何在 PyCharm 中配置并激活 Abaqus Python 环境**3. **创建 Windows 批处理脚本自动执行 Abaqus Python 脚本**总结二、方法1:通过下面输出获取安装路径导入pycharm方法2:终端脚本执行批处理脚本…

LeetCode Hot100 - 矩阵篇

前言 刷力扣hot100&#xff0c;记录一下每题的思路~ 这次是矩阵相关的题目 &#xff08;1&#xff09;73. 矩阵置零 ①两个boolean数组记录要置零的行列号&#xff0c;遍历数组置零对应的行列 class Solution {public void setZeroes(int[][] matrix) {int mmatrix.length, …

《地质学报》

《地质学报》是中国地质学会主办的地质科学学术刊物。《地质学报》反映地质科学各分支学科及边缘学科中最新、最高水平的基础理论研究和基本地质问题研究成果。《地质学报》登载论文范围主要包括&#xff1a;古生物与地层、构造地质、地球物理、矿物、岩石、地球化学、矿床、石…

文件上传代码分析

目录 不同类型的语言脚本语⾔/解释型语⾔⼀次编译到处运⾏编译型语⾔ 不同语⾔的webshell上传差异脚本语⾔/解释型语⾔⼀次编译到处运⾏编译型语⾔ ⽂件上传到webshell任意⽂件上传js检测解析规则MIME⽂件头后缀检测失效 NTFS Tricks 不同类型的语言 脚本语⾔/解释型语⾔ 代表…

通信原理实验:HDB3编译码(256KHz归零码实验)

目录 一、实验目的 二、实验器材 三、实验原理 HDB3 编译码实验原理框图:​编辑 实验框图说明: 编码规则: 四、实验步骤 五、实验记录与处理 六、实验结果及分析 一、实验目的 了解几种常用的数字基带信号的特征和作用。掌握 HDB3 码的编译规则。了解滤波法位同步在…

20241121 android中树结构列表(使用recyclerView实现)

1、adapter-item的布局 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"wrap_content&…