【发布时间】:2019-12-13 08:02:23
【问题描述】:
&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);
}
当然还有主要问题:
注意 — 感谢 trentcl 提供pointing out this example actually causes a copy when creating p2。这可以通过将u8 替换为非Copy 类型来确认。然后编译器抱怨移动。可悲的是,这并没有让我更接近答案,只是提醒我,我可以得到意外的行为而不是未定义的行为,这仅仅是因为 Rust 的移动语义。
fn main() {
let mut value: u8 = 42;
let p1: *mut u8 = &mut value as *mut _;
// this part was edited, left in so it's easy to spot
// it's not important how I got this value, what's important is that it points to same variable and allows mutating it
// I did it this way, hoping that trying to access real value then grab new pointer again, would break something, if it was UB to do this
//let p2: *mut u8 = &mut unsafe { *p1 } as *mut _;
let p2: *mut u8 = p1;
unsafe {
*p1 += 1;
*p2 += 1;
*p1 -= 1;
*p2 -= 1;
}
println!("{}", value);
}
两者都有:
42
这是否意味着指向同一位置并在不同时间被取消引用的两个可变指针不是未定义的行为?
我不认为在编译器上测试它是一个好主意,因为未定义的行为可能会发生任何事情,甚至打印 42 就好像没有任何问题一样。无论如何我都会提到它,因为这是我尝试过的事情之一,希望得到一个客观的答案。
我不知道如何编写一个测试,该测试可能会强制执行不稳定的行为,这会让人很明显这是行不通的,因为它没有按预期使用,如果可能的话。
我知道这很可能是未定义的行为,无论如何都会在多线程环境中中断。不过,我希望得到比这更详细的答案,特别是如果可变指针别名不是未定义的行为。 (这实际上很棒,因为虽然我使用 Rust 的原因和其他人一样——至少可以说是内存安全......我希望仍然保留一把霰弹枪,我可以指向任何地方,而不会被锁定在我的脚上。我可以在 C 语言中使用别名“可变指针”而不用大惊小怪。)
这是一个关于我是否可以的问题,而不是关于我是否应该的问题。我想深入了解不安全的 Rust,只是为了了解它,但感觉不像 C 等“可怕”语言,没有足够的信息来说明什么是未定义行为,什么不是。
【问题讨论】:
-
您可以在没有任何
unsafe的情况下创建别名可变指针,因此根据定义,创建它们必须是安全的。当然,使用它们是另一回事...... -
你的第二个例子没有按照你的想法做:
p1和p2不要别名。 proof -
第一个示例仍然是 UB,因为编译器需要将
&mut引用到*p以便对其执行+=。是的,您不能“仅仅”将(非Copy)类型从*mut指针中移出,因为这样做甚至更多 不安全,而不仅仅是取消引用该东西——你为此需要使用ptr::read。 -
@trentcl 第二个例子的第一个版本中令人惊讶的一点是
unsafe { &mut *p1 }与&mut unsafe { *p1 }不同。 unsafe 块将 place 表达式转换为 value 表达式,从而触发移动。
标签: rust undefined-behavior unsafe