Ik heb een eigenschap voor een andere eigenschap geïmplementeerd, maar kan geen methoden van beide eigenschappen aanroepen

Ik heb een eigenschap genaamd Sleep:

pub trait Sleep {
    fn sleep(&self);
}

Ik zou voor elke structuur een andere implementatie van slaap kunnen bieden, maar het blijkt dat de meeste mensen op een heel klein aantal manieren slapen. Je kunt in een bed slapen:

pub trait HasBed {
    fn sleep_in_bed(&self);
    fn jump_on_bed(&self);
}
impl Sleep for HasBed {
    fn sleep(&self) {
        self.sleep_in_bed()
    }
}

Als je aan het kamperen bent, kun je in een tent slapen:

pub trait HasTent {
    fn sleep_in_tent(&self);
    fn hide_in_tent(&self);
}
impl Sleep for HasTent {
    fn sleep(&self) {
        self.sleep_in_tent()
    }
}

Er zijn enkele rare gevallen. Ik heb een vriend die tegen een muur kan slapen, maar de meeste mensen vallen meestal in een eenvoudig geval.

We definiëren enkele structuren en laten ze slapen:

struct Jim;
impl HasBed for Jim {
    fn sleep_in_bed(&self) {}
    fn jump_on_bed(&self) {}
}
struct Jane;
impl HasTent for Jane {
    fn sleep_in_tent(&self) {}
    fn hide_in_tent(&self) {}
}
fn main() {
    use Sleep;
    let jim = Jim;
    jim.sleep();
    let jane = Jane;
    jane.sleep();
}

Uh-oh! Compileerfout:

error[E0599]: no method named `sleep` found for type `Jim` in the current scope
  --> src/main.rs:44:9
   |
27 | struct Jim;
   | ----------- method `sleep` not found for this
...
44 |     jim.sleep();
   |         ^^^^^
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `sleep`, perhaps you need to implement it:
           candidate #1: `Sleep`
error[E0599]: no method named `sleep` found for type `Jane` in the current scope
  --> src/main.rs:47:10
   |
34 | struct Jane;
   | ------------ method `sleep` not found for this
...
47 |     jane.sleep();
   |          ^^^^^
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `sleep`, perhaps you need to implement it:
           candidate #1: `Sleep`

Deze compilerfout is vreemd, want als er iets mis was met een eigenschap die een andere eigenschap implementeerde, verwachtte ik dat ik het lang geleden zou horen toen ik dat deed, niet helemaal onderaan het programma wanneer ik het resultaat probeer te gebruiken.

In dit voorbeeld zijn er slechts 2 structs en 2 manieren om te slapen, maar in het algemeen zijn er veel structs en verschillende manieren om te slapen (maar niet zoveel manieren als er structs zijn).

Een Bedis meestal een implementatie voor Sleep, maar in het algemeen heeft een Bedveel toepassingen en kan veel dingen implementeren.

De enige direct voor de hand liggende aanpak is om impl Sleep for...om te zetten in een macro die zichzelf structureert, maar die hacky en verschrikkelijk lijkt.


Antwoord 1, autoriteit 100%

U moet de tweede eigenschap implementeren voor objecten die de eerste eigenschap implementeren:

impl<T> Sleep for T
where
    T: HasBed,
{
    fn sleep(&self) {
        self.sleep_in_bed()
    }
}

Voorheen implementeerde u Sleepvoor het type eigenschap, beter uitgedrukt als dyn HasBed. Zie Wat betekent “dyn” in een type?voor meer details.

Dit gaat echter mis zodra u een tweede algemene implementatie toevoegt:

impl<T> Sleep for T
where
    T: HasTent,
{
    fn sleep(&self) {
        self.sleep_in_tent()
    }
}

Met

error[E0119]: conflicting implementations of trait `Sleep`:
  --> src/main.rs:24:1
   |
10 | / impl<T> Sleep for T
11 | | where
12 | |     T: HasBed,
13 | | {
...  |
16 | |     }
17 | | }
   | |_- first implementation here
...
24 | / impl<T> Sleep for T
25 | | where
26 | |     T: HasTent,
27 | | {
...  |
30 | |     }
31 | | }
   | |_^ conflicting implementation

Het is mogelijk dat iets zowelHasBedals HasTentimplementeert. Als er iets zou verschijnen dat beide implementeert, zou de code nu dubbelzinnig zijn. De oplossing hiervoor zou specialisatiezijn, maar daar is nog geen stabiele implementatie van.

Hoe bereik je je doel? Ik denk dat je de huidige beste oplossing al hebt voorgesteld – schrijf een macro. U kunt ook uw eigen afgeleide macro schrijven. Macro’s zijn echt niet zo slecht, maar ze kunnen onpraktisch zijn om te schrijven.

Een ander ding, dat volledig gebaseerd kan zijn op de namen die je voor je voorbeeld hebt gekozen, zou zijn om structs eenvoudig in andere structs in te sluiten en ze optioneel openbaar te maken. Aangezien uw implementatie van Sleepin principe alleen afhangt van het bed / de tent, gaat er geen functionaliteitverloren door dit te doen. Natuurlijk kunnen sommige mensen het gevoel hebben dat de inkapseling wordt verbroken. Je zou opnieuw macro’s kunnen maken om een ​​soort delegatie te implementeren.

trait Sleep {
    fn sleep(&self);
}
struct Bed;
impl Bed {
    fn jump(&self) {}
}
impl Sleep for Bed {
    fn sleep(&self) {}
}
struct Tent;
impl Tent {
    fn hide(&self) {}
}
impl Sleep for Tent {
    fn sleep(&self) {}
}
struct Jim {
    bed: Bed,
}
struct Jane {
    tent: Tent,
}
fn main() {
    let jim = Jim { bed: Bed };
    jim.bed.sleep();
}

Antwoord 2, autoriteit 55%

We kunnen hier gerelateerde items gebruiken.

pub trait Sleep: Sized {
    type Env: SleepEnv;
    fn sleep(&self, env: &Self::Env) {
        env.do_sleep(self);
    }
    fn get_name(&self) -> &'static str;
}
pub trait SleepEnv {
    fn do_sleep<T: Sleep>(&self, &T);
}

Vervolgens implementeren we twee verschillende slaapomgevingen.

struct Bed;
struct Tent;
impl SleepEnv for Bed {
    fn do_sleep<T: Sleep>(&self, person: &T) {
        println!("{} is sleeping in bed", person.get_name());
    }
}
impl SleepEnv for Tent {
    fn do_sleep<T: Sleep>(&self, person: &T) {
        println!("{} is sleeping in tent", person.get_name());
    }
}

Het laatste stuk zijn de concrete implementaties ervan.

struct Jim;
struct Jane;
impl Sleep for Jim {
    type Env = Bed;
    fn get_name(&self) -> &'static str {
        "Jim"
    }
}
impl Sleep for Jane {
    type Env = Tent;
    fn get_name(&self) -> &'static str {
        "Jane"
    }
}

Testcode:

fn main() {
    let bed = Bed;
    let tent = Tent;
    let jim = Jim;
    let jane = Jane;
    jim.sleep(&bed);
    jane.sleep(&tent);
}

Other episodes