对于共享引用和可变引用,语义是明确的:只要您具有对值的共享引用,其他任何内容都不能具有可变访问权限,并且不能共享可变引用.
所以这段代码:
#[no_mangle]
pub extern fn run_ref(a: &i32, b: &mut i32) -> (i32, i32) {
let x = *a;
*b = 1;
let y = *a;
(x, y)
}
Run Code Online (Sandbox Code Playgroud)
编译(在x86_64上):
run_ref:
movl (%rdi), %ecx
movl $1, (%rsi)
movq %rcx, %rax
shlq $32, %rax
orq %rcx, %rax
retq
Run Code Online (Sandbox Code Playgroud)
请注意,内存a指向只读一次,因为编译器知道写入b必须没有修改内存
a.
原始指针更复杂.原始指针算术和强制转换是"安全的",但取消引用它们不是.
我们可以将原始指针转换回共享和可变引用,然后使用它们; 这肯定意味着通常的引用语义,编译器可以相应地进行优化.
但是如果我们直接使用原始指针,那么语义是什么?
#[no_mangle]
pub unsafe extern fn run_ptr_direct(a: *const i32, b: *mut f32) -> (i32, i32) {
let x = *a;
*b = 1.0; …Run Code Online (Sandbox Code Playgroud) &mut T并&mut T导致编译错误;太好了,两次可变地借款是客观上的错误。
是*mut T和*mut T未定义的行为,还是这是完全正确的事情?也就是说,可变指针别名有效吗?
是什么使事情变得更糟的是,&mut T和*mut T实际上编译和作品如预期,我可以通过引用,指针,然后再参考修改值...但我看到有人说,这是不确定的行为。是的,“有人说过”是我唯一的信息。
这是我测试的内容:
fn main() {
let mut value: u8 = 42;
let r: &mut u8 = &mut value;
let p: *mut u8 = r as *mut _;
*r += 1;
unsafe { *p += 1; }
*r -= 1;
unsafe { *p -= 1; }
println!("{}", value);
}
Run Code Online (Sandbox Code Playgroud)
当然,主要的问题是:
注意 —感谢trentcl 指出此示例在创建时实际上会导致复制p2。可以通过用u8非Copy类型替换来确认。然后,编译器抱怨此举。可悲的是,这并不能使我更接近答案,仅是提醒我,仅由于Rust的move语义,我就可以得到意想不到的行为而不会成为未定义的行为。
fn main() { …Run Code Online (Sandbox Code Playgroud)