两次可变借款中的终身不匹配

时间:2019-07-05 06:21:03

标签: rust borrow-checker

我正在尝试修改可变值的借用,这是一个最小的示例:

fn main() {
    let mut w: [char; 5] = ['h', 'e', 'l', 'l', 'o'];
    let mut wslice: &mut [char] = &mut w;
    advance_slice(&mut wslice);
    advance_slice(&mut wslice);
}

fn advance_slice(s: &mut &mut [char]) {
    let new: &mut [char] = &mut s[1..];
    *s = new;
}

编译器给我这个错误:

error[E0623]: lifetime mismatch
  --> src/main.rs:10:10
   |
8  | fn advance_slice(s: &mut &mut [char]) {
   |                     ----------------
   |                     |
   |                     these two types are declared with different lifetimes...
9  |     let new: &mut [char] = &mut s[1..];
10 |     *s = new;
   |          ^^^ ...but data from `s` flows into `s` here

我试图给两个人以相同的一生,但没有成功。 如果我删除w的可变性,这也可以工作。

1 个答案:

答案 0 :(得分:3)

这个错误消息确实很不幸,我认为它不能很好地解释这里发生的事情。这个问题有点涉及。

该错误是advance_slice()函数的局部错误,这就是我们需要查看的全部内容。切片项目的类型无关紧要,因此让我们采用以下函数定义:

fn advance_slice_mut<T>(s: &mut &mut [T]) {
    let new = &mut s[1..];
    *s = new;
}

第一行将在原始切片的第一项之后创建一个新的切片对象。

为什么甚至允许这样做?我们现在没有对相同数据的两个可变引用吗?原始切片*s包括新切片new,并且两者都允许修改数据。这是合法的,原因是创建子切片时*s隐式重新借入的,并且*s在借用的整个生命周期内都不能再次使用,因此我们仍然只有一个借用主动引用子切片中的数据。重借项的作用域为函数advance_slice_mut(),因此其生存期比原始切片短,这是您得到错误的根本原因–您正在有效地分配一个仅生存到切片末尾的切片将该函数保存到比该函数调用更长的内存位置。

每次调用通过可变引用接受参数的函数时,都会发生这种隐式重借,包括在index_mut()中对&mut s[1..]的隐式调用中。可变引用不能被复制,因为这将创建两个对同一内存的可变引用,Rust语言设计人员决定隐式重借是比默认情况下移动可变引用更符合人体工程学的解决方案。但是,共享引用不会重借,因为它们可以自由复制。这意味着&s[1..]将具有与原始范围相同的生存期,因为两个重叠的不可变片可以共存是非常好的。这解释了为什么您的函数定义对不可变切片能正常工作。

那么我们如何解决这个问题?我相信您打算做的事情是绝对安全的–在将新的片重新分配给旧的片之后,旧的片消失了,因此我们没有两个并发的可变引用同时指向同一内存。要使用安全代码创建与原始切片具有相同寿命的切片,我们需要将原始切片移出所获得的引用。我们可以通过将其替换为空片来实现:

pub fn advance_slice_mut<T>(s: &mut &mut [T]) {
    let slice = std::mem::replace(s, &mut []);
    *s = &mut slice[1..];
}

或者,我们也可以求助于不安全的代码:

use std::slice::from_raw_parts_mut;

pub fn advance_slice_mut<T>(s: &mut &mut [T]) {
    unsafe {
        assert!(!s.is_empty());
        *s = from_raw_parts_mut(s.as_mut_ptr().add(1), s.len() - 1);
    }
}