rust-參考與借用

參考與借用

在清單4-5中的元組代碼的問題在于,我們必須將String返回給調用函數,這樣我們才能在調用calculate_length之后繼續使用String,因為String已經被移動到了calculate_length中。相反,我們可以提供一個對String值的引用。引用類似于指針,它是一個地址,我們可以沿著它訪問存儲在該地址的數據;這些數據由其他某個變量擁有。與指針不同的是,引用在引用的生命周期內保證指向一個特定類型的有效值。

以下是如何定義和使用一個以對象的引用作為參數而不是獲取值的所有權的calculate_length函數:

文件名:src/main.rs

fn main() {let s1 = String::from("hello");let len = calculate_length(&s1);println!("'{}'的長度是{}。", s1, len);
}
fn calculate_length(s: &String) -> usize {s.len()
}

首先,注意變量聲明和函數返回值中的所有元組代碼都消失了。其次,注意我們將&s1傳遞給calculate_length,在其定義中,我們接受&String而不是String。這些&符號代表引用,它們允許你在不獲取所有權的情況下引用某個值。圖4-6展示了這一概念。
4-6

圖4-6:一個指向String s1&String s的示意圖

注意:使用&來引用的相反操作是解引用,這可以通過解引用運算符*來完成。我們將在第8章看到一些解引用運算符的用法,并在第15章詳細討論解引用的細節。

讓我們更仔細地看看這里的函數調用:

let s1 = String::from("hello");let len = calculate_length(&s1);

&s1的語法讓我們創建了一個引用,它指向s1的值,但并不擁有它。因為引用并不擁有它,所以當引用不再被使用時,它所指向的值不會被釋放。

同樣,函數的簽名使用&來表明參數s的類型是一個引用。我們來添加一些解釋性的注釋:

fn calculate_length(s: &String) -> usize { // s是一個指向String的引用s.len()
} // 在這里,s的作用域結束了。但由于s并不擁有它所引用的內容,所以值不會被釋放。

變量s的有效作用域與任何函數參數的作用域相同,但引用所指向的值在s不再被使用時不會被釋放,因為s并不擁有它。當函數的參數是引用而不是實際值時,我們不需要返回值以交還所有權,因為我們從未擁有過所有權。

我們將創建引用的行為稱為借用。就像在現實生活中一樣,如果一個人擁有某樣東西,你可以從他那里借來。當你用完后,你必須歸還。你并不擁有它。

那么,如果我們嘗試修改我們正在借用的內容會發生什么呢?嘗試清單4-6中的代碼。劇透警告:它無法工作!

文件名:src/main.rs

這段代碼無法編譯!

fn main() {let s = String::from("hello");change(&s);
}fn change(some_string: &String) {some_string.push_str(", world");
}

清單4-6:嘗試修改一個被借用的值

這是錯誤信息:

