rust - prentice - Implementé un rasgo para otro rasgo pero no puedo llamar a métodos de ambos rasgos
marketing 8va edicion (2)
Debe implementar el segundo rasgo para los objetos que implementan el primer rasgo :
impl<T> Sleep for T
where
T: HasBed,
{
fn sleep(&self) {
self.sleep_in_bed()
}
}
Anteriormente, estaba implementando
Sleep
para el tipo de rasgo, mejor expresado como
dyn HasBed
.
Consulte
¿Qué significa "dyn" en un tipo?
para más detalles.
Sin embargo, esto se interrumpirá tan pronto como agregue una segunda implementación general:
impl<T> Sleep for T
where
T: HasTent,
{
fn sleep(&self) {
self.sleep_in_tent()
}
}
Con
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
Es posible que algo implemente
tanto
HasBed
como
HasTent
.
Si apareciera algo que implementara ambos, entonces el código ahora sería ambiguo.
La solución para esto sería la
especialización
, pero aún no hay una implementación estable de eso.
¿Cómo logras tu objetivo? Creo que ya ha sugerido la mejor solución actual: escribir una macro. También podría escribir su propia macro derivada . Las macros realmente no son tan malas, pero pueden ser difíciles de escribir.
Otra cosa, que puede basarse completamente en los nombres que eligió para su ejemplo, sería simplemente integrar estructuras en otras estructuras, opcionalmente haciéndolas públicas.
Dado que su implementación de
Sleep
básicamente solo depende de la cama / tienda de campaña, no se perderá ninguna
funcionalidad
al hacer esto.
Por supuesto, algunas personas pueden sentir que eso rompe la encapsulación.
Puede 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();
}
Tengo un rasgo llamado
Sleep
:
pub trait Sleep {
fn sleep(&self);
}
Podría proporcionar una implementación diferente del sueño para cada estructura, pero resulta que la mayoría de las personas duerme 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 acampando, 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 extraños. Tengo un amigo que puede dormir parado contra una pared, pero la mayoría de las personas, la mayoría de las veces, caen en un caso simple.
Definimos algunas estructuras y las dejamos dormir:
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 compilación:
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`
Este error del compilador es extraño porque si había algo mal con un rasgo que implementaba otro rasgo, esperaba escucharlo cuando lo hice, no en la parte inferior del programa 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 tantas formas como 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 el
impl Sleep for...
en una macro que las estructuras usan, pero eso parece extraño y terrible.
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<T: Sleep>(&self, &T);
}
Luego, implementamos dos entornos de sueño diferentes.
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());
}
}
La última pieza son las implementaciones concretas 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);
}