我在rust:中有一个2D网格的主要工作实现
use itertools::Itertools;
#[derive(Debug)]
pub struct Grid<T> {
width: usize,
height: usize,
items: Vec<T>,
}
impl<T> Grid<T> {
pub fn new(width: usize, height: usize, initializer: impl Fn() -> T) -> Self {
Self {
width,
height,
items: (0..height * width).map(|_| initializer()).collect(),
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn size(&self) -> usize {
self.width * self.height
}
pub fn get(&self, x: usize, y: usize) -> Result<&T, &str> {
if self.on_grid(x as isize, y as isize) {
Ok(&self.items[self.coordinate_to_index(x, y)])
} else {
Err("coordinate not on grid")
}
}
pub fn get_mut(&mut self, x: usize, y: usize) -> Result<&mut T, &str> {
if self.on_grid(x as isize, y as isize) {
let index = self.coordinate_to_index(x, y);
Ok(&mut self.items[index])
} else {
Err("coordinate not on grid")
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.items.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.items.iter_mut()
}
pub fn enumerate(&self) -> impl Iterator<Item = (usize, usize, &T)> {
self.items.iter().enumerate().map(|(index, item)| {
let (x, y) = self.index_to_coordinate(index);
(x, y, item)
})
}
pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
self.items.iter_mut().enumerate().map(move |(index, item)| {
let (x, y) = self.index_to_coordinate(index);
(x, y, item)
})
}
pub fn neighbors(&self, x: usize, y: usize) -> impl Iterator<Item = (usize, usize, &T)> {
self.neighbor_indices(x, y)
.map(|(x, y)| (x, y, &self.items[self.coordinate_to_index(x, y)]))
}
fn coordinate_to_index(&self, x: usize, y: usize) -> usize {
y * self.width + x
}
fn index_to_coordinate(&self, index: usize) -> (usize, usize) {
let x = index % self.width;
let y = (index - x) / self.width;
(x, y)
}
fn neighbor_indices(&self, x: usize, y: usize) -> impl Iterator<Item = (usize, usize)> + '_ {
neighbor_offsets(2)
.map(move |item| (x as isize + item[0], y as isize + item[1]))
.filter(|&(x, y)| self.on_grid(x, y))
.map(|(dx, dy)| (dx as usize, dy as usize))
}
fn on_grid(&self, x: isize, y: isize) -> bool {
0 <= x && x < self.width as isize && 0 <= y && y < self.height as isize
}
}
fn neighbor_offsets(dimension: usize) -> impl Iterator<Item = Vec<isize>> {
(-1..1)
.map(|index| index as isize)
.combinations_with_replacement(dimension)
// skip zero offset
.filter(|items| !items.iter().all(|&item| item == 0))
}
Cargo.toml
[package]
name = "grid"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "*"
然而,对于enumerate_mut()
,的编译当前失败
$ cargo build
Compiling either v1.8.0
Compiling itertools v0.10.5
Compiling grid v0.1.0 (/home/neumann/Projekte/grid)
error[E0505]: cannot move out of `self` because it is borrowed
--> src/lib.rs:64:47
|
63 | pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
| - let's call the lifetime of this reference `'1`
64 | self.items.iter_mut().enumerate().map(move |(index, item)| {
| --------------------- ^^^^^^^^^^^^^^^^^^^^ move out of `self` occurs here
| |
| _________borrow of `self.items` occurs here
| |
65 | | let (x, y) = self.index_to_coordinate(index);
| | ---- move occurs due to use in closure
66 | | (x, y, item)
67 | | })
| |__________- returning this value requires that `self.items` is borrowed for `'1`
For more information about this error, try `rustc --explain E0505`.
error: could not compile `grid` due to previous error
warning: build failed, waiting for other jobs to finish...
我知道我可以将方法index_to_coordinate()
写成一个纯函数,但我想要一个类似于enumerate()
使用成员函数的解决方案。这能做到吗?如果是,如何?
我想要一个解决方案,它使用类似于enumerate((的成员函数。这能做到吗?如果是,如何?
无法执行。不能借用self
,而self.items
是可变借用的。句点考虑在没有成员函数的情况下工作的公式:
pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
self.items.iter_mut().enumerate().map(|(index, item)| {
let x = index % self.width;
let y = (index - x) / self.width;
(x, y, item)
})
}
唯一可行的方法是因为不相交的借用,即编译器知道.width
和.height
是与.items
完全分离的对象(实际上这只适用于2021版,因为不相交借用发生在闭包内外(。但是,如果您使用self
的成员,编译器就失去了这一知识,必须考虑self
可以访问该成员函数中的items
,这是不允许的,因为前面的可变借位必须保持独占。在enumerate()
方法中使用成员函数是有效的,因为可以共享不可变的借用。
函数签名是借用检查、类型检查和类型推理构建的契约。如果成员函数可以访问self
,则必须假设它将访问self
的所有。跨函数接口的部分借用没有语法。
根本做不到?即使我使用unsafe
即使使用了unsafe
,也不允许违反Rust的借用规则,因此不能在.items
被可变借用时使用&Self
。可以访问.width
和.height
,但必须通过指针:*const Self
来完成。以下是它的样子(在操场上与Miri一起测试(:
pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
let this = self as *const Self;
self.items.iter_mut().enumerate().map(move |(index, item)| {
let (x, y) = unsafe { Self::index_to_coordinate(this, index) };
(x, y, item)
})
}
unsafe fn index_to_coordinate(this: *const Self, index: usize) -> (usize, usize) {
let width = *std::ptr::addr_of!((*this).width);
let height = *std::ptr::addr_of!((*this).height);
let x = index % width;
let y = (index - x) / width;
(x, y)
}
然而,我希望我们能同意,这将不再是成员函数,因为它缺少self.index_to_coordinate
语法。当然,我根本不主张你使用这个,这只是一个证明,即使走向荒谬也不可能。
只需使用免费函数。:(