Implementé un rasgo para otro rasgo pero no puedo invocar métodos de ambos rasgos

Tengo un rasgo llamado Sleep :

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

Podría proporcionar una implementación diferente del sleep para cada estructura, pero resulta que la mayoría de las personas duermen de muy pocas maneras. Puedes dormir en una cama:

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

Si estás de camping, puedes dormir en una tienda de campaña:

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

Hay algunos casos raros. Tengo un amigo que puede dormir de pie contra la pared, pero la mayoría de las personas, la mayoría de las veces, entran en un caso simple.

Definimos algunas estructuras y las dejamos 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! Error de comstackción:

 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` 

Este error de comstackción es extraño porque si había algo mal con un rasgo que implementaba otro rasgo, esperaba escucharlo cuando lo hacía, no en el fondo del progtwig cuando trato de usar el resultado.

En este ejemplo, solo hay 2 estructuras y 2 formas de dormir, pero en el caso general hay muchas estructuras y varias formas de dormir (pero no de tantas formas como hay estructuras).

Una Bed es principalmente una implementación para Sleep , pero en el caso general una Bed tiene muchos usos y podría implementar muchas cosas.

El único enfoque inmediatamente obvio es convertir impl Sleep for... en una macro que se usa a sí misma, pero que parece hacky y terrible.

Debe implementar el segundo rasgo para los objetos que implementan el primer rasgo :

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

Sin embargo, esto se romperá tan pronto como agregue un segundo:

 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 

Es posible que algo implemente tanto HasBed como HasTent . Si apareciera algo que implementara ambos, entonces el código sería ahora ambiguo.

¿Cómo logras tu objective? Creo que ya ha sugerido la mejor solución actual: escribir una macro. Esto podría considerarse una medida provisional hasta que pueda escribir su propio derivado . Las macros realmente no son tan malas, pero pueden ser difíciles de escribir.

Otra cosa, que puede estar completamente basada en los nombres que eligió para su ejemplo, sería simplemente insertar estructuras en otras estructuras, opcionalmente haciéndolas públicas. Dado que la implementación de Sleep depende básicamente de la cama / carpa, no se perderá ninguna funcionalidad al hacer esto. Por supuesto, algunas personas pueden sentir que rompe la encapsulación. Podría volver a crear macros para implementar una especie de delegación.

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

Podemos usar elementos asociados aquí.

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

Luego, implementamos dos entornos de sueño diferentes.

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

La última pieza es la implementación concreta de ellos.

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

Código de prueba:

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