Rust工具: https://www.rust-lang.org/tools/install
1 cargo
cargo new my_test
開始於單元包的根節點:在編譯一個單元包時,編譯器會從單元包的根節點文件開始編譯(通常是庫單元包中的src/lib.rs,或二進制單元包中的src/main.rs)。
2 thread
在 Rust 中,handle.join().unwrap() 是用於等待線程完成並獲取其返回值的常見操作。
join() 方法返回一個 Result<T, Box<dyn Error>>,其中 T 是被等待線程的返回值類型。使用 unwrap() 是一種簡單的錯誤處理方式,它會:
- 如果結果是
Ok(t),則返回內部的值t - 如果結果是
Err(e),則會觸發 panic 並顯示錯誤信息
fn main() {
// 創建一個線程並獲取其句柄
let handle = thread::spawn(|| {
thread::sleep(Duration::from_secs(1));
"線程執行完成" // 線程的返回值
});
// 等待線程完成並獲取返回值
let result = handle.join().unwrap();
println!("{}", result); // 輸出: 線程執行完成
}
在 Rust 中,let _ = handle.join(); 是一種處理線程 JoinHandle 的方式,它的作用是:
- 調用
join()方法阻塞當前線程,等待被 spawn 的線程執行完成 - 使用
let _ =忽略join()返回的 Result 值
與handle.join().unwrap()不同,這種寫法會靜默忽略任何可能的錯誤,包括線程恐慌。
3 安裝
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
或者
wget --https-only --secure-protocol=TLSv1_2 -qO- https://sh.rustup.rs | sh
刷新環境變量
安裝完成後,需要讓終端識別新安裝的 rustup 命令,執行:
source $HOME/.cargo/env
3.1 問題
- 安裝rustup時報錯:
[22:35:07] root@ceph-221:/home/code/eza# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
info: downloading installer
warn: It looks like you have an existing installation of Rust at:
warn: /usr/bin
warn: It is recommended that rustup be the primary Rust installation.
warn: Otherwise you may have confusion unless you are careful with your PATH.
warn: If you are sure that you want both rustup and your already installed Rust
warn: then please reply `y' or `yes' or set RUSTUP_INIT_SKIP_PATH_CHECK to yes
warn: or pass `-y' to ignore all ignorable checks.
error: cannot install while Rust is installed
解決方法:
這個錯誤是因為系統中已經通過包管理器(如 apt、yum 等)安裝了 Rust,而 rustup 檢測到了現有安裝,為了避免環境衝突而終止了安裝。解決方法如下:
為了讓 rustup 成為主要的 Rust 工具鏈管理器,建議先卸載系統預裝的 Rust:
sudo dnf remove rust cargo
卸載完成後,重新運行 rustup 安裝腳本。
4 格式化輸出
在 Rust 中,println!("{:?}", other); 是一個用於打印變量 other 調試信息的宏調用,其中 {:?} 是格式化佔位符,對應 Debug 格式化輸出。
4.1.1 核心作用
{:?}要求被打印的類型實現了標準庫的std::fmt::Debugtrait,該 trait 用於提供類型的調試友好格式(通常包含詳細的內部結構)。- 與
{}(對應Displaytrait)不同,Debug輸出更偏向開發者調試,格式可能更冗長(例如包含字段名、引號等),且通常由編譯器自動派生(通過#[derive(Debug)]),無需手動實現。 {:?}的變體{:#?}會生成帶縮進的多行格式,適合複雜結構(如嵌套的結構體、長列表):
5 所有權
在Rust中,由Copy trait來區分值語義和引用語義。與此同時,Rust也引入了新的語義:複製(Copy)語義和移動(Move)語義。複製語義對應值語義,移動語義對應引用語義。
Rust的借用檢查器(borrow checker),借用檢查器會檢查所有數據訪問是否合法。借用檢查依賴於3個緊密關聯的概念:所有權、生命週期和借用。
- 所有權(ownership)是一個引申而來的比喻,在Rust中,所有權與針對不再需要的值的清理有關。
- 值的生命週期是一個時間段,在此時間段內對該值的訪問是有效的行為。
- 借用一個值意味着要訪問它。
所有權的特點:
- 控制資源(不僅僅是內存)的釋放
- 出借所有權,包括不可變(共享)和可變(獨佔)的
- 通過使用&操作完成所有權租借
- 在不可變借用期間,所有者不能修改資源,也不能在進行可變借用
- 在可變借用期間,所有者不能訪問資源,並且也不能再出借所有權
- 不可變借用可以出借多次,因為他不能修改內存數據;可變借用只能出借一次,否則難以預料數據何時何地被修改。
- 轉移所有權
生命週期參數的目的是幫助借用檢查器驗證合法的引用,消除懸垂指針
- 借用的生命週期不能長於出借方的生命週期
- 結構體實例的生命週期應短於或等於任意一個成員的生命週期
省略生命週期參數
- 每個輸入位置上省略的生命週期都將成為一個不同的生命週期參數
- 如果只有一個輸入生命週期的位置(不管是否忽略),則該生命週期都將分配給輸出生命週期
- 如果存在多個輸入生命週期的位置,但是其中包含着&self或&mut self,則self的生命週期都將分配給輸出生命週期
對於Box<T>類型來説,如果包含的類型T屬於複製語義,則執行按位複製;如果屬於移動語義,則移動所有權
6 類型
6.1 ?Sized
在 Rust 中,?Sized 是一個用於 trait bound 的特殊標記,用於表示“允許類型不實現 Sized trait”。要理解它,首先需要了解 Sized trait 本身:
6.1.1 Sized trait 是什麼?
Sized 是 Rust 中的一個自動實現的 trait,用於標記“在編譯時已知大小的類型”(例如 i32、String、自定義結構體等)。
- 對於這類類型,編譯器知道它們在內存中佔據的精確大小,因此可以直接在棧上分配,也能作為函數參數/返回值直接傳遞。
- 反之,動態大小類型(DST,Dynamically Sized Type) 則不實現
Sized,例如:- 切片
[T](長度未知,需通過&[T]等指針間接使用); - trait 對象(如
dyn Trait,具體類型大小未知); - 字符串字面量的底層類型
str(長度未知,需通過&str使用)。
- 切片
?Sized 的作用:放寬 Sized 限制
Rust 中,泛型默認隱含 Sized 約束。例如:
fn foo<T>(x: T) { ... }
// 等價於
fn foo<T: Sized>(x: T) { ... }
這意味着泛型 T 只能接受編譯時大小已知的類型(Sized 類型)。
而 ?Sized 的作用是取消這種默認約束,允許泛型接受“可能不實現 Sized 的類型”。例如:
fn bar<T: ?Sized>(x: &T) { ... }
這裏 T 可以是 Sized 類型(如 i32),也可以是動態大小類型(如 str、dyn Trait)。
6.1.2 使用場景
?Sized 通常用於需要處理動態大小類型的場景,常見情況:
-
接受 trait 對象:
trait 對象(dyn Trait)是 DST,因此泛型需要?Sized才能接受它:trait MyTrait { fn do_something(&self); } // 允許 T 為 dyn MyTrait(DST) fn call_trait<T: MyTrait + ?Sized>(x: &T) { x.do_something(); } // 使用:可以傳入任何實現 MyTrait 的類型的引用,或 trait 對象 let obj: &dyn MyTrait = &SomeType; call_trait(obj); // 合法 -
處理切片或字符串:
直接使用[T]或str時(通常通過引用):// 接受 str(DST)的引用 fn print_str<T: ?Sized>(s: &T) where T: AsRef<str> { println!("{}", s.as_ref()); } print_str("hello"); // 字符串字面量是 &str,底層是 str(DST) -
定義容納 DST 的類型:
例如自定義智能指針時,指向 DST:struct MyBox<T: ?Sized>(*const T); impl<T: ?Sized> MyBox<T> { fn new(x: &T) -> Self { MyBox(x as *const T) } }
6.1.3 注意點
?Sized僅用於泛型約束,不能直接修飾具體類型。- 由於 DST 無法在棧上直接存儲或作為值傳遞,使用
?Sized的泛型通常需要通過引用(&T) 或指針(如Box<T>、Rc<T>) 間接操作。 ?Sized是“允許不Sized”,而非“必須不Sized”,因此仍能接受Sized類型。
7 log
RUST_LOG 是 Rust 生態中用於控制 日誌輸出 的環境變量,主要配合 Rust 的日誌庫(如 log、tracing)使用,用於動態調整日誌的 級別、模塊範圍 和 輸出內容,無需修改代碼即可靈活控制程序的日誌行為。
log是Rust 生態中最基礎、應用最廣泛的 日誌抽象庫(crate),它本身不直接實現日誌的輸出功能,而是定義了一套統一的日誌接口(如日誌級別、宏定義),讓其他庫或應用可以基於這套接口實現日誌記錄,同時保證不同日誌實現之間的兼容性
- 提供統一的日誌接口:
定義了trace!、debug!、info!、warn!、error!等日誌宏,以及Log、Level、Metadata等核心 trait 和枚舉,讓開發者可以用一致的方式編寫日誌代碼,無需關心底層如何輸出(如打印到終端、寫入文件、發送到日誌服務器等)。 - 解耦日誌生產與消費:
庫開發者只需依賴log庫編寫日誌(如info!("初始化完成")),而應用開發者可以自由選擇日誌的實現方式(如env_logger、tracing、fern等),兩者通過log的接口對接,避免了庫與特定日誌實現的強耦合。 - 常用搭配的日誌實現庫
log庫本身不輸出日誌,必須配合具體的 “日誌實現庫” 才能生效,常見的有:env_logger:通過RUST_LOG環境變量控制日誌輸出,適合命令行工具和開發調試。tracing:更強大的日誌和追蹤庫,支持結構化日誌、跨度(span)追蹤,適合複雜應用和分佈式系統。fern:支持將日誌輸出到文件、終端等多種目標,可自定義格式和滾動策略。simple_logger:簡單輕量的實現,適合快速上手,無需複雜配置。
7.1 日誌級別(從低到高)
Rust 日誌庫定義了 5 個標準級別(級別越高,輸出日誌越少):
trace:最詳細的調試信息(如函數調用參數、循環步驟),通常用於開發階段細粒度調試。debug:調試信息(如關鍵流程節點、變量值),適合開發和測試環境。info:普通運行信息(如程序啓動、任務完成),生產環境常用。warn:警告信息(如不影響運行的異常情況,如“配置項缺失,使用默認值”)。error:錯誤信息(如功能失敗、資源不可用),必須關注的問題。
規則:設置某一級別後,會輸出該級別及所有更高級別的日誌。例如,RUST_LOG=info 會輸出 info、warn、error 級別的日誌。
7.2 基本設置方法
7.2.1 全局設置日誌級別
通過 RUST_LOG=<級別> 控制全局日誌輸出:
# 只輸出 error 及以上級別日誌(最簡潔)
RUST_LOG=error cargo run
# 輸出 info 及以上級別(info, warn, error)
RUST_LOG=info ./my_rust_program
# 輸出 debug 及以上級別(開發調試常用)
RUST_LOG=debug cargo test
# 輸出所有級別(包括 trace,最詳細)
RUST_LOG=trace ./my_rust_program
7.2.2 限定模塊/ crate 的日誌範圍
通過 RUST_LOG=<模塊路徑>=<級別> 只輸出特定模塊的日誌,避免全局日誌冗餘:
# 只輸出 my_project 中 network 模塊的 debug 級別日誌
RUST_LOG=my_project::network=debug cargo run
# 輸出 tokio 庫的 info 日誌 + 自己代碼的 debug 日誌
RUST_LOG=tokio=info,my_project=debug ./my_program
# 禁用某個模塊的日誌(設置為 off)
RUST_LOG=my_project::legacy=off ./my_program
- 模塊路徑對應代碼中的
mod結構(如crate::utils::file)。 - 可以指定第三方 crate 的名稱(如
tokio、hyper),控制其日誌輸出。
7.2.3 組合設置(多模塊 + 不同級別)
用逗號分隔多個規則,實現精細化控制:
# 全局 info 級別,但 network 模塊用 debug,tokio 庫用 warn
RUST_LOG=info,my_project::network=debug,tokio=warn ./my_program
7.2.4 在代碼中讀取 RUST_LOG
需配合日誌庫(如 log + env_logger)在程序中初始化日誌系統,才能讓 RUST_LOG 生效。示例:
-
在
Cargo.toml中添加依賴:[dependencies] log = "0.4" # 日誌基礎庫 env_logger = "0.9" # 解析 RUST_LOG 的庫 -
在代碼中初始化:
use log::{info, debug, error}; fn main() { // 初始化日誌系統,讀取 RUST_LOG 環境變量 env_logger::init(); info!("程序啓動"); debug!("配置文件路徑: ./config.toml"); // 僅 RUST_LOG>=debug 時輸出 error!("數據庫連接失敗"); }