为什么可以从函数返回对文字的可变引用?

Aid*_*lly 7 rust borrow-checker

当前版本的The Rustonomicon有这个示例代码:

use std::mem;

pub struct IterMut<'a, T: 'a>(&'a mut [T]);

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        let slice = mem::replace(&mut self.0, &mut []);
        if slice.is_empty() {
            return None;
        }

        let (l, r) = slice.split_at_mut(1);
        self.0 = r;
        l.get_mut(0)
    }
}
Run Code Online (Sandbox Code Playgroud)

我特别对这条线感到困惑:

let slice = mem::replace(&mut self.0, &mut []);
//                                    ^^^^^^^ 
Run Code Online (Sandbox Code Playgroud)

这个借书怎么查?如果这是一个不可变的借用,RFC 1414指出[]右值应该有'static生命周期,这样一个不可变的借用会借用检查,但这个例子显示了一个可变的借用!似乎必须发生两件事之一:

  • 要么[]是临时的(以便它可以可变地使用),在这种情况下它没有'static生命周期,并且不应该借用检查;
  • 或者它[]'static生命周期,因此不应该进行可变借用(因为我们在借用时不保证独占访问),并且不应该借用检查。

我错过了什么?

有关的:

She*_*ter 7

TL;DR:空数组在编译器中是特殊情况,它是安全的,因为您永远不能取消对零长度数组的指针的引用,因此没有可能的可变别名。


RFC 1414,右值静态提升,讨论了将值提升为static值的机制。它有一个关于可变引用的可能扩展的部分(我的粗体):

&'static mut只要存在引用类型为零大小的附加约束,就可以扩展对引用的支持。

在数组引用构造函数中再次具有优先级

// valid code today
let y: &'static mut [u8] = &mut [];
Run Code Online (Sandbox Code Playgroud)

规则是类似的:

  • 如果采用对 constexpr 右值的可变引用。( &mut <constexpr>)
  • 而且 constexpr 不包含UnsafeCell { ... }构造函数。
  • 并且 constexpr 不包含返回包含UnsafeCell.
  • 并且右值的类型为零大小。
  • 然后,不是将值转换为堆栈槽,而是将其转换为静态内存位置并为结果引用提供 'static生命周期。

存在零大小限制是因为别名可变引用仅对零大小类型是安全的(因为您永远不会为它们取消引用指针)

由此,我们可以看出对空数组的可变引用目前在编译器中是特殊情况。在锈1.39,所讨论的分机已没有得到落实:

struct Zero;

fn example() -> &'static mut Zero {
    &mut Zero
}
Run Code Online (Sandbox Code Playgroud)
struct Zero;

fn example() -> &'static mut Zero {
    &mut Zero
}
Run Code Online (Sandbox Code Playgroud)

虽然阵列版本确实有效:

fn example() -> &'static mut [i32] {
    &mut []
}
Run Code Online (Sandbox Code Playgroud)

也可以看看: