【Rust自學】4.4. 引用與借用

4.4.0 寫在正文之前

這一節的內容其實就相當于C++的智能指針移動語義在編譯器層面做了一些約束。Rust中引用的寫法通過編譯器的約束寫成了C++中最理想、最規范的指針寫法。所以學過C++的人對這一章肯定會非常熟悉。

喜歡的話別忘了點贊、收藏加關注哦(加關注即可閱讀全文),對接下來的教程有興趣的可以關注專欄。謝謝喵!(=・ω・=)

4.4.1. 引用

引用讓函數使用某個值而不獲得其所有權,聲明時在類型前加上&即代表引用。例如String的引用就是&String。如果學過C++的話,C++中的解引用符號是*,Rust中也是一樣的。

學了引用之后,就可以把上一篇文章最后的示例代碼給簡化

這是先前的代碼:

fn main(){let s1 = String::from("hello");let (s2, len) = calculate_length(s1);println!("The length of '{}' is {}", s2, len);
}fn calculate_length(s:String) -> (String, uszie) {let length = s.len();(s, length)
}

這是修改后的代碼:

fn main(){let s1 = String::from("hello");let length = calculate_length(&s1);println!("The length of '{}' is {}", s1, length);
}fn calculate_length(s:&String) -> usize {s.len()
}

對比兩者,后者中數據的指針被傳入函數calculate_length供其操作,而數據所有權依然在變量s1上。不需要返回元組,也不需要再聲明一個變量s2,更加簡潔。

函數calculate_length的參數s實際上是一個指針,指向s所在棧內存位置(不會直接指向堆內存中的數據)。這個指針在走出作用域時,Rust并不會消除其指向的數據(因為s沒有所有權),只會彈出棧上所存儲的指針信息,也就是釋放下圖中的最左側的部分所占的內存。
請添加圖片描述

這種以引用作為函數的參數叫做借用

4.4.2. 借用的特性

借用的內容是不能被修改的,除非是可變引用

以房產為例:你把自己有房產權的房子租給別人就是借用,租戶只能住不能亂裝修,這就是借用的內容不能被修改的特性;如果你允許租客裝修,這就是可變引用。

以這個代碼為例:

fn main(){let s1 = String::from("hello");let length = calculate_length(&s1);println!("The length of '{}' is {}", s1, length);
}fn calculate_length(s:&String) -> usize {s.push_str(", world");s.len()
}

在編譯時這個代碼會報錯:

error[E0596]: cannot borrow `*s` as mutable, as it is behind a `&` reference

報錯的原因在于s.push_str(", world");這一行:引用默認是不可變的,但這一行修改了其數據內容。

引用跟普通的變量聲明一樣,默認不可變,但加上mut關鍵字后就可變了:

fn main(){let mut s1 = String::from("hello");let length = calculate_length(&mut s1);println!("The length of '{}' is {}", s1, length);
}fn calculate_length(s:&mut String) -> usize {s.push_str(", world");s.len()
}

這樣寫就不會報錯了(但記得在聲明s1時把s1聲明為可變變量)

這種可以修改數據內容的引用就叫做可變引用

4.4.3. 可變引用的限制

可變引用有兩個非常重要的限制,其一是:在特定作用域內,對某一塊數據,只能有一個可變的引用。

以這個代碼為例:

fn main() {let mut s = String::from("hello");let s1 = &mut s;let s2 = &mut s;
}

因為s1s2都是指向s的可變引用,且在同一個作用域內,所以在編譯時會報錯:

error[E0499]: cannot borrow `s` as mutable more than once at a time

這么做的目的是防止數據競爭,以下三種條件同時滿足時會發生數據競爭:

  • 兩個或多個指針同時訪問同一個數據
  • 至少有一個指針用于寫入數據
  • 沒有使用任何機制來同步對數據的訪問

在報錯信息中提及了at a time,意思為同時(也就是在同一個作用域內)。所以說,只要不同時,也就是兩個可變引用在不同的作用域指向同一塊數據是可以的。下面的代碼就體現了這一點:

fn main() {let mut s = String::from("hello");{let s1 = &mut s;}let s2 = &mut s;
}

s1s2作用域不相同,所以指向同一塊數據是允許的。

可變引用的第二個重要限制是:不可以同時擁有一個可變引用和一個不變的引用 因為可變引用存在的目的是修改數據內容,不變的引用存在的作用就是為了保持數據內容不變,如果兩者同時存在,可變引用修改值之后,不可變引用的作用就失效了。

fn main() {let mut s = String::from("hello");let s1 = &mut s;let s2 = &s;
}

因為s1是可變引用,s2是不可變引用,兩者出現在同一個作用域指向同一塊數據,所以編譯器會報錯:

error[E0502]: cannot borrow `s` as mutable because it also borrowed as immutable

當然,多個不可變的引用是可以同時出現的

總結:多個讀(不可變引用)是可以同時存在的,多個寫(可變引用)可以存在但不能同時,多個寫和同時讀寫是不允許的。

4.4.4. 懸空引用(Dangling References)

在使用指針時非常容易引起叫做懸空指針(Dangling Pointer) 的錯誤,其定義為:一個指針引用了內存中的某個地址,而這塊內存可能已經釋放并分配給其他人使用了。

如果你引用了某些數據,Rust編譯器保證在引用離開作用域前數據不會離開作用域。 這是Rust保證懸空引用永遠不會出現的做法。

以這個代碼為例:

fn main() {let r = dangle();
}fn dangle() -> &String {let s = String::from("hello");&s
}
  • 創建了一個局部變量 s:
    變量s是一個String,它被分配在棧上,但其底層數據存儲在堆上。
  • 返回對s的引用:
    函數最后通過&s返回了s的引用。
  • s 的作用域結束:
    在函數dangle返回后,變量s離開了作用域,根據Rust所有權規則,s的內存被自動釋放,&s所指向的內存數據已不再存儲s的數據,返回的引用指向的是已經被釋放的內存地址,變成了懸空引用(Dangling Pointer)。

Rust的編譯器會檢查到這一點,在編譯時會報錯。

4.4.5. 引用的規則

  • 在任何給定的時刻,只能滿足下列條件之一:
    • 一個可變的引用
    • 任意數量不可變的引用
  • 引用必須一直有效

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/63523.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/63523.shtml
英文地址,請注明出處:http://en.pswp.cn/web/63523.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深入解析 StarRocks 物化視圖:全方位的查詢改寫機制

小編導讀: 本文將重點介紹如何利用物化視圖進行查詢改寫。文章將全面介紹物化視圖的基本原理、關鍵特性、應用案例、使用場景、代碼細節以及主流大數據產品的物化視圖改寫能力對比。 物化視圖在 StarRocks 中扮演著至關重要的角色,它是進行數據建模和加速…

2. petalinux-build失敗

NOTE 解決因為網絡原因產生的編譯錯誤分享詳細的解決步驟 報錯的情況 因為網絡原因產生編譯錯誤 現象 找不到適合的包文件(No suitable stageing package found) 不能發現文件(Fetcher failure for URL) 解決方法 采用本地加載本地文件的方式,步驟如下 進入…

sql server msdb數據庫備份恢復

備份 BACKUP DATABASE [msdb] TO DISK ND:\liyuanshuai\test\sqlserver_bakfile\msdb20241219.bak WITH NOFORMAT, NOINIT, NAME Nlys-完整 數據庫 備份, SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS 10 GO然后刪除2個測試的job,停止 SQL Server 代理…

web實驗二

web實驗二 2024.12.19 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>青島理工大學</title>&l…

bain.js(十二):RNN神經網絡實戰教程 - 音樂樂譜生成 -人人都是作曲家~

系列文章&#xff1a; &#xff08;一&#xff09;&#xff1a;可以在瀏覽器運行的、默認GPU加速的神經網絡庫概要介紹&#xff08;二&#xff09;&#xff1a;項目集成方式詳解&#xff08;三&#xff09;&#xff1a;手把手教你配置和訓練神經網絡&#xff08;四&#xff09…

WebSocket入門與結合redis

WebSocket是什么 WebSocket 是一種用于在客戶端和服務器之間建立雙向通信的協議&#xff0c;它能實現實時、持久的連接。與傳統的 HTTP 請求響應模式不同&#xff0c;WebSocket 在建立連接后允許客戶端和服務器之間相互發送消息&#xff0c;直到連接關閉。由于 WebSocket 具有…

Hive是什么,Hive介紹

官方網站&#xff1a;Apache Hive Hive是一個基于Hadoop的數據倉庫工具&#xff0c;主要用于處理和查詢存儲在HDSF上的大規模數據?。Hive通過將結構化的數據文件映射為數據庫表&#xff0c;并提供類SQL的查詢功能&#xff0c;使得用戶可以使用SQL語句來執行復雜的?MapReduce任…

OpenHarmony和OpenVela的技術創新以及兩者對比

兩款有名的國內開源操作系統&#xff0c;OpenHarmony&#xff0c;OpenVela都非常的優秀。本文對二者的創新進行一個簡要的介紹和對比。 一、OpenHarmony OpenHarmony具有諸多有特點的技術突破和重要貢獻&#xff0c;以下是一些主要方面&#xff1a; 架構設計創新 分層架構…

Electron-Vue 開發下 dev/prod/webpack server各種路徑設置匯總

背景 在實際開發中&#xff0c;我發現團隊對于這幾個路徑的設置上是純靠猜的&#xff0c;通過一點點地嘗試來找到可行的路徑&#xff0c;這是不應該的&#xff0c;我們應該很清晰地了解這幾個概念&#xff0c;以下通過截圖和代碼進行細節講解。 npm run dev 下的路徑如何處理&…

