13.泛型、trait和生命周期(下)

目录

  • 6. 生命周期与引用有效性
    • 6.1 生命周期引入
    • 6.2 得到长度最大的String值
    • 6.3 生命周期标注语法
      • 1)说明
      • 2)普通标注示例
      • 3)函数参数中的生命周期标注
    • 6.4 深入理解生命周期
    • 6.5 结构体定义中的生命周期标注
    • 6.6 生命周期省略
  • 6.7 方法定义中的生命周期标注
    • 6.8 静态生命周期
    • 6.9 结合泛型类型参数、trait bounds 和生命周期

6. 生命周期与引用有效性

  • Rust 中的每一个引用都有其生命周期(lifetime);
  • 大部分时候生命周期是隐含并可以推断的;
  • 当出现引用的生命周期以一些不同方式相关联的情况,需要使用泛型生命周期参数来注明他们的关系;

6.1 生命周期引入

fn main() {let r;{let x = 5;r = &x;}println!("r: {}", r);
}

这段代码报错
在这里插入图片描述

  • x 在到达第 7 行内部作用域结束时就离开了作用域,r的引用值也就不存在了;

6.2 得到长度最大的String值

下面的代码会得到传入两个参数中最长的String,为了避免所有权的占用,代码采用引入的方法传参

fn longest(src1: &str, src2: &str) -> &str {if src1.len() > src2.len() {src1}else{src2}
}fn main() {let string1 = String::from("abcd");let string2 = "xyz";let result = longest(string1.as_str(), string2);println!("The longest string is {}", result);
}
  • 然而,编译时提示存在生命周期的错误
    在这里插入图片描述

6.3 生命周期标注语法

1)说明

  • 生命周期标注并不改变任何引用的生命周期的长短;
  • 指定了泛型生命周期后函数也能接受任何生命周期的引用;
  • 生命周期标注描述了多个引用生命周期相互的关系,而不影响其生命周期;
  • 生命周期参数名称必须以撇号(')开头,其名称通常全是小写, 'a 是大多数人默认使用的名称;
  • 生命周期参数标注位于引用的 & 之后,并有一个空格来将引用类型与生命周期标注分隔开;

2)普通标注示例

&i32       		 	// 引用
&'a i32     		// 带有显式生命周期的引用
&'a mut i32 		// 带有显式生命周期的可变引用

3)函数参数中的生命周期标注

fn longest<'a>(src1: &'a str, src2: &'a str) -> &'a str {if src1.len() > src2.len() {src1}else{src2}
}
  • 生命周期参数需要声明在函数名和参数列表之间的尖括号中;
  • 这里说明关于参数中的引用和返回值之间的限制是他们都必须拥有相同的生命周期;
  • 生命周期标记仅仅出现在函数的参数中,不存在于函数体中的任何代码中;
  • 泛型生命周期’a的具体生命周期等同于src1 和src2的生命周期中较小的那一个;

传递拥有不同具体生命周期的引用来限制longest函数的使用。

fn main() {let string1 = String::from("long string is long");{let string2 = String::from("xyz");let result = longest(string1.as_str(), string2.as_str());println!("The longest string is {}", result);    //The longest string is long string is long}
}
  • string1直到main函数结束之前都是有效的;
  • string2只在最里面的{}内才有效,而result则引用了直到内部作用域结束都是有效的值;

更改代码再尝试

fn main() {let string1 = String::from("long string is long");let result;{let string2 = String::from("xyz");result = longest(string1.as_str(), string2.as_str());}println!("The longest string is {}", result);
}

无法通过编译
在这里插入图片描述

  • 第18行的result变量是有效的;
  • longest函数的参数和返回值使用了相同的生命周期参数;
  • 因此,string2需要直到main函数结束都前都是有效的;

6.4 深入理解生命周期

指定生命周期参数的正确方式依赖函数实现的具体功能;

fn longest<'a>(x: &'a str, y: &str) -> &'a str {x
}
  • longest总是返回第一个参数,因此不需要为参数y指定生命周期;

从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配

fn longest<'a>(x: &str, y: &str) -> &'a str {let result = String::from("really long string");result.as_str()
}

编译报错
在这里插入图片描述

  • 返回的引用没有指向任何一个参数;
  • 因此它指向的是一个内部创建的值,这就会造成悬垂引用,因此无法编译通过;

6.5 结构体定义中的生命周期标注

  • 可以定义包含引用的结构体;
  • 需要为结构体定义中的每一个引用添加第一期周期标注;
struct ImportantExcerpt<'a> {part: &'a str,
}fn main() {let novel = String::from("Call me Ishmael. Some years ago...");let first_sentence = novel.split('.').next().expect("Could not find a '.'");let i = ImportantExcerpt { part: first_sentence };
}
  • ImportantExcerpt结构体中存放了一个字符串切片类型(&str ),因此需要声明生命周期;
  • 在结构体名称后面的尖括号中声明泛型生命周期参数;
  • main函数创建了一个ImportantExcerpt实例,它存放了变量novel的引用;
  • novel在ImportantExcerpt之前已经存在,且在ImportantExcerpt离开作用域之后novel都不会离开作用域;
  • 因此能通过编译;

6.6 生命周期省略

  • 每一个引用都有一个生命周期;
  • 需要为那些使用了引用的函数或结构体指定生命周期;

然而,下面的代码能通过编译器检查

fn first_word(s: &str) -> &str {let bytes = s.as_bytes();for (i, &item) in bytes.iter().enumerate() {if item == b' ' {return &s[0..i];}}&s[..]
}
  • 函数定义了一个参数和返回值都是引用却没有使用生命周期标注的函数;
  • 这是一种生命周期省略规则,省略规则只需要提示出错的时候补全生命周期标注即可;
  • 规则适用于fn定义以及fmpl块;

引用不需要标注生命周期的规则

  • 函数或方法的参数的生命周期被称为输入生命周期;
  • 返回值的生命周期被称为输出生命周期;

适用于输入生命周期的规则

规则一:每一个是引用的参数都有它自己的生命周期参数。
例如:

fn foo<'a>(x: &'a i32){}
fn foo<'a, 'b>(x: &'a i32, y: &'b i32){}

因此

fn first_word(s: &str) -> &str{}

被当作

fn first_word<'a>(s: &'a str) -> &str{}

适用于输出生命周期的规则

规则一:如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数

例如

fn foo<'a>(x: &'a i32) -> &'a i32

因此

fn first_word(s: &str) -> &str{}

被当作

fn first_word<'a>(s: &'a str) -> &'a str {

规则二:如果方法有多个输入生命周期参数并且其中一个参数是&self或&mut self,说明是个对象的方法, 那么所有输出生命周期参数被赋予 self 的生命周期

6.7 方法定义中的生命周期标注

  • 为带有生命周期的结构体实现方法时,其语法依然类似泛型类型参数的语法;
  • 声明和使用生命周期参数的位置依赖于生命周期参数是否同结构体字段或方法参数和返回值相关;
  • 实现方法时,结构体字段的生命周期必须总是在 impl 关键字之后声明并在结构体名称之后被使用;
  • impl 块里的方法签名中,引用可能与结构体字段中的引用相关联,也可能是独立的
struct ImportantExcerpt<'a> {part: &'a str,
}impl<'a> ImportantExcerpt<'a> {fn level(&self) -> i32 {3}
}
  • 为ImportantExcerpt实现了方法level;
  • 方法level唯一的参数是self引用;
  • impl和类型名称之后的生命周期参数是必须要有的;
  • 不必须标注 self 引用的生命周期;

适用于输出生命周期第二条规则的例子

impl<'a> ImportantExcerpt<'a> {fn announce_and_return_part(&self, announcement: &str) -> &str {println!("Attention please: {}", announcement);self.part}
}
  • 有两个输入生命周期,Rust应用输入生命周期的规则给予 &self 和 announcement 他们各自的生命周期;
  • 由于其中一个参数是&self,返回值类型被赋予了&self 的生命周期;

6.8 静态生命周期

  • 'static的生命周期能够存活于整个程序期间;
  • 所有的字符串字面量都拥有'static生命周期;
  • 也可以显式标注;
  • 字符串的文本被直接储存在程序的二进制文件中而这个文件总是可用的;
let s: &'static str = "I have a static lifetime.";

6.9 结合泛型类型参数、trait bounds 和生命周期

