如何在 Rust 中实现向量的多个可变借用? [英] How to implement multiple mutable borrows of a vector in Rust?

查看:36
本文介绍了如何在 Rust 中实现向量的多个可变借用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 Rust 中实现矩阵.代码已针对示例进行了修改,但可能存在小错误:

I am implementing matrices in Rust. The code is adapted for the example, but there might be minor mistakes:

#[derive(Debug, PartialEq)]
pub struct Matrix<T> {
    inner: Vec<Vec<T>>,
}

impl<T> Matrix<T> {
    pub fn dim(&self) -> (usize, usize) {
        if self.inner.len() == 0 {
            (0, 0)
        } else {
            (self.inner.len(), self.inner[0].len())
        }
    }
}

我希望能够获得矩阵的象限:

I want to have the ability to get quadrants of the matrix:

+----+----+
| Q1 | Q2 |
+----+----+
| Q3 | Q4 |
+----+----+

我引入了 SliceSliceMut 结构来借用矩阵的一部分:

I introduced the Slice and SliceMut structures to borrow a part of the matrix:

pub struct Slice<'a, T: 'a> {
    matrix: &'a Matrix<T>,
    start: (usize, usize),
    end: (usize, usize),
}

pub struct SliceMut<'a, T: 'a> {
    matrix: &'a mut Matrix<T>,
    start: (usize, usize),
    end: (usize, usize),
}

现在我要实现两个功能:

Now I want to implement two functions:

  • quadrants - 得到四片元组
  • quadrants_mut - 获得四个可变切片的元组
  • quadrants - to get a tuple of four slices
  • quadrants_mut - to get a tuple of four mutable slices

我不能在 quadrants_mut 中多次可变地借用一个矩阵:

I cannot mutably borrow one matrix several times in quadrants_mut:

fn quadrants_mut<'a, T>(matrix: &'a mut Matrix<T>) -> (SliceMut<'a, T>, SliceMut<'a, T>, SliceMut<'a, T>, SliceMut<'a, T>) {
    let (rows, cols) = matrix.dim();

    let mid_rows = rows / 2;
    let mid_cols = cols / 2;

    let a = SliceMut { matrix: matrix, start: (0, 0), end: (mid_rows, mid_cols) };
    let b = SliceMut { matrix: matrix, start: (0, mid_rows), end: (mid_cols, cols) };
    let c = SliceMut { matrix: matrix, start: (mid_rows, rows), end: (0, mid_cols) };
    let d = SliceMut { matrix: matrix, start: (mid_rows, rows), end: (mid_cols, cols) };

    (a, b, c, d)
}

当我尝试编译它时,出现错误:

When I try to compile that, I have an error:

error[E0499]: cannot borrow `*matrix` as mutable more than once at a time
  --> src/matrix/slice.rs:62:13
   |
59 |     let a = SliceMut { matrix: matrix, start: (0, 0), end: (mid_rows, mid_cols) };
   |                        ------ first mutable borrow occurs here
...
60 |     let b = SliceMut { matrix: matrix, start: (0, mid_rows), end: (mid_cols, cols) };
   |                        ^^^^^^ second mutable borrow occurs here
...
66 | }

我试图可变地借用矩阵四次.我应该如何更改代码以使其编译?

I am trying to mutably borrow a matrix four times. How should I change the code to make it compile?

推荐答案

你想做的,绝对是可能的.然而这很难.真的很难.

What you want to do, is definitely possible. However it's hard. Really hard.

幸运的是,我只会展示如何做简单的部分.

Luckily, I'll only show how to do the easy part.

如果你看看如何split_at_mut 已实现,您可以注意到它需要您创建一个模拟quadrants_mut 返回值的并行结构(即SliceMut):

If you look at how split_at_mut is implemented, you can notice it requires you to create a parallel structure that mimics return value of quadrants_mut (i.e. SliceMut):

pub struct SliceMut<'a, T: 'a> {
    matrix: &'a mut Matrix<T>,
    start: (usize, usize),
    end: (usize, usize),
}

#[repr(C)]
struct MatrixRaw<T> {
    data: *const Matrix<T>,
    start: (usize, usize),
    end: (usize, usize),
}

注意这两种结构之间的相似性.如果它们在任何时候出现分歧,您的 mem::transmute 要么停止工作,要么您的安全代码出现段错误.

Note the similarity between these two structures. If at any point they diverge, your mem::transmute will either stop working, or your safe code will experience segfaults.

然后我们创建一个将 MatrixRaw 转换为 SliceMut 的方法.

Then we create a method that transmutes MatrixRaw into SliceMut.

#[inline]
pub unsafe fn from_raw_mat_mut<'a, T>(
    p: *mut Matrix<T>,
    start: (usize, usize),
    end: (usize, usize),
) -> SliceMut<'a, T> {
    mem::transmute(MatrixRaw {
        data: p,
        start: start,
        end: end,
    })
}

作为最后一步,我们向 quadrant_mut 添加一个 unsafe 块:

As a last step, we add an unsafe block to quadrant_mut:

unsafe {
    let a = from_raw_mat_mut(matrix, (0, 0), (mid_rows, mid_cols));
    let b = from_raw_mat_mut(matrix, (0, mid_rows), (mid_cols, cols));
    let c = from_raw_mat_mut(matrix, (mid_rows, rows), (0, mid_cols));
    let d = from_raw_mat_mut(matrix, (mid_rows, rows), (mid_cols, cols));

    (a, b, c, d)
}

链接到游乐场

难的部分:难的部分来了 - 确保您的方法和迭代器不会意外地使您的数据和不变量无效.这在 Matrix 情况下很难实现.

HARD PART: Here comes the hard part - making sure your methods, and iterators, don't accidentally invalidate your data and invariants. This is extremely hard to achieve in the Matrix case.

为什么?好吧,因为没有一种很好的方法可以对您的数据说不要触摸这些部分",就像使用数组一样.使用数组,您只需抵消您的数据就可以了.但是Matrix?这并非不可能,但我怀疑我不知道一种不会引入性能损失的方法.

Why? Well, because there isn't a nice way to say to your data, "don't touch these parts" like you can with an array. With an array, you just offset your data and you're pretty much good to go. But a Matrix? It's not impossible, but I suspect I don't know of a way that doesn't introduce performance penalties.

这篇关于如何在 Rust 中实现向量的多个可变借用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