我试图使用一个迭代器Rust 并掉到某个地方的模式,显然很简单.

我想遍历一个容器,找到一个带有谓词[a](简单)的元素,但希望使用另一个谓词,得到该值[B],并使用[B]以某种方式对[a]进行变异.在这种情况下,[A]是可变的,[B]可以是不可变的;这对我来说没什么区别,只是对借钱人来说(正确的).

通过一个简单的场景来理解这一点会有所帮助,所以我添加了一个小片段,让大家看到问题/try 的目标.我玩过itertools,并try 了for/while循环,尽管我希望尽可能保持惯用.

Silly Example scenario

查找一个偶数,找到下一个可被3整除的数,并将其添加到初始数.

#[allow(unused)]
fn is_div_3(num: &u8) -> bool {
    num % 3 == 0
}

fn main() {
    let mut data: Vec<u8> = (0..100).collect();

    let count = data.iter_mut()
        .map(|x| {
            if *x % 2 == 0 {
                // loop through numbers forward to next is_div_3,
                // then x = x + that number
            }
            true
        })
        .count();

    println!("data {:?}, count was {} ", data, count);
}

playground

推荐答案

Warning: The iterator presented right below is 100 because it allows one to obtain multiple aliases to a single mutable element; skip to the second part for the corrected version. (It would be alright if the return type contained immutable references).

如果您愿意编写自己的窗口迭代器,那么它就变得非常简单.

首先,迭代器的所有血淋淋的细节:

use std::marker::PhantomData;

struct WindowIterMut<'a, T>
    where T: 'a
{
    begin: *mut T,
    len: usize,
    index: usize,
    _marker: PhantomData<&'a mut [T]>,
}

impl<'a, T> WindowIterMut<'a, T>
    where T: 'a
{
    pub fn new(slice: &'a mut [T]) -> WindowIterMut<'a, T> {
        WindowIterMut {
            begin: slice.as_mut_ptr(),
            len: slice.len(),
            index: 0,
            _marker: PhantomData,
        }
    }
}

impl<'a, T> Iterator for WindowIterMut<'a, T>
    where T: 'a
{
    type Item = (&'a mut [T], &'a mut [T]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.index > self.len { return None; }

        let slice: &'a mut [T] = unsafe {
            std::slice::from_raw_parts_mut(self.begin, self.len)
        };
        let result = slice.split_at_mut(self.index);

        self.index += 1;

        Some(result)
    }
}

[1, 2, 3]调用它将返回(&[], &[1, 2, 3])然后返回(&[1], &[2, 3])...直到(&[1, 2, 3], &[])年.简而言之,它迭代片的所有潜在分区(无需洗牌).

可以安全地用作:

fn main() {
    let mut data: Vec<u8> = (1..100).collect();

    for (head, tail) in WindowIterMut::new(&mut data) {
        if let Some(element) = head.last_mut() {
            if *element % 2 == 0 {
                if let Some(n3) = tail.iter().filter(|i| *i % 3 == 0).next() {
                    *element += *n3;
                }
            }
        }
    }

    println!("{:?}", data);
}

不幸的是,它也可以用作:

fn main() {
    let mut data: Vec<u8> = (1..100).collect();

    let mut it = WindowIterMut::new(&mut data);
    let first_0 = { it.next(); &mut it.next().unwrap().0[0] };
    let second_0 = &mut it.next().unwrap().0[0];

    println!("{:?} {:?}", first_0 as *const _, second_0 as *const _);
}

当运行print:0x7f73a8435000 0x7f73a8435000时,显示两个可变引用别名相同元素的大小写.


因为我们无法摆脱别名,所以我们需要摆脱可变性;或者至少推迟到interior mutability(这里是Cell,因为u8Copy).

幸运的是,Cell没有运行时成本,但它确实在人体工程学方面花费了一点(所有的.get().set()).

我也借此机会让迭代器稍微更通用一些,并对其重命名,因为Window已经是一个不同概念的常用名称.

struct FingerIter<'a, T>
    where T: 'a
{
    begin: *const T,
    len: usize,
    index: usize,
    _marker: PhantomData<&'a [T]>,
}

impl<'a, T> FingerIter<'a, T>
    where T: 'a
{
    pub fn new(slice: &'a [T]) -> FingerIter<'a, T> {
        FingerIter {
            begin: slice.as_ptr(),
            len: slice.len(),
            index: 0,
            _marker: PhantomData,
        }
    }
}

impl<'a, T> Iterator for FingerIter<'a, T>
    where T: 'a
{
    type Item = (&'a [T], &'a T, &'a [T]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.len { return None; }

        let slice: &'a [T] = unsafe {
            std::slice::from_raw_parts(self.begin, self.len)
        };

        self.index += 1;
        let result = slice.split_at(self.index);

        Some((&result.0[0..self.index-1], result.0.last().unwrap(), result.1))
    }
}

我们将其用作建筑砖:

fn main() {
    let data: Vec<Cell<u8>> = (1..100).map(|i| Cell::new(i)).collect();

    for (_, element, tail) in FingerIter::new(&data) {
        if element.get() % 2 == 0 {
            if let Some(n3) = tail.iter().filter(|i| i.get() % 3 == 0).next() {
                element.set(element.get() + n3.get());
            }
        }
    }

    let data: Vec<u8> = data.iter().map(|cell| cell.get()).collect();

    println!("{:?}", data);
}

On the playpen这个打印:[1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...],这似乎是正确的.

Rust相关问答推荐

Rust kill std::processs::child

如何使用Match比较 struct 中的值

为什么Rust函数的移植速度比C++慢2倍?

为潜在的下游实现使用泛型绑定而不是没有泛型绑定的trait

取得本地对象字段的所有权

Tokio_Postgres行上未显示退回特性的生存期,且生命周期 不够长

当对VEC;U8>;使用serde_json时,Base64编码是保护空间的好方法吗?

在rust sqlx中使用ilike和push bind

对于已经被认为是未定义行为的相同数据,纯粹存在`&;[u32]`和`&;mut[u32]`吗?

`RwLockWriteGuard_,T`不实现T实现的特征

Rust函数的返回值不能引用局部变量或临时变量

在macro_rule中拆分模块和函数名

在 Rust 中,为什么 10 个字符的字符串的 size_of_val() 返回 24 个字节?

`tokio::pin` 如何改变变量的类型?

实现AsyncWrite到hyper Sender时发生生命周期错误

如何刷新 TcpStream

在异步 Rust 中,Future 如何确保它只调用最近的 Waker?

没有通用参数的通用返回

Rust,我如何正确释放堆分配的内存?

如何从 Rust 函数返回数组而不复制它们?