use std::fmt::Display;fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a strwhere T: Display
{println!("Announcement! {}", ann);if x.len() > y.len() {x} else {y}
}
  • 上面演示了在同一函数中指定泛型类型参数、trait bounds 和生命周期的语法;
  • 示例返回两个字符串 slice 中较长者的函数且带有一个额外的参数 ann;
  • ann 的类型是泛型 T,它可以被放入任何实现了 where 从句中指定的 Display trait 的类型;
  • 这个额外的参数会在函数比较字符串 slice 的长度之前被打印出来(因此Display trait bound是必须的);
  • 生命周期也是泛型,所以生命周期参数 'a 和泛型类型参数 T 都位于函数名后的同一尖括号列表中;

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

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

相关文章

应急响应 | 基本技能 | 01-系统排查

系统排查 目录 系统基本信息 Windows系统Linux系统 用户信息 Windows系统 1、命令行方式2、图形界面方法3、注册表方法4、wmic方法 Linux系统 查看所有用户信息分析超级权限账户查看可登录的用户查看用户错误的登录信息查看所有用户最后的登录信息查看用户最近登录信息查看当…

机器学习周报第46周

目录 摘要Abstract一、文献阅读1.1 摘要1.2 研究背景1.3 论文方法1.4 模块分析1.5 网络规格1.6 高效的端到端对象检测1.7 mobile former模块代码 目录 摘要Abstract一、文献阅读1.1 摘要1.2 研究背景1.3 论文方法1.4 模块分析1.5 网络规格1.6 高效的端到端对象检测1.7 mobile f…

btstack协议栈实战篇--SPP Server - Heartbeat Counter over RFCOMM

btstack协议栈---总目录_bt stack是什么-CSDN博客 目录 1.SPP Service Setup 2.Periodic Timer Setup 3.Bluetooth Logic 4.btstack_main 5.log信息 串行端口配置文件(SPP)被广泛使用,因为它通过蓝牙。SPP反例演示了如何设置SPP服务,并通过RFCOMM提供周期性定时…

C++ 58 之 计算器案例

虚函数,vitual function C动态多态性是通过虚函数来实现的&#xff0c;虚函数允许子类&#xff08;派生类&#xff09;重新定义父类&#xff08;基类&#xff09;成员函数&#xff0c;而子类&#xff08;派生类&#xff09;重新定义父类&#xff08;基类&#xff09;虚函数的做…

day38-39| 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯 62.不同路径 343. 整数拆分 96.不同的二叉搜索树

文章目录 前言动态规划理论基础509. 斐波那契数思路方法一 完整动态规划方法二 dp简化版方法三 使用递归 70. 爬楼梯思路方法一 动态规划方法一2 教程里面的简化方法方法二 拓展 746. 使用最小花费爬楼梯思路方法一方法二 拓展 62.不同路径思路 动态规划方法一方法二 递归 63. …

设计模式学习(二)工厂模式——工厂方法模式

设计模式学习&#xff08;二&#xff09;工厂模式——工厂方法模式 前言工厂方法模式简介示例优点缺点使用场景 前言 前一篇文章介绍了简单工厂模式&#xff0c;提到了简单工厂模式的缺点&#xff08;违反开闭原则&#xff0c;扩展困难&#xff09;&#xff0c;本文要介绍的工…

Linux系统内核作用

Linux 内核主要有以下重要作用&#xff1a; 1. 进程管理&#xff1a;负责进程的创建、调度、终止等&#xff0c;合理分配 CPU 资源给各个进程&#xff0c;确保多任务的高效运行。 2. 内存管理&#xff1a;管理系统的物理内存和虚拟内存&#xff0c;包括内存分配、回收、页面交换…

Hadoop 2.0:主流开源云架构(四)

目录 五、Hadoop 2.0访问接口&#xff08;一&#xff09;访问接口综述&#xff08;二&#xff09;浏览器接口&#xff08;三&#xff09;命令行接口 六、Hadoop 2.0编程接口&#xff08;一&#xff09;HDFS编程&#xff08;二&#xff09;Yarn编程 五、Hadoop 2.0访问接口 &am…

java打印helloworld

源代码 public class Function1 {public static void main(String[] args) {System.out.println("hello world");}} 打印结果

MongoDB 自动增长

