Избегание `Sync` для общих переменных c - PullRequest
2 голосов
/ 30 мая 2020

У меня есть следующие определения:

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)
    }
}

Проблема более высокого уровня:

Я пытаюсь создать глобальную c (и неизменяемую) карту строк для структур, которые реализовать трейт Factory. Цель состоит в том, чтобы для любого элемента-значения карты я мог рассматривать его как 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`

Вещи, которые я пробовал:

  • Реализовать Sync против признака : Объявление признака как имеющего trait Factory : Sync (то есть все элементы, реализующие признак, должны реализовывать Sync) и / или определение:

       |
    66 | unsafe impl Sync for dyn Factory {}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
    
  • Обертывание Arc<dyn Factory>: я пробовал использовать Mutex / RefCell / OnceCell и другие, все приводило к той же ошибке . Похоже, должен быть способ рассматривать базовый MyCustomFactory как синглтон, который можно заблокировать. Это нормально (и ожидается), что существует только один экземпляр этой структуры во всем мире.

1 Ответ

1 голос
/ 30 мая 2020

Вы были близки с добавлением Sync. Если вы посмотрите внимательно, то увидите, что ошибка затем рекомендует добавить Send:

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

Добавьте оба Send и Sync, и он компилируется. Вы можете сделать:

trait Factory: Send + Sync {
    ...
}

Или, по моему предпочтению, потребовать их специально на карте stati c, так как они там нужны:

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<_>;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...