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

1 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;
}
Run Code Online (Sandbox Code Playgroud)

编译器给我这个错误:

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
Run Code Online (Sandbox Code Playgroud)

我试图为两者借用相同的生命周期,但没有成功。如果我删除 的可变性,这也有效w

Sve*_*ach 5

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

\n\n

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

\n\n
fn advance_slice_mut<T>(s: &mut &mut [T]) {\n    let new = &mut s[1..];\n    *s = new;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

为什么这甚至被允许?我们现在不是有两个对同一数据的可变引用吗?原始切片*s包含新切片new,并且两者都允许修改数据。这是合法的原因是*s在创建子切片时隐式重新借用*s,并且在该借用的生命周期内不能再次使用,因此我们仍然只有一个对子切片中数据的活动引用。重新借用的作用域为 function advance_slice_mut(),因此具有比原始切片更短的生命周期,这是您收到 \xe2\x80\x93 错误的根本原因,您实际上正在尝试分配一个仅存活到末尾的切片将函数转移到比函数调用寿命更长的内存位置。

\n\n

每当您调用通过可变引用获取参数的函数时,包括在对index_mut()in的隐式调用中,都会发生这种隐式重借用&mut s[1..]。可变引用无法复制,因为这会创建对同一内存的两个可变引用,并且 Rust 语言设计者认为隐式重借是比默认情况下移动可变引用更符合人体工程学的解决方案。不过,共享引用不会发生重新借用,因为它们可以自由复制。这意味着它将&s[1..]具有与原始范围相同的生命周期,因为两个重叠的不可变切片共存是完全可以的。这解释了为什么您的函数定义适用于不可变切片。

\n\n

那么我们如何解决这个问题呢?我相信,在将新切片重新分配给旧切片后,您打算做的事情是完全安全的 \xe2\x80\x93 ,旧切片消失了,因此我们没有对同一内存的两个并发可变引用。要在安全代码中创建与原始切片具有相同生命周期的切片,我们需要将原始切片移出我们获得的引用。我们可以通过用空切片替换它来做到这一点:

\n\n
pub fn advance_slice_mut<T>(s: &mut &mut [T]) {\n    let slice = std::mem::replace(s, &mut []);\n    *s = &mut slice[1..];\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
use std::slice::from_raw_parts_mut;\n\npub fn advance_slice_mut<T>(s: &mut &mut [T]) {\n    unsafe {\n        assert!(!s.is_empty());\n        *s = from_raw_parts_mut(s.as_mut_ptr().add(1), s.len() - 1);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 你*可以*假设它是安全的,因为该函数没有被标记为“不安全”。由实现该函数的人来确保所有必需的不变量都成立,即使它们内部使用了“不安全”块。 (2认同)