可变引用上的自定义迭代器中的生命周期参数问题

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)

Bre*_*rby 6

根据作者在评论中的澄清,我假设这里的目标是迭代矩阵的矩形子矩阵。例如,给定一个矩阵

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