如何防止同步的自动实现

时间:2017-07-29 17:06:05

标签: multithreading struct rust traits unsafe

我有一个包含不安全代码的结构,使用以下方法:

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实现,我想阻止它。

我找到了两种方法来做到这一点,两者都不是那么好......

  1. 添加一个没有实现Sync的结构字段,例如*const u8,这显然是相当糟糕的,因为它最终导致不必要的和不清楚的代码,并没有清楚地显示我的意图

  2. impl !Sync for Struct {}在稳定版中不可用,将根据this issue删除。 相应的错误告诉我使用标记类型,但the documention也没有提供解决问题的方法。

  3. 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 {}
       | ^^^^^^^^^^^^^^^^^^^^^^^^
    

1 个答案:

答案 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未实现SyncFoo<T>也不会实现Sync,因此无需使用否定实现或任何标记。

1 如果你不直接使用它,很可能你使用的抽象是建立在它上面的。它尽可能特别:它是一个lang项目,由其属性#[lang = "unsafe_cell"]表示。