MongoDB 自动增长 MongoDB 是一个流行的 NoSQL 数据库&#xff0c;以其灵活的数据模型和强大的查询语言而闻名。在关系型数据库中&#xff0c;我们通常使用自动增长的整数作为主键&#xff0c;以确保唯一性。然而&#xff0c;MongoDB 的文档模型与此略有不同。MongoDB 使用 _i…

为什么说Python 是胶水语言?

​ "Python 是胶水语言"这一说法是指它很擅长将不同的程序或代码库连接在一起&#xff0c;能够让来自不同编程语言或框架的组件无缝协作。Python 具有丰富的库和简单的语法&#xff0c;使得它可以轻松调用其他语言编写的程序或使用不同技术栈的模块。 ​ 以下是几个…

linux下nvidia驱动安装-ubuntu22.04安装2060-notebook驱动

原文链接&#xff1a;linux下gcc编译安装与卸载-ubuntu22.04安装gcc-12.3.0 导言 nvidia驱动是显卡稳定运行的重要保证&#xff0c;不同的显卡有不同驱动&#xff0c;不同驱动对操作系统/cuda支持都存在一定差别。本次驱动安装主要完成2060-notebook显卡在linux系统下的驱动安…

C学习自学笔记-会陆续完善对应章节编程经典例子

C学习笔记 0>C语言概述 为什么学习C语言 1&#xff09;C的起源和发展------了解即可 B语言、C语言、C语言的产生地&#xff1a;都出自 美国贝尔实验室 2&#xff09;C的特点 优点&#xff1a;代码量小、速度快、功能强大 缺点&#xff1a;危险性高、开发周期长、可移植性…

可持久化数据结构详解与实现

一、引言 在计算机科学中&#xff0c;数据结构是用于组织、存储和管理数据的方式。然而&#xff0c;随着数据量的不断增长和数据处理需求的复杂化&#xff0c;传统的数据结构在某些场景下显得力不从心。为了应对这些挑战&#xff0c;可持久化数据结构应运而生。可持久化数据结…

MATLAB直方图中bin中心与bin边界之间的转换

要将 bin 中心转换为 bin 边界&#xff0c;请计算 centers 中各连续值之间的中点。 d diff(centers)/2; edges [centers(1)-d(1), centers(1:end-1)d, centers(end)d(end)];要将 bin 边界转换为bin 中心 bincenters binedges(1:end-1)diff(binedges)/2;

【AI应用探讨】— 星火大模型应用场景

目录 1 金融行业 2 零售行业 3 物流行业 4 教育行业 5 办公场景 6 医疗场景 7 工业场景 1 金融行业 风险评估与风控决策&#xff1a;星火大模型可以利用大数据和人工智能技术&#xff0c;对客户的信用数据、行为数据等进行分析和建模&#xff0c;帮助金融机构实现更精确…

Qt事件系统

概述 在Qt中&#xff0c;事件是对象&#xff0c;派生自抽象的QEvent类&#xff0c;它表示应用程序内部发生的事情或作为应用程序需要知道的外部活动的结果。事件可以由QObject子类的任何实例接收和处理&#xff0c;但它们与小部件特别相关。本文档描述了在典型应用程序中如何传…

每日一练——用队列实现栈

225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; Queue.h #pragma once #include<stdlib.h> #include<assert.h> #include<stdbool.h>typedef int QDataType;typedef struct QNode {QDataType data;struct QNode* next; } QNode;typedef struct …

安全测试框架 二

使用安全测试框架进行测试&#xff0c;可以遵循以下步骤进行&#xff0c;以确保测试的全面性和系统性&#xff1a; 一、明确测试目标和需求 确定测试的范围和重点&#xff0c;明确要测试的系统或应用的安全性方面的关键点和重要性。根据业务需求和安全标准&#xff0c;制定详…

【办公类-04-03】华为助手导出照片视频分类(根据图片、视频的文件名日期分类导出)

背景需求&#xff1a; 用华为手机助手导出的照片视频&#xff0c;只能将jpg照片&#xff08;exifread读取图片的exif拍摄日期&#xff0c;Png、JPEG、mp4都无法识别到exif信息&#xff09; 【办公类-04-02】华为助手导出照片&#xff08;jpg&#xff09;读取拍摄时间分类导出…