[英]Can I use an iterator in global state in Rust?
我想在 Rust 中使用迭代器作為全局狀態。 簡化示例:
static nums = (0..).filter(|&n|n%2==0);
這可能嗎?
否。出於多種原因:
static
必須顯式輸入。 在這種情況下,類型仍然相對簡單: core::iter::Filter<core::ops::RangeFrom<i32>, fn(&i32) -> bool>
。Iterator
的 main 方法next
需要一個&mut self
參數。 默認情況下static
不能是可變的,因為這不安全。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>
,但它與實現密切相關,並且您必須盡快更改它切換到不同的組合器。 為了避免麻煩,最好將迭代器裝箱,這會擦除用於實現它的實際類型。 (擦除類型涉及動態分派,但通過函數指針指定過濾器函數也是如此。)類型擦除的迭代器必須標記為Send
和Sync
才能在多個線程中使用。
最后,我們將迭代器包裝在一個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.