我为另一个特征实现了一个特征,但不能从两个特征中调用方法

时间:2015-03-25 13:05:10

标签: rust traits

我有一个名为Sleep的特性:

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

我可以为每个结构提供sleep的不同实现,但事实证明大多数人以很少的方式睡觉。你可以睡在床上:

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

有一些古怪的案例。我有一个可以靠墙站着睡觉的朋友,但大多数时候,大多数人都陷入了一些简单的情况。

我们定义一些结构并让它们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();
}

嗯,哦!编译错误:

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`

这个编译器错误很奇怪,因为如果实现另一个特性的特性出现问题,我希望在我这样做的时候听到它回来,当我尝试使用结果时,不是在程序的最底层。

在这个例子中,只有2种结构和2种睡眠方式,但在一般情况下,有许多结构和几种睡眠方式(但没有结构的多种方式)。

Bed主要是Sleep的实现,但在一般情况下,Bed有很多用途,可以实现很多功能。

唯一明显的方法是将impl Sleep for...转换为结构自身使用的宏,但这看起来很糟糕而且非常糟糕。

2 个答案:

答案 0 :(得分:16)

您需要为实现第一个特征的对象实现第二个特征

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

但是,只要添加第二个,这就会破裂:

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:52:1
   |
42 | / impl<T> Sleep for T
43 | | where
44 | |     T: HasBed,
45 | | {
...  |
48 | |     }
49 | | }
   | |_- first implementation here
...
52 | / impl<T> Sleep for T
53 | | where
54 | |     T: HasTent,
55 | | {
...  |
58 | |     }
59 | | }
   | |_^ conflicting implementation

有可能实施 HasBedHasTent。如果出现了同时实现的内容,那么代码现在就不明确了。

你如何实现目标?我想你已经提出了目前最好的解决方案 - 写一个宏。在您can write your own deriving之前,这可能被视为权宜之计。宏确实不是那么糟糕,但写起来却很笨拙。

另一件事,可能完全基于您为您的示例选择的名称,将简单地将结构嵌入到其他结构中,可选地将它们公之于众。由于您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();
}

答案 1 :(得分:15)

我们可以在这里使用相关项目。

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