在不安全的代码,是正确的有几个可变引用(不是指针)相同的数组,只要他们不习惯写于相同的索引?
我想,得到底层数组的几个(不同)可变的视图,我可以从不同的线程修改。
如果不相交的部分是连续的,这是小事,通过只调用split_at_mut
片上:
let mut v = [1, 2, 3, 4];
{
let (left, right) = v.split_at_mut(2);
left[0] = 5;
right[0] = 6;
}
assert!(v == [5, 2, 6, 4]);
但我也想揭露非连续的分离部分。为简单起见,假设我们要检索一个可变的“视图”换偶指数,而另一个可变“视图”为奇数索引。
相反,split_at_mut()
,我们无法检索2个可变引用(我们希望有一个安全的抽象!),所以我们使用了两个结构实例,而,暴露甚至只有可变访问索引(RESP奇怪。):
let data = &mut [0i32; 11];
let (mut even, mut odd) = split_fields(data);
// …
对于一些不安全的代码,很容易得到这样一个安全的抽象。这里是a possible implementation:
use std::marker::PhantomData;
struct SliceField<'a> {
ptr: *mut i32,
len: usize,
field: usize,
marker: PhantomData<&'a mut i32>,
}
impl SliceField<'_> {
fn inc(&mut self) {
unsafe {
for i in (self.field..self.len).step_by(2) {
*self.ptr.add(i) += 1;
}
}
}
fn dec(&mut self) {
unsafe {
for i in (self.field..self.len).step_by(2) {
*self.ptr.add(i) -= 1;
}
}
}
}
unsafe impl Send for SliceField<'_> {}
fn split_fields(array: &mut [i32]) -> (SliceField<'_>, SliceField<'_>) {
(
SliceField {
ptr: array.as_mut_ptr(),
len: array.len(),
field: 0,
marker: PhantomData,
},
SliceField {
ptr: array.as_mut_ptr(),
len: array.len(),
field: 1,
marker: PhantomData,
},
)
}
fn main() {
let data = &mut [0i32; 11];
{
let (mut even, mut odd) = split_fields(data);
rayon::join(|| even.inc(), || odd.dec());
}
// this prints [1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1]
println!("{:?}", data);
}
到现在为止还挺好。
不过,访问原始指针是远为方便:违背片,我们不能使用运营商[]
或迭代器。
unsafe {
for i in (self.field..self.len).step_by(2) {
*self.ptr.add(i) += 1;
}
}
最明显的想法是本地原始指针转换为不安全执行切片:
let slice = unsafe { slice::from_raw_parts_mut(self.ptr, self.len) };
那么我们就可以,例如,改写我们的函数式执行:
slice.iter_mut().skip(self.field).step_by(2).for_each(|x| *x += 1);
对于本示例,它可能不值得,但对于更复杂的代码,它可能是迄今为止使用片而不是原始指针更方便。
这个对吗?
这显然违反了borrowing rules:两个线程可同时容纳一个可变引用非常相同的内存位置。然而,他们可能永远不会写于相同的索引。
可变引用走样不列为unsafe superpower,但名单没有给出详尽无遗。
是别名可变引用正确的
不,这是从来没有正确的别名可变引用(可变的指针是更细致入微的概念)。这打破了主要rules of references之一。
你不管提供的资格都没有 - 你不能引用易变走样。该代码作为一个unsafe
块的内部都没有区别。这样做是自动和即时未定义的行为。
fn main() {
let mut x = [42, 84];
let x_raw = &mut x as *mut _;
let x_even: &mut [i32; 2] = unsafe { &mut *x_raw };
let x_odd: &mut [i32; 2] = unsafe { &mut *x_raw };
println!("{}, {}", x_even[0], x_odd[1]);
}
美里指出:
error[E0080]: constant evaluation error: Borrow being dereferenced (Uniq(1772)) does not exist on the stack
--> src/main.rs:8:24
|
8 | println!("{}, {}", x_even[0], x_odd[1]);
| ^^^^^^^^^ Borrow being dereferenced (Uniq(1772)) does not exist on the stack
|
= note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:34
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:53
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:297:40
= note: inside call to `std::panicking::try::do_call::<[closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:293:5
= note: inside call to `std::panicking::try::<i32, [closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:388:9
= note: inside call to `std::panic::catch_unwind::<[closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:25
= note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:5
= note: inside call to `std::rt::lang_start::<()>`
虽然一个UnsafeCell
可以帮助您构建安全的抽象,你还是要坚持引用的规则。与UnsafeCell
更换类型不会让事情变得神奇的工作:
use std::cell::UnsafeCell;
fn main() {
let x = UnsafeCell::new([42, 84]);
let x_even: &mut [i32; 2] = unsafe { &mut *x.get() };
let x_odd: &mut [i32; 2] = unsafe { &mut *x.get() };
println!("{}, {}", x_even[0], x_odd[1]);
}
error[E0080]: constant evaluation error: Borrow being dereferenced (Uniq(1776)) does not exist on the stack
--> src/main.rs:9:24
|
9 | println!("{}, {}", x_even[0], x_odd[1]);
| ^^^^^^^^^ Borrow being dereferenced (Uniq(1776)) does not exist on the stack
|
= note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:34
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:53
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:297:40
= note: inside call to `std::panicking::try::do_call::<[closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:293:5
= note: inside call to `std::panicking::try::<i32, [closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:388:9
= note: inside call to `std::panic::catch_unwind::<[closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:25
= note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:5
= note: inside call to `std::rt::lang_start::<()>`
UnsafeCell
的文档显式调用了这一点:
甲
&mut T
参考可以被释放到提供安全代码既不其他&mut T
也不&T
共存它。一个&mut T
必须是唯一的。
事实上,即使你的片不会在同一个点开始,但它们重叠不知何故,这也是走样和未定义的行为:
fn main() {
let mut x = [0, 1, 2];
let x_raw = &mut x as *mut [i32];
let x_0: &mut [i32] = unsafe { &mut (*x_raw)[0..2] };
let x_1: &mut [i32] = unsafe { &mut (*x_raw)[1..3] };
if x_0 == x_1 {
println!("They are equal");
}
}
error[E0080]: constant evaluation error: Borrow being dereferenced (Uniq(1807)) does not exist on the stack
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/cmp.rs:1041:65
|
1041 | fn eq(&self, other: &&'b mut B) -> bool { PartialEq::eq(*self, *other) }
| ^^^^^ Borrow being dereferenced (Uniq(1807)) does not exist on the stack
|
note: inside call to `std::cmp::impls::<impl std::cmp::PartialEq<&'b mut B> for &'a mut A><[i32], [i32]>::eq` at src/main.rs:8:8
--> src/main.rs:8:8
|
8 | if x_0 == x_1 {
| ^^^^^^^^^^
= note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:34
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:53
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:297:40
= note: inside call to `std::panicking::try::do_call::<[closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:293:5
= note: inside call to `std::panicking::try::<i32, [closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:388:9
= note: inside call to `std::panic::catch_unwind::<[closure@DefId(1/1:1900 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:25
= note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:5
= note: inside call to `std::rt::lang_start::<()>`
对于UnsafeCell
状态的文档:
该
UnsafeCell<T>
类型是获得被认为是可变aliasable数据的唯一合法途径。 [...] 编译器可以基于该&T
没有性情不定地别名或突变知识的优化,以及&mut T
是独一无二的。
所以,不,你正在尝试是无效的,除非您使用的UnsafeCell
。