考虑以下内容(在 Rust >= 1.54 上):
pub fn assign_refs(i: &mut u32, j: &mut u32) -> u32 {
*i = 42;
*j = 7;
*i
}
Run Code Online (Sandbox Code Playgroud)
如果可变引用之间没有别名,则编译为:
mov dword ptr [rdi], 42
mov dword ptr [rsi], 7
mov eax, 42
ret
Run Code Online (Sandbox Code Playgroud)
现在考虑:
mov dword ptr [rdi], 42
mov dword ptr [rsi], 7
mov eax, 42
ret
Run Code Online (Sandbox Code Playgroud)
(请注意,一次仅存在一个可变引用,因此这不是未定义的行为 if i == j)。
由于指针可能存在别名,因此必须重新加载最后一个表达式:
mov dword ptr [rdi], 42
mov dword ptr [rsi], 7
mov eax, dword ptr [rdi]
ret
Run Code Online (Sandbox Code Playgroud)
下一个示例是未定义的行为,如果j指向i:
pub fn assign_undefined_behavior_if_same(i: &mut u32, j: *mut u32) -> u32 {
*i = 42;
*unsafe {&mut *j} = 7; // UB if j points to i, second mut ref.
*i
}
Run Code Online (Sandbox Code Playgroud)
因此,它编译为与 相同的代码assign_refs,返回“错误”值。
我的问题是关于:
pub fn assign_mixed(i: &mut u32, j: *mut u32) -> u32 {
*i = 42;
let i_ptr = i as *mut u32;
std::convert::identity(i); // *Not* a reborrow, a move and drop.
// i no longer exists.
// *i = 42; // use of moved value: `i`
// At this point, why not the same as assign_ptrs?
*unsafe {&mut *j} = 7;
// Assumes that i_ptr is not aliased just because it came from a &mut?
unsafe {*i_ptr}
}
Run Code Online (Sandbox Code Playgroud)
这编译成与 相同的东西assign_refs,我发现这令人惊讶。
唯一的别名引用i在函数中途结束。到那时,为什么i_ptr我们会j受到同样的对待,就像我们在一样assign_ptrs?指针允许别名,因此j可以指向i/i_ptr 并且 i不再存在。
作为参考,可以这样称呼:
fn test() {
let mut i = 0;
let mut i_ref = &mut i;
let i_ptr = i_ref as *mut u32;
assign_mixed(i_ref, i_ptr);
}
Run Code Online (Sandbox Code Playgroud)
这是过度激进的 noalias 传播吗?
Rust 遵循来自 LLVM 的noalias模型,请参阅Behaviorthoughtundefined:&mut T
这表明在函数执行期间,通过基于参数或返回值的指针值访问的内存位置也不会通过不基于参数或返回值的指针值访问。此保证仅适用于在函数执行期间通过任何方式修改的内存位置。返回值上的属性还具有下面描述的附加语义。调用者与被调用者共同承担确保满足这些要求的责任。
因此,如果我正确理解,这意味着只需i: &mut u32在参数列表中期望它应该没有别名。甚至使用辅助函数,例如:
pub unsafe fn assign_mixed(i: &mut u32, j: *mut u32) -> u32 {
aux(i, j)
}
pub unsafe fn aux(i: *mut u32, j: *mut u32) -> u32 {
*i = 42;
*j = 7;
*i
}
Run Code Online (Sandbox Code Playgroud)
行不通。
我认为拥有类似的东西的唯一方法是使用UnsafeCell:
use std::cell::UnsafeCell;
pub unsafe fn assign_mixed(i: &UnsafeCell<u32>, j: *mut u32) -> u32 {
*i.get() = 42;
*j = 7;
*i.get()
}
Run Code Online (Sandbox Code Playgroud)
将产生所需的汇编代码。请务必不要用于&mut UnsafeCell此用途。