【Rust练习】23.生命周期

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

1

/* 为 `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

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

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

3

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

答案:

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

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

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

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

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

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

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

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

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

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<'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处理这类问题的一个基本原则是:调用者的生命周期一定要比被调用的短,否则就会出现空的调用。

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

6

/* 增加合适的生命周期标准,让代码工作 */// `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有没有什么编码规范,规定了生命周期的命名。

#[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

/* 让代码工作 */#[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已经被释放了。

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(foo: &Example) -> &NoCopyType
{ foo.b }fn main()
{let no_copy = NoCopyType {};let example = Example { a: &1, b: &no_copy };fix_me(&example);println!("Success!")
}

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

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

9

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

答案

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

10

/* 移除所有可以消除的生命周期标注 */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() {}

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/478111.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汽车HiL测试:利用TS-GNSS模拟器掌握硬件性能的仿真艺术

一、汽车HiL测试的概念 硬件在环&#xff08;Hardware-in-the-Loop&#xff0c;简称HiL&#xff09;仿真测试&#xff0c;是模型基于设计&#xff08;Model-Based Design&#xff0c;简称MBD&#xff09;验证流程中的一个关键环节。该步骤至关重要&#xff0c;因为它整合了实际…

Vue——响应式数据,v-on,v-bind,v-if,v-for(内含项目实战)

目录 响应式数据 ref reactive 事件绑定指令 v-on v-on 鼠标监听事件 v-on 键盘监听事件 v-on 简写形式 属性动态化指令 v-bind iuput标签动态属性绑定 img标签动态属性绑定 b标签动态属性绑定 v-bind 简写形式 条件渲染指令 v-if 遍历指令 v-for 遍历对象的值 遍历…

Redis 常用数据类型插入性能对比:循环插入 vs. 批量插入

Redis 是一款高性能的键值数据库&#xff0c;其支持多种数据类型&#xff08;String、Hash、List、Set、ZSet、Geo&#xff09;。在开发中&#xff0c;经常会遇到需要插入大量数据的场景。如果逐条插入&#xff0c;性能会显得较低&#xff0c;而采用 Pipeline 批量插入 能大幅提…

开源动态表单form-create-designer 扩展个性化配置的最佳实践教程

在开源低代码表单设计器 form-create-designer 的右侧配置面板里&#xff0c;field 映射规则为开发者提供了强大的工具去自定义和增强组件及表单配置的显示方式。通过这些规则&#xff0c;你可以简单而高效地调整配置项的展示&#xff0c;提升用户体验。 源码地址: Github | G…

Java语言编程,通过阿里云mongo数据库监控实现数据库的连接池优化

一、背景 线上程序连接mongos超时&#xff0c;mongo监控显示连接数已使用100%。 java程序报错信息&#xff1a; org.mongodb.driver.connection: Closed connection [connectionId{localValue:1480}] to 192.168.10.16:3717 because there was a socket exception raised by…

深入浅出分布式缓存:原理与应用

文章目录 概述缓存分片算法1. Hash算法2. 一致性Hash算法3. 应用场景Redis集群方案1. Redis 集群方案原理2. Redis 集群方案的优势3. Java 代码示例:Redis 集群数据定位Redis 集群中的节点通信机制:Gossip 协议Redis 集群的节点通信:Gossip 协议Redis 集群的节点通信流程Red…

Loom篇之java虚拟线程那些事儿

我们在之前的文章中提到了java推出纤程的背景和原因。在近三十年来&#xff0c;Java 开发人员一直依赖线程作为并发服务器应用程序的构建块。每个方法中的每个语句都在线程内执行&#xff0c;并且由于 Java 是多线程的&#xff0c;因此多个执行线程会同时发生。线程是 Java 的并…

自然语言处理: RAG优化之Embedding模型选型重要依据:mteb/leaderboard榜

本人项目地址大全&#xff1a;Victor94-king/NLP__ManVictor: CSDN of ManVictor git地址&#xff1a;https://github.com/opendatalab/MinerU 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff0c;笔芯!!! 写在前面: 笔者更新不易&#xff0c;希望走过路…

如何选择服务器

如何选择服务器 选择服务器时应考虑以下几个关键因素&#xff1a; 性能需求。根据网站的预期流量和负载情况&#xff0c;选择合适的处理器、内存和存储容量。考虑网站是否需要处理大量动态内容或高分辨率媒体文件。 可扩展性。选择一个可以轻松扩展的服务器架构&#xff0c;以便…

