Rust中的智能指針Box
Box 允許将一個值放在堆上而不是棧上,留在棧上的則是指向堆資料的指針。Box 是一個指向堆的智能指針,當一個 Box 超出作用域時,它的析構函數被調用,内部對象被銷毀,堆上的記憶體被釋放。
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
Box 沒有運作上的性能損失,雖然如此,但它卻隻在以下場景中比起預設的棧上配置設定更适用:
- 當有一個在編譯時未知大小的類型,而又想要在需要确切大小的上下文中使用這個類型值的時候
- 當有大量資料并希望在確定資料不被拷貝的情況下轉移所有權的時候
- 當希望擁有一個值并隻關心它的類型是否實作了特定 trait 而不是其具體類型的時候
場景1代碼示例:
// 場景1:當有一個在編譯時未知大小的類型,而又想要在需要确切大小的上下文中使用這個類型值的時候
// 例如遞歸的類型,自己可以包含自己
// ConsList 每一項包含兩個元素:目前項和下一項,若為Nil則是結束項
// ConsList(0, ConsList(1, ConsList(2, Nil)))
enum List {
Cons(i32, Box<List>),
Nil,
}
fn example1() {
let list = List::Cons(0, Box::new(List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))))));
}
場景2代碼示例:
// 場景2:當有大量資料并希望在確定資料不被拷貝的情況下轉移所有權的時候
fn example2() {
let a = [0; 1024 * 512];
let a_box = Box::new(a);
// 這兩種寫法等價,都會先在棧上配置設定資料,然後再拷貝資料到堆上,不過後面再轉移所有權時不會再拷貝資料,隻會轉移位址
let a_box2 = Box::new([0; 1024 * 512]);
}
場景3代碼示例:
// 場景3:當希望擁有一個值并隻關心它的類型是否實作了特定 trait 而不是其具體類型的時候
fn example3() -> Result<(), Box<dyn std::error::Error>>{
let f = std::fs::read("/tmp/not_exist")?;
Ok(())
}
Rust中的引用計數Rc
你可以将 Rc 看作 Box 的進階版本:它是帶引用計數的智能指針。隻有當它的引用計數為 0 時,資料才會被清理。
考慮我們上一小節提到的 ConsList 場景,如果多個節點共享一個節點,例如:
0 -> 1 \
|-> 4
2 -> 3 /
節點 4 它所擁有的值會有多個所有者,這個時候就需要使用 Rc 來進行包裝。
代碼示例:
use std::rc::Rc;
enum List2 {
Cons(i32, Rc<List2>),
Nil,
}
fn rc_example() {
// 通過Rc可以實作一個值可以有多個所有者
let four = Rc::new(List2::Cons(4, Rc::new(List2::Nil)));
// 調用 clone 時,Rc的引用計數會加一
let zero_one = List2::Cons(0, Rc::new(List2::Cons(1, four.clone())));
// 另一種等價寫法
// let zero_one = List2::Cons(0, Rc::new(List2::Cons(1, Rc::clone(&four))));
let two_three = List2::Cons(2, Rc::new(List2::Cons(3, four)));
}
Rust中的Vector動态數組
Vector 是動态大小的數組。與切片一樣,它們的大小在編譯時是未知的,但它們可以随時增長或收縮,向量使用 3 個參數表示:
- 指向資料的指針
- 長度
- 容量
容量表示為向量預留了多少記憶體。 一旦長度大于容量,向量将申請更大的記憶體進行重新配置設定:
fn main() {
let mut v: Vec<i32> = Vec::new();
v.push(1);
println!("{:?}", v[0]);
// 通過宏建立Vector的寫法
let v2: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
println!("{:?}", v2[2]);
// 修改Vector内的元素值
for e in v.iter_mut() {
*e *= 2;
}
}
Rust中的HashMap
HashMap 是一種從 Key 映射到 Value 的資料結構。與 Vector 一樣,HashMap 也是可以動态調整大小的,可以使用以下方法建立一個 HashMap:
use std::collections::HashMap;
fn main() {
let map = HashMap::new();
map.insert("key", "value");
println!("{:?}", map.get(&"key"));
map.remove(&"key");
for (&k, &v) in map.iter() {
println!("{:?} {:?}", k, v);
}
}
大多數資料類型都可以作為 HashMap 的 Key,隻要它們實作了 Eq 和 Hash traits。
Rust 中的多種字元串String
Rust 裡表示“字元串”有多種方式,你可能已經見過
str
,
&str
與
String
,但它們之間有什麼差別?如果我想使用“字元串”類型,我應當如何使用?

fn main() {
let s = "Hello World!";
// "Hello World!" 是字元串字面量, 它直接存儲在二進制檔案中. 我們無法直接操作這些資料,
// 必須借由 &str, 即它的引用來與之互動. 由于 "Hello World!" 存在二進制檔案中, 是以它
// 具有靜态生命周期: 它在程式的整個運作期間都有效.
// 變量 s 的類型是 &str, 表明它是 str 的一個引用. 但 str 是什麼? str 是存儲在記憶體中的
// 字元串資料. 這裡的記憶體可以是棧, 可以是堆, 也可以是資料段(二進制檔案中). &str 是對 str
// 的引用.
let t = String::from(s);
// t 的類型是 String. String 存在堆中, 是一個可增長的緩沖區, 它擁有它的資料, 是以我們
// 可以修改 t 的内容.
}
- 你幾乎不會用到
類型str
- 如果你在定義一個函數,該函數接收字元串類型,那麼請使用
&str
- 如果你在定義一個結構體,結構體中包含一個字元串成員,那麼使用
是更好的選擇String
Rust中的系統時間SystemTime
use std::thread::sleep;
use std::time::{Duration, SystemTime};
fn main() {
// 擷取目前時間
let mut now = SystemTime::now();
println!("{:?}", now);
// 擷取 UNIX TIMESTAMP
let timestamp = now.duration_since(SystemTime::UNIX_EPOCH);
println!("{:?}", timestamp);
sleep(Duration::from_secs(4));
// 擷取流逝的時間
println!("{:?}", now.elapsed());
// 時刻的增減
now.checked_add(Duration::from_secs(60))
}