为什么可以 return 对函数文字的可变引用?
Why is it possible to return a mutable reference to a literal from a function?
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
生命周期,因此应该不可能进行可变借用(因为我们在进行借用时不保证独占访问),并且应该不是借支票。
我错过了什么?
相关:
本题侧重于不可变引用;这个问题是关于 mutable references.
这道题的重点是在函数内部获取引用;这个问题是关于返回一个参考。
TL;DR: 空数组在编译器中是特殊情况,它是安全的,因为你永远不能取消引用零长度数组的指针,所以不可能有可变别名。
RFC 1414, rvalue static promotion, discusses the mechanism by which values are promoted to static
values. It has a section about possible extensions for mutable references(加粗我的):
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 []
}
另请参阅:
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
生命周期,因此应该不可能进行可变借用(因为我们在进行借用时不保证独占访问),并且应该不是借支票。
我错过了什么?
相关:
本题侧重于不可变引用;这个问题是关于 mutable references.
这道题的重点是在函数内部获取引用;这个问题是关于返回一个参考。
TL;DR: 空数组在编译器中是特殊情况,它是安全的,因为你永远不能取消引用零长度数组的指针,所以不可能有可变别名。
RFC 1414, rvalue static promotion, discusses the mechanism by which values are promoted to static
values. It has a section about possible extensions for mutable references(加粗我的):
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 []
}
另请参阅: