为什么可以从函数返回一个字面值的可变引用?

8

《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)
    }
}

我对这行特别困惑:
let slice = mem::replace(&mut self.0, &mut []);
//                                    ^^^^^^^ 

这个借用怎么检查?如果这是一个不可变借用,RFC 1414 表示 [] rvalue 应该具有 'static 生命周期,这样不可变借用就可以进行借用检查,但是示例显示了一个可变借用!似乎有两种可能情况:

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

我漏掉了什么?

相关:


1
好的,所以[]是一个字面量,因此是静态分配的,我猜这可能是其中的一部分。但是为什么我们能够对常见结构取可变引用呢?可变引用应该是排他的... - Aidan Cully
1
答案的另一部分似乎是零大小类型允许可变引用。引用 rfc1414:"...只有对于零大小类型,别名可变引用才是安全的(因为您从不为它们解引用指针)"。 - Aidan Cully
@Shepmaster 我认为这是与原始问题不同的问题,我已经重新编写以使其更清晰。我不确定我之前的评论是否准确反映了当前状态(我从rfc1414引用的文本表明,在该RFC的扩展中可以改进零大小类型的处理),因此可能需要更权威的回答。 - Aidan Cully
1个回答

9
TL;DR: 编译器对空数组进行了特殊处理,这是安全的,因为您永远无法引用零长度数组的指针,所以不存在可能的可变别名。

RFC 1414,rvalue静态提升,讨论了将值提升为static值的机制。它还有一节关于可变引用的可能扩展(我加粗了):

It would be possible to extend support to &'static mut references, as long as there is the additional constraint that the referenced type is zero sized.

This again has precedence in the array reference constructor:

// valid code today
let y: &'static mut [u8] = &mut [];

The rules would be similar:

  • If a mutable reference to a constexpr rvalue is taken. (&mut <constexpr>)
  • And the constexpr does not contain a UnsafeCell { ... } constructor.
  • And the constexpr does not contain a const fn call returning a type containing a UnsafeCell.
  • And the type of the rvalue is zero-sized.
  • Then instead of translating the value into a stack slot, translate it into a static memory location and give the resulting reference a 'static lifetime.

The zero-sized restriction is there because aliasing mutable references are only safe for zero sized types (since you never dereference the pointer for them).

从这里我们可以看出,在编译器中,对于空数组的可变引用目前是特殊处理的。在 Rust 1.39 中,讨论中提到的扩展 尚未 实现:
struct Zero;

fn example() -> &'static mut Zero {
    &mut Zero
}

error[E0515]: cannot return reference to temporary value
 --> src/lib.rs:4:5
  |
4 |     &mut Zero
  |     ^^^^^----
  |     |    |
  |     |    temporary value created here
  |     returns a reference to data owned by the current function

虽然数组版本可以工作:
fn example() -> &'static mut [i32] {
    &mut []
}

另请参阅:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接