$ cargo runCompiling ownership v0.1.0 (file:///projects/ownership)
error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference--> src/main.rs:8:5|
8 |     some_string.push_str(", world");|     ^^^^^^^^^^^ `some_string`是一個`&`引用,因此它所引用的數據不能被借用為可變的|
help: 考慮將其改為可變引用|
7 | fn change(some_string: &mut String) {|                         +++

有關此錯誤的更多信息,請嘗試rustc --explain E0596

錯誤:由于之前的1個錯誤,無法編譯ownership(二進制文件“ownership”)

正如變量默認是不可變的一樣,引用也是不可變的。我們不允許修改我們引用的內容。

可變引用

我們可以通過一些小的調整來修復清單4-6中的代碼,從而允許我們修改一個被借用的值。這些調整使用的是可變引用:

文件名:src/main.rs

fn main() {let mut s = String::from("hello");change(&mut s);
}fn change(some_string: &mut String) {some_string.push_str(", world");
}

首先,我們將s改為mut。然后,我們在調用change函數時使用&mut s創建一個可變引用,并更新函數簽名以接受一個可變引用some_string: &mut String。這使得change函數將修改它借用的值這一行為變得非常清晰。

可變引用有一個重要的限制:如果你有一個對某個值的可變引用,那么你不能有其他對該值的引用。這段嘗試為s創建兩個可變引用的代碼將無法通過編譯:

文件名:src/main.rs

這段代碼無法編譯!

let mut s = String::from("hello");let r1 = &mut s;
let r2 = &mut s;println!("{}, {}", r1, r2);

這是錯誤信息:

$ cargo runCompiling ownership v0.1.0 (file:///projects/ownership)
error[E0499]: cannot borrow `s` as mutable more than once at a time--> src/main.rs:5:14|
4 |     let r1 = &mut s;|              ------ 第一個可變借用發生在這里
5 |     let r2 = &mut s;|              ^^^^^^ 第二個可變借用發生在這里
6 |
7 |     println!("{}, {}", r1, r2);|                        -- 第一個借用在此處后續使用有關此錯誤的更多信息,請嘗試`rustc --explain E0499`。
錯誤:由于之前的1個錯誤,無法編譯`ownership`(二進制文件“ownership”)

這個錯誤表明這段代碼是無效的,因為我們不能同時多次將s借用為可變的。第一個可變借用在r1中,必須持續到它在println!中被使用為止,但在那個可變引用創建和使用之間,我們試圖在r2中創建另一個可變引用,它與r1借用相同的數據。

防止同時對相同數據進行多次可變引用的限制允許進行修改,但這種修改是受到嚴格控制的。新Rustaceans(Rust新手)常常會在這個問題上掙扎,因為大多數語言允許你在任何時候進行修改。這種限制的好處是Rust可以在編譯時防止數據競爭。數據競爭類似于競態條件,當出現以下三種行為時就會發生:

  • 兩個或更多的指針同時訪問相同的數據。
  • 至少有一個指針被用來寫入數據。
  • 沒有機制被用來同步對數據的訪問。

數據競爭會導致未定義行為,并且在運行時試圖追蹤它們時很難診斷和修復;Rust通過拒絕編譯存在數據競爭的代碼來防止這個問題!

正如我們總是可以使用大括號來創建一個新作用域一樣,我們可以允許有多個可變引用,只要它們不是同時存在的即可:

let mut s = String::from("hello");{let r1 = &mut s;
} // r1在這里結束作用域,因此我們可以毫無問題地創建一個新的引用。let r2 = &mut s;

Rust對組合可變引用和不可變引用也執行類似的規則。這段代碼會導致錯誤:

這段代碼無法編譯!

let mut s = String::from("hello");let r1 = &s; // 沒有問題
let r2 = &s; // 沒有問題
let r3 = &mut s; // 大問題println!("{}, {}, and {}", r1, r2, r3);

這是錯誤信息:

$ cargo runCompiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable--> src/main.rs:6:14|
4 |     let r1 = &s; // 沒有問題|              -- 不可變借用發生在這里
5 |     let r2 = &s; // 沒有問題
6 |     let r3 = &mut s; // 大問題|              ^^^^^^ 可變借用發生在這里
7 |
8 |     println!("{}, {}, and {}", r1, r2, r3);|                                -- 不可變借用在此處后續使用有關此錯誤的更多信息,請嘗試`rustc --explain E0502`。
錯誤:由于之前的1個錯誤,無法編譯`ownership`(二進制文件“ownership”)

呼!我們也不能在有對同一個值的不可變引用的同時擁有一個可變引用。

不可變引用的使用者不會期望值在他們不知情的情況下突然改變!然而,允許多個不可變引用是合理的,因為那些只是讀取數據的人無法影響其他人的讀取。

注意,引用的作用域從它被引入的地方開始,并持續到最后一次使用該引用的地方。例如,這段代碼可以編譯,因為不可變引用的最后一次使用是在println!中,這在可變引用被引入之前:

let mut s = String::from("hello");let r1 = &s; // 沒有問題
let r2 = &s; // 沒有問題
println!("{r1} and {r2}");
// 變量r1和r2在此點之后將不再被使用。let r3 = &mut s; // 沒有問題
println!("{r3}");

不可變引用r1r2的作用域在它們最后一次被使用的println!之后結束,這在可變引用r3被創建之前。這些作用域沒有重疊,所以這段代碼是被允許的:編譯器可以判斷出在作用域結束之前引用不再被使用。

接下來,我們將看看另一種引用類型:切片。

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

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

相關文章

深入解析HDFS Federation:如何有效解決單NameNode瓶頸問題

HDFS Federation簡介與背景在Hadoop分布式文件系統(HDFS)的經典架構中,NameNode作為核心組件承擔著整個文件系統的元數據管理職責。這一設計雖然簡潔高效,但隨著數據規模的爆炸式增長,單NameNode架構逐漸暴露出難以克服…

為什么選擇EasyGBS?

作為集 算法倉、算力設備接入、視頻云平臺 于一體的綜合性智能安防監控平臺,EasyGBS有哪些優勢是您的必選理由呢?一、設備與協議的兼容性EasyGBS不挑設備品牌型號。只要支持GB28181、RTSP、ONVIF、RTMP標準協議里的任一種,就能將視頻接入。但…

【形態學變換】——圖像預處理(OpenCV)

目錄 1 核 2 腐蝕 3 膨脹 4 開運算 5 閉運算 6 禮帽運算 7 黑帽運算 8 形態學梯度 形態學變換是一種基于形狀的簡單變換,處理對象是二值化后的圖像。有兩個輸入:原圖像和核,一個輸出:形態學變換后的圖像。基本操作有以下四…

一次“非法指令”(SIGILL)問題的完整調試過程:CPU指令集兼容性探秘

一次"非法指令"問題的完整調試過程:CPU指令集兼容性探秘一、問題概述二、問題現象與初步分析1. 環境與現象2. 官方文檔的線索3. 重現問題4. 懷疑方向:CPU指令兼容性5. 關鍵發現:AVX512指令三、詳細調試過程1. 搭建調試環境 (KVM虛擬…

Node.js - 創建 Express 項目

創建 Express 項目 安裝 npm i -g express-generatorornpm i -g express-generator4# 注意:Windows有可能碰到提示:npm : 無法加載文件 C:\Program Files\nodejs\npm.ps1,因為在此系統上禁止運行腳本。 # 如果碰到這個錯誤,需要…

高并發系統設計面試題

高并發系統設計面試題🔥🔥🔥 超高頻問題(幾乎必問)讓你設計一個秒殺系統,你會考慮哪些問題?如果你的業務量突然提升100倍QPS你會怎么做?庫存扣減如何避免超賣和少賣?訂單…

【通識】如何看電路圖

1. 電路圖 1.1 基礎概念 電路圖即電原理圖。 電路圖第一種是說明模擬電子電路工作原理,用圖形符號表示電阻器、電容器、開關、晶體管等實物,用線條把元器件和單元電路按工作原理的關系連接起來。 第二種則是說明數字電子電路工作原理的。用圖形符號表示…

SpringBoot實戰指南:從快速入門到生產級部署(2025最新版)

一、為什么SpringBoot依然是Java開發的首選? SpringBoot自2014年發布以來,已成為Java企業級開發的事實標準框架。根據2025年最新調研數據顯示,全球78%的Java微服務項目基于SpringBoot構建,其核心優勢在于: 約定優于配置…

新房裝修是中央空調還是壁掛空調好?

這個要看戶型和投資金額,大戶型空間適合裝中央空調,因為空間大有足夠的地方安裝,功率也可以根據面積大小進行配置,整體配置一個外機就行了,整體的裝修效果比較規整,就是多花點,使用成本也稍高點…

如何理解泊松分布

文章目錄一、引例——鯨魚研究二、泊松分布一、引例——鯨魚研究 有生態學家對生活在北冰洋水域的鯨魚進行了跟蹤研究,他們利用一臺水下無人機來探測鯨魚數量,這是近十天的數據: 第1天第2天第3天第4天第5天第6天第7天第8天第9天第10天10101…

python學習DAY22打卡

作業: 自行學習參考如何使用kaggle平臺,寫下使用注意點,并對下述比賽提交代碼 kaggle泰坦尼克號人員生還預測 import warnings warnings.filterwarnings("ignore") #忽略警告信息 # 數據處理清洗包 import pandas as pd import …

在 Ansys CFX Pre 中配置 RGP 表的分步指南

掌握在 Ansys CFX Pre 中設置 RGP 表的技巧,以優化仿真精度和效率。挑戰在計算流體動力學 (CFD) 領域,RGP(真實氣體屬性)表對于準確模擬流體在不同條件下的行為至關重要。這些表格提供了詳細的熱力學屬性&a…

C語言————原碼 補碼 反碼 (日漸清晰版)

本文的內容通下面這篇文章有著緊密的聯系,讀者可以選擇性閱讀 C語言————二、八、十、十六進制的相互轉換-CSDN博客 目錄 基本概念 原碼 反碼 補碼 轉換 數據的存儲方式 基本存儲單位 數據的計算方式 補碼的模運算原理 移位操作符 左移操作符 右移操…

函數-變量的作用域和生命周期

變量的作用域 引入問題 我們在函數設計的過程中,經常要考慮對于參數的設計,換句話說,我們需要考慮函數需要幾個參數,需要什么類型的參數,但我們并沒有考慮函數是否需要提供參數,如果說函數可以訪問到已定義…

Ansible在配置管理中的應用

Ansible是一個開源的配置管理和應用程序部署工具,它使用YAML語言編寫的Playbook來描述配置和應用部署過程。通過SSH協議與目標機器通信,Ansible可以實現批量操作,極大地提升了工作效率。核心功能Ansible的核心功能包括:配置管理&a…

【學習路線】Go語言云原生開發之路:從簡潔語法到微服務架構

一、Go語言基礎入門(1-2個月) (一)環境搭建與工具鏈Go環境安裝 官方安裝:從golang.org下載安裝包版本管理:g、gvm等Go版本管理工具環境變量:GOROOT、GOPATH、GOPROXY配置Go Modules:…

軟件工廠:推動新質生產力的組織躍遷

引言:軟件工廠的建設,不在于工具多,而在于理解深;不在于上線快,而在于體系穩。不僅是“看得見的流水線”,更是“看不見的組織變革”。在新質生產力的時代命題下,軟件工廠正成為連接創新與效率、…

9.0% 年增速驅動!全球自清潔滾輪拖布機器人市場2031年將邁向 946 百萬美元

自清潔滾輪拖布機器人是重要的智能清潔設備,采用滾筒式拖布結構,集掃拖功能,通過高速旋轉加壓擦洗地面,深度除污。其活水清潔系統可實時自清潔、回收污水,避免二次污染,提升清潔效率與效果,帶來…

新能源工廠的可視化碳中和實驗:碳足跡追蹤看板與能源調度策略仿真

摘要新能源工廠明明用著風電、光伏等清潔能源,碳排放數據卻依舊居高不下?某鋰電池廠耗費百萬升級設備,碳足跡卻難以精準追蹤,能源調度全靠經驗“拍腦袋”,導致成本飆升。而隔壁企業通過可視化碳中和實驗,碳…

數據結構自學Day13 -- 快速排序--“非遞歸利用棧實現”

一、快速排序回顧 快速排序本質上是**“分而治之”(Divide and Conquer)策略的遞歸應用。但遞歸其實就是函數棧的一種體現,因此我們也可以顯式使用棧(stack)來模擬遞歸過程**,從而實現非遞歸版本的快速排序…