Rust中的引用與借用舉例詳解
1、引用與借用
在之前我們將String 類型的值返回給調(diào)用函數(shù),這樣會(huì)導(dǎo)致這個(gè)String會(huì)被移動(dòng)到函數(shù)中,這樣在原來(lái)的作用域不可訪問(wèn)了,但是我們功能一個(gè)String值得引用,這樣就不會(huì)導(dǎo)致這個(gè)String類型的值被移動(dòng),而傳遞的只是一個(gè)引用。引用更像一個(gè)指針,因?yàn)槭且粋€(gè)地址,我們就可以基于這個(gè)地址找到改地址上存儲(chǔ)的數(shù)據(jù)。 與指針不同,引用確保指向某個(gè)特定類型的有效值。
下面是一個(gè)引用傳遞的示例:
fn main() {
let str = String::from("hello world!");
let len = _length(&str);
println!("str is value: {}", str);
println!("str length is: {}", len)
}
fn _length(s: &String) ->usize {
s.len()
}運(yùn)行結(jié)果所示所示:

根據(jù)以上代碼可以看出_length方法中傳遞的參數(shù)為&str,所以這里傳遞的是str值的引用,用&符號(hào)代表引用
以下是一張對(duì)應(yīng)的示意圖:

根據(jù)上圖也能看出s是s1的引用,引用的是s1在堆中對(duì)應(yīng)類型的值。
注意:與使用
&引用相反的操作是 解引用(dereferencing),它使用解引用運(yùn)算符,*。
變量 s 有效的作用域與函數(shù)參數(shù)的作用域一樣,不過(guò)當(dāng) s 停止使用時(shí)并不丟棄引用指向的數(shù)據(jù),因?yàn)?nbsp;s 并沒(méi)有所有權(quán)。當(dāng)函數(shù)使用引用而不是實(shí)際值作為參數(shù),無(wú)需返回值來(lái)交還所有權(quán),因?yàn)榫筒辉鴵碛兴袡?quán)。
我們將創(chuàng)建一個(gè)引用的行為稱為 借用(borrowing),因?yàn)槲覀儾](méi)有擁有它的所有權(quán),只是暫時(shí)借用以下。
我們可以嘗試修改一下引用,把引用值改了,看下是否可以,這就類似于我借了別人的東西,然后把東西換了個(gè)樣子,看看是不是可以呢?
fn main() {
let str = String::from("hello world!");
let len = _length(&str);
println!("str is value: {}", str);
}
fn _length(s: &String) {
s.push_str("我把你給改了..........");
}運(yùn)行一下,看下結(jié)果:

根據(jù)提示可以s是一個(gè)引用,因此它引用的數(shù)據(jù)不能作為可變數(shù)據(jù)借用。
1.1 可變引用
允許我們修改一個(gè)借用的值,這就是 可變引用,把上面的示例改一下,如下所示:
fn main() {
let mut str = String::from("hello world!");
_length(&mut str);
println!("str is value: {}", str);
}
fn _length(s: &mut String) {
s.push_str("我把你給改了..........");
}運(yùn)行代碼,再看一下結(jié)果:

首先定義str必須時(shí)可變的,在方法中傳遞參數(shù),也要指定引用為可變引用,因?yàn)橐弥赶虻氖潜灰玫牡刂?,所以就?huì)改變?cè)械闹怠?/p>
注意:可變引用有一個(gè)很大的限制:如果你有一個(gè)對(duì)該變量的可變引用,你就不能再創(chuàng)建對(duì)該變量的引用。
看以下示例:
fn main() {
let mut str = String::from("hello world!");
let a = &mut str;
let b = &mut str;
println!("a {}, b{}", a, b)
} 
根據(jù)錯(cuò)誤提示,可以知道同一時(shí)間不能多次借用str作為可變變量。Rust這樣限制是因?yàn)榭梢栽诰幾g時(shí)就避免數(shù)據(jù)競(jìng)爭(zhēng)。數(shù)據(jù)競(jìng)爭(zhēng)(data race)類似于競(jìng)態(tài)條件,它可由這三個(gè)行為造成:
- 兩個(gè)或更多指針同時(shí)訪問(wèn)同一數(shù)據(jù)。
- 至少有一個(gè)指針被用來(lái)寫(xiě)入數(shù)據(jù)。
- 沒(méi)有同步數(shù)據(jù)訪問(wèn)的機(jī)制。
再看下以下示例:
fn main() {
let mut str = String::from("hello world!");
let c = &mut str;
let b = &str;
println!("b{} {}", b, c)
} 
也會(huì)報(bào)錯(cuò),借用和可變借用不能同時(shí)被使用。
再看下一個(gè)示例:
fn main() {
let mut str = String::from("hello world!");
let c = &mut str;
println!("{}", c);
let b = &str;
println!("{}", b)
} 
可以看到這次是可以打印處結(jié)果的,在第一次打印的時(shí)候,變量的作用域也就結(jié)束了,因而在下次進(jìn)行賦值時(shí)可以的。
1.2 懸垂引用
在具有指針的語(yǔ)言中,很容易通過(guò)釋放內(nèi)存時(shí)保留指向它的指針而錯(cuò)誤地生成一個(gè) 懸垂指針(dangling pointer),所謂懸垂指針是其指向的內(nèi)存可能已經(jīng)被分配給其它持有者。相比之下,在 Rust 中編譯器確保引用永遠(yuǎn)也不會(huì)變成懸垂?fàn)顟B(tài):當(dāng)你擁有一些數(shù)據(jù)的引用,編譯器確保數(shù)據(jù)不會(huì)在其引用之前離開(kāi)作用域。
下面時(shí)一個(gè)懸垂應(yīng)用的示例:
fn main() {
dp();
}
fn dp() -> &String { // 返回字符串的引用
let str = String::from("hello world!"); // 創(chuàng)建一個(gè)字符串
&str // 返回字符串的引用
} // str 的作用域結(jié)束
// 方法返回的時(shí)字符串的引用,而字符串離開(kāi)作用與,被釋放,然在此返回該字符串的引用,
// 就會(huì)導(dǎo)致返回的結(jié)果不是預(yù)期的結(jié)果,在Rust中是不讓這樣操作的。直接運(yùn)行,會(huì)報(bào)如下錯(cuò)誤:

