Ho implementato un tratto per un altro tratto ma non posso chiamare metodi da entrambi i tratti

Ho un tratto chiamato Sleep :

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

Potrei fornire una diversa implementazione del sleep per ogni struttura, ma risulta che la maggior parte delle persone dorme in un numero molto limitato di modi. Puoi dormire in un letto:

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

Se sei in campeggio, puoi dormire in una tenda:

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

Ci sono alcuni casi strani. Ho un amico che può dormire in piedi contro un muro, ma la maggior parte delle persone, la maggior parte delle volte, cade in un caso semplice.

Definiamo alcune strutture e lasciamo sleep :

 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! Errore di compilazione:

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

Questo errore del compilatore è strano perché se c’era qualcosa di sbagliato in un tratto che implementava un altro tratto, mi aspettavo di sentirlo da capo quando l’ho fatto, non alla fine del programma quando provo a usare il risultato.

In questo esempio, ci sono solo 2 strutture e 2 modi per dormire, ma nel caso generale ci sono molte strutture e diversi modi per dormire (ma non tanti modi quanti sono le strutture).

Un Bed è principalmente un’implementazione per il Sleep , ma nel caso generale un Bed ha molti usi e potrebbe implementare molte cose.

L’unico approccio immediatamente ovvio è quello di convertire impl Sleep for... in una macro che le strutture stesse usano, ma che sembra hacky e terribile.

È necessario implementare la seconda caratteristica per gli oggetti che implementano la prima caratteristica :

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

Tuttavia, questo si romperà non appena ne aggiungi un secondo:

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

Con

 error[E0119]: conflicting implementations of trait `Sleep`: --> src/main.rs:52:1 | 42 | / impl Sleep for T 43 | | where 44 | | T: HasBed, 45 | | { ... | 48 | | } 49 | | } | |_- first implementation here ... 52 | / impl Sleep for T 53 | | where 54 | | T: HasTent, 55 | | { ... | 58 | | } 59 | | } | |_^ conflicting implementation 

È ansible che qualcosa implementi sia HasBed che HasTent . Se dovesse apparire qualcosa che ha implementato entrambi, allora il codice sarebbe ora ambiguo.

Come realizzi il tuo objective? Penso che tu abbia già suggerito l’attuale migliore soluzione: scrivere una macro. Questo potrebbe essere considerato un punto fermo fino a quando non puoi scrivere il tuo derivante . I macro non sono poi così male, ma possono essere ingombranti da scrivere.

Un’altra cosa, che potrebbe essere interamente basata sui nomi che hai scelto per il tuo esempio, sarebbe semplicemente incorporare le strutture in altre strutture, opzionalmente rendendole pubbliche. Poiché la tua implementazione di Sleep dipende fondamentalmente solo dal letto / tenda, nessuna funzionalità andrebbe persa in questo modo. Certo, alcune persone potrebbero sentire che rompe l’incapsulamento. È ansible creare nuovamente macro per implementare una sorta di delega.

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

Possiamo usare gli articoli associati qui.

 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(&self, &T); } 

Quindi, implementiamo due diversi ambienti di sonno.

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

L’ultimo pezzo è l’implementazione concreta di loro.

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

Codice di prova:

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