T.S*_*hin 2 iterator lifetime rust
我想实现一个如下所示的自定义迭代器,但无法解决引用问题。
use itertools::Product;
use std::ops::Range;
struct Iter2DMut<'a, T: 'a> {
data: &'a mut [T],
shape: (usize, usize),
idx_iter: Product<Range<usize>, Range<usize>>,
}
impl<'a, T: 'a> Iterator for Iter2DMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
if let Some((i, j)) = self.idx_iter.next() {
Some(&mut self.data[i + self.shape.0 * j])
} else {
None
}
}
}
Run Code Online (Sandbox Code Playgroud)
并收到以下错误消息。
use itertools::Product;
use std::ops::Range;
struct Iter2DMut<'a, T: 'a> {
data: &'a mut [T],
shape: (usize, usize),
idx_iter: Product<Range<usize>, Range<usize>>,
}
impl<'a, T: 'a> Iterator for Iter2DMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
if let Some((i, j)) = self.idx_iter.next() {
Some(&mut self.data[i + self.shape.0 * j])
} else {
None
}
}
}
Run Code Online (Sandbox Code Playgroud)
根据作者在评论中的澄清,我假设这里的目标是迭代矩阵的矩形子矩阵。例如,给定一个矩阵
100 200 300 400 500 600
110 210 310 410 510 610
120 220 320 420 520 620
130 230 330 430 530 630
Run Code Online (Sandbox Code Playgroud)
由按行优先顺序的切片表示
[100, 200, 300, 400, 500, 600, 110, ..., 530, 630]
Run Code Online (Sandbox Code Playgroud)
我们想要迭代一个子矩阵,例如
210 310 410 510
220 320 420 520
Run Code Online (Sandbox Code Playgroud)
再次按行优先顺序,因此我们将得到的元素按顺序为:
210, 310, 410, 510, 220, 320, 420, 520
Run Code Online (Sandbox Code Playgroud)
在这种情况下,使用安全的 Rust 可以相对有效地解决这个问题。诀窍是使用字段中切片的split_at_mut方法,以便根据需要一次剥离一个可变引用。随着迭代的进行,该字段被更新为越来越小的切片,以便它不再包含已经迭代过的元素;这是必要的,因为在任何给定的迭代中,Rust 不允许我们生成对元素的可变引用,同时还保留包含该元素的可变切片。通过更新切片,我们可以确保它始终与所有先前调用生成的可变引用不相交,满足 Rust 借用检查器。以下是如何做到这一点:dataIter2DMutdatanext()
use itertools::{Itertools, Product};
use std::ops::Range;
use std::mem;
struct Iter2DMut<'a, T: 'a> {
data: &'a mut [T],
full_shape: (usize, usize),
sub_shape: (usize, usize),
idx_iter: Product<Range<usize>, Range<usize>>,
}
impl<'a, T> Iter2DMut<'a, T> {
fn new(
data: &'a mut [T],
full_shape: (usize, usize),
sub_shape: (usize, usize),
offset: (usize, usize),
) -> Self {
assert!(full_shape.0 * full_shape.1 == data.len());
assert!(offset.0 + sub_shape.0 <= full_shape.0);
assert!(offset.1 + sub_shape.1 <= full_shape.1);
Iter2DMut {
data: &mut data[offset.0 * full_shape.1 + offset.1 ..],
full_shape,
sub_shape,
idx_iter: (0..sub_shape.0).cartesian_product(0..sub_shape.1)
}
}
}
Run Code Online (Sandbox Code Playgroud)
impl<'a, T: 'a> Iterator for Iter2DMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
if let Some((_, j)) = self.idx_iter.next() {
let mut data: &'a mut [T] = &mut [];
mem::swap(&mut self.data, &mut data);
let (first, rest) = data.split_at_mut(1);
data = rest;
if j == self.sub_shape.1 - 1 {
let n_skip = self.full_shape.1 - self.sub_shape.1;
let (_, rest) = data.split_at_mut(n_skip);
data = rest;
}
self.data = data;
Some(&mut first[0])
} else {
None
}
}
}
Run Code Online (Sandbox Code Playgroud)
fn main() {
let mut v: Vec<usize> = vec![
100, 200, 300, 400, 500, 600,
110, 210, 310, 410, 510, 610,
120, 220, 320, 420, 520, 620,
130, 230, 330, 430, 530, 630,
];
for x in Iter2DMut::new(&mut v, (4, 6), (2, 4), (1, 1)) {
println!("{}", x);
}
}
Run Code Online (Sandbox Code Playgroud)
这里还有一个值得注意的技巧:我们通常将字段从 中mem::swap移出以便调用它。我们暂时换入一个虚拟值;这是必要的,因为 Rust 不允许我们将值从(可变的)借用的结构中移出(即使是暂时的)而不同时将某些内容放回其中。另一方面,如果我们没有尝试移出,而是直接调用(如 中所示),那么借用检查器就会失败,因为这样我们就会借用,而该借用仅在引用输入到方法,它的生命周期不一定像我们需要的那样长。dataIter2DMutsplit_at_mut&mut []datasplit_at_mutself.data.split_at_mut(1)self.data&mut selfnext'a
| 归档时间: |
|
| 查看次数: |
835 次 |
| 最近记录: |