集合(Collection)是數(shù)據(jù)結(jié)構(gòu)中最普遍的數(shù)據(jù)存放形式,Rust 標(biāo)準(zhǔn)庫(kù)中提供了豐富的集合類型幫助開發(fā)者處理數(shù)據(jù)結(jié)構(gòu)的操作。
向量(Vector)是一個(gè)存放多值的單數(shù)據(jù)結(jié)構(gòu),該結(jié)構(gòu)將相同類型的值線性的存放在內(nèi)存中。
向量是線性表,在 Rust 中的表示是 Vec<T>。
向量的使用方式類似于列表(List),我們可以通過這種方式創(chuàng)建指定類型的向量:
let vector: Vec<i32> = Vec::new(); // 創(chuàng)建類型為 i32 的空向量 let vector = vec![1, 2, 4, 8]; // 通過數(shù)組創(chuàng)建向量
我們使用線性表常常會(huì)用到追加的操作,但是追加和棧的 push 操作本質(zhì)是一樣的,所以向量只有 push 方法來追加單個(gè)元素:
fn main() {
let mut vector = vec![1, 2, 4, 8];
vector.push(16);
vector.push(32);
vector.push(64);
println!("{:?}", vector);
}運(yùn)行結(jié)果:
[1, 2, 4, 8, 16, 32, 64]
append 方法用于將一個(gè)向量拼接到另一個(gè)向量的尾部:
fn main() {
let mut v1: Vec<i32> = vec![1, 2, 4, 8];
let mut v2: Vec<i32> = vec![16, 32, 64];
v1.append(&mut v2);
println!("{:?}", v1);
}運(yùn)行結(jié)果:
[1, 2, 4, 8, 16, 32, 64]
get 方法用于取出向量中的值:
fn main() {
let mut v = vec![1, 2, 4, 8];
println!("{}", match v.get(0) {
Some(value) => value.to_string(),
None => "None".to_string()
});
}運(yùn)行結(jié)果:
1
因?yàn)橄蛄康拈L(zhǎng)度無法從邏輯上推斷,get 方法無法保證一定取到值,所以 get 方法的返回值是 Option 枚舉類,有可能為空。
這是一種安全的取值方法,但是書寫起來有些麻煩。如果你能夠保證取值的下標(biāo)不會(huì)超出向量下標(biāo)取值范圍,你也可以使用數(shù)組取值語法:
fn main() {
let v = vec![1, 2, 4, 8];
println!("{}", v[1]);
}運(yùn)行結(jié)果:
2
但如果我們嘗試獲取 v[4] ,那么向量會(huì)返回錯(cuò)誤。
遍歷向量:
fn main() {
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
}運(yùn)行結(jié)果:
100 32 57
如果遍歷過程中需要更改變量的值:
fn main() {
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
}字符串類(String)到本章為止已經(jīng)使用了很多,所以有很多的方法已經(jīng)被讀者熟知。本章主要介紹字符串的方法和 UTF-8 性質(zhì)。
新建字符串:
let string = String::new();
基礎(chǔ)類型轉(zhuǎn)換成字符串:
let one = 1.to_string(); // 整數(shù)到字符串 let float = 1.3.to_string(); // 浮點(diǎn)數(shù)到字符串 let slice = "slice".to_string(); // 字符串切片到字符串
包含 UTF-8 字符的字符串:
let hello = String::from("?????? ?????");
let hello = String::from("Dobry den");
let hello = String::from("Hello");
let hello = String::from("???????");
let hello = String::from("??????");
let hello = String::from("こんにちは");
let hello = String::from("?????");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");字符串追加:
let mut s = String::from("run");
s.push_str("oob"); // 追加字符串切片
s.push('!'); // 追加字符用 + 號(hào)拼接字符串:
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2;這個(gè)語法也可以包含字符串切片:
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;使用 format! 宏:
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);字符串長(zhǎng)度:
let s = "hello"; let len = s.len();
這里 len 的值是 5。
let s = "你好"; let len = s.len();
這里 len 的值是 6。因?yàn)橹形氖?UTF-8 編碼的,每個(gè)字符長(zhǎng) 3 字節(jié),所以長(zhǎng)度為6。但是 Rust 中支持 UTF-8 字符對(duì)象,所以如果想統(tǒng)計(jì)字符數(shù)量可以先取字符串為字符集合:
let s = "hello你好"; let len = s.chars().count();
這里 len 的值是 7,因?yàn)橐还灿?7 個(gè)字符。統(tǒng)計(jì)字符的速度比統(tǒng)計(jì)數(shù)據(jù)長(zhǎng)度的速度慢得多。
遍歷字符串:
fn main() {
let s = String::from("hello中文");
for c in s.chars() {
println!("{}", c);
}
}運(yùn)行結(jié)果:
h e l l o 中 文
從字符串中取單個(gè)字符:
fn main() {
let s = String::from("EN中文");
let a = s.chars().nth(2);
println!("{:?}", a);
}運(yùn)行結(jié)果:
Some('中')注意:nth 函數(shù)是從迭代器中取出某值的方法,請(qǐng)不要在遍歷中這樣使用!因?yàn)?UTF-8 每個(gè)字符的長(zhǎng)度不一定相等!
如果想截取字符串字串:
fn main() {
let s = String::from("EN中文");
let sub = &s[0..2];
println!("{}", sub);
}運(yùn)行結(jié)果:
EN
但是請(qǐng)注意此用法有可能肢解一個(gè) UTF-8 字符!那樣會(huì)報(bào)錯(cuò):
fn main() {
let s = String::from("EN中文");
let sub = &s[0..3];
println!("{}", sub);
}運(yùn)行結(jié)果:
thread 'main' panicked at 'byte index 3 is not a char boundary; it is inside '中' (bytes 2..5) of `EN中文`', src\libcore\str\mod.rs:2069:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
映射表(Map)在其他語言中廣泛存在。其中應(yīng)用最普遍的就是鍵值散列映射表(Hash Map)。
新建一個(gè)散列值映射表:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("color", "red");
map.insert("size", "10 m^2");
println!("{}", map.get("color").unwrap());
}注意:這里沒有聲明散列表的泛型,是因?yàn)?Rust 的自動(dòng)判斷類型機(jī)制。
運(yùn)行結(jié)果:
red
insert 方法和 get 方法是映射表最常用的兩個(gè)方法。
映射表支持迭代器:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("color", "red");
map.insert("size", "10 m^2");
for p in map.iter() {
println!("{:?}", p);
}
}運(yùn)行結(jié)果:
("color", "red")
("size", "10 m^2")迭代元素是表示鍵值對(duì)的元組。
Rust 的映射表是十分方便的數(shù)據(jù)結(jié)構(gòu),當(dāng)使用 insert 方法添加新的鍵值對(duì)的時(shí)候,如果已經(jīng)存在相同的鍵,會(huì)直接覆蓋對(duì)應(yīng)的值。如果你想"安全地插入",就是在確認(rèn)當(dāng)前不存在某個(gè)鍵時(shí)才執(zhí)行的插入動(dòng)作,可以這樣:
map.entry("color").or_insert("red");這句話的意思是如果沒有鍵為 "color" 的鍵值對(duì)就添加它并設(shè)定值為 "red",否則將跳過。
在已經(jīng)確定有某個(gè)鍵的情況下如果想直接修改對(duì)應(yīng)的值,有更快的辦法:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, "a");
if let Some(x) = map.get_mut(&1) {
*x = "b";
}
}