簡體   English   中英

我可以在 Rust 中使用處於全局狀態的迭代器嗎?

[英]Can I use an iterator in global state in Rust?

我想在 Rust 中使用迭代器作為全局狀態。 簡化示例:

static nums = (0..).filter(|&n|n%2==0);

這可能嗎?

否。出於多種原因:

  1. 迭代器類型往往很復雜。 這通常不是問題,因為迭代器類型很少必須命名,但static必須顯式輸入。 在這種情況下,類型仍然相對簡單: core::iter::Filter<core::ops::RangeFrom<i32>, fn(&i32) -> bool>
  2. Iterator的 main 方法next需要一個&mut self參數。 默認情況下static不能是可變的,因為這不安全。
  3. 迭代器只能迭代一次。 因此,首先擁有一個全局迭代器毫無意義。
  4. 初始化static的值必須是常量表達式。 您的初始化程序不是常量表達式。

你可以做到,但你必須在此過程中與語言作斗爭。

首先,使用static聲明創建的真正 Rust static需要是編譯時常量。 所以像static FOO: usize = 10這樣的東西會編譯,但static BAR: String = "foo".to_string()不會,因為BAR需要運行時分配。 雖然您的迭代器不需要運行時分配(盡管使用它會使您的生活更簡單,正如您稍后將看到的),但它的類型足夠復雜,以至於它不支持編譯時初始化。

其次,Rust 靜態是只讀的。 這是因為 Rust 是多線程的,並且允許寫入靜態數據將構成數據競爭。 迭代器的next()方法采用&mut self ,這是有道理的,因為它必須更新底層計數器。 要推進您的全局迭代器,它必須包含在Mutex

有了這些,我們可以看看實現:

use lazy_static::lazy_static;
use std::sync::Mutex;

lazy_static! {
    static ref NUMS: Mutex<Box<dyn Iterator<Item = u32> + Send + Sync>> =
        Mutex::new(Box::new((0..).filter(|&n| n % 2 == 0)));
}

我們使用lazy_static來實現首次使用時創建的習慣用法:第一次訪問NUM ,它將創建迭代器。

迭代器本身是裝箱的,因為我們必須拼出類型。 正如 mcarton 指出的那樣,您可以將類型輸出為Filter<RangeFrom<i32>, fn(&i32) -> bool> ,但它與實現密切相關,並且您必須盡快更改它切換到不同的組合器。 為了避免麻煩,最好將迭代器裝箱,這會擦除用於實現它的實際類型。 (擦除類型涉及動態分派,但通過函數指針指定過濾器函數也是如此。)類型擦除的迭代器必須標記為SendSync才能在多個線程中使用。

最后,我們將迭代器包裝在一個Mutex ,這樣我們就可以安全地推進它。 結果使用如下:

fn main() {
    assert_eq!(
        Vec::from_iter(NUMS.lock().unwrap().by_ref().take(5)),
        vec![0, 2, 4, 6, 8]
    );
    assert_eq!(
        Vec::from_iter(NUMS.lock().unwrap().by_ref().take(5)),
        vec![10, 12, 14, 16, 18]
    );
}

操場

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM