如何实现迭代器产生可变引用 [英] How to implement Iterator yielding mutable references

查看:40
本文介绍了如何实现迭代器产生可变引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一个简单的查找迭代器:

I am trying to implement a simple lookup iterator:

pub struct LookupIterMut<'a, D> {
    data : &'a mut [D],
    indices : &'a [usize],
    i: usize
}

impl<'a, D> Iterator for LookupIterMut<'a, D> {
    type Item = &'a mut D;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.indices.len() {
            None
        } else {
            let index = self.indices[self.i] as usize;
            self.i += 1;
            Some(&mut self.data[index]) // error here
        }
    }
}

这个想法是允许调用者连续可变地访问内部存储.但是我收到错误 cannot infer an适当生命周期参数 in function call 由于冲突要求.

The idea was to allow a caller consecutive mutable access to an internal storage. However I am getting the error cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements.

据我所知,我必须将函数签名更改为 next(&'a mut self) ->.. 但这将不再是一个迭代器.

As far as I understand I would have to change the function signature to next(&'a mut self) -> .. but this would not be an Iterator anymore.

我还发现我可以简单地使用原始指针,但我不确定这在这里是否合适:

I also discovered that I could simply use raw pointers, though I am not sure if this is appropriate here:

// ...
type Item = *mut D;
// ...

感谢您的帮助

推荐答案

您的代码无效,因为您尝试返回对具有相同生命周期 'a 的同一个切片的多个可变引用.

Your code is invalid because you try to return multiple mutable references to the same slice with the same lifetime 'a.

为了让这样的事情起作用,您需要为每个返回的 Item 设置不同的生命周期,这样您就不会持有对同一切片的 2 个可变引用.您现在不能这样做,因为它需要通用关联类型:

For such a thing to work, you would need a different lifetime for each returned Item so that you wouldn't hold 2 mutable references to the same slice. You cannot do that for now because it requires Generic Associated Types:

type Item<'item> = &'item mut D; // Does not work today

一种解决方案是检查索引是否唯一,并在 unsafe 块中将引用项的生命周期重新绑定到 'a.这是安全的,因为所有索引都是唯一的,因此用户不能持有对同一项目的 2 个可变引用.

One solution is to check that the indices are unique and to rebind the lifetime of the referenced item to 'a in an unsafe block. This is safe because all the indices are unique, so the user cannot hold 2 mutable references to the same item.

不要忘记将整个代码封装在一个模块中,这样如果没有检查new就无法构建结构:

Don't forget to encapsulate the whole code inside a module, so that the struct cannot be build without the check in new:

mod my_mod {
    pub struct LookupIterMut<'a, D> {
        data: &'a mut [D],
        indices: &'a [usize],
        i: usize,
    }

    impl<'a, D> LookupIterMut<'a, D> {
        pub fn new(data: &'a mut [D], indices: &'a [usize]) -> Result<Self, ()> {
            let mut uniq = std::collections::HashSet::new();
            let all_distinct = indices.iter().all(move |&x| uniq.insert(x));

            if all_distinct {
                Ok(LookupIterMut {
                    data,
                    indices,
                    i: 0,
                })
            } else {
                Err(())
            }
        }
    }

    impl<'a, D> Iterator for LookupIterMut<'a, D> {
        type Item = &'a mut D;

        fn next(&mut self) -> Option<Self::Item> {
            self.indices.get(self.i).map(|&index| {
                self.i += 1;

                unsafe { std::mem::transmute(&mut self.data[index]) }
            })
        }
    }
}

请注意,如果一个索引超出范围,您的代码将发生混乱.

Note that your code will panic if one index is out of bounds.

这篇关于如何实现迭代器产生可变引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