避免使用`Sync`来处理共享的静态变量

5

我有以下的定义:

struct MyCustomFactory;

trait Factory {
    fn new<'a>(entity: &'a str) -> Arc<dyn Display + 'a>
    where
        Self: Sized;
}

impl Factory for MyCustomFactory {
    fn new<'a>(entity: &'a str) -> Arc<dyn Display + 'a>
    where
        Self: Sized,
    {
        Arc::new(entity)
    }
}

更高层次的问题:

我试图创建一个全局静态(不可变)字符串到实现Factory trait的结构体的映射表。目标是,对于地图中的任何值成员,我可以像处理Factory一样对待它,调用new,然后会得到某些东西,这个东西(在本例中)实现了Display trait。

我有以下内容:

static ITEMS: Lazy<BTreeMap<&'static str, Arc<dyn Factory>>> =
    Lazy::new(|| {
        let mut map = BTreeMap::new();
        let value = Arc::new(MyCustomFactory) as Arc<dyn Factory>;
        map.insert("foo", value);
        map
    });

编译器报错如下:
77 | / static ITEMS: Lazy<BTreeMap<&'static str, Arc<dyn Factory>>> =
78 | |     Lazy::new(|| {
79 | |         let map = BTreeMap::new();
80 | |         let value = Arc::new(MyCustomFactory) as Arc<dyn Factory>;
...  |
88 | |     });
   | |_______^ `(dyn Factory + 'static)` cannot be shared between threads safely
   |
   = help: the trait `std::marker::Sync` is not implemented for `(dyn Factory + 'static)`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<(dyn Factory + 'static)>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `alloc::collections::btree::node::Root<&'static str, std::sync::Arc<(dyn Factory + 'static)>>`
   = note: required because it appears within the type `std::collections::BTreeMap<&'static str, std::sync::Arc<(dyn Factory + 'static)>>`
   = note: required because of the requirements on the impl of `std::marker::Sync` for `once_cell::imp::OnceCell<std::collections::BTreeMap<&'static str, std::sync::Arc<(dyn Factory + 'static)>>>`
   = note: required because it appears within the type `once_cell::sync::OnceCell<std::collections::BTreeMap<&'static str, std::sync::Arc<(dyn Factory + 'static)>>>`
   = note: required because of the requirements on the impl of `std::marker::Sync` for `once_cell::sync::Lazy<std::collections::BTreeMap<&'static str, std::sync::Arc<(dyn Factory + 'static)>>>`
   = note: shared static variables must have a type that implements `Sync`

我尝试过的方法:

  • Implement Sync against the trait: Declaring the trait to have trait Factory : Sync, (meaning all items which implement the trait, must implement Sync) and/or defining:

       |
    66 | unsafe impl Sync for dyn Factory {}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
    
  • Wrapping Arc<dyn Factory>: I've tried using Mutex / RefCell / OnceCell and others, all resulting in the same error. It seems like there should be a way to treat the underlying MyCustomFactory as a Singleton that can be locked around. It's fine (and expected) that's there's only once instance of this struct globally.

1个回答

2
你在添加 Sync 方面很接近了。如果你仔细看,就会发现错误提示建议添加 Send
= help: the trait `std::marker::Send` is not implemented for `(dyn Factory + 'static)`

添加SendSync,然后编译。你可以这样做:
trait Factory: Send + Sync {
    ...
}

或者,我更喜欢在静态地图中明确要求它们,因为那是它们所需的地方。
static ITEMS: Lazy<BTreeMap<&'static str, Arc<dyn Factory + Send + Sync>>> =
    Lazy::new(|| {
        let mut map = BTreeMap::new();
        let value = Arc::new(MyCustomFactory) as Arc<dyn Factory + Send + Sync>;
        map.insert("foo", value);
        map
    });

你可以通过让它推断第二个 Arc 来减少重复:
let value = Arc::new(MyCustomFactory) as Arc<_>;

哇!这个修复了,非常感谢!我没有意识到类型签名本身会影响行为。 - James Taylor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接