前端-處理數據的函數

判斷數據是否為空,對象是否存在某屬性,屬性值是否為空,對大數據進行換算,對單位進行轉換. 目錄 1.格式化數據 2.判斷值是否為空(包括對象、數組、字符串、數值類型) &#xff08;1&#xff09;值是0不表示空 &#xff08;2&#xff09;值是0表示空 3. 檢查對象是否具有指定名…

基礎入門-Web應用蜜罐系統堡壘機運維API內外接口第三方拓展架構部署影響

知識點&#xff1a; 1、基礎入門-Web應用-蜜罐系統 2、基礎入門-Web應用-堡壘機運維 3、基礎入門-Web應用-內外API接口 4、基礎入門-Web應用-第三方拓展架構 一、演示案例-Web-拓展應用-蜜罐-釣魚誘使 蜜罐&#xff1a;https://hfish.net/ 測試系統&#xff1a;Ubuntu 20.04 …

Android運行低版本項目可能遇到的問題

Android運行低版本項目可能遇到的問題 低版本項目總是遇到各種問題的&#xff0c;耐心點 一、gradle-xxx.xxx.xxx.zip一直下載不下來 在gradle-wrapper.properties可以試下 distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists zipStoreBaseGRADLE_USER_HOME …

springboot中Controller內文件上傳到本地以及阿里云

上傳文件的基本操作 <form action"/upload" method"post" enctype"multipart/form-data"> <h1>登錄</h1> 姓名&#xff1a;<input type"text" name"username" required><br> 年齡&#xf…

智慧城市工程:相關學點、優勢、未來發展

目錄 相關學點&#xff1a; 智慧城市的優勢 挑戰與未來發展 智慧城市工程是利用現代信息技術和數據分析手段&#xff0c;提升城市管理和服務水平&#xff0c;實現城市運行的智能化、便捷化和高效化的一種新型城市發展模式。智慧城市通過整合物聯網&#xff08;IoT&#xff0…

授權模型MAC

MAC&#xff08;Mandatory Access Control&#xff09;是一種授權模型&#xff0c;用于實現對系統資源訪問的強制控制。在MAC模型中&#xff0c;授權是基于預先定義的安全策略&#xff0c;且該策略由系統管理員來配置和管理。 在MAC模型中&#xff0c;每個用戶和每個資源都被賦…

看板工具助力餐飲與酒店行業實現數字化轉型,提升管理與運營效率

在餐飲與酒店行業&#xff0c;服務質量和客戶體驗是衡量企業成功的關鍵因素。隨著客戶需求的不斷多樣化以及市場競爭的加劇&#xff0c;傳統的管理模式逐漸難以滿足高效運營的需求。尤其在高峰期&#xff0c;如何優化內部流程、提高服務效率和響應速度&#xff0c;成為了許多餐…

2024年CCF 非專業級軟件能力認證CSP-J/S 第二輪( 提高組) 染色(color)

完整題目內容可前往下方鏈接&#xff1a; 染色&#xff08;color&#xff09;_C_嗨信奧-玩嗨信息奧林匹克競賽-少兒編程題庫學習中心https://www.hixinao.com/tiku/cpp/show-4118.html 若需更多真題&#xff0c;可前往題庫中心查找&#xff0c;題庫中心涵蓋白名單賽事真題&am…

OpenIPC開源FPV之Adaptive-Link天空端代碼解析

OpenIPC開源FPV之Adaptive-Link天空端代碼解析 1. 源由2. 框架代碼2.1 消息機制2.2 超時機制 3. 報文處理3.1 special報文3.2 普通報文 4. 工作流程4.1 Profile 競選4.2 Profile 研判4.2.1 回退策略4.2.2 保持策略 4.3 Profile 應用 5. 總結6. 參考資料7. 補充資料7.1 RSSI 和 …

labelme標簽批量轉換數據集json_to_dataset

文章目錄 labelme標簽批量轉換數據集json_to_dataset轉換原理單張圖片轉換多張圖片批量轉換bat腳本循環法 標注圖片提取標注圖片轉單通道 labelme標簽批量轉換數據集json_to_dataset 轉自labelme批量制作數據集教程。 轉換原理 在安裝了labelme的虛擬環境中有一個labelme_js…

Apache Kylin最簡單的解析、了解

官網&#xff1a;Overview | Apache Kylin 一、Apache Kylin是什么&#xff1f; 由中國團隊研發具有濃厚的中國韻味&#xff0c;使用神獸麒麟&#xff08;kylin&#xff09;為名 的一個OLAP多維數據分析引擎:&#xff08;據官方給出的數據&#xff09; 亞秒級響應&#xff…