【问题标题】:Why is it possible to return a mutable reference to a literal from a function?为什么可以从函数返回对文字的可变引用?
【发布时间】:2019-05-09 19:43:15
【问题描述】:

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

我特别对这一行感到困惑:

let slice = mem::replace(&mut self.0, &mut []);
//                                    ^^^^^^^ 

这个借位如何检查?如果这是一个不可变借用,RFC 1414 表示[] 右值应该具有'static 生命周期,因此不可变借用将借用检查,但示例显示可变借用!似乎必须发生以下两件事之一:

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

我错过了什么?

相关:

【问题讨论】:

  • 好的,所以[] 是一个文字,因此是静态分配的,我猜这可能是其中的一部分。但是,为什么我们能够对通用结构进行可变引用呢?可变引用应该是排他的......
  • 答案的另一部分似乎是零大小的类型允许可变引用。引用rfc1414:“...别名可变引用仅对零大小类型是安全的(因为您永远不会取消引用它们的指针)。”
  • @Shepmaster 我认为这是一个与原始问题不同的问题,我已重新编写以使其更清楚。我不确定我上面的 cmets 是否准确地反映了当前状态(我从 rfc1414 引用的文本表明可以在该 RFC 的扩展中改进对零大小类型的处理),因此这可能值得更权威的回应。

标签: rust borrow-checker


【解决方案1】:

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


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

可以将支持扩展到&amp;'static mut 引用, 只要有额外的约束 引用类型的大小为零。

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

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

规则类似:

  • 如果采用对 constexpr 右值的可变引用。 (&amp;mut &lt;constexpr&gt;)
  • 并且 constexpr 不包含 UnsafeCell { ... } 构造函数。
  • 并且 constexpr 不包含返回包含 UnsafeCell 的类型的 const fn 调用。
  • 并且右值的类型是零大小的。
  • 然后不是将值转换为堆栈槽,而是转换 将其放入静态内存位置并为生成的引用提供 'static 终生。

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

由此,我们可以看出,对空数组的可变引用目前在编译器中是特殊情况。在 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 []
}

另见:

【讨论】:

    猜你喜欢
    • 2020-01-09
    • 2018-10-24
    • 2020-07-25
    • 1970-01-01
    • 2014-05-04
    • 2015-12-17
    相关资源
    最近更新 更多