本文介绍了自定义迭代器中基于可变引用的生命周期参数问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个类似下面的自定义迭代器,但是不能解决引用问题.

I'd like to implement a custom Iterator like below, but cannot solve reference problem.

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
        }
    }
}

并获得以下错误消息.

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/main.rs:13:23
   |
13 |             Some(&mut self.data[i + self.shape.0 * j])
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |

推荐答案

基于作者在评论中的澄清,我假设此处的目标是迭代矩阵的矩形子矩阵.例如,给定矩阵

Based on the author's clarification in the comments, I'm assuming that the goal here is to iterate over a rectangular submatrix of a matrix. For example, given a matrix

100  200  300  400  500  600
110  210  310  410  510  610
120  220  320  420  520  620
130  230  330  430  530  630

以行优先顺序的切片表示

as represented by a slice in row-major order

[100, 200, 300, 400, 500, 600, 110, ..., 530, 630]

我们要遍历子矩阵,例如

we want to iterate over a submatrix such as

210  310  410  510
220  320  420  520

再次以行优先的顺序

,因此我们将获得的元素依次为

again in row-major order, so the elements we would get would be, in order,

210, 310, 410, 510, 220, 320, 420, 520

在这种情况下,可以使用安全的Rust相对有效地解决此问题.诀窍是使用...的 split_at_mut 方法在Iter2DMutdata字段中进行切片,以根据需要一次剥离一个可变参考.随着迭代的进行,data字段将更新为越来越小的片,以使其不再包含已被迭代的元素;这是必要的,因为在任何给定的迭代中,Rust都不允许我们生成对元素的可变引用,同时还保留包含该元素的可变切片.通过更新切片,我们可以确保它始终与所有先前对next()的调用所生成的可变引用不相交,从而满足Rust借阅检查器的要求.这是可以做到的:

In this situation, it is possible to solve this problem relatively efficiently using safe Rust. The trick is to use the split_at_mut method of the slice in the data field of Iter2DMut, in order to peel off one mutable reference at a time as needed. As the iteration proceeds, the data field is updated to a smaller and smaller slice, so that it no longer encompasses elements which have already been iterated over; this is necessary, because at any given iteration Rust would not allow us to produce a mutable reference to an element while also retaining a mutable slice containing that element. By updating the slice, we can ensure that it is always disjoint from the mutable references which have been produced by all previous calls to next(), satisfying the Rust borrow checker. Here is how this can be done:

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)
        }
    }
}
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
        }
    }
}
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);
    }
}

这里还有一个值得注意的技巧:我们使用mem::swapIter2DMut中移出data字段,以便在其上调用split_at_mut.我们临时交换一个哑数值&mut [];这是必要的,因为Rust不允许我们(随机地)从(可变)借来的结构中移出一个值,而不能同时放回一些东西.另一方面,如果我们没有尝试移出data而是直接调用split_at_mut,就像在self.data.split_at_mut(1)中那样,它将使借阅检查程序失败,因为那样的话,我们将一直借用self.data它的生存时间与next方法中的&mut self引用输入一样长,而不一定与我们需要的'a生存期一样长.

There's one other trick here worth noting: we use mem::swap to move out the data field from the Iter2DMut in order to call split_at_mut on it. We temporarily swap in a dummy value &mut []; this is necessary since Rust won't allow us to move a value out of a (mutably) borrowed struct (even temporarily) without putting something back in at the same time. On the other hand, if we hadn't tried to move data out but had simply called split_at_mut directly, as in self.data.split_at_mut(1), it would have failed the borrow checker, because then we would have been borrowing self.data which only lives as long as the the &mut self reference input into the next method, which is not necessarily long as the 'a lifetime that we need it to be.

这篇关于自定义迭代器中基于可变引用的生命周期参数问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 11:23