我有一个包含不安全代码的结构,使用以下方法:
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
的结构字段,例如*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 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
答案 0 :(得分:4)
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"]
表示。