迭代 Vec<Vec<Object>> 中特定索引周围的元素
Iterator over elements around specific index in Vec<Vec<Object>>
我有一个网格:Vec<Vec<Object>>
和一对 x/y 索引。我想找到 周围 索引的所有元素。
不幸的是,我不能简单地遍历元素,因为这最终会借用 Vec
两次并且借用检查器对我尖叫:
let mut cells = Vec::with_capacity(8);
for cx in xstart..xend {
for cy in ystart..yend {
if cx != x || cy != y {
cells.push(&mut squares[cy as usize][cx as usize]);
}
}
}
cells.into_iter()
我将其更改为迭代器链的最佳尝试也以惊人的失败告终:
let xstart = if x == 0 { x } else { x - 1 };
let xlen = if x + 2 > squares[0].len() { x + 1 } else { 3 };
let ystart = if y == 0 { y } else { y - 1 };
let ylen = if y + 2 > squares.len() { y + 1 } else { 3 };
let xrel = x - xstart;
let yrel = y - ystart;
squares.iter().enumerate()
.skip(ystart).take(ylen).flat_map(|(i, ref row)|
row.iter().enumerate()
.skip(xstart).take(xlen).filter(|&(j, &c)| i != yrel || j != xrel))
有人知道我该怎么做吗?
您想获得对周围所有元素的可变引用,对吧?我认为这是不可能直接做到的。问题是,Rust 无法静态地证明您想要对 不同 单元格的可变引用。如果它忽略了这一点,那么,例如,您可能会在索引中犯一个小错误并获得对同一数据的两个可变引用,这是 Rust 保证可以防止的事情。因此它不允许这样做。
在语言层面,这是由 IndexMut
特性引起的。您可以看到其唯一方法的 self
参数生命周期如何与结果生命周期相关联:
fn index_mut(&'a mut self, index: Idx) -> &'a mut Self::Output;
这意味着如果调用此方法(隐含地通过索引操作),那么 whole 对象将被可变地借用,直到生成的引用超出范围。这样可以防止多次调用 &mut a[i]
。
解决此问题的最简单和最安全的方法是以 "double buffering" 方式重构您的代码 - 您有两个字段实例,并在每个步骤中相互复制数据。或者,您可以在每个步骤上创建一个临时字段,并在所有计算后用它替换主要字段,但它可能不如交换两个字段效率低。
解决这个问题的另一种方法自然是使用原始 *mut
指针。这是 unsafe
,只能作为最后的手段直接使用。但是,您可以使用不安全来实现安全抽象,例如
fn index_multiple_mut<'a, T>(input: &'a mut [Vec<T>], indices: &[(usize, usize)]) -> Vec<&'a mut T>
首先检查所有索引是否不同,然后使用 unsafe
和一些指针转换(可能使用 transmute
)来创建结果向量。
第三种可能的方法是以某种巧妙的方式使用 split_at_mut()
方法,但我不确定是否可行,如果可行,可能不太方便。
就我个人而言,当元素的相对位置可能很重要时,我不确定使用迭代器是否会舒服。相反,我会寻求创建这些元素的 "view"。
gist can be found here,但是思路很简单所以这里是核心结构。
#[derive(Debug)]
struct NeighbourhoodRow<'a, T>
where T: 'a
{
pub left : Option<&'a mut T>,
pub center : Option<&'a mut T>,
pub right : Option<&'a mut T>,
}
#[derive(Debug)]
struct Neighbourhood<'a, T>
where T: 'a
{
pub top : NeighbourhoodRow<'a, T>,
pub center : NeighbourhoodRow<'a, T>,
pub bottom : NeighbourhoodRow<'a, T>,
}
为了构建它们,我使用了健康剂量的 split_at_mut
:
fn take_centered_trio<'a, T>(row: &'a mut [T], x: usize) ->
(Option<&'a mut T>, Option<&'a mut T>, Option<&'a mut T>)
{
fn extract<'a, T>(row: &'a mut [T], x: usize) -> (Option<&'a mut T>, &'a mut [T]) {
if x+1 > row.len() {
(None, row)
} else {
let (h, t) = row.split_at_mut(x+1);
(Some(&mut h[x]), t)
}
}
let (prev, row) = if x > 0 { extract(row, x-1) } else { (None, row) };
let (elem, row) = extract(row, 0);
let (next, _ ) = extract(row, 0);
(prev, elem, next)
}
剩下的就是一些无趣的构造函数。
当然,您可以在 那些.
上构建某种迭代器
最后我在 #rust
的人的帮助下制作了一个自定义迭代器
我已经 type
给出了我的结构,以便为您提供实际代码。正如 #rust
中的人指出的那样,如果不使用无论如何都使用 unsafe
的不同迭代器,就无法从迭代器中安全地 return &mut
,并且考虑到这里的数学很简单足以确保它不会出错的方法是不安全的。
type FieldSquare = u8;
use std::iter::Iterator;
pub struct SurroundingSquaresIter<'a> {
squares: &'a mut Vec<Vec<FieldSquare>>,
center_x: usize,
center_y: usize,
current_x: usize,
current_y: usize,
}
pub trait HasSurroundedSquares<'a> {
fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a>;
}
impl<'a> HasSurroundedSquares<'a> for Vec<Vec<FieldSquare>> {
fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a> {
SurroundingSquaresIter {
squares: self,
center_x: x,
center_y: y,
current_x: if x == 0 { x } else { x - 1 },
current_y: if y == 0 { y } else { y - 1 },
}
}
}
impl<'a> Iterator for SurroundingSquaresIter<'a> {
type Item = &'a mut FieldSquare;
fn next(&mut self) -> Option<&'a mut FieldSquare> {
if self.current_y + 1 > self.squares.len() || self.current_y > self.center_y + 1 {
return None;
}
let ret_x = self.current_x;
let ret_y = self.current_y;
if self.current_x < self.center_x + 1 && self.current_x + 1 < self.squares[self.current_y].len() {
self.current_x += 1;
}
else {
self.current_x = if self.center_x == 0 { self.center_x } else { self.center_x - 1 };
self.current_y += 1;
}
if ret_x == self.center_x && ret_y == self.center_y {
return self.next();
}
Some(unsafe { &mut *(&mut self.squares[ret_y][ret_x] as *mut _) })
}
}
我有一个网格:Vec<Vec<Object>>
和一对 x/y 索引。我想找到 周围 索引的所有元素。
不幸的是,我不能简单地遍历元素,因为这最终会借用 Vec
两次并且借用检查器对我尖叫:
let mut cells = Vec::with_capacity(8);
for cx in xstart..xend {
for cy in ystart..yend {
if cx != x || cy != y {
cells.push(&mut squares[cy as usize][cx as usize]);
}
}
}
cells.into_iter()
我将其更改为迭代器链的最佳尝试也以惊人的失败告终:
let xstart = if x == 0 { x } else { x - 1 };
let xlen = if x + 2 > squares[0].len() { x + 1 } else { 3 };
let ystart = if y == 0 { y } else { y - 1 };
let ylen = if y + 2 > squares.len() { y + 1 } else { 3 };
let xrel = x - xstart;
let yrel = y - ystart;
squares.iter().enumerate()
.skip(ystart).take(ylen).flat_map(|(i, ref row)|
row.iter().enumerate()
.skip(xstart).take(xlen).filter(|&(j, &c)| i != yrel || j != xrel))
有人知道我该怎么做吗?
您想获得对周围所有元素的可变引用,对吧?我认为这是不可能直接做到的。问题是,Rust 无法静态地证明您想要对 不同 单元格的可变引用。如果它忽略了这一点,那么,例如,您可能会在索引中犯一个小错误并获得对同一数据的两个可变引用,这是 Rust 保证可以防止的事情。因此它不允许这样做。
在语言层面,这是由 IndexMut
特性引起的。您可以看到其唯一方法的 self
参数生命周期如何与结果生命周期相关联:
fn index_mut(&'a mut self, index: Idx) -> &'a mut Self::Output;
这意味着如果调用此方法(隐含地通过索引操作),那么 whole 对象将被可变地借用,直到生成的引用超出范围。这样可以防止多次调用 &mut a[i]
。
解决此问题的最简单和最安全的方法是以 "double buffering" 方式重构您的代码 - 您有两个字段实例,并在每个步骤中相互复制数据。或者,您可以在每个步骤上创建一个临时字段,并在所有计算后用它替换主要字段,但它可能不如交换两个字段效率低。
解决这个问题的另一种方法自然是使用原始 *mut
指针。这是 unsafe
,只能作为最后的手段直接使用。但是,您可以使用不安全来实现安全抽象,例如
fn index_multiple_mut<'a, T>(input: &'a mut [Vec<T>], indices: &[(usize, usize)]) -> Vec<&'a mut T>
首先检查所有索引是否不同,然后使用 unsafe
和一些指针转换(可能使用 transmute
)来创建结果向量。
第三种可能的方法是以某种巧妙的方式使用 split_at_mut()
方法,但我不确定是否可行,如果可行,可能不太方便。
就我个人而言,当元素的相对位置可能很重要时,我不确定使用迭代器是否会舒服。相反,我会寻求创建这些元素的 "view"。
gist can be found here,但是思路很简单所以这里是核心结构。
#[derive(Debug)]
struct NeighbourhoodRow<'a, T>
where T: 'a
{
pub left : Option<&'a mut T>,
pub center : Option<&'a mut T>,
pub right : Option<&'a mut T>,
}
#[derive(Debug)]
struct Neighbourhood<'a, T>
where T: 'a
{
pub top : NeighbourhoodRow<'a, T>,
pub center : NeighbourhoodRow<'a, T>,
pub bottom : NeighbourhoodRow<'a, T>,
}
为了构建它们,我使用了健康剂量的 split_at_mut
:
fn take_centered_trio<'a, T>(row: &'a mut [T], x: usize) ->
(Option<&'a mut T>, Option<&'a mut T>, Option<&'a mut T>)
{
fn extract<'a, T>(row: &'a mut [T], x: usize) -> (Option<&'a mut T>, &'a mut [T]) {
if x+1 > row.len() {
(None, row)
} else {
let (h, t) = row.split_at_mut(x+1);
(Some(&mut h[x]), t)
}
}
let (prev, row) = if x > 0 { extract(row, x-1) } else { (None, row) };
let (elem, row) = extract(row, 0);
let (next, _ ) = extract(row, 0);
(prev, elem, next)
}
剩下的就是一些无趣的构造函数。
当然,您可以在 那些.
上构建某种迭代器最后我在 #rust
我已经 type
给出了我的结构,以便为您提供实际代码。正如 #rust
中的人指出的那样,如果不使用无论如何都使用 unsafe
的不同迭代器,就无法从迭代器中安全地 return &mut
,并且考虑到这里的数学很简单足以确保它不会出错的方法是不安全的。
type FieldSquare = u8;
use std::iter::Iterator;
pub struct SurroundingSquaresIter<'a> {
squares: &'a mut Vec<Vec<FieldSquare>>,
center_x: usize,
center_y: usize,
current_x: usize,
current_y: usize,
}
pub trait HasSurroundedSquares<'a> {
fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a>;
}
impl<'a> HasSurroundedSquares<'a> for Vec<Vec<FieldSquare>> {
fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a> {
SurroundingSquaresIter {
squares: self,
center_x: x,
center_y: y,
current_x: if x == 0 { x } else { x - 1 },
current_y: if y == 0 { y } else { y - 1 },
}
}
}
impl<'a> Iterator for SurroundingSquaresIter<'a> {
type Item = &'a mut FieldSquare;
fn next(&mut self) -> Option<&'a mut FieldSquare> {
if self.current_y + 1 > self.squares.len() || self.current_y > self.center_y + 1 {
return None;
}
let ret_x = self.current_x;
let ret_y = self.current_y;
if self.current_x < self.center_x + 1 && self.current_x + 1 < self.squares[self.current_y].len() {
self.current_x += 1;
}
else {
self.current_x = if self.center_x == 0 { self.center_x } else { self.center_x - 1 };
self.current_y += 1;
}
if ret_x == self.center_x && ret_y == self.center_y {
return self.next();
}
Some(unsafe { &mut *(&mut self.squares[ret_y][ret_x] as *mut _) })
}
}