Я реализовал черту для другого признака, но не могу вызывать методы из обеих черт
У меня есть черта под названием Sleep
:
pub trait Sleep {
fn sleep(&self);
}
Я мог бы предоставить разные реализации сна для каждой структуры, но оказалось, что большинство людей спят очень небольшим количеством способов. Вы можете спать в кровати:
pub trait HasBed {
fn sleep_in_bed(&self);
fn jump_on_bed(&self);
}
impl Sleep for HasBed {
fn sleep(&self) {
self.sleep_in_bed()
}
}
Если вы в походе, вы можете спать в палатке:
pub trait HasTent {
fn sleep_in_tent(&self);
fn hide_in_tent(&self);
}
impl Sleep for HasTent {
fn sleep(&self) {
self.sleep_in_tent()
}
}
Есть несколько странных случаев. У меня есть друг, который может спать, стоя у стены, но большинство людей в большинстве случаев попадают в простой случай.
Мы определяем некоторые структуры и позволяем им спать:
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();
}
Ой-ой! Ошибка компиляции:
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'
Эта ошибка компилятора странная, потому что если с чертой, реализующей другую черту, было что-то не так, я ожидал услышать об этом, когда делал это, не в самой нижней части программы, когда пытался использовать результат.
В этом примере есть только 2 структуры и 2 способа спать, но в общем случае есть много структур и несколько способов спать (но не так много, как есть структуры).
Bed
- это, в основном, реализация для Sleep
, но в общем случае Bed
имеет много применений и может реализовать много вещей.
Единственный очевидный подход состоит в том, чтобы преобразовать impl Sleep for...
в макрос, который использует себя, но кажется хакерским и ужасным.
Ответы
Ответ 1
Вам необходимо реализовать вторую черту для объектов, которые реализуют первую черту:
impl<T> Sleep for T
where
T: HasBed,
{
fn sleep(&self) {
self.sleep_in_bed()
}
}
Ранее вы реализовывали Sleep
для типа черты, лучше выраженного как dyn HasBed
. Смотрите, что означает "dyn" в типе? Больше подробностей.
Тем не менее, это сломается, как только вы добавите вторую реализацию:
impl<T> Sleep for T
where
T: HasTent,
{
fn sleep(&self) {
self.sleep_in_tent()
}
}
С
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
Для чего-то можно реализовать как HasBed
и HasTent
. Если бы появилось что-то, что реализовывало бы и то и другое, то код теперь был бы неоднозначным. Обходным путем для этого может стать специализация, но пока нет стабильной реализации этого.
Как вы достигаете своей цели? Я думаю, что вы уже предложили текущее лучшее решение - написать макрос. Вы также можете написать свой собственный производный макрос. Макросы на самом деле не так уж и плохи, но писать их неудобно.
Еще одна вещь, которая может полностью основываться на именах, выбранных вами в качестве примера, состоит в том, чтобы просто встроить структуры в другие структуры, при желании сделать их общедоступными. Так как ваша реализация Sleep
основном зависит только от кровати/палатки, никакая функциональность не будет потеряна при этом. Конечно, некоторые люди могут чувствовать, что нарушает инкапсуляцию. Вы можете снова создать макросы для реализации своего рода делегирования.
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();
}
Ответ 2
Здесь мы можем использовать связанные элементы.
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);
}
Затем мы реализуем две разные среды сна.
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());
}
}
Последний фрагмент - это конкретные реализации из них.
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"
}
}
Тестовый код:
fn main() {
let bed = Bed;
let tent = Tent;
let jim = Jim;
let jane = Jane;
jim.sleep(&bed);
jane.sleep(&tent);
}