简体   繁体   English

我可以在 Rust 中使用处于全局状态的迭代器吗?

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

I want to use an iterator as global state in Rust.我想在 Rust 中使用迭代器作为全局状态。 Simplified example:简化示例:

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

Is this possible?这可能吗?

No. For multiple reasons:否。出于多种原因:

  1. Iterators types tend to be complicated.迭代器类型往往很复杂。 This is usually not a problem because iterator types must rarely be named, but static s must be explicitly typed.这通常不是问题,因为迭代器类型很少必须命名,但static必须显式输入。 In this case the type is still relatively simple: core::iter::Filter<core::ops::RangeFrom<i32>, fn(&i32) -> bool> .在这种情况下,类型仍然相对简单: core::iter::Filter<core::ops::RangeFrom<i32>, fn(&i32) -> bool>
  2. Iterator 's main method, next , needs a &mut self parameter. Iterator的 main 方法next需要一个&mut self参数。 static s can't be mutable by default, as this would not be safe.默认情况下static不能是可变的,因为这不安全。
  3. Iterators can only be iterated once.迭代器只能迭代一次。 Therefore it makes little sense to have a global iterator in the first place.因此,首先拥有一个全局迭代器毫无意义。
  4. The value to initialize a static must be a constant expression.初始化static的值必须是常量表达式。 Your initializer is not a constant expression.您的初始化程序不是常量表达式。

You can do it, but you'll have to fight the language along the way.你可以做到,但你必须在此过程中与语言作斗争。

First, true Rust statics created with the static declaration need to be compile-time constants.首先,使用static声明创建的真正 Rust static需要是编译时常量。 So something like static FOO: usize = 10 will compile, but static BAR: String = "foo".to_string() won't, because BAR requires a run-time allocation.所以像static FOO: usize = 10这样的东西会编译,但static BAR: String = "foo".to_string()不会,因为BAR需要运行时分配。 While your iterator doesn't require a run-time allocation (though using it will make your life simpler, as you'll see later), its type is complex enough that it doesn't support compile-time initialization.虽然您的迭代器不需要运行时分配(尽管使用它会使您的生活更简单,正如您稍后将看到的),但它的类型足够复杂,以至于它不支持编译时初始化。

Second, Rust statics are read-only.其次,Rust 静态是只读的。 This is because Rust is multi-threaded, and allowing writing to a static would constitute a data race.这是因为 Rust 是多线程的,并且允许写入静态数据将构成数据竞争。 Iterator's next() method takes &mut self , which makes sense, as it must update the underlying counter.迭代器的next()方法采用&mut self ,这是有道理的,因为它必须更新底层计数器。 To advance your global iterator, it must be wrapped in a Mutex .要推进您的全局迭代器,它必须包含在Mutex

With those out of the way, we can take a look at the implementation:有了这些,我们可以看看实现:

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)));
}

We use lazy_static to implement the create-on-first-use idiom: the first time NUM is accessed, it will create the iterator.我们使用lazy_static来实现首次使用时创建的习惯用法:第一次访问NUM ,它将创建迭代器。

The iterator itself is boxed because we have to spell out the type.迭代器本身是装箱的,因为我们必须拼出类型。 As mcarton points out, you could out the type as Filter<RangeFrom<i32>, fn(&i32) -> bool> , but it'd be closely tied to the implementation, and you'd have to change it as soon as you switch to a different combinator.正如 mcarton 指出的那样,您可以将类型输出为Filter<RangeFrom<i32>, fn(&i32) -> bool> ,但它与实现密切相关,并且您必须尽快更改它切换到不同的组合器。 To avoid the hassle it's better to box the iterator, which erases the actual type used to implement it.为了避免麻烦,最好将迭代器装箱,这会擦除用于实现它的实际类型。 (Erasing the type involves dynamic dispatch, but so would specifying the filter function through a function pointer.) The type-erased iterator must be marked as Send and Sync to be usable from multiple threads. (擦除类型涉及动态分派,但通过函数指针指定过滤器函数也是如此。)类型擦除的迭代器必须标记为SendSync才能在多个线程中使用。

Finally, we wrap the iterator in a Mutex , so we can safely advance it.最后,我们将迭代器包装在一个Mutex ,这样我们就可以安全地推进它。 The result is used as follows:结果使用如下:

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]
    );
}

Playground 操场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM