如何 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 子矩阵会是什么样子?
您想优化并使用 分配 Vec
s?当然是 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 })
}
}
我学习 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 子矩阵会是什么样子?
您想优化并使用 分配 Vec
s?当然是 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 })
}
}