在闭包中使用 split_at_mut 的 Rust 错误 E0495

Rust error E0495 using split_at_mut in a closure

我遇到了一个 "error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements" 的简单函数:

fn assign_split_at_mut<'a, 'b, T>(s: &'b mut &'a mut [T], mid: usize) -> &'a mut [T] {
    let (x, y) = (*s: &'a mut [T]).split_at_mut(mid);
    *s = y;
    x
}

我写了一个 playpen example,其中包含 split_at_mut 的这个 unsafe 变体,它确实有效。

fn assign_split_at_mut_unsafe<'a, T>(s: &mut &'a mut [T], mid: usize) -> &'a mut [T] {
    let len = (*s: &'a mut [T]).len();
    let ptr = (*s: &'a mut [T]).as_mut_ptr();

    unsafe {
        use std::slice::from_raw_parts_mut;
        assert!(mid <= len);
        *s: &'a mut [T] = from_raw_parts_mut(ptr.offset(mid as isize), len - mid);
        from_raw_parts_mut(ptr, mid)
    }
}

其实我想大致这样写:

pub fn slice_header<'a>(&'static self, mut header: &'a mut [u8])
  -> MyResult<HeaderRefs<'a>>
{
    // ...
    let take = |l: usize| -> &'a mut [u8] {
        let (x,y) = header.split_at_mut(l);
        header = y;  x
    };
    let hr = HeaderRefs {
        params: self,
        alpha: array_mut_ref![take(32),0,32],
        gamma: array_mut_ref![take(16),32,16],
        beta: take(self.beta_length as usize),
        surb_log: take(self.surblog_length as usize),
        surb: take(self.surb_length()),
    };
    // ...
    Ok(hr)
}

我相信如果我简单地写出一堆就可以了

let (alpha,header) = header.split_at_mut(32);
let (gamma,header) = header.split_at_mut(16);
// ...

如果我将它们放入一个数组中,也许它会起作用。我无法让它与闭包一起使用,这看起来会更干净。

一位 IRC 用户 nox 通过使用 mem::replace 将需要突变的 &mut [T] 首先移开,提供了一个清晰的答案:

fn reserve<'heap, T>(heap: &mut &'heap mut [T], len: usize) -> &'heap mut [T] {
    let tmp: &'heap mut [T] = ::std::mem::replace(&mut *heap, &mut []);
    let (reserved, tmp) = tmp.split_at_mut(len);
    *heap = tmp;
    reserved
}

这是一个借用问题:

fn assign_split_at_mut<'a, 'b, T>(s: &'b mut &'a mut [T], mid: usize) -> &'a mut [T] {
    let (x, y) = (*s: &'a mut [T]).split_at_mut(mid);
    *s = y;
    x
}

具体来说,split_at_mut 借用了 s,所以当 s 被借用时,您不能 分配给 s

为了理解这个问题,假设我们在这里讨论向量,s: &mut Vec<T>:你可以先从 Vec 中借用一个切片,然后使用 s 来改变它.

这就是为什么 Rust 指定 整个访问路径 被借用,而不仅仅是叶子。


好的,那现在呢?

如@nox所述,解决方案是"dance":

  • &'a mut [T] 的所有权从 s 移至局部变量
  • 借用这个局部变量
  • 分配给s

这样,借用检查器就安心了,因为它知道修改 s 不会影响局部变量及其借用。

有多种方法可以根据情况将所有权移出&mut X,一些常见的方法是:

  • std::mem::replace,
  • std::mem::swap,
  • Option::take 如果 XOption,
  • ...

在您的情况下,replace 更简单。 @nox 提供的解决方案非常简单:

fn reserve<'heap, T>(heap: &mut &'heap mut [T], len: usize) -> &'heap mut [T] {
    let tmp: &'heap mut [T] = ::std::mem::replace(&mut *heap, &mut []);
    let (reserved, tmp) = tmp.split_at_mut(len);
    *heap = tmp;
    reserved
}