Как приводить трейт-объекты с помощью Arc‹Mutex‹››?

Я пытаюсь вызвать функцию, которая принимает указатель на Mutex некоторого типаж-объекта, и я хочу иметь возможность реализовать черту для Mutex этого типаж-объекта, чтобы можно было рассматривать Mutex как экземпляр типаж-объекта для абстракции. .

Чтобы привести пример этого, представьте себе настройку слушателя Event как таковую:

use std::sync::{Arc, Mutex, Weak};

// Define a simple event
trait Event: Send + Sync + 'static {}
impl Event for String {}


// Define the listener interface
trait Listener<E: Event> {
    fn notify(&self, event: &E);
}

// Extend the listener interface to listenrs wrapped by a mutex
impl<E: Event> Listener<E> for Mutex<Listener<E>> {
    fn notify(&self, event: &E) {
        self.lock().unwrap().notify(event);
    }
}


// Contrived thing to listen for messages
struct Console;
impl Listener<String> for Console {
    fn notify(&self, event: &String) {
        println!("{}", event);
    }
}


// Simple function which may be called asynchronously and then sends a message
// when it is complete
fn do_stuff(l: Arc<Listener<String>>) {
    // Would normally cast to a Weak<...> and then store in a list of listneners
    // For some sort of object
    let m = String::from("I did stuff!");
    l.notify(&m);
}

fn main() {
    let l: Arc<Mutex<Console>> = Arc::new(Mutex::new(Console));

    let t1 = Arc::clone(&l) as Arc<Mutex<Listener<String>>>; //this part is ok
    // Here is where we run into issues... This *should* be equvlient to
    // do_stuff(t1), but with the corercion explicit
    let t2 = Arc::clone(&t1) as Arc<Listener<String>>;
    do_stuff(t2);

    // This is a simple, working example of it interpreting a Mutex<Listener<E>>
    // as just a Listener<E>
    let m = String::from("Somthing else...");
    (l as Arc<Mutex<Listener<String>>>).notify(&m);
}

(Игровая площадка)

Проблема в следующем:

error[E0277]: the trait bound `Listener<std::string::String>: std::marker::Sized` is not satisfied in `std::sync::Mutex<Listener<std::string::String>>`
  --> src/main.rs:45:14
   |
45 |     let t2 = Arc::clone(&t1) as Arc<Listener<String>>;
   |              ^^^^^^^^^^^^^^^ `Listener<std::string::String>` does not have a constant size known at compile-time
   |
   = help: within `std::sync::Mutex<Listener<std::string::String>>`, the trait `std::marker::Sized` is not implemented for `Listener<std::string::String>`
   = note: required because it appears within the type `std::sync::Mutex<Listener<std::string::String>>`
   = note: required for the cast to the object type `Listener<std::string::String>`

Почему это так? Поскольку Arc является указателем на данные, насколько я понимаю, он должен указывать на Listener<String>, который оказывается Listener<Mutex<String>>.

Я вижу по крайней мере два способа избежать этого, первый — просто impl Listener<String> for Mutex<Listener<String>>, однако в реальном коде это может потребовать взаимозависимости, которой следует избегать, поскольку трейт может быть реализован только там, где определен трейт или структура (а Mutex не определена). определено в моем коде).

Второй — переместить Mutex в объект Listener, чтобы вызывающему объекту вообще не нужно было приводить его. Это сработает и может быть лучшим решением. Несмотря на это, мне любопытно, почему предложенный кастинг не работает и что можно изменить, чтобы заставить его работать.


person Eadword    schedule 19.11.2017    source источник


Ответы (1)


Поскольку Arc является указателем на данные, насколько я понимаю, он должен указывать на Listener<String>

Да, это правда. Я считаю, что ваша проблема в том, что вы (случайно?) требуете, чтобы у вас был Mutex<Listener<String>> в какой-то момент. Это недопустимо, потому что значение внутри Mutex не находится за указателем, что делает весь тип неразмерным.

Хотя хорошо иметь Arc<Mutex<Listener<String>>>.

Вместо этого я бы реализовал трейт для Mutex типа любого, который реализует тот же трейт. Я бы сделал то же самое для ссылок и Boxed трейт-объектов трейта. Во всех случаях я бы удалил привязку Sized, чтобы разрешить трейт-объекты:

use std::sync::{Arc, Mutex};

trait Event: Send + Sync + 'static {}
impl Event for String {}

trait Listener<E: Event> {
    fn notify(&self, event: &E);
}

impl<L, E> Listener<E> for Mutex<L>
where
    L: ?Sized + Listener<E>,
    E: Event,
{
    fn notify(&self, event: &E) {
        self.lock().unwrap().notify(event);
    }
}

impl<'a, L, E> Listener<E> for &'a L
where
    L: ?Sized + Listener<E>,
    E: Event,
{
    fn notify(&self, event: &E) {
        (**self).notify(event);
    }
}

struct Console;
impl Listener<String> for Console {
    fn notify(&self, event: &String) {
        println!("{}", event);
    }
}

fn do_stuff(l: Arc<Listener<String>>) {
    let m = String::from("I did stuff!");
    l.notify(&m);
}

fn main() {
    let l: Arc<Mutex<Console>> = Arc::new(Mutex::new(Console));
    let l2 = Arc::clone(&l) as Arc<Listener<String>>;
    let l3 = Arc::clone(&l) as Arc<Listener<String>>;

    do_stuff(l);
    do_stuff(l2);

    let m = String::from("Something else...");
    l3.notify(&m);
}
person Shepmaster    schedule 20.11.2017
comment
Благодарю вас! Это имеет большой смысл. Одна часть, в которой я немного не уверен: какова цель impl<'a, L, E> Listener<E> for &'a L? - person Eadword; 20.11.2017
comment
@Eadword Думаю, я оставил это там во время отладки; это не кажется необходимым для настоящего кода. - person Shepmaster; 20.11.2017