Rust 1.95.0 釋出:cfg_select! 巨集、if-let 守衛、原子操作新 API
The Rust Blog · 2026-04-16
Rust 1.95.0 於 2026 年 4 月 16 日發布,帶來數項語言層面的改動與 API 穩定化。以下逐條說明本版本的技術細節。
cfg_select! 巨集
cfg_select! 是一個新的編譯期條件巨集,語意類似 match 陳述式,但匹配的是 cfg 謂詞(configuration predicates)。它將先前只能透過外部 crate cfg-if 實現的功能內建進標準函式庫,語法與 cfg-if 不同但功能相等。開發者現在可以用以下形式撰寫平台條件碼:
cfg_select! {
[target_os = "linux"] => { /* Linux 特定實作 */ }
[target_os = "windows"] => { /* Windows 特定實作 */ }
_ => { /* 其他平台預設值 */ }
}Match 運算式中的 if-let 守衛
延伸 1.88 版穩定的 let chains 功能,1.95.0 讓 match 模式的守衛(guard)可內嵌 if let:
match value {
Some(x) if let Ok(y) = compute(x) => { use(y) }
_ => {}
}需注意:if let 守衛中的模式綁定不參與 match 的窮盡性(exhaustiveness)檢查,這是語意上刻意的選擇,避免規則變得過於複雜。
穩定化的 API
記憶體與型別轉換:MaybeUninit、Cell 的陣列版 From、AsRef、AsMut 實作;bool: TryFrom<{integer}> 整數轉布林。
原子操作:AtomicPtr、AtomicBool、AtomicIsize、AtomicUsize 取得 update 與 try_update 方法,可在不寫全 compare-exchange loop 的情況下進行 fetch-modify-store 操作。
核心工具:core::range 模組引入 RangeInclusive 及迭代器支援;core::hint::cold_path 用於向最佳化器標記冷路徑;不安全指標方法 as_ref_unchecked、as_mut_unchecked 穩定化。
集合操作:Vec、VecDeque、LinkedList 的可變參考方法(push_mut、insert_mut 等)穩定化;Layout 新增 dangling_ptr、repeat、repeat_packed、extend_packed。
const 環境:fmt::from_fn、ControlFlow::is_break、ControlFlow::is_continue 現可在 const 環境中使用。
破壞性變更:JSON 目標規格去穩定化
自 1.95.0 起,透過 JSON 自訂目標規格(custom target specifications)的功能在穩定版 Rust 上不再支援,需使用 nightly 版本的 -Z build-std 特性才能為自訂目標建構標準函式庫。此舉影響嵌入式開發者與裸機目標的工具鏈設定,需要遷移至 nightly 工具鏈或等待官方支援。
Go 1.26 型別建構重構:循環偵測的演算法改進
The Go Blog · 2026-03-24
Go 1.26 的型別檢查器(type checker)進行了一項重大內部重構:以系統化的循環偵測(cycle detection)機制取代舊有複雜的型別建構流程。此改動對一般 Go 使用者不可見,但修復了多個編譯器崩潰問題(#75918、#76383、#76384、#76478 等)。
型別建構的基本模型
型別檢查器在處理每個型別時,會建立對應的內部表示(Defined struct),並追蹤兩種狀態:建構中(Under Construction)——尚未完整、欄位未填滿;完成(Complete)——所有欄位已填齊、可安全解構。型別以深度優先(depth-first)順序完成,依賴項先完成,再由依賴它的型別完成。
簡單遞迴型別如 type T []U; type U *T 可同時完成——因為 *T 只需知道 T 的「指標性」(pointerness),不需解構其完整內部結構。
循環問題:unsafe.Sizeof 案例
問題出現在:
type T [unsafe.Sizeof(T{})]int此處 T{} 是一個「上游(upstream)」——對未完成型別 T 的型別轉換。計算陣列長度需要 T 完成,但 T 的完成又需要陣列長度——形成死鎖。
上游偵測策略
舊方法在「下游運算子」偵測循環(複雜且難以窮舉);新方法改在「上游」——即產生不完整值的起點——進行偵測。上游包括:型別轉換 T()、回傳不完整型別的函式呼叫 f()、型別斷言 i.(T)、channel 接收 <-(make(<-chan T))、map 存取 make(map[int]T)[42]、*new(T)。而 new(T)(所有指標同尺寸)等操作不需解構,屬於安全操作,不納入偵測。
實作模式
func callExpr(call *syntax.CallExpr) operand {
x := typeOrValue(call.Fun)
switch x.mode() {
case typeExpr:
T := x.typ()
if !isComplete(T) {
reportCycleErr(T)
return invalid
}
// 安全解構 T
}
}此重構讓型別檢查器的循環偵測邏輯變得可枚舉、可測試,消除了多個邊緣案例下的編譯器恐慌(panic)。
Linux 最快時間戳:TSC、vDSO 與快取策略的效能剖析
hmpcabral.com · 2026-04-26
在延遲預算為 1–10 微秒的低延遲分散式追蹤管線中,初始以 std::chrono 實作的時間戳方案每次呼叫耗費約 47ns——幾乎消耗了整個預算。本文對 Linux 時間戳機制進行系統性剖析。
主要時脈來源
TSC(Timestamp Counter):64 位元計數器,在現代 CPU 上以不變速率遞增。Intel 文件定義「Invariant TSC 在所有 ACPI P-、C-、T-state 下以固定速率運行」。讀取方式為 lfence; rdtsc,約 25–32 個時脈週期。需要核心頻率估算才能準確轉換為奈秒。
vDSO(Virtual Dynamically Shared Object):核心將時間資料頁映射至使用者空間,避免 syscall 的 context switch 開銷。資料頁包含:當前時間估算值、TSC 計數值、用於轉換的乘數/位移量。以 seqlock 保護,防止核心更新時讀到不一致資料。
基準測試結果
| 實作方案 | 中位數 (ns) | 改善幅度 |
|---|---|---|
| 原始 3× clock_gettime() | 47.2 | 基準線 |
| TscTimer(週期估算) | 28.3 | 40% |
| VdsoTimer(核心頻率) | 20.5 | 57% |
| TscCacheTimer(快取) | 19.6 | 58% |
| VdsoCacheTimer(快取) | 20.0 | 58% |
尾延遲問題
標準 vDSO 方案在核心更新資料頁時會出現約 200ns 的尾延遲,原因是:seqlock 自旋等待更新完成,以及 L1/L2 快取失中(amortized 後每 100 次呼叫中約有 100× 的代價)。
解決策略
直接讀取 TSC:以 rdtsc() 取代單調時鐘呼叫,消除 vDSO 開銷。最小化換算:只計算 TSC 差值乘法,跳過奈秒正規化。繞過 vDSO:透過 ELF 輔助向量 AT_SYSINFO_EHDR 直接存取資料頁。快取策略:預快取乘數/位移值,僅以可接受頻率(≤HZ=1000)刷新,徹底消除尾延遲。
需注意:繞過 vDSO 需要核心版本特定的資料頁佈局知識(Linux 6.15 後有所改變)。推薦方案 TscCacheTimer 在 1μs 任務上的額外開銷僅 2%,相比初始方案的 4.3% 有顯著改善。