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

news2025/1/12 9:05:03

目录

  • 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 str
    where 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.coloradmin.cn/o/1827990.html

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

相关文章

AI大模型技术揭秘-参数,Token,上下文和温度

深入理解 AI 大模型:参数、Token、上下文窗口、上下文长度和温度 人工智能技术的飞速发展使AI大模型大放异彩,其中涉及的“参数”、“Token”、“上下文窗口”、“上下文长度”及“温度”等专业术语备受瞩目。这些术语背后究竟蕴含何意?它们如何影响AI大模型的性能?一起揭开…

[SWPUCTF 2022 新生赛]善哉善哉(隐写,新佛曰,MD5)

题目&#xff1a; 我们看到&#xff1a;题目就是一张图片便联想到隐写术。、 首先查看图片的详细信息我们看到 又看到有关MD5加密。 我们利用小鲨鱼(Stegsolve)破解得到&#xff1a; 小鲨鱼下载可参考&#xff1a;CSDN小鲨鱼下载安装配置 最后面有一段摩斯密码&#xff0c;破…

Explain Python Machine Learning Models with SHAP Library

Explain Python Machine Learning Models with SHAP Library – Minimatech &#xff08;能翻墙直接看原文&#xff09; Explain Python Machine Learning Models with SHAP Library 11 September 2021Muhammad FawiMachine Learning Using SHapley Additive exPlainations …

Elixir学习笔记——输入输出和文件系统

本章介绍输入/输出机制、文件系统相关任务以及相关模块&#xff08;如 IO、File 和 Path&#xff09;。IO 系统提供了一个很好的机会来阐明 Elixir 和 Erlang VM 的一些思维模式和新奇思想。 输入输出模块 输入输出模块是 Elixir 中读写标准输入/输出 (:stdio)、标准错误 (:s…

HTML表格的跨行与跨列:《红楼梦》人物与小学课表示例

在HTML中&#xff0c;表格不仅可以按常规行和列排列数据&#xff0c;还可以通过跨行&#xff08;rowspan&#xff09;和跨列&#xff08;colspan&#xff09;属性来合并单元格&#xff0c;以适应更复杂的数据展示需求。以下是跨行与跨列属性的介绍&#xff0c;以及两个示例&…

centos环境上:k8s 简单安装教程

本次演示安装3节点k8s环境&#xff0c;无需多言&#xff0c;直接上操作步骤&#xff1a; 1、环境准备 k8s部署前&#xff0c;首先需要准备好环境&#xff0c;除了1.4 步骤&#xff0c;其他步骤在所有&#xff08;3个&#xff09;节点上都要执行&#xff1a; 1.1 关闭防火墙 s…

移动硬盘数据恢复方法哪个好?六个硬盘恢复,新手也能用!

移动硬盘数据恢复方法哪个好&#xff1f;移动硬盘&#xff0c;作为我们存储重要数据的常用设备&#xff0c;一旦里面的视频、文档、音频等资料突然消失&#xff0c;确实会令人烦恼和担忧。然而&#xff0c;因为数据丢失的原因可能多种多样&#xff0c;因此恢复方法也会有所不同…

ABBYY Finereader 15软件下载及安装教程

ABBYY FineReader 是一款功能强大的 OCR&#xff08;Optical Character Recognition&#xff09;软件&#xff0c;可以将扫描的文档转换为可编辑的文本文件。它不仅可以识别文本&#xff0c;还可以识别表格、图像和布局&#xff0c;使得文档的转换更加准确和方便。 安 装 包 获…

【计算机毕业设计】基于Springboot的车辆管理系统【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…

基于51单片机的烟雾报警器设计-ADC0809

一.硬件方案 火灾报警器采用51单片机为核心控制器&#xff0c;利用气体传感器MQ-2、ADC0809模数转换器、DS18B20温度传感器等实现基本功能。通过这些传感器和芯片&#xff0c;当环境中可燃气体浓度或温度等发生变化时系统会发出相应的灯光报警信号和声音报警信号&#xff0c;以…

使用python绘制三维曲线图

使用python绘制三维曲线图 三维曲线图定义特点 效果代码 三维曲线图 三维曲线图&#xff08;3D曲线图&#xff09;是一种用于可视化三维数据的图表&#xff0c;它展示了数据在三个维度&#xff08;X、Y、Z&#xff09;上的变化。 定义 三维曲线图通过在三维坐标系中绘制曲线…

Arduino入门2——常用函数及用法

Arduino入门2——串口驱动函数及用法 IO串口 上期&#xff0c;我们简单的认识了一下Arduino&#xff0c;浅浅的入了个门&#xff0c;这一期我们介绍以下Arduino串口常用的函数及用法 IO 常用串口库函数如下&#xff1a; 函数名用法及解析pinMode()用于IO口初始化digitalWrite…

新政出台,房市热闹起来,卖房容易了,上海二手房东多卖360万

在517新政之后&#xff0c;上海的中介和房地产商售楼部都表示&#xff0c;如今咨询买房的人士大幅增长&#xff0c;而二手房东也迅速调涨价格&#xff0c;某二手房东表示当天调价&#xff0c;当天就成功卖出&#xff0c;卖出价格比之前增加了360万。 据悉该房东其实早已有意卖房…

数据结构01 栈及其相关问题讲解【C++实现】

栈是一种线性数据结构&#xff0c;栈的特征是数据的插入和删除只能通过一端来实现&#xff0c;这一端称为“栈顶”&#xff0c;相应的另一端称为“栈底”。 栈及其特点 用一个简单的例子来说&#xff0c;栈就像一个放乒乓球的圆筒&#xff0c;底部是封住的&#xff0c;如果你想…

python-基础篇-类与对象/面向对象程序设计-是什么

文章目录 定义一&#xff1a;面对对象是一种编程思想定义一&#xff1a;面向对象是一种抽象1、面向对象的两个基本概念2、面向对象的三大特性 定义一&#xff1a;你是土豪&#xff0c;全家都是土豪面向对象编程基础类和对象定义类创建和使用对象访问可见性问题面向对象的支柱 定…

java打印helloworld

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

PyQT5 键盘模拟/鼠标连点器的实现

近来在玩一个游戏,找不到合适的鼠标连点器,不是有广告就是功能太复杂,自己写了一个,分享出来,如果有需要的可以自行运行研究。 准备工作 Python版本:Python 3.12.3;运行前确保pyQT5已经安装: pip install PyQt5程序运行界面: 程序代码: 通过引入单独的常量和变…

C++ 53 之 继承中同名成员处理

#include <iostream> #include <string> using namespace std;class Base06{ public:int m_a;Base06(){this->m_a 10;}void fun(){cout << "父类的fun函数" << endl;}void fun(int a){cout << "父类的fun(int a)函数" &…

模仿qsort实现一个通用的冒泡排序

目录 前言 模仿 排序整型数组 排序结构体数组 排序字符数组 前言 qsort在前面我们讲到底层逻辑是快速排序的方式&#xff0c;是不是可以发现有了qsort来进行排序的话&#xff0c;就更加的方便快捷&#xff0c;我们在使用的时候 一方面&#xff0c;代码量会大大的减少 另一…

JVM 性能分析——jdk 自带命令分析工具(jps/jstat/jinfo/jmap/jhat/jstack)

文章目录 jps&#xff08;Java Process Status&#xff09;&#xff1a;查看正在运行的Java进程jstat&#xff08;JVM Statistics Monitoring Tool&#xff09;&#xff1a;查看 JVM 的统计信息jinfo&#xff08;Configuration Info for Java&#xff09;&#xff1a;实时查看和…