如何防止同步
问题描述:
的autoimplementation我有一个包含不安全的代码用下面的方法一个结构:如何防止同步
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 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
答
内饰易变性拉斯特需要 使用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
完全没有必要的:它的目的毕竟,允许内部可变性。请注意它的get
方法如何返回原始指针,如果没有unsafe
块,则无法解除引用。
由于UnsafeCell
没有实现Sync
,Foo<T>
不会执行Sync
任一,因此,不需要使用负的实施方式或任何标记物。
如果你不直接使用它,机会是你使用的是建立在它的抽象。它可能是特殊的:它是一个lang项目,如其属性#[lang = "unsafe_cell"]
所示。
请尝试提供[MCVE],最好是连接到操场。当不需要猜测什么被省略时,测试解决方案的正确性就容易得多。 –
(MCVE示例:https://play.rust-lang.org/?gist=bcfd5e96cfcd390de67bc738bd821108&version=stable) –
不,即使使用单个线程,代码也不完全安全。 UB将&T投射到&mut T.你应该为此使用UnsafeCell,这也应该解决你的同步问题。 – BurntSushi5