Как мне сделать так, чтобы вызывающий абонент увидел реализацию признака метода? - PullRequest
2 голосов
/ 04 августа 2020

С кодом:

trait Msg {
    fn apply_to_state(&self, state: &mut State);
}

trait ApplicableToStateOne: Msg {
    fn apply_to_state(&self, state: &mut State) {
        match state {
            State::StateOne(s) => {
                self.apply_to_state_one(s) 
            }
            _ => {
                //TODO: return an error
            }
        }
    }
    fn apply_to_state_one(&self, state_one: &mut StateOne);
}

#[derive(Debug, Clone)]
pub struct MsgA {
    pub field_a: u8,
}

impl Msg for MsgA {}
impl ApplicableToStateOne for MsgA {
    fn apply_to_state_one(&self, state_one: &mut StateOne) {
        state_one.one_special += 31; // just a mutability test
    }
}

// this is a stub for receiving different kinds of messages from the network
fn recv() -> Box<dyn Msg> {
    Box::new(MsgA { field_a: 42 })
}

fn main() {
    let mut state = State::StateOne(StateOne { common: 0, one_special: 1 });
    for _ in 0..100 { // this would be loop, but that makes the playground timeout
        let incoming = recv(); // this would block
        incoming.apply_to_state(&mut state)
    }
}

(площадка: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7c89a2bbc765380fc002864e2be80e55)

компилятор жалуется:

error[E0046]: not all trait items implemented, missing: `apply_to_state`
  --> src/bin/sandbox6.rs:83:1
   |
2  |     fn apply_to_state(&self, state: &mut State);
   |     -------------------------------------------- `apply_to_state` from trait
...
83 | impl Msg for MsgA {}
   | ^^^^^^^^^^^^^^^^^ missing `apply_to_state` in implementation

В моем (очевидно недостаточное) понимание, я ожидал, что будет вызвана trait ApplicableToStateOne реализация apply_to_state.

Как я могу это сделать?

Обновление:

Говоря более абстрактно, этот вопрос касается:

  1. получения из сети упакованного объекта суперпризнания, а затем
  2. выяснения, какой субстрат он имеет и, наконец,
  3. вызов метода, подходящего для вычитания (возможно, каким-то образом через суперсвойство).

Все это можно сделать довольно подробно, используя перечисления вместо черт, но это добавляет иерархию перечислений.

Иерархия перечислений - это плохо, потому что:

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

Ответы [ 2 ]

4 голосов
/ 04 августа 2020

Вы можете использовать generi c для реализации Msg для всего, что реализует ApplicableToStateOne:

struct State {}

trait Msg {
    fn apply_to_state(&self, state: &mut State);
}

trait ApplicableToStateOne: Msg {
    fn apply_to_state_one(&self, state: &mut State) {
        todo!();
    }
}

impl<T: ApplicableToStateOne> Msg for T {
    fn apply_to_state(&self, state: &mut State) {
        self.apply_to_state_one (state);
    }
}

#[derive(Debug, Clone)]
pub struct MsgA {
    pub field_a: u8,
}

impl ApplicableToStateOne for MsgA {}
// No need to implement Msg explicitly for MsgA

Playground

0 голосов
/ 04 августа 2020

Подумайте о каждом из методов, которые нужно связать с Trait, а не с самим объектом. Посмотрите на этот более простой пример:

trait Foo {
    fn foo(&self) -> &'static str;
}

trait FooPrime {
    fn foo(&self) -> &'static str;
}

struct Bar {}

impl Foo for Bar {
    fn foo(&self) -> &'static str {
        "foo"
    }
}

impl FooPrime for Bar {
  fn foo(&self) -> &'static str {
        "foo prime"
    }  
}

fn main() {
    let bar = Bar{};
    println!("{} : {}", bar.foo(), bar.foo());
}

При компиляции мы получаем следующую ошибку:

   Compiling playground v0.0.1 (/playground)
error[E0034]: multiple applicable items in scope
  --> src/main.rs:25:29
   |
25 |     println!("{} : {}", bar.foo(), bar.foo());
   |                             ^^^ multiple `foo` found
   |
note: candidate #1 is defined in an impl of the trait `Foo` for the type `Bar`
  --> src/main.rs:12:5
   |
12 |     fn foo(&self) -> &'static str {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl of the trait `FooPrime` for the type `Bar`
  --> src/main.rs:18:3
   |
18 |   fn foo(&self) -> &'static str {
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #1
   |
25 |     println!("{} : {}", Foo::foo(&bar), bar.foo());
   |                         ^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #2
   |
25 |     println!("{} : {}", FooPrime::foo(&bar), bar.foo());
   |                         ^^^^^^^^^^^^^^^^^^^

error[E0034]: multiple applicable items in scope
  --> src/main.rs:25:40
   |
25 |     println!("{} : {}", bar.foo(), bar.foo());
   |                                        ^^^ multiple `foo` found
   |
note: candidate #1 is defined in an impl of the trait `Foo` for the type `Bar`
  --> src/main.rs:12:5
   |
12 |     fn foo(&self) -> &'static str {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl of the trait `FooPrime` for the type `Bar`
  --> src/main.rs:18:3
   |
18 |   fn foo(&self) -> &'static str {
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #1
   |
25 |     println!("{} : {}", bar.foo(), Foo::foo(&bar));
   |                                    ^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #2
   |
25 |     println!("{} : {}", bar.foo(), FooPrime::foo(&bar));

Нам нужно явно указать компилятору, какой метод использовать :

fn main() {
    let bar = Bar{};
    println!("{} : {}", Foo::foo(&bar), FooPrime::foo(&bar));
}

Площадка

По той же причине в вашем коде не используется метод для другого признака, поскольку он не является частью предыдущего признака. Вот ссылка на книгу

Возможно, вы захотите использовать функцию supertrait для создания импликации по умолчанию для другой черты на основе «родительской»:

trait FooPrime : Foo {
    fn foo(&self) -> &'static str {
        Foo::foo(self)
    }
}

Детская площадка

...