避免共享静态变量的“Sync”

Jam*_*lor 5 rust

我有以下定义:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

更高层次的问题:

我正在尝试创建一个全局静态(且不可变)的字符串映射到实现该Factory特征的结构。目标是,对于映射的任何值成员,我可以将其视为 a Factorynew对其进行调用,然后将得到(在本例中)实现该特征的东西Display

我有以下内容:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨:

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
    });
Run Code Online (Sandbox Code Playgroud)

我尝试过的事情:

  • Sync针对特征实施:声明该特征具有trait Factory : Sync(意味着实现该特征的所有项目都必须实现Sync)和/或定义:

    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`
    
    Run Code Online (Sandbox Code Playgroud)
  • 换行Arc<dyn Factory>:我尝试使用Mutex/ RefCell/OnceCell和其他方法,都导致了相同的错误。似乎应该有一种方法将底层MyCustomFactory视为可以锁定的单例。很好(也是预期的),这个结构在全球范围内只有一个实例。

Joh*_*ica 2

您已接近添加Sync. 如果仔细观察,您会发现该错误然后建议添加Send

= help: the trait `std::marker::Send` is not implemented for `(dyn Factory + 'static)`
Run Code Online (Sandbox Code Playgroud)

添加SendSync并编译。你可以做:

trait Factory: Send + Sync {
    ...
}
Run Code Online (Sandbox Code Playgroud)

或者,我的偏好是在静态地图中专门要求它们,因为那是需要它们的地方:

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
    });
Run Code Online (Sandbox Code Playgroud)

您还可以通过让它推断第二个来减少重复Arc

let value = Arc::new(MyCustomFactory) as Arc<_>;
Run Code Online (Sandbox Code Playgroud)