【问题标题】:Rust error E0495 using split_at_mut in a closureRust 错误 E0495 在闭包中使用 split_at_mut
【发布时间】:2017-06-28 23:09:38
【问题描述】:

我遇到了一个“错误[E0495]:由于需求冲突,无法为自动引用推断适当的生命周期”,使用这个简单的函数:

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 包括 unsafe 的变体 split_at_mut 确实有效。

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);
// ...

如果我将它们放入一个数组中,也许它会起作用。我不能让它与一个看起来更干净的闭包一起工作。

【问题讨论】:

    标签: rust lifetime borrow-checker


    【解决方案1】:

    这是一个借贷问题:

    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: &amp;mut Vec&lt;T&gt;:您可以先从Vec 借用一个切片,然后使用s 对其进行变异。

    这就是 Rust 指定整个访问路径被借用的原因,而不仅仅是叶子。


    好的,那现在呢?

    正如@nox 所说,解决方案是“跳舞”:

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

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

    视情况而定,有多种方法可以将所有权移出&amp;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
    }
    

    【讨论】:

    • 我会为“舞蹈”给出这个最佳答案。 ;)
    • @JeffBurdges: Quote of the week 166: 是的,这就像在你的搭档(借用检查员)已经知道所有步骤的情况下学习跳舞。当您刚开始时,您会经常踩到他们的脚趾,但随着时间的推移,您会逐渐降低动作。最终,你可以开始预测他们的动作,并开始欣赏音乐作为舞蹈的一部分,而不是只专注于让你的脚在正确的位置。
    【解决方案2】:

    IRC 用户nox 提供了一个明确的答案,他使用mem::replace 将需要突变的&amp;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
    }
    

    【讨论】:

    • 面对 nox 的建议缺少tmp 上的类型注释。我出于不必要的偏执而添加了它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-18
    • 2022-12-01
    • 2017-04-10
    • 2020-12-17
    • 1970-01-01
    • 1970-01-01
    • 2015-01-07
    相关资源
    最近更新 更多