我有一个包含不安全代码的结构,方法如下:
use std::sync::Arc;
use std::thread;
#[derive(Debug)]
struct Foo<T> {
items: Vec<Box<(T, String)>>,
}
impl<T> Foo<T> {
pub fn add_element(&self, element: T, key: String) {
if !(self.items.iter().any( |i| i.1 == key)) {
let mut items = unsafe {change_mut(&(self.items))};
items.push(Box::new((element,key)));
}
}
}
unsafe fn change_mut<T>(x: &T) -> &mut T { // changes &self to &mut self
&mut *(x as *const T as *mut T)
}
fn main() {
let foo = Arc::new(Foo { items: vec!() });
let clone = foo.clone();
// This should not be possible, as it might lead to UB
thread::spawn(move || clone.add_element(1, String::from("one")));
println!("{:?}", *foo);
}
这个结构是完全安全的,直到有人在多线程时开始使用这个方法。但是,由于该结构仅包含
Vec<Box<T,String>>
,因此默认情况下会实现 Sync
,我想阻止这种情况。我找到了两种方法来做到这一点,这两种方法都不是很好......
Sync
的 struct 字段,例如 *const u8
,这显然是相当糟糕的,因为它最终导致了不必要和不清楚的代码,并且没有清楚地表明我的意图。 impl !Sync for Struct {}
在稳定版上不可用,将根据 this issue 删除。相应的错误告诉我改用标记类型,但 the documention 也没有提供解决我的问题的方法。
error: negative trait bounds are not yet fully implemented; use marker types for
now (see issue #13231)
--> src\holder.rs:44:1
|
44 | impl !Sync for Struct {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
最佳答案
Rust 中的内部可变性需要 1 使用 UnsafeCell
作为提示编译器正常规则 不适用 。
因此,您的结构应如下所示:
#[derive(Debug)]
struct Foo<T> {
items: UnsafeCell<Vec<Box<(T, String)>>>,
}
然后,
add_element
的实现调整为:impl<T> Foo<T> {
pub fn add_element(&self, element: T, key: String) {
if !(self.items.iter().any( |i| i.1 == key)) {
let mut items = unsafe { &mut *self.items.get() };
// ^~~~~~~~~~~~~~~~~~~~~~
items.push(Box::new((element,key)));
}
}
}
UnsafeCell
的使用使得 change_mut
完全没有必要:毕竟 UnsafeCell
的目的是允许内部可变性。请注意它的 get
方法如何返回一个原始指针,如果没有 unsafe
块就不能取消引用。由于
UnsafeCell
没有实现 Sync
, Foo<T>
也不会实现 Sync
,因此没有必要使用否定实现或任何标记。1 如果你不直接使用它,你使用的抽象很可能是建立在它之上的。它非常特别:它是一个 lang 项,由其属性
#[lang = "unsafe_cell"]
表示。关于multithreading - 如何防止同步的自动实现,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45391874/