正如Rust参考文献所说
打破指针别名规则。&mut T和&T遵循LLVM的scoped noalias模型,除非&T包含一个UnsafeCell。
这真的很模棱两可。我想知道的是,在什么时候,一个未定义行为的 &mut
Noalias发生在Rust。
是以下任何一种,还是其他什么?
- 当定义两个
&mut
指向同一个地址的?- 当两个
&mut
指向同一个地址的东西,会不会生锈?- 当对一个
&mut
指向任何其他公司的同一地址的&mut
例如: 此代码 是观察性的UB。
unsafe {
let mut x = 123usize;
let a = (&mut x as *mut usize).as_mut().unwrap(); // created, but not accessed
let b = (&mut x as *mut usize).as_mut().unwrap(); // created, accessed
*b = 666;
drop(a);
}
但是,如果我修改代码像 这个:
struct S<'a> {
ref_x: &'a mut usize
}
fn main() {
let mut x = 123;
let s = S { ref_x: &mut x }; // like the `T` in `ManuallyDrop<T>`
let taken = unsafe { std::ptr::read(&s as *const S) }; // like `ManuallyDrop<T>::take`
// at thist ime, we have two `&mut x`
*(taken.ref_x) = 666;
drop(s);
// UB or not?
}
第二版也是UB吗?第二版完全是一样的实现,以达到以下目的 std::mem::ManuallyDrop. 如果第二个版本是UB,是否是一个安全漏洞的 std::mem::ManuallyDrop<T>
?
其实常见的有 多重 现有 &mut T
对同一项目进行别名。
最简单的例子就是。
fn main() {
let mut i = 32;
let j = &mut i;
let k = &mut *j;
*k = 3;
println!("{}", i);
}
请注意,由于 借贷规则 你不能 访问 的其他别名同时进行。
如果你看一下 ManuallyDrop::take
:
pub unsafe fn take(slot: &mut ManuallyDrop<T>) -> T { ptr::read(&slot.value) }
你会注意到,没有 同时 无障碍 &mut T
:调用函数重播 ManuallyDrop
制作 slot
唯一可访问的可变引用。
这真的是模棱两可。我想知道,一个未定义的行为到底是在什么时候发生的?
&mut
Noalias发生在Rust。
运气不好,因为正如在 Nomicon:
不幸的是,Rust还没有真正定义它的别名模型。
原因是语言团队希望确保他们达成的定义既是 安全的 (显然如此)。实用但却没有 关门 到可能的完善。这是一个很高的要求。
在 铁锈不安全规范准则工作组 仍在研究建立确切的边界,特别是Ralf Jung正在研究一种叫做 "别名 "的操作模型。叠加借款.
注意:Stacked Borrows模型是在MIRI中实现的,因此你可以通过在MIRI中执行你的代码来验证你的代码与Stacked Borrows模型。当然Stacked Borrows还是实验性的,所以这并不能保证什么。
我个人赞同谨慎行事。由于具体型号未明确,所以规则是 千变万化 因此,我建议采取 更严格 解释的可能。
因此,我将无锯齿规则解释为 &mut T
作为。
在代码中的任何时候,在作用域中不得有两个可访问的引用,它们别名为同一存储器,如果其中一个是:
&mut T
.
也就是说,我认为 形成 a &mut T
到一个实例 T
为此 &T
或 &mut T
在范围内 橆 使别名无效(通过借用)是不妥当的。
很可能是过于谨慎了,但至少如果别名模式最后比计划中更保守,我的代码还是有效的。