rust - 双可变借用中的生命周期不匹配

标签 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 的可变性,这也有效。

最佳答案

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

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

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

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

为什么允许这样做?我们现在不是有两个 对同一数据的可变引用吗?原始切片*s包含新切片new,并且都允许修改数据。这是合法的原因是 *s 在创建子切片时被隐式地重新借用,并且 *s 在其生命周期内不能再次使用借用,所以我们仍然只有一个对子切片中数据的事件引用。 reborrow 的作用域是函数 advance_slice_mut(),因此它的生命周期比原始切片短,这是你得到错误的根本原因——你实际上是在尝试分配一个切片,它只生存到函数结束时到比函数调用生命周期更长的内存位置。

每次调用通过可变引用获取参数的函数时,都会发生这种隐式重新借用,包括在 &mut s[1..]< 中对 index_mut() 的隐式调用。不能复制可变引用,因为这会创建对同一内存的两个可变引用,并且 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);
    }
}

关于rust - 双可变借用中的生命周期不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56897524/

相关文章:

rust - 我为什么要使用不同的功能?

rust - “BorrowMutError”使用内部可变性模式

rust - 如果让借用在返回后保留,即使使用 #![feature(nll​​)]

struct - 是否有用于构造包含对临时对象的引用的结构的单行语法?

generics - 如何将外部关联类型强制为我的本地结构类型?

reference - 为什么我可以返回对本地文字的引用,而不是对变量的引用?

java - Rust stdout无法在Java应用程序中打印

recursion - 如何使用递归迭代器展平递归结构?

hash - 为什么我的程序错误地计算了MD2哈希?

rust - 如何运行特定模块下的所有测试功能?