我正在空间网格上实现光标,例如,对于空间[2, 2],光标应该分别访问[0, 0][1, 0][0, 1][1, 1].我有下面写的代码,但当我使用for cursor in SpaceCursor::<2>::new(&[2, 2])时,我注意到光标不是借来的,而是复制的.由于游标将始终在SpaceCursor生命周期内,我想知道我是否可以使用它的类型&[i32; 2]而不是[i32; 2]

struct SpaceCursor<const D: usize> {
    cursor: [i32; D],
    boundary: [usize; D],
}

impl<const D: usize> SpaceCursor<D> {
    pub fn new(boundary: &[usize]) -> Self {
        let mut result = SpaceCursor::<D> {
            cursor: [0; D],
            boundary: boundary.try_into().expect("Bad boundary"),
        };
        if D >= 1 {
            result.cursor[0] = -1;
        }
        result
    }
}

impl<const D: usize> Iterator for SpaceCursor<D> {
    type Item = [i32; D];
    fn next(&mut self) -> Option<Self::Item> {
        let mut index = 0;
        while index < D {
            self.cursor[index] += 1;
            if self.cursor[index] < self.boundary[index] as i32 {
                break;
            }
            index += 1;
        }
        if index == D {
            None
        } else {
            for i in 0..index {
                self.cursor[i] = 0;
            }
            Some(self.cursor)
        }
    }
}

推荐答案

如果需要迭代器来生成引用,则迭代器本身必须包含对底层数据的引用.这要归功于Iterator个特征的设计:

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

在实现Iterator(其中Item是引用)时,您需要给它一个生命周期.但您只需要 Select 一个生存期,该生存期必须对所有项有效,并且该生存期必须比迭代器的生存期更长.只有当所有项都已保存在内存中的某个位置并且将比使用迭代器的时间更长时,这才起作用.

在Rust 1.65中,这种情况将(在某种程度上)改变.在STRISE中将提供一个新功能--泛型关联类型(GATS),它将允许您定义如下所示的"流"迭代器:

trait StreamingIterator {
    type Item<'a> where Self: 'a;
    fn stream_next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}

这里的不同之处在于,生命周期 'a每隔callstream_next实例化一次,因此每次都可以不同.调用者还可以 Select 将引用保留多长时间,并且仅受流迭代器本身的可变borrow 的限制.可变性还意味着你一次只能借一件东西,所以你不能在不克隆它们的情况下把它们收集到Vec中.

您的迭代器可以像这样移植:

impl<const D: usize> StreamingIterator for SpaceCursor<D> {
    type Item<'a> = &'a [i32; D];
    fn stream_next<'a>(&'a mut self) -> Option<Self::Item<'a>> {
        let mut index = 0;
        while index < D {
            self.cursor[index] += 1;
            if self.cursor[index] < self.boundary[index] as i32 {
                break;
            }
            index += 1;
        }
        if index == D {
            None
        } else {
            for i in 0..index {
                self.cursor[i] = 0;
            }
            Some(&self.cursor)
        }
    }
}

请注意,一次只能借阅一件物品的"限制"对于这项工作非常关键,因为您正在覆盖cursor的值.这也应该是为什么这不能与非流Iterator特性一起工作的另一个提示.

因为StreamingIterator不是一个内置的特征,所以在循环中没有对它的语法支持,所以必须显式地使用它:

let data = vec![1,3,7,9];
let mut cursor = SpaceCursor::<4>::new(&data);

while let Some(i) = cursor.stream_next() {
    println!("{i:?}"); 
}

你现在可以在Rust 1.64测试版中每晚试一试,或者等待大约两周的Rust 1.65.

Rust相关问答推荐

使用pyo3::Types::PyIterator的无限内存使用量

如何在Bevy/Rapier3D中获得碰撞机的计算质量?

通过解引用将值移出Box(以及它被脱糖到什么地方)?

制作一片连续整数的惯用Rust 方法?

在Rust中,在实现特征`Display`时,如何获取调用方指定的格式?

如何指定不同的类型来常量Rust中的泛型参数?

我如何使用AWS SDK for Rust获取我承担的角色的凭据?

Rust中WPARAM和VIRTUAL_KEY的比较

详尽的匹配模式绑定

Google chrome 和 Apple M1 中的计算着色器

如何在Rust中使用Serde创建一个自定义的反序列化器来处理带有内部标记的枚举

Rust中的位移操作对范围有什么影响?

Rust中如何实现一个与Sized相反的负特性(Unsized)

max(ctz(x), ctz(y)) 有更快的算法吗?

从 Rust 中的 if/else 中的引用创建 MappedRwLockWriteGuard

为什么在 rust 中删除 vec 之前应该删除元素

是否有适当的方法在参考 1D 中转换 2D 数组

当用作函数参数时,不强制执行与绑定的关联类型

为什么这个 Trait 无效?以及改用什么签名?

基于名称是否存在的条件编译