Spring 框架七大模块(Java EE 学习笔记03)

​ ​核心容器模块&#xff08;Core Container&#xff09; 核心容器模块在Spring的功能体系中起着支撑性作用&#xff0c;是其他模块的基石。核心容器层主要由Beans模块、Core模块、Contex模块和SpEL模块组成。 &#xff08;1&#xff09;Beans模块。它提供了BeanFactory类&…

2025-2026财年美国CISA国际战略规划(下)

文章目录 前言四、加强综合网络防御&#xff08;一&#xff09;与合作伙伴共同实施网络防御&#xff0c;降低集体风险推动措施有效性衡量 &#xff08;二&#xff09;大规模推动标准和安全&#xff0c;以提高网络安全推动措施有效性衡量 &#xff08;三&#xff09;提高主要合作…

【大数据技术与开发实训】携程景点在线评论分析

景点在线评论分析 题目要求实验目标技术实现数据采集获取所有相关景点页面的 URL获取所有相关景点对应的 poiId 及其他有用信息通过 poiId 获取所有景点的全部评论数据采集结果 数据预处理景点信息的数据预处理查看数据基本信息缺失值处理 用户评论的数据处理缺失值处理分词、去…

《第十部分》1.STM32之通信接口《精讲》之IIC通信---介绍

经过近一周的USART学习&#xff0c;我深刻体会到通信对单片机的重要性。它就像人类的手脚和大脑&#xff0c;只有掌握了通信技术&#xff0c;单片机才能与外界交互&#xff0c;展现出丰富多彩的功能&#xff0c;变得更加强大和实用。 单片机最基础的“语言”是二进制。可惜&am…

ES 基本使用与二次封装

概述 基本了解 Elasticsearch 是一个开源的分布式搜索和分析引擎&#xff0c;基于 Apache Lucene 构建。它提供了对海量数据的快速全文搜索、结构化搜索和分析功能&#xff0c;是目前流行的大数据处理工具之一。主要特点即高效搜索、分布式存储、拓展性强 核心功能 全文搜索:…

单片机_简单AI模型训练与部署__从0到0.9

IDE&#xff1a; CLion MCU&#xff1a; STM32F407VET6 一、导向 以求知为导向&#xff0c;从问题到寻求问题解决的方法&#xff0c;以兴趣驱动学习。 虽从0&#xff0c;但不到1&#xff0c;剩下的那一小步将由你迈出。本篇主要目的是体验完整的一次简单AI模型部署流程&#x…

Python3 爬虫 Scrapy的安装

Scrapy是基于Python的分布式爬虫框架。使用它可以非常方便地实现分布式爬虫。Scrapy高度灵活&#xff0c;能够实现功能的自由拓展&#xff0c;让爬虫可以应对各种网站情况。同时&#xff0c;Scrapy封装了爬虫的很多实现细节&#xff0c;所以可以让开发者把更多的精力放在数据的…

golang实现TCP服务器与客户端的断线自动重连功能

1.服务端 2.客户端 生成服务端口程序: 生成客户端程序: 测试断线重连: 初始连接成功

【Spring Boot】# 使用@Scheduled注解无法执行定时任务

1. 前言 在 Spring Boot中&#xff0c;使用Scheduled注解来定义定时任务时&#xff0c;定时任务不执行&#xff1b;或未在规定时间执行。 import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;Component public c…

java 老矣,尚能饭否?

随笔 从千万粉丝“何同学”抄袭开源项目说起&#xff0c;为何纯技术死路一条&#xff1f; 数据源的统一与拆分 监控报警系统的指标、规则与执行闭环 java 老矣&#xff0c;尚能饭否&#xff1f; 一骑红尘妃子笑&#xff0c;无人知是荔枝来! java 老吗&#xff1f; 去年看…

[译]Elasticsearch Sequence ID实现思路及用途

原文地址:https://www.elastic.co/blog/elasticsearch-sequence-ids-6-0 如果 几年前&#xff0c;在Elastic&#xff0c;我们问自己一个"如果"问题&#xff0c;我们知道这将带来有趣的见解&#xff1a; "如果我们在Elasticsearch中对索引操作进行全面排序会怎样…