如何 return 为 Rust 中的任意数组子集借用迭代器

How to return borrowed iterators for arbitrary subsets of arrays in Rust

我学习 Rust 并想实现一些玩具项目:DFS/BFS 数独求解器

为此,我选择了底层数据结构作为糖衣 [Option<u8>; 81]。我在定义板上 return 行和列的方法时遇到了一些问题。我还试图实现的是进行某种有效的实施。例如检查所有 / 是否有效(提前中止)或仅在网格中显示所有值。因此,我想检查是否可以不复制任何数据,而是让迭代器指向正确的数据单元。

这是我想出的:

struct Board {
    fields: [Option<u8>; 81],
}

impl Board {
    pub fn rows(&self) -> impl Iterator<Item = &[Option<u8>]> {
        (0..81)
            .step_by(9)
            .map(|index| &self.fields[index..(index + 9)])
    }

    pub fn columns(&self) -> impl Iterator<Item = Vec<&Option<u8>>> {
        (0..9).map(|offset| {
            let map = (0..9).map(|index| &self.fields[9 * index + offset]);
            let row: Vec<&Option<u8>> = map.collect();
            row
        })
    }
}

fn main() {
    let board = Board {
        fields: std::iter::repeat(Some(1))
            .take(81)
            .collect::<Vec<_>>()
            .try_into()
            .unwrap(),
    };
    println!("{}", board);
}

impl std::fmt::Display for Board {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        for row in self.rows() {
            for field in row.iter() {
                match field {
                    Some(number) => write!(f, "{}", number)?,
                    None => write!(f, " ")?,
                }
            }
            write!(f, "\n")?
        }
        Ok(())
    }
}

尽管行和列的概念相似,但 return 类型却大不相同。包含选项的数组引用的迭代器(对我来说似乎是正确的)和选项引用向量的迭代器。特别是 columns 方法在我看来是错误的,因为它实现了 Vec 并引用了内部的选项。如何对齐那些 return 类型?奖励:3x3 子矩阵会是什么样子?

您想优化并使用 分配 Vecs?当然是 mis-optimization.

的例子

我建议您不要尝试优化它,除非性能分析显示存在瓶颈。复制 162 字节 (81 * mem::size_of::<Option<u8>>()) 不太可能成为瓶颈,您可以进一步优化内存布局(想到的最简单的选择之一是对单元格使用 Option<std::num::NonZeroU8>,因为它仅占用一个字节(而不是两个字节)并且单元格不能包含零)。

无论如何,回答你的问题:&[Option<u8>](对 Option<u8> 的切片的引用)不被拥有,而 Vec<&Option<u8>> 是。第一个是集合的视图——因此,您只能从中获取引用。也就是说,你不能离开它。因此,使用 &[&Option<u8>] 没有意义。从不同的角度来看,<&[Option<u8>]>::into_iter()(实际上与 <&[Option<u8>]>::iter() 相同)将为您提供一个迭代器 &Option<u8>。另一方面,Vec<T> 是拥有的 - 因此您必须明确引用,因为 <Vec<Option<u8>>>::into_iter() 提供了拥有的 Option<T>s.

的迭代器

如果坚持使用迭代器,可以returniterator-of-iterators代替容器。例如:


    pub fn rows(&self) -> impl Iterator<Item = impl Iterator<Item = &Option<u8>>> {
        (0..81)
            .step_by(9)
            .map(move |index| self.fields[index..(index + 9)].iter())
    }

    pub fn columns(&self) -> impl Iterator<Item = impl Iterator<Item = &Option<u8>>> {
        (0..9).map(move |offset| (0..9).map(move |index| &self.fields[9 * index + offset]))
    }

尽管我可能会使用 easier-to-maintain 自定义 Iterator 类型:

struct Row<'a> {
    left: std::slice::Iter<'a, Option<u8>>,
}
impl Iterator for Row<'_> {
    type Item = Option<u8>;
    fn next(&mut self) -> Option<Self::Item> {
        self.left.next().copied()
    }
    // May implement `size_hint()` and `ExactSizeIterator` for better performance
}

struct Column<'a> {
    board: &'a Board,
    index: usize,
}
impl Iterator for Column<'_> {
    type Item = Option<u8>;
    fn next(&mut self) -> Option<Self::Item> {
        let result = self.board.fields.get(self.index)?;
        self.index += 9;
        Some(*result)
    }
}

impl Board {
    pub fn rows(&self) -> impl Iterator<Item = Row<'_>> {
        self.fields
            .chunks_exact(9)
            .map(|row| Row { left: row.iter() })
    }

    pub fn columns(&self) -> impl Iterator<Item = Column<'_>> {
        (0..9).map(move |index| Column { board: self, index })
    }
}