← SEARCHABLE ARCHIVE
Rust · 2026-08-12

Rust 從基礎到精通 — 第 10 講

透過安全邊界和測試清理 Rust 設計

INTERACTIVE READING MODE →

該部落格上撰寫的文章是出於教學目的而創建的。 另外,文章中的資訊可能不正確,請謹慎。

很好地使用 Rust 並不涉及所有生命週期和智慧指標的複雜使用。 它是為了保持所有權邊界、錯誤、並發操作和不安全區域較小,並使行為可測試。

確定國家的所有者

struct GameState {
    players: Vec<Player>,
    monsters: Vec<Monster>,
}

impl GameState {
    fn attack(&mut self, player_index: usize, monster_index: usize) -> bool {
        let Some(player) = self.players.get(player_index) else {
            return false;
        };
        let Some(monster) = self.monsters.get_mut(monster_index) else {
            return false;
        };

        monster.take_damage(player.attack);
        true
    }
}

GameState 擁有戰鬥狀態,而該方法僅借用其短暫需要的欄位。 減少全域可變狀態還可以簡化引用生命週期和並發性。

將外部函數放在特徵後面

trait SaveStore {
    fn save(&self, state: &GameState) -> Result<(), SaveError>;
    fn load(&self) -> Result<Option<GameState>, SaveError>;
}

fn save_game(store: &impl SaveStore, state: &GameState) -> Result<(), SaveError> {
    store.save(state)
}

檔案和網路實作細節不包含在規則程式碼中。 在測試中,您可以實現記憶體儲存以根據需要建立失敗和成功。

縮小不安全的界限

fn value_at(values: &[i32], index: usize) -> Option<i32> {
    values.get(index).copied()
}

如果可以用安全的 API 來解決,那就沒有理由使用 unsafe。 如果由於 FFI 或低階最佳化而需要不安全,則應將其隱藏在一個小函數中,並且應透過安全類型和檢查來保證呼叫者必須滿足的條件。

unsafe 不是一個關閉所有編譯器檢查的函式。 這表明開發人員將直接證明一些責任,例如指針解引用。

測試合約

#[test]
fn invalid_target_does_not_change_state() {
    let mut state = sample_state();
    let before = state.monsters[0].health;

    assert!(!state.attack(0, 99));
    assert_eq!(state.monsters[0].health, before);
}

測試檢查外部契約,例如在它們失敗時是否維持狀態,而不是內部實現。 Rust 的優點並不在於它絕對沒有錯誤,而是它在執行之前透過類型和編譯檢查減少了許多不正確的狀態。