【问题标题】:Iterator over elements around specific index in Vec<Vec<Object>>迭代 Vec<Vec<Object>> 中特定索引周围的元素
【发布时间】:2015-05-25 16:33:24
【问题描述】:

我有一个网格:Vec&lt;Vec&lt;Object&gt;&gt; 和一对 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))

有人知道我该怎么做吗?

【问题讨论】:

  • 纯粹出于好奇,您是在制作康威的生命游戏吗?
  • 不,扫雷:)
  • 您应该将您的示例更新为MCVE。就像现在一样,我们没有许多变量的值或类型。

标签: rust


【解决方案1】:

就我个人而言,当元素的相对位置可能很重要时,我不确定我是否愿意使用迭代器。相反,我会寻求创建这些元素的“视图”。

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)
}

剩下的只是一些无趣的构造函数。

当然,您可以在那些之上构建某种迭代器。

【讨论】:

  • 有趣的是,没有办法为IterMut 实现一个接受负整数的get_mut 方法,从而将IterMut 本身变成一个视图,因为IterMut 没有起始指针。
【解决方案2】:

您想获得对所有周围元素的可变引用,对吗?我认为这是不可能直接做到的。问题是,Rust 不能静态地证明您想要对 不同 单元格的可变引用。如果它忽略了这一点,那么,例如,您可能在索引时犯了一个小错误,并获得对同一数据的两个可变引用,这是 Rust 保证可以防止的。因此它不允许这样做。

在语言层面,这是由IndexMut trait 引起的。您可以看到其唯一方法的 self 参数生命周期是如何与结果生命周期相关联的:

fn index_mut(&'a mut self, index: Idx) -> &'a mut Self::Output;

这意味着如果这个方法被调用(隐式地通过索引操作),那么整个对象将被可变地借用,直到结果引用超出范围。这可以防止多次调用&amp;mut a[i]

解决此问题的最简单和最安全的方法是以“双缓冲”方式重构您的代码 - 您有两个字段实例并在每个步骤中相互复制数据。或者,您可以在每个步骤上创建一个临时字段,并在所有计算后用它替换主要字段,但它可能不如交换两个字段效率。

解决这个问题的另一种方法自然是使用原始*mut 指针。这是unsafe,只能作为最后的手段直接使用。但是,您可以使用 unsafety 来实现安全抽象,例如

fn index_multiple_mut<'a, T>(input: &'a mut [Vec<T>], indices: &[(usize, usize)]) -> Vec<&'a mut T>

您首先检查所有索引是否不同,然后使用 unsafe 和一些指针强制转换(可能使用 transmute)来创建结果向量。

第三种可能的方法是以某种巧妙的方式使用split_at_mut() 方法,但我不确定这是否可行,如果可以,它可能不太方便。

【讨论】:

  • 不应该有办法用迭代器做到这一点吗?我的想法是创建一个可以做到这一点的迭代器链,但如果不可能,我可以编写一个自定义迭代器。
【解决方案3】:

最后我在#rust的人的帮助下制作了一个自定义迭代器

我已经 typed 我的结构出来给你实际的代码。正如#rust 中的人所指出的那样,如果不使用使用unsafe 的不同迭代器,您将无法安全地从迭代器返回&amp;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 _) })
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 2021-08-26
    • 2020-01-22
    • 2019-07-14
    • 1970-01-01
    相关资源
    最近更新 更多