Rust 編程範式最佳實踐

面向對象的 Rust

與前面討論的函數式和命令式示例相反,讓我們引入一個新的結構體 FileFilter,它封裝了過濾文件和文件迭代的邏輯。

pub struct FileFilter {
    predicates: Vec<Box<Predicate>>,
    start: Option<PathBuf>,
    stack: Vec<fs::ReadDir>,
}

每個 FileFilter 對象都攜帶其狀態:用於過濾的謂詞集合、起始路徑和用於迭代的目錄棧。

Predicate 是這樣定義的:

type Predicate = dyn Fn(&Path) -> bool;

你可能會驚訝地發現這裏有一個 dyn。在 Rust 中,沒有兩個閉包具有相同的類型,即使它們完全相同!

爲了在 Vec 集合中適應這一點,我們使用帶有動態分派的 trait 對象。通過 “裝箱” 這些閉包,我們創建了一個 Box 類型 (本質上是 Box<dyn Fn(&Path) -> bool>),這允許我們在同一個 Vec 中存儲不同的 Predicate 閉包,儘管它們的類型不同。

在函數式編程中,我們利用迭代器和閉包的強大功能來過濾文件。在命令式風格中,我們直接使用循環和條件來操作向量。在面向對象風格中,結構體 FileFilter 抽象掉了這些細節。

看一下 add_filter 方法:

pub fn add_filter(mut self, predicate: impl Fn(&Path) -> bool + 'static) -> Self {
    self.predicates.push(Box::new(predicate));
    self
}

這允許我們通過鏈接調用輕鬆地添加多個過濾器:

let filter = FileFilter::new()
    .add_filter(|path| {
        // 檢查路徑是否以“foo”開頭
        path.file_name()
            .and_then(OsStr::to_str)
            .map(|name| name.starts_with("foo"))
            .unwrap_or(false)
    })  
    .add_filter(|path| path.extension() == Some(OsStr::new("xml")));

真正展示 Rust 中面向對象方法的是 FileFilter 的 Iterator trait 的實現:

impl Iterator for FileFilter {
    type Item = Result<PathBuf>;

    fn next(&mut self) -> Option<Self::Item> {
        todo!()
    }
}

這樣,FileFilter 就成爲了一個構建塊,它與 Rust 強大的迭代器生態系統完美地集成在一起,可以像其他任何迭代器一樣在所有相同的地方使用,這種設計允許將複雜的迭代邏輯封裝在對象中。

FileFilter 示例說明了 Rust 中的 OOP 如何進行可靠的封裝和模塊化,我們將內容 (謂詞) 與方式 (迭代和過濾邏輯) 分開。trait 系統輕鬆地將自定義迭代器與生態系統的其餘部分集成在一起,使用這些工具可以使代碼更易於組合和重用。

編程範式最佳實踐

在 Rust 中混合不同的編程風格不僅是可能的,而且是鼓勵的!從 Rust 對其語言設計的主要理念中也可以看出這一點。C、Haskell、OCaml 和 Erlang 等各種各樣的影響塑造了 Rust 的設計。

一開始,Rust 在本質上更偏向於函數式,但後來它演變成了一種更加平衡的語言,支持各種風格,問題是如何在不同的編程範例之間劃清界限。

以下是一些最佳實踐:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/q4gZ4emk6y0hqNXnwTW2ZQ