目錄
1、使用 Vector 儲(chǔ)存列表
1.1?新建 vector
1.2?更新 vector
1.3?讀取 vector 的元素
1.4?遍歷 vector 中的元素
1.5?使用枚舉來(lái)儲(chǔ)存多種類型
1.6?丟棄 vector 時(shí)也會(huì)丟棄其所有元素
2、使用字符串儲(chǔ)存 UTF-8 編碼的文本
2.1?什么是字符串?
2.2?新建字符串
2.3?更新字符串
2.3.1?使用 push_str 和 push 附加字符串
2.3.2?使用 + 運(yùn)算符或 format! 宏拼接字符串
2.3.3?索引字符串
2.3.4?字符串 slice
2.3.5?遍歷字符串的方法
2.3.6?字符串并不簡(jiǎn)單
Rust 標(biāo)準(zhǔn)庫(kù)中包含一系列被稱為?集合(collections)的非常有用的數(shù)據(jù)結(jié)構(gòu)。大部分其他數(shù)據(jù)類型都代表一個(gè)特定的值,不過(guò)集合可以包含多個(gè)值。不同于內(nèi)建的數(shù)組和元組類型,這些集合指向的數(shù)據(jù)是儲(chǔ)存在堆上的,這意味著數(shù)據(jù)的數(shù)量不必在編譯時(shí)就已知,并且還可以隨著程序的運(yùn)行增長(zhǎng)或縮小。每種集合都有著不同功能和成本,而根據(jù)當(dāng)前情況選擇合適的集合,以下三種是在 Rust 程序中被廣泛使用的集合;
- vector?允許我們一個(gè)挨著一個(gè)地儲(chǔ)存一系列數(shù)量可變的值
-
字符串(string)是字符的集合。我們之前見過(guò)?
String
?類型,不過(guò)在本章我們將深入了解。 - 哈希 map(hash map)允許我們將值與一個(gè)特定的鍵(key)相關(guān)聯(lián)。這是一個(gè)叫做?map?的更通用的數(shù)據(jù)結(jié)構(gòu)的特定實(shí)現(xiàn)。
1、使用 Vector 儲(chǔ)存列表
我們要講到的第一個(gè)類型是?Vec<T>
,也被稱為?vector。vector 允許我們?cè)谝粋€(gè)單獨(dú)的數(shù)據(jù)結(jié)構(gòu)中儲(chǔ)存多于一個(gè)的值,它在內(nèi)存中彼此相鄰地排列所有的值。vector 只能儲(chǔ)存相同類型的值。
1.1?新建 vector
為了創(chuàng)建一個(gè)新的空 vector,可以調(diào)用?Vec::new
?函數(shù),如下所示:
let v: Vec<i32> = Vec::new();
這里,定義了一個(gè)變量v,是vector類型,類型后面使用一對(duì)尖括號(hào)來(lái)表示Vector類型的泛型,在這里指定的是i32類型,所以變量v存儲(chǔ)的是一組i32組成的集合。
通常,我們會(huì)用初始值來(lái)創(chuàng)建一個(gè)?Vec<T>
?而 Rust 會(huì)推斷出儲(chǔ)存值的類型,所以很少會(huì)需要這些類型注解。為了方便 Rust 提供了?vec!
?宏,這個(gè)宏會(huì)根據(jù)我們提供的值來(lái)創(chuàng)建一個(gè)新的 vector。
fn main() {
let v = vec![1, 2, 3, 4, 5, 6];
println!("{:?}", v.len()) // 6
}
因?yàn)槲覀兲峁┝?i32
?類型的初始值,Rust 可以推斷出?v
?的類型是?Vec<i32>
,因此類型聲明就不是必須的。
1.2?更新 vector
對(duì)于新建一個(gè) vector 并向其增加元素,可以使用?push
?方法
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
println!("{:?}", v.len()) // 3
}
1.3?讀取 vector 的元素
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
let first = &v[0];
println!("{:?}", first); // 3
println!("{:?}", &v[100]); // index out of bounds:
println!("{:?}", v.get(1)); // Some(2)
}
這里有幾個(gè)細(xì)節(jié)需要注意。我們使用索引值?2
?來(lái)獲取第三個(gè)元素,因?yàn)樗饕菑臄?shù)字 0 開始的。使用?&
?和?[]
?會(huì)得到一個(gè)索引位置元素的引用。當(dāng)使用索引作為參數(shù)調(diào)用?get
?方法時(shí),會(huì)得到一個(gè)可以用于?match
?的?Option<&T>
。
當(dāng)我們獲取了 vector 的第一個(gè)元素的不可變引用并嘗試在 vector 末尾增加一個(gè)元素的時(shí)候,如果嘗試在函數(shù)的后面引用這個(gè)元素是行不通的:
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
let first = &v[0];
v.push(100); // 報(bào)錯(cuò)......
println!("{:?}", first); // 3
println!("{:?}", &v[100]); // index out of bounds:
println!("{:?}", v.get(1)); // Some(2)
}
會(huì)拋出如下錯(cuò)誤:
所以在 vector 的結(jié)尾增加新元素時(shí),在沒(méi)有足夠空間將所有元素依次相鄰存放的情況下,可能會(huì)要求分配新內(nèi)存并將老的元素拷貝到新的空間中。這時(shí),第一個(gè)元素的引用就指向了被釋放的內(nèi)存。借用規(guī)則阻止程序陷入這種狀況。
1.4?遍歷 vector 中的元素
如果想要依次訪問(wèn) vector 中的每一個(gè)元素,我們可以遍歷其所有的元素而無(wú)需通過(guò)索引一次一個(gè)的訪問(wèn)。如下所示:
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
for i in &v {
println!("{}", i)
}
}
我們也可以遍歷可變 vector 的每一個(gè)元素的可變引用以便能改變它們。如下所示:
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
for i in &mut v {
*i *= 10;
}
println!("{:?}", v) // [10, 20, 30]
}
修改可變引用所指向的值,在使用 *=
?運(yùn)算符之前必須使用解引用運(yùn)算符(*
)獲取?i
?中的值。
1.5?使用枚舉來(lái)儲(chǔ)存多種類型
vector 只能儲(chǔ)存相同類型的值。這是很不方便的;絕對(duì)會(huì)有需要儲(chǔ)存一系列不同類型的值的用例。幸運(yùn)的是,枚舉的成員都被定義為相同的枚舉類型,所以當(dāng)需要在 vector 中儲(chǔ)存不同類型值時(shí),我們可以定義并使用一個(gè)枚舉!看一下下面這個(gè)示例:
fn main() {
#[derive(Debug)]
enum Value {
Int(i32),
Float(f32),
Text(String),
Valid(bool),
}
let mut v = Vec::new();
v.push(Value::Int(1));
v.push(Value::Float(1.1));
v.push(Value::Text(String::from("wangwu")));
v.push(Value::Valid(true));
println!("{:?}", v) // [Int(1), Float(1.1), Text("wangwu"), Valid(true)]
}
Rust 在編譯時(shí)就必須準(zhǔn)確的知道 vector 中類型的原因在于它需要知道儲(chǔ)存每個(gè)元素到底需要多少內(nèi)存。第二個(gè)好處是可以準(zhǔn)確的知道這個(gè) vector 中允許什么類型。如果 Rust 允許 vector 存放任意類型,那么當(dāng)對(duì) vector 元素執(zhí)行操作時(shí)一個(gè)或多個(gè)類型的值就有可能會(huì)造成錯(cuò)誤。
1.6?丟棄 vector 時(shí)也會(huì)丟棄其所有元素
類似于任何其他的?struct
,vector 在其離開作用域時(shí)會(huì)被釋放,如下所示:
{
let v = vec![1, 2, 3, 4];
// todo
} // 超出作用域,v被丟棄,對(duì)應(yīng)的元素值也會(huì)被丟棄
2、使用字符串儲(chǔ)存 UTF-8 編碼的文本
2.1?什么是字符串?
在開始深入這些方面之前,我們需要討論一下術(shù)語(yǔ)?字符串?的具體意義。Rust 的核心語(yǔ)言中只有一種字符串類型:字符串 slice?str
,它通常以被借用的形式出現(xiàn),&str
。第四章講到了?字符串 slices:它們是一些對(duì)儲(chǔ)存在別處的 UTF-8 編碼字符串?dāng)?shù)據(jù)的引用。舉例來(lái)說(shuō),由于字符串字面值被儲(chǔ)存在程序的二進(jìn)制輸出中,因此字符串字面值也是字符串 slices。
字符串(String
)類型由 Rust 標(biāo)準(zhǔn)庫(kù)提供,而不是編入核心語(yǔ)言,它是一種可增長(zhǎng)、可變、可擁有、UTF-8 編碼的字符串類型。當(dāng) Rustaceans 提及 Rust 中的 "字符串 "時(shí),他們可能指的是?String
?或 string slice?&str
?類型,而不僅僅是其中一種類型。雖然本節(jié)主要討論?String
,但這兩種類型在 Rust 的標(biāo)準(zhǔn)庫(kù)中都有大量使用,而且?String
?和 字符串 slices 都是 UTF-8 編碼的。
2.2?新建字符串
很多?Vec
?可用的操作在?String
?中同樣可用,事實(shí)上?String
?被實(shí)現(xiàn)為一個(gè)帶有一些額外保證、限制和功能的字節(jié) vector 的封裝。其中一個(gè)同樣作用于?Vec<T>
?和?String
?函數(shù)的例子是用來(lái)新建一個(gè)實(shí)例的?new
?函數(shù),如下所示:
let mut s = String::new();
上面創(chuàng)建了一個(gè)叫做?s
?的空的字符串,接著我們可以向其中裝載數(shù)據(jù)。通常字符串會(huì)有初始數(shù)據(jù),因?yàn)槲覀兿M婚_始就有這個(gè)字符串。為此,可以使用?to_string
?方法,它能用于任何實(shí)現(xiàn)了?Display
?trait 的類型,比如字符串字面值。如下所示:
fn main() {
let data = "test string";
let s = data.to_string();
// 該方法也可直接用于字符串字面值:
let s = "test string".to_string();
println!("{:?}", s) // test string
}
也可以使用?String::from
?函數(shù)來(lái)從字符串字面值創(chuàng)建?String
。如下所示:
let data = String::from("test string");
2.3?更新字符串
String
?的大小可以增加,其內(nèi)容也可以改變,就像可以放入更多數(shù)據(jù)來(lái)改變?Vec
?的內(nèi)容一樣。另外,可以方便的使用?+
?運(yùn)算符或?format!
?宏來(lái)拼接?String
?值。
2.3.1?使用 push_str 和 push 附加字符串
可以通過(guò)?push_str
?方法來(lái)附加字符串 slice,從而使?String
?變長(zhǎng),如下所示:
fn main() {
let mut s = String::from("hello ");
s.push_str("world");
println!("{}", s) // hello world
}
push
?方法被定義為獲取一個(gè)單獨(dú)的字符作為參數(shù),并附加到?String
?中。如下所示:
fn main() {
let mut s = String::from("hell");
s.push('o');
println!("{s}");
}
2.3.2?使用 + 運(yùn)算符或 format! 宏拼接字符串
通常你會(huì)希望將兩個(gè)已知的字符串合并在一起。一種辦法是像這樣使用?+
?運(yùn)算符,如下所示:
fn main() {
let h = String::from("hello ");
let w = String::from("world");
let res = h + &w;
println!("{res}"); // hello world
}
執(zhí)行完代碼之后,h在相加之后不再有效,+運(yùn)算符調(diào)用時(shí)跟函數(shù)簽名有關(guān),+運(yùn)算符使用了add函數(shù),這個(gè)函數(shù)看起來(lái)像這樣:
fn add(mut self, other: &str) -> String {
self.push_str(other);
self
}
首先,w?使用了?&
,意味著我們使用第二個(gè)字符串的?引用?與第一個(gè)字符串相加。這是因?yàn)?add
?函數(shù)的?s
?參數(shù):只能將?&str
?和?String
?相加,不能將兩個(gè)?String
?值相加。不過(guò)等一下 ——?&w
的類型是?&String
, 而不是?add
?第二個(gè)參數(shù)所指定的?&str
。那么為什么還能編譯呢?
之所以能夠在?add
?調(diào)用中使用?&w
?是因?yàn)?&String
?可以被?強(qiáng)轉(zhuǎn)(coerced)成?&str
。當(dāng)add
函數(shù)被調(diào)用時(shí),Rust 使用了一個(gè)被稱為?Deref 強(qiáng)制轉(zhuǎn)換(deref coercion)的技術(shù),你可以將其理解為它把?&w
?變成了?&w[..]
。因?yàn)?add
?沒(méi)有獲取參數(shù)的所有權(quán),所以 w?在這個(gè)操作后仍然是有效的?String
。
其次,可以發(fā)現(xiàn)簽名中?add
?獲取了?self
?的所有權(quán),因?yàn)?self
?沒(méi)有?使用?&
。這意味著示例 8-18 中的 h?的所有權(quán)將被移動(dòng)到?add
?調(diào)用中,之后就不再有效。所以雖然?let res?= h?+ &w;
?看起來(lái)就像它會(huì)復(fù)制兩個(gè)字符串并創(chuàng)建一個(gè)新的字符串,而實(shí)際上這個(gè)語(yǔ)句會(huì)獲取 h?的所有權(quán),附加上從 w?中拷貝的內(nèi)容,并返回結(jié)果的所有權(quán)。換句話說(shuō),它看起來(lái)好像生成了很多拷貝,不過(guò)實(shí)際上并沒(méi)有:這個(gè)實(shí)現(xiàn)比拷貝要更高效。
如果想要級(jí)聯(lián)多個(gè)字符串,+
?的行為就顯得笨重了:
fn main() {
let h = String::from("hello ");
let w = String::from("world");
let t = String::from(", 123");
let res = h + &w + &t;
println!("{res}"); // hello world, 123
}
對(duì)于更為復(fù)雜的字符串鏈接,我們可以使用?format!
?宏:
fn main() {
let h = String::from("hello ");
let w = String::from("world");
let t = String::from(", 123");
println!("{h}{w}{t}"); // hello world, 123
}
2.3.3?索引字符串
在很多語(yǔ)言中,通過(guò)索引來(lái)引用字符串中的單獨(dú)字符是有效且常見的操作。然而在 Rust 中,如果你嘗試使用索引語(yǔ)法訪問(wèn)?String
?的一部分,會(huì)出現(xiàn)一個(gè)錯(cuò)誤。
fn main() {
let h = String::from("hello ");
let res = h[0];
}
我們可以看下字符串的實(shí)現(xiàn)。
pub struct String {
vec: Vec<u8>,
}
所以在獲取索引的時(shí)候,并不會(huì)返回我們所期望的第一個(gè)字母,而在Rust在編譯過(guò)程就會(huì)阻止,并報(bào)錯(cuò)。
2.3.4?字符串 slice
字符串索引應(yīng)該返回的類型是不明確的:字節(jié)值、字符、字形簇或者字符串 slice。因此,如果你真的希望使用索引創(chuàng)建字符串 slice 時(shí),Rust 會(huì)要求你更明確一些。為了更明確索引并表明你需要一個(gè)字符串 slice,相比使用?[]
?和單個(gè)值的索引,可以使用?[]
?和一個(gè) range 來(lái)創(chuàng)建含特定字節(jié)的字符串 slice:
fn main() {
let h = "Здравствуйте";
let res = &h[0..4];
println!("{res}") // Зд
}
如果獲取?&h[0..1]
?會(huì)發(fā)生什么呢?答案是:Rust 在運(yùn)行時(shí)會(huì) panic,就跟訪問(wèn) vector 中的無(wú)效索引時(shí)一樣:
2.3.5?遍歷字符串的方法
操作字符串每一部分的最好的方法是明確表示需要字符還是字節(jié)。對(duì)于單獨(dú)的 Unicode 標(biāo)量值使用?chars
?方法。對(duì) “Зд” 調(diào)用?chars
?方法會(huì)將其分開并返回兩個(gè)?char
?類型的值,接著就可以遍歷其結(jié)果來(lái)訪問(wèn)每一個(gè)元素了:
fn main() {
let str = "Здравствуйте";
for i in str.chars() {
println!("{i}") // З д р ...
}
}
另外?bytes
?方法返回每一個(gè)原始字節(jié),這可能會(huì)適合你的使用場(chǎng)景:
fn main() {
let str = "Здравствуйте";
for i in str.bytes() {
println!("{i}") // 208 151 ...
}
}
2.3.6?字符串并不簡(jiǎn)單
總而言之,字符串還是很復(fù)雜的。不同的語(yǔ)言選擇了不同的向程序員展示其復(fù)雜性的方式。Rust 選擇了以準(zhǔn)確的方式處理?String
?數(shù)據(jù)作為所有 Rust 程序的默認(rèn)行為,這意味著程序員們必須更多的思考如何預(yù)先處理 UTF-8 數(shù)據(jù)。這種權(quán)衡取舍相比其他語(yǔ)言更多的暴露出了字符串的復(fù)雜性,不過(guò)也使你在開發(fā)周期后期免于處理涉及非 ASCII 字符的錯(cuò)誤。
好消息是標(biāo)準(zhǔn)庫(kù)提供了很多圍繞?String
?和?&str
?構(gòu)建的功能,來(lái)幫助我們正確處理這些復(fù)雜場(chǎng)景。請(qǐng)務(wù)必查看這些使用方法的文檔,例如?contains
?來(lái)搜索一個(gè)字符串,和?replace
?將字符串的一部分替換為另一個(gè)字符串。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-779868.html
稱作?String
?的類型是由標(biāo)準(zhǔn)庫(kù)提供的,而沒(méi)有寫進(jìn)核心語(yǔ)言部分,它是可增長(zhǎng)的、可變的、有所有權(quán)的、UTF-8 編碼的字符串類型。當(dāng) Rustacean 們談到 Rust 的 “字符串”時(shí),它們通常指的是?String
?或字符串 slice?&str
?類型,而不特指其中某一個(gè)。雖然本部分內(nèi)容大多是關(guān)于?String
?的,不過(guò)這兩個(gè)類型在 Rust 標(biāo)準(zhǔn)庫(kù)中都被廣泛使用,String
?和字符串 slices 都是 UTF-8 編碼的。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-779868.html
到了這里,關(guān)于Rust 常用集合(上)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!