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

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 没有实现 SyncFoo<T> 也不会实现 Sync ,因此没有必要使用否定实现或任何标记。

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

    关于multithreading - 如何防止同步的自动实现,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45391874/

    10-11 19:40
    查看更多