根據(jù)報(bào)錯(cuò)可知,此函數(shù)的返回類型包含借用值,但沒(méi)有可供借用的值,在返回類型引用處,提示: 錯(cuò)誤的聲明周期修飾符。
我們改以下返回字符串本身,看一下結(jié)果怎么樣?
fn main() {
println!("value is {}", dp())
}
fn dp() -> String {
let str = String::from("hello world!");
return str
} 運(yùn)行一下看看:

發(fā)現(xiàn)對(duì)應(yīng)的值給打印出來(lái),所有權(quán)交出去了,所以,可打印出對(duì)應(yīng)的值。
1.3 引用的規(guī)則
根據(jù)之前的結(jié)果,我們可以總結(jié)出以下兩點(diǎn):
- 在任意給定時(shí)間,要么 只能有一個(gè)可變引用,要么 只能有多個(gè)不可變引用。
- 引用必須總是有效的。
2、slice 類型
slice 允許你引用集合中一段連續(xù)的元素序列,而不用引用整個(gè)集合。slice 是一類引用,所以它沒(méi)有所有權(quán)。
以下有個(gè)slice示例:
fn main() {
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
println!("{}---{}", hello, world)
} 運(yùn)行以下,看下結(jié)果如何:

根據(jù)以上結(jié)果,可以知道hello變量從字符串(hello world)中進(jìn)行截取的,開(kāi)始的位置為0,長(zhǎng)度為5,所以打印的結(jié)果為hello,而world變量是從開(kāi)始索引6開(kāi)始,11結(jié)束,11-5=6,那么它的長(zhǎng)度也是5,所以打印的結(jié)果為world.
Slice 的主要結(jié)果包括2部分:
- 第一部分,是指針,指向數(shù)據(jù)開(kāi)始的位置
- 第二部分,是長(zhǎng)度,就是元素結(jié)束減去開(kāi)始位置的值
以下是一個(gè)示意圖,能夠更加清楚知道slice與字符串的關(guān)系:

其他寫(xiě)法,例如取前5個(gè)字符:
fn main() {
let s = String::from("hello world");
let hello = &s[0..5];
let hello1 = &s[..5]; // hello 和 hello1 是等價(jià)的
println!("{}---{}", hello, hello1)
} 例如取最后5個(gè)字符:
fn main() {
let s = String::from("hello world");
let world = &s[6..];
let world1 = &s[6..];
println!("{}---{}", world, world1)
} 取整個(gè)長(zhǎng)度的切片:
fn main() {
let s = String::from("hello world");
let world = &s[..];
let world1 = &s[..];
println!("{}---{}", world, world1)
} 注意:字符串 slice range 的索引必須位于有效的字符邊界內(nèi),如果嘗試從超過(guò)邊界訪問(wèn)超出索引范圍將導(dǎo)致panic錯(cuò)誤。
2.1 字符串字面量其實(shí)就是一個(gè)slice
一個(gè)示例如下所示:

這里 s1 的類型是 &str:world 的類型也是&str,所以s1它是一個(gè)指向二進(jìn)制程序特定位置的 slice。這也就是為什么字符串字面值是不可變的;&str 是一個(gè)不可變引用。
2.2 總結(jié)
所有權(quán)、借用和 slice 這些概念讓 Rust 程序在編譯時(shí)確保內(nèi)存安全。Rust 語(yǔ)言提供了跟其他系統(tǒng)編程語(yǔ)言相同的方式來(lái)控制你使用的內(nèi)存,但擁有數(shù)據(jù)所有者在離開(kāi)作用域后自動(dòng)清除其數(shù)據(jù)的功能意味著你無(wú)須額外編寫(xiě)和調(diào)試相關(guān)的控制代碼。
到此這篇關(guān)于Rust中引用與借用的文章就介紹到這了,更多相關(guān)Rust引用與借用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust中的Drop特性之解讀自動(dòng)化資源清理的魔法
Rust通過(guò)Drop特性實(shí)現(xiàn)了自動(dòng)清理機(jī)制,確保資源在對(duì)象超出作用域時(shí)自動(dòng)釋放,避免了手動(dòng)管理資源時(shí)可能出現(xiàn)的內(nèi)存泄漏或雙重釋放問(wèn)題,智能指針如Box、Rc和RefCell都依賴于Drop來(lái)管理資源,提供了靈活且安全的資源管理方案2025-02-02
Rust實(shí)現(xiàn)構(gòu)建器模式和如何使用Bon庫(kù)中的構(gòu)建器
這篇文章主要介紹了Rust實(shí)現(xiàn)構(gòu)建器模式和如何使用Bon庫(kù)中的構(gòu)建器,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-08-08
利用rust實(shí)現(xiàn)一個(gè)命令行工具
這篇文章主要為大家詳細(xì)介紹了如何使用?Rust?和?clap?4.4.0?創(chuàng)建一個(gè)命令行工具?my_dev_tool,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考下2023-12-12
MacBook Pro安裝rust編程環(huán)境的過(guò)程
rustup是一個(gè)用于管理Rust版本和工具鏈的工具,這篇文章主要介紹了MacBook Pro安裝rust編程環(huán)境的過(guò)程,感興趣的朋友跟隨小編一起看看吧2024-02-02

