如果迭代器有生命周期,如何在可变迭代器上实现下一个方法?
How to implement the next method on mutable Iterator if the Iterator has a lifetime?
我想在 Rust 中实现一个 table 类型,所以我有以下代码:
use std::ops::{Index, IndexMut};
#[derive(Clone)]
pub struct Table<T>
where
T: Clone,
{
row: usize,
col: usize,
data: Vec<Vec<T>>,
}
impl<T> Table<T>
where
T: Clone,
{
pub fn new(row: usize, col: usize) -> Self {
let mut t = Table {
row,
col,
data: Vec::<Vec<T>>::with_capacity(row),
};
t.data.resize(row, Vec::<T>::with_capacity(col));
t
}
pub fn size(&self) -> (usize, usize) {
(self.row(), self.col())
}
pub fn col(&self) -> usize {
self.col
}
pub fn row(&self) -> usize {
self.row
}
pub fn cell_iter(&self) -> TableCellIter<T> {
TableCellIter {
table: self,
row: 0,
col: 0,
}
}
pub fn cell_iter_mut(&mut self) -> TableCellIterMut<'_, T> {
TableCellIterMut {
table: self,
row:0,
col:0,
}
}
}
impl<T> Index<(usize, usize)> for Table<T>
where
T: Clone,
{
type Output = T;
fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
if row >= self.row() {
panic!("out of range")
}
&self.data[row][col]
}
}
impl<T> IndexMut<(usize, usize)> for Table<T>
where
T: Clone,
{
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
while self.data.len() <= row {
self.data.push(Vec::<T>::with_capacity(self.col()));
}
&mut self.data[row][col]
}
}
pub struct TableCellIter<'a, T>
where
T: Clone,
{
table: &'a Table<T>,
row: usize,
col: usize,
}
impl<'a, T> Iterator for TableCellIter<'a, T>
where
T: Clone,
{
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.col == self.table.col() {
self.row += 1;
self.col = 0;
}
if self.row >= self.table.row() {
return None;
}
let ref cell = self.table[(self.row, self.col)];
self.col += 1;
Some(cell)
}
}
pub struct TableCellIterMut<'a, T>
where
T: Clone,
{
table: &'a mut Table<T>,
row: usize,
col: usize,
}
impl<'a, T> Iterator for TableCellIterMut<'a, T>
where
T: Clone,
{
type Item = &'a mut T;
fn next<'b: 'a>(&'b mut self) -> Option<Self::Item>
{
if self.col == self.table.col() {
self.row += 1;
self.col = 0;
}
if self.row >= self.table.row() {
return None;
}
let ref mut cell = self.table[(self.row, self.col)];
self.col += 1;
Some(cell)
}
}
在 TableCellIter 类型工作正常的地方,TableCellIterMut 不能。编译器警告我:
error[E0195]: lifetime parameters or bounds on method `next` do not match the trait declaration
--> src/lib.rs:125:12
|
125 | fn next<'b: 'a>(&'b mut self) -> Option<Self::Item>
| ^^^^^^^^ lifetimes do not match method in trait
error: aborting due to previous error
For more information about this error, try `rustc --explain E0195`.
error: could not compile `sample`
但是如果我删除 TableCellIterMut 的下一个生命周期范围:
fn next(&mut self) -> Option<Self::Item>
似乎无法推断下一个生命周期参数:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src/lib.rs:135:28
|
135 | let ref mut cell = self.table[(self.row, self.col)];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 126:13...
--> src/lib.rs:126:13
|
126 | fn next(&mut self) -> Option<Self::Item>
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:135:28
|
135 | let ref mut cell = self.table[(self.row, self.col)];
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 119:6...
--> src/lib.rs:119:6
|
119 | impl<'a, T> Iterator for TableCellIterMut<'a, T>
| ^^
note: ...so that the types are compatible
--> src/lib.rs:127:5
|
127 | / {
128 | | if self.col == self.table.col() {
129 | | self.row += 1;
130 | | self.col = 0;
... |
137 | | Some(cell)
138 | | }
| |_____^
= note: expected `Iterator`
found `Iterator`
error: aborting due to previous error
我的问题是,为什么 TableCellIter 中的 next 是正确的,而 TableCellIterMut 中的是错误的?
下一步如何正确执行?
由迭代器编辑的项目引用 return 可以具有重叠的生命周期,因为正如您所注意到的,它们的生命周期不能与 self
参数的生命周期绑定到 next()
.如果 next()
方法对同一个可变引用 return 两次,你最终会得到对同一个对象的多个可变引用,这是不允许的。这意味着您的代码只有在所有 return 引用都是不相交的情况下才是安全的,如果正确实现了迭代器就是这种情况。然而,这种保证只能通过对代码的语义进行推理来得出,而不能仅仅通过查看所涉及的函数调用的原型来得出。这是borrow checker做不到的(事实上,在一般情况下可以证明这是不可能的),所以编译器拒绝了代码。
对于不可变引用,这不是问题,因为允许对同一对象有多个不可变引用。
有几种方法可以解决这个问题。我的建议是将数据表示从 Vec<Vec<T>>
更改为平面 Vec<T>
,并相应地实施索引操作。这将使您的代码整体上更简单,同时效率更高。然后可以通过推迟到 Vec<>
.
上的现有迭代器来实现迭代器
如果您想保留 Vec<Vec<T>>
结构,您仍然可以使用 Vec
上的现有迭代器:
pub fn cell_iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.data.iter_mut().flat_map(|row| row.iter_mut())
}
请注意,您甚至不需要显式定义自定义迭代器类型。 iter()
方法可以实现类似的实现。
如果你想从头开始创建一个完全自定义的容器类型,你通常需要对可变迭代器使用不安全的代码,但在这种情况下,依赖 Vec
的迭代器实现要好得多(内部使用不安全代码)。
我想在 Rust 中实现一个 table 类型,所以我有以下代码:
use std::ops::{Index, IndexMut};
#[derive(Clone)]
pub struct Table<T>
where
T: Clone,
{
row: usize,
col: usize,
data: Vec<Vec<T>>,
}
impl<T> Table<T>
where
T: Clone,
{
pub fn new(row: usize, col: usize) -> Self {
let mut t = Table {
row,
col,
data: Vec::<Vec<T>>::with_capacity(row),
};
t.data.resize(row, Vec::<T>::with_capacity(col));
t
}
pub fn size(&self) -> (usize, usize) {
(self.row(), self.col())
}
pub fn col(&self) -> usize {
self.col
}
pub fn row(&self) -> usize {
self.row
}
pub fn cell_iter(&self) -> TableCellIter<T> {
TableCellIter {
table: self,
row: 0,
col: 0,
}
}
pub fn cell_iter_mut(&mut self) -> TableCellIterMut<'_, T> {
TableCellIterMut {
table: self,
row:0,
col:0,
}
}
}
impl<T> Index<(usize, usize)> for Table<T>
where
T: Clone,
{
type Output = T;
fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
if row >= self.row() {
panic!("out of range")
}
&self.data[row][col]
}
}
impl<T> IndexMut<(usize, usize)> for Table<T>
where
T: Clone,
{
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
while self.data.len() <= row {
self.data.push(Vec::<T>::with_capacity(self.col()));
}
&mut self.data[row][col]
}
}
pub struct TableCellIter<'a, T>
where
T: Clone,
{
table: &'a Table<T>,
row: usize,
col: usize,
}
impl<'a, T> Iterator for TableCellIter<'a, T>
where
T: Clone,
{
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.col == self.table.col() {
self.row += 1;
self.col = 0;
}
if self.row >= self.table.row() {
return None;
}
let ref cell = self.table[(self.row, self.col)];
self.col += 1;
Some(cell)
}
}
pub struct TableCellIterMut<'a, T>
where
T: Clone,
{
table: &'a mut Table<T>,
row: usize,
col: usize,
}
impl<'a, T> Iterator for TableCellIterMut<'a, T>
where
T: Clone,
{
type Item = &'a mut T;
fn next<'b: 'a>(&'b mut self) -> Option<Self::Item>
{
if self.col == self.table.col() {
self.row += 1;
self.col = 0;
}
if self.row >= self.table.row() {
return None;
}
let ref mut cell = self.table[(self.row, self.col)];
self.col += 1;
Some(cell)
}
}
在 TableCellIter 类型工作正常的地方,TableCellIterMut 不能。编译器警告我:
error[E0195]: lifetime parameters or bounds on method `next` do not match the trait declaration
--> src/lib.rs:125:12
|
125 | fn next<'b: 'a>(&'b mut self) -> Option<Self::Item>
| ^^^^^^^^ lifetimes do not match method in trait
error: aborting due to previous error
For more information about this error, try `rustc --explain E0195`.
error: could not compile `sample`
但是如果我删除 TableCellIterMut 的下一个生命周期范围:
fn next(&mut self) -> Option<Self::Item>
似乎无法推断下一个生命周期参数:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src/lib.rs:135:28
|
135 | let ref mut cell = self.table[(self.row, self.col)];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 126:13...
--> src/lib.rs:126:13
|
126 | fn next(&mut self) -> Option<Self::Item>
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:135:28
|
135 | let ref mut cell = self.table[(self.row, self.col)];
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 119:6...
--> src/lib.rs:119:6
|
119 | impl<'a, T> Iterator for TableCellIterMut<'a, T>
| ^^
note: ...so that the types are compatible
--> src/lib.rs:127:5
|
127 | / {
128 | | if self.col == self.table.col() {
129 | | self.row += 1;
130 | | self.col = 0;
... |
137 | | Some(cell)
138 | | }
| |_____^
= note: expected `Iterator`
found `Iterator`
error: aborting due to previous error
我的问题是,为什么 TableCellIter 中的 next 是正确的,而 TableCellIterMut 中的是错误的?
下一步如何正确执行?
由迭代器编辑的项目引用 return 可以具有重叠的生命周期,因为正如您所注意到的,它们的生命周期不能与 self
参数的生命周期绑定到 next()
.如果 next()
方法对同一个可变引用 return 两次,你最终会得到对同一个对象的多个可变引用,这是不允许的。这意味着您的代码只有在所有 return 引用都是不相交的情况下才是安全的,如果正确实现了迭代器就是这种情况。然而,这种保证只能通过对代码的语义进行推理来得出,而不能仅仅通过查看所涉及的函数调用的原型来得出。这是borrow checker做不到的(事实上,在一般情况下可以证明这是不可能的),所以编译器拒绝了代码。
对于不可变引用,这不是问题,因为允许对同一对象有多个不可变引用。
有几种方法可以解决这个问题。我的建议是将数据表示从 Vec<Vec<T>>
更改为平面 Vec<T>
,并相应地实施索引操作。这将使您的代码整体上更简单,同时效率更高。然后可以通过推迟到 Vec<>
.
如果您想保留 Vec<Vec<T>>
结构,您仍然可以使用 Vec
上的现有迭代器:
pub fn cell_iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.data.iter_mut().flat_map(|row| row.iter_mut())
}
请注意,您甚至不需要显式定义自定义迭代器类型。 iter()
方法可以实现类似的实现。
如果你想从头开始创建一个完全自定义的容器类型,你通常需要对可变迭代器使用不安全的代码,但在这种情况下,依赖 Vec
的迭代器实现要好得多(内部使用不安全代码)。