问题描述
&mut T
和 &mut T
导致编译错误;太好了,两次可变借用客观上是错误的.
&mut T
and &mut T
results in a compilation error; this is great, it's objectively wrong to borrow mutably twice.
*mut T
和*mut T
是未定义的行为还是完全有效?也就是说,可变指针别名是否有效?
Is *mut T
and*mut T
undefined behaviour or is this a perfectly valid thing to do? That is, is mutable pointer aliasing valid?
更糟糕的是 &mut T
和 *mut T
实际上编译并按预期工作,我可以通过引用修改一个值,指针,然后再次引用......但我看到有人说这是未定义的行为.是的,有人这么说"是我掌握的唯一信息.
What makes it even worse is that &mut T
and *mut T
actually compiles and works as intended, I can modify a value through the reference, the pointer, and then the reference again... but I've seen someone say that it's undefined behaviour. Yeah, "someone said so" is the only information I have.
这是我测试过的:
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 在创建 p2
时指出这个例子实际上会导致一个副本.这可以通过将 u8
替换为非 Copy
类型来确认.然后编译器抱怨移动.可悲的是,这并没有让我更接近答案,只是提醒我,我可以得到非预期的行为,而不是未定义的行为,仅仅是因为 Rust 的移动语义.
Note — Thanks to trentcl for pointing out this example actually causes a copy when creating p2
. This can be confirmed by replacing u8
with a non-Copy
type. The compiler then complains about a move. Sadly, this does not get me closer to the answer, only reminds me that I can get unintended behaviour without it being undefined behaviour, simply because of Rust's move semantics.
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
这是否意味着指向同一位置并在不同时间被取消引用的两个可变指针不是未定义行为?
Does this imply that two mutable pointers pointing to the same location and being dereferenced at different times is not undefined behaviour?
我不认为在编译器上测试它是一个好主意,因为未定义的行为可能会发生任何事情,甚至打印 42
就好像没有错一样.无论如何我都会提到它,因为这是我尝试过的事情之一,希望得到一个客观的答案.
I don't think testing this on compiler is a good idea to begin with, as undefined behaviour could have anything happen, even printing 42
as if nothing is wrong. I mention it anyway as this is one of things I tried, hoping to get an objective answer.
我不知道如何编写一个测试来强制执行不稳定的行为,这会使它很明显不起作用,因为它没有按预期使用,如果甚至可能这样做.
I have no clue how to write a test that could force erratic behaviour that would make it dead obvious that this doesn't work because it's not used as intended, if that's even possible to do so.
我知道这很可能是未定义的行为,无论如何都会在多线程环境中中断.不过,我希望得到比这更详细的答案,特别是如果可变指针别名不是未定义的行为.(这实际上很棒,因为虽然我使用 Rust 的原因和其他人一样 - 至少可以说是内存安全......我希望仍然保留一把可以指向任何地方的霰弹枪,而不会被锁定在我的脚上.我可以为可变指针"设置别名,而不会在 C 中引起轰动.)
I'm aware that this is very likely to be undefined behaviour and break in a multithreaded environment no matter what. I would expect a more detailed answer than that, though, especially if mutable pointer aliasing IS NOT undefined behaviour. (This would in fact be awesome, because while I use Rust for reasons like everyone else - memory safety, to say the least... I expect to still retain a shotgun that I could point anywhere, without it being locked onto my feet. I can have aliased "mutable pointers" without blowing my feet off in C.)
这是一个关于我是否可以的问题,而不是我是否应该的问题.我想一头扎进不安全的 Rust,只是为了了解它,但感觉没有足够的信息,不像像 C 这样的可怕的"语言,关于什么是未定义行为,什么不是.
This is a question about whether I can, not about whether I should. I want to dive head-on into unsafe Rust, just to learn about it, but it feels like there's not enough information unlike in "horrible" languages like C about what's undefined behaviour and what's not.
推荐答案
规则引用(&T
和 &mut T
)很简单:
- 在任何给定时间,您都可以拥有一个可变引用或任意数量的不可变引用.
- 引用必须始终有效.
没有原始指针规则".原始指针(*const T
和 *mut T
)可以在任何地方别名任何东西,或者它们可以根本不指向任何东西.
There are no "rules of raw pointers". Raw pointers (*const T
and *mut T
) can alias anything, anywhere, or they can point to nothing at all.
当您取消引用一个原始指针时,可能会发生未定义的行为,隐式或显式将其转换为引用.这个引用仍然必须遵守引用规则,即使 &
在源代码中没有明确.
Undefined behavior can happen when you dereference a raw pointer, implicitly or explicitly turning it into a reference. This reference still must obey the rules of references, even when the &
isn't explicit in the source code.
在你的第一个例子中,
unsafe { *p += 1; }
*p += 1;
将 &mut
引用到 *p
以使用 +=
操作符,就像你写的一样
*p += 1;
takes a &mut
reference to *p
in order to use the +=
operator, as if you had written
unsafe { AddAssign::add_assign(&mut *p, 1); }
(编译器实际上并没有使用AddAssign
来为u8
实现+=
,但是语义是一样的.)
(The compiler does not actually use AddAssign
to implement +=
for u8
, but the semantics are the same.)
因为 &mut *p
是另一个引用的别名,即 r
,引用的第一条规则被违反,导致未定义的行为.
Because &mut *p
is aliased by another reference, namely r
, the first rule of references is violated, causing undefined behavior.
您的第二个示例(自编辑后)不同,因为没有对别名的引用,只有另一个指针,并且没有管理指针的别名规则.因此,这
Your second example (since editing) is different because there is no reference to alias, only another pointer, and there are no aliasing rules that govern pointers. Therefore, this
let p1: *mut u8 = &mut value;
let p2: *mut u8 = p1;
unsafe {
*p1 += 1;
*p2 += 1;
*p1 -= 1;
*p2 -= 1;
}
在没有任何其他对 value
的引用的情况下,是完美的.
in the absence of any other references to value
, is perfectly sound.
这篇关于别名可变原始指针 (*mut T) 会导致未定义的行为吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!