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…

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;本文要介绍的工…

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");}} 打印结果

为什么说Python 是胶水语言?

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

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

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

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;

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

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 …

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

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

LeetCode | 709.转换成小写字母

这道题可以用api也可以自己实现&#xff0c;都不难&#xff0c;大小字母之前相差了32&#xff0c;检查到大写字母时加上32即可 class Solution(object):def toLowerCase(self, s):""":type s: str:rtype: str"""return s.lower()class Solution…

试论地产需求政策的有效性边界

分析师通过对传统框架因子的分析和美日地产的回顾&#xff0c;指出收入政策将成为核心&#xff0c;测算认为地方收储面积约0.5-1.1亿平、收储资金0.8-1.9万亿元&#xff0c;70城二手房价降幅收窄至[-4.5%&#xff0c;-1.6%]。 事件&#xff1a;2024年5月17日&#xff0c;央行印…

git 快速将当前目录添加仓储

一、进入目录 git init git add . git commit -m "init" git remote add origin http://192.168.31.104/root/AutoBuildDemo.git 二、登录gitlab&#xff0c;创建项目AutoBuildDemo 最后执行&#xff1a; git push -u origin master

Github 2024-06-12 C开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10PHP项目1PLpgSQL项目1C++项目1Ventoy: 100%开源的可启动USB解决方案 创建周期:1534 天开发语言:C协议类型:GNU General Public Licen…

2024年7款硬盘恢复软件:即刻恢复硬盘删除的文件!

当文件被删除后&#xff0c;它并不是立即从硬盘中消失&#xff0c;而是被标记为“已删除”&#xff0c;等待垃圾回收处理。因此&#xff0c;在文件被删除后&#xff0c;有几种方法可以尝试恢复删除的数据。 以下是7款常用的数据恢复软件&#xff0c;以及它们的详细介绍&#xf…

单片机与DHT11温湿度检测设计

本次设计是采用STC89C54单片机加上低成本的温湿度模块DHT11构成的温湿度检测系统。设计主要由硬件与软件两部分设计构成。硬件方面包括单片机STC89C54、温湿度模块DHT11、显示模块LCD1602、电池电源、I2C存储器以及控制按键等5个部分。此系统完全基于单片机最小系统并进行一定的…

【C++ | 静态成员】类的 静态(static)数据成员、静态(static)成员函数 详解及例子代码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-16 0…

Electron+vite+vuetify项目搭建

最近想用Electron来进行跨平台的桌面应用开发。同时想用vuetify作为组件&#xff0c;于是想搭建一个这样的开发环境。其中踩了不少坑&#xff0c;总是会出现各种的编译错误和问题&#xff0c;依赖的各种问题&#xff0c;搞了好久最终环境终于弄好可正常开发了。这里分享下快速搭…