【Rust中级教程】1.12. 生命周期(进阶) Pt.2:生命周期变型、协变、不变、逆变

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

这篇文章在Rust初级教程的基础上对生命周期这一概念进行了补充,建议先看【Rust自学】专栏的第10章的文章。
请添加图片描述

1.12.1. 生命周期变型(Lifetime Variance)

变型(Variance)是Rust类型系统中的一个概念,它描述了泛型参数(特别是生命周期参数)在类型层次结构中的继承关系

我们可以简单地理解为变型是用于描述哪些类型是其他类型的“子类”的,这里的“子类”比较类似于Java和C#中的子类。

除此以外,变型还会关注什么时候“子类”能够替换“超类”(反之亦然)

通常来说,如果A是B的子类,那么A至少和B一样有用。举一个Rust语言的例子:如果函数接收&'a str的参数,那么就可以传入&'static str的参数。因为'static'a的子类,'static至少跟任何'a存活的时间一样长('static能够在程序运行中一直保持有效)

1.12.2. 生命周期的3种变型

所有类型都有变型,每个类型所对应的变型定义了哪些类似类型可以用在该类型的位置上。

注意:以下的内容比较难,建议你先回忆一下高中学的充分条件、必要条件等知识

1. 协变(covariant)

协变(covariant)指的是某类型只能用“子类型”来代替。

协变表示:

如果 A <: B(A是B的子类型),那么 F<A> <: F<B>(F<A>也是F<B>的子类型)

这是一个从小到大继承关系的传递,类似于充分条件的推导:如果 A 成立,则 B 一定成立(A 是 B 的充分条件)。

例如&'static T可以代替&'a T,因为&T是对'a这个生命周期的协变,所以'a可以由它的子类(比如说'static)来替代。

2. 不变(invariant)

不变(invariant)意味着必须提供指定的类型。

不变表示:

A <: B 不能推导出 F<A> <: F<B> 也不能 F<B> <: F<A>

这意味着F<A>F<B>之间没有足够的关系,无法形成推导关系,因此它们既不是充分条件,也不是必要条件,它们是独立的。

比如说&mut T这个可变引用对于T来说就是不变的。

3. 逆变(contravariant)

逆变表示:

如果 A <: B(A是B的子类型),那么 F<B> <: F<A>(F<B>反而是F<A>的子类型)

这里的逻辑是 “想要F<A>成立,B必须满足A的条件”,更像必要条件如果 B 成立,则 A 也必须成立(A 是 B 的必要条件)。

你可以把逆变理解为“成反比”:函数吧对参数的要求越低,参数可发挥的作用越大。

举两个例子:

  • 假如有两个变量x1x2,其中x1的生命周期是'staticx2的生命周期是'a。那么毫无疑问x1的作用比x2大,因为它活得更长。

  • 假如有两个函数take_func1take_func2,其中take_func1接收的参数是&'static strtake_func2接收的参数是&'a str。毫无疑问,take_func1对参数的要求比take_func2严格,这就导致了take_func1没有take_func2作用大。

由上述的两个例子看出,给变量声明一个更长的生命周期会使它的作用更大,但是要求函数的参数是更长的生命周期就会使函数的作用更小。这就是所谓的逆变。

那么这叫谁和谁的逆变呢?就是函数对它里面的参数类型的逆变。

1.12.3. 生命周期变型的作用

我们通过一个例子来看一下生命周期变型的作用:

struct MutStr<'a, 'b> {  s: &'a mut &'b str,  
}  fn main() {  let mut s = "hello";  *MutStr { s: &mut s }.s = "world";  println!("{}", s);  
}

这个代码令人比较困惑的地方在于MutStr这个结构体,我们来解析一下:

  • 这个结构体只有一个字段,但是拥有两个生命周期
  • &'a mut表示 一个可变引用,这个可变引用的生命周期是'a
  • &'b str表示 一个字符串切片的引用,这个字符串切片的生命周期是'b
  • 换句话说,MutStr允许你存储一个可变引用,这个引用指向一个字符串切片的引用。你可以修改s本身,但不能修改&'b str指向的字符串内容

接下来我们来看一下主函数的逻辑:

  • let mut s = "hello";声明了s这个变量,类型是&str,值是"hello"

  • *MutStr { s: &mut s }.s = "world";这其实是好几步被合在了一行,我们分开来看:

    • MutStr { s: &mut s }传递给MutStr结构体s的可变引用,此时MutStr下的s字段的值就是"hello"
    • *MutStr { s: &mut s }.s = "world";.s表示访问s字段(此时这个字段的值是&mut s)。*解引用s,即获得s这个字符串切片的引用本身。= "hello"修改了指向的值——s之前指向"hello",现在被修改为"world",即s = "world"

那如果只有一个生命周期还能这么写吗?

struct MutStr<'a> {  s: &'a mut str,  
}  fn main() {  let mut s = "hello";  *MutStr { s: &mut s }.s = "world";  println!("{}", s);  
}

这段代码的问题在于类型不匹配和 Rust 中可变引用的不变性(invariance)。

具体来说:

  • 变量s的类型是&str(引用一个字符串切片)。
  • 当写&mut s时,其类型实际上是&mut &str,即对变量s的可变引用。然而,结构体MutStr的定义要求字段s的类型&mut str

虽然不可变引用(&T)支持“unsizing coercion”(例如可以将&[T; N]自动转换为&[T]),但是 可变引用 (&mut T) 是不变(invariant)的,这意味着Rust不允许自动将&mut &str转换为&mut str

因此,编译器会报类型不匹配的错误,因为它无法将&mut s(类型&mut &str)传给MutStr { s: ... }期望的&mut str参数。

在这里解释一下不变性:

  • 不可变引用 (&T) 是协变(variant)的:允许自动的unsizing转换,比如可以将&[T; N]当作&[T]使用
  • 可变引用 (&mut T) 是不变(invariant)的:这就意味着&mut &str&mut str被视为完全不同的类型,编译器不会自动进行转换

也可以这么理解:

  • 字符串字面值("hello"和"world"就是字符串字面值)是&str类型,有隐式的'static生命周期标注,也就是说&str实际上是&'static str。原本的结构体里的'b对应它
  • 结构体的’a对应可变引用的生命周期,也就是*MutStr { s: &mut s }.s = "world"这一行中的&mut这个可变引用的生命周期
  • 修改之后的这个只有一个泛型生命周期的结构体无法同时对应这两个生命周期标注,因为可变引用&mut T是不变(invariant)的,&mut &str&mut str被视为完全不同的类型,所以导致了报错

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

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

相关文章

Vue 项目登录的基本流程

Vue 用户登录的基本流程包括以下6个步骤&#xff1a; 步骤&#xff1a; 1. 创建登录表单 在前端&#xff0c;首先要创建一个登录表单&#xff0c;用户输入账号&#xff08;用户名、邮箱、手机号等&#xff09;和密码。 示例&#xff1a;Login.vue <template><div…

【算法】回溯算法

回溯算法 什么是回溯 人生无时不在选择。在选择的路口&#xff0c;你该如何抉择 ..... 回溯&#xff1a; 是一种选优搜索法&#xff0c;又称为试探法&#xff0c;按选优条件向前搜索&#xff0c;以达到目标。但当探索到某一步时&#xff0c;发现原先选择并不优或达不到目标&am…

SpringAI系列 - RAG篇(三) - ETL

目录 一、引言二、组件说明三、集成示例一、引言 接下来我们介绍ETL框架,该框架对应我们之前提到的阶段1:ETL,主要负责知识的提取和管理。ETL 框架是检索增强生成(RAG)数据处理的核心,其将原始数据源转换为结构化向量并进行存储,确保数据以最佳格式供 AI 模型检索。 …

2025 docker可视化管理面板DPanel的安装

1.什么是 DPanel &#xff1f; DPanel 是一款 Docker 可视化管理面板&#xff0c;旨在简化 Docker 容器、镜像和文件的管理。它提供了一系列功能&#xff0c;使用户能够更轻松地管理和部署 Docker 环境。 软件特点&#xff1a; 可视化管理&#xff1a;提供直观的用户界面&#…

基于Python的深度学习音乐推荐系统(有配套论文)

音乐推荐系统 提供实时音乐推荐功能&#xff0c;根据用户行为和偏好动态调整推荐内容 Python、Django、深度学习、卷积神经网络 、算法 数据库&#xff1a;MySQL 系统包含角色&#xff1a;管理员、用户 管理员功能&#xff1a;用户管理、系统设置、音乐管理、音乐推荐管理、系…

微信小程序---计划时钟设计与实现

微信小程序-计划时钟已上线,欢迎各位小伙伴的测试和使用~(微信小程序搜计划时钟即可使用) 在这篇博客中,我们将探讨如何在微信小程序中设计和实现一个任务管理功能,该功能允许用户添加、删除和查看任务。任务管理系统的核心是基于日期和时间的任务管理,可以设置任务的开…

RPA-实例(UiPath )

UiPath 是一个流行的机器人流程自动化(RPA)工具,用于自动化重复性任务。以下是一个简单的实例,展示如何使用 UiPath 自动化一个常见的任务:从 Excel 文件中读取数据并将其输入到网页表单中。 实例:从 Excel 读取数据并自动填写网页表单 步骤 1:准备工作 安装 UiPath S…

华为固态电池引发的思索

华为固态电池真牛&#xff01; 超长续航&#xff1a;单次充电即可行驶3000公里 极速充电&#xff1a;五分钟内充满80% 极致安全&#xff1a;不可燃、不漏液 长寿命设计&#xff1a;循环寿命达10000次以上 如上是华为电池展示的优势项&#xff0c;每一条都让我们心动不已。…

算法分析—— 《归并排序》

《排序数组》 题目描述&#xff1a; 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 你必须在 不使用任何内置函数 的情况下解决问题&#xff0c;时间复杂度为 O(nlog(n))&#xff0c;并且空间复杂度尽可能小。 示例 1&#xff1a; 输入&#xff1a;nums [5,2…

UEFI Spec 学习笔记---11 - Protocols — UEFI Driver Model(1)

11.UEFI Driver Model 遵循 UEFI model 的 EFI driver 是不允许去遍历所有的 controller 来识别需要安装到哪个 controller 上的&#xff0c;而是通过 EFI_BOOT_SERVICES 的 ConnectController 和调用 Binding Driver 来实现&#xff1b; 具体实现如下&#xff1a; CoreConne…

10G EPON光模块

一、10G EPON对称光模块 工作模式&#xff1a;上行突发接收、下行连续发射。 工作原理&#xff1a;当需要发送信号时&#xff0c;系统信号通过光模块的电接口把信号传送到驱动芯片&#xff0c;芯片处理后&#xff0c;驱动激光器发出调制光信号&#xff0c;经光纤发到远端&…

整合SaToken 实现登录功能

整合SaToken 实现登录功能 1.整合redis 1.1添加相关依赖 // 省略...<!-- Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Redi…

Vue 项目中逐步引入 TypeScript 的类型检查

在现有的 Vue 项目中逐步引入 TypeScript 的类型检查 本文源于一道面试题&#xff1a;注&#xff1a;两种问法一个意思哈&#xff01;&#xff01; 问题一&#xff1a;“ 老项目Js写的&#xff0c;如何轻量方式享受 ts 类型&#xff1f;” 问题二&#xff1a;“如何 在现有的 …

python后端调用Deep Seek API

python后端调用Deep Seek API 需要依次下载 ●Ollama ●Deepseek R1 LLM模型 ●嵌入模型nomic-embed-text / bge-m3 ●AnythingLLM 参考教程&#xff1a; Deepseek R1打造本地化RAG知识库:安装部署使用详细教程 手把手教你&#xff1a;deepseek R1基于 AnythingLLM API 调用本地…

本地部署MindSearch(开源 AI 搜索引擎框架),然后上传到 hugging face的Spaces——L2G6

部署MindSearch到 hugging face Spaces上——L2G6 任务1 在 官方的MindSearch页面 复制Spaces应用到自己的Spaces下&#xff0c;Space 名称中需要包含 MindSearch 关键词&#xff0c;请在必要的步骤以及成功的对话测试结果当中 实现过程如下&#xff1a; 2.1 MindSearch 简…

matlab下载安装图文教程

【matlab介绍】 MATLAB是一款由美国MathWorks公司开发的专业计算软件&#xff0c;主要应用于数值计算、可视化程序设计、交互式程序设计等高科技计算环境。以下是关于MATLAB的简要介绍&#xff1a; MATLAB是MATrix LABoratory&#xff08;矩阵实验室&#xff09;的缩写&#…

捷米特 JM - RTU - TCP 网关应用 F - net 协议转 Modbus TCP 实现电脑控制流量计

一、项目背景 在某工业生产园区的供水系统中&#xff0c;为了精确监测和控制各个生产环节的用水流量&#xff0c;需要对分布在不同区域的多个流量计进行集中管理。这些流量计原本采用 F - net 协议进行数据传输&#xff0c;但园区的监控系统基于 Modbus TCP 协议进行数据交互&…

4.1 Hugging Face Datasets实战:构建企业级数据流水线

Hugging Face Datasets实战:构建企业级数据流水线 一、Datasets库核心优势 1.1 企业级数据处理需求全景 # 支持的数据格式示例 data_formats = {"结构化数据": ["CSV", "Parquet", "SQL"]

深入解析队列与广度优先搜索(BFS)的算法思想:原理、实现与应用

目录 1. 队列的基本概念 2. 广度优先搜索&#xff08;BFS&#xff09;的基本概念 3. 队列在BFS中的作用 4. BFS的实现细节 5. C实现BFS 6. BFS的应用场景 7. 复杂度分析 8. 总结 1. 队列的基本概念 队列&#xff08;Queue&#xff09;是一种先进先出&#xff08;FIFO, …

【学术投稿-第四届材料工程与应用力学国际学术会议(ICMEAAE 2025】材料工程与应用力学的探讨

重要信息 官网&#xff1a;www.icmeaae.com 时间&#xff1a;2025年3月7-9日 地点&#xff1a;中国西安 简介 第四届材料工程与应用力学&#xff08;ICMEAAE 2025&#xff09;将于2025年3月7日至9日在中国西安召开。本次会议将重点讨论材料科学、应用力学等领域的最新研究进…