我想在 Rust 中使用迭代器作为全局状态。以下是一个简化的示例:
这可能吗?
static nums = (0..).filter(|&n|n%2==0);
这可能吗?
static FOO: usize = 10
` 的东西可以编译,但是 `static BAR: String = "foo".to_string()
` 不能,因为 `BAR
` 需要运行时分配。虽然您的迭代器不需要运行时分配(尽管使用它将使您的生活更加简单,稍后您将看到),但其类型足够复杂,不支持编译时初始化。 其次,Rust 静态变量需要事先指定完整类型。对于任意迭代器来说,这是个问题,人们想通过组合迭代器适配器和闭包来创建。虽然在这种特殊情况下,如 mcarton 所指出的那样,一个人可以将类型指定为 `Filter<RangeFrom<i32>, fn(&i32) -> bool>
`,但它会与当前实现紧密耦合。当您切换到其他组合器时,您必须更改该类型。为了避免麻烦,最好将迭代器隐藏在 dyn Iterator
引用后面,即通过将其放入 Box
中来擦除类型。擦除类型涉及动态调度,但通过函数指针指定过滤函数也会这样做。第三,Rust 静态变量是只读的,并且 Iterator::next()
接受 &mut self
,因为它更新迭代的状态。静态必须是只读的,因为 Rust 是多线程的,而不证明没有读者或其他编写器的情况下编写静态将允许数据竞争在安全代码中。因此,要推进您的全局迭代器,您必须将其包装在 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
用于实现“第一次使用时创建”的惯用语,以解决非常量初始值的问题。当第一次访问NUMS
时,它将创建迭代器。
如上所述,迭代器本身被装箱并包装在一个Mutex
中。由于全局变量可能被多个线程访问,我们的装箱迭代器除了实现Iterator
外还实现了Send
和Sync
。
结果的使用如下:
fn main() {
assert_eq!(NUMS.lock().unwrap().next(), Some(0)); // take single value
assert_eq!(
// take multiple values
Vec::from_iter(NUMS.lock().unwrap().by_ref().take(5)),
vec![2, 4, 6, 8, 10]
);
}
core::iter::Filter<core::ops::RangeFrom<i32>, fn(&i32) -> bool>
。next
需要一个&mut self
参数。静态变量默认情况下不能是可变的,因为这不安全。static
所需的值必须是常量表达式。您的初始值不是常量表达式。
get_next_id = itertools.count().__next__
的全局变量并不罕见。 - user4815162342next_id
示例只是说明全局迭代器可能是一个有用的东西。 - user4815162342