【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.pswp.cn/web/70123.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70123.shtml
英文地址,請注明出處:http://en.pswp.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日在中國西安召開。本次會議將重點討論材料科學、應用力學等領域的最新研究進…