2015-03-25 47 views
17

我有一個特點叫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種睡眠方式,但在一般情況下,有許多結構和幾種睡眠方式(但結構並不多)。

A Bed主要是Sleep的實現,但在一般情況下,Bed有很多用途,可以實現很多功能。

唯一立即明顯的方法是將impl Sleep for...轉換爲自己使用的宏,但這看起來很駭人而且可怕。

回答

10

您需要實現的第二個特點爲實現第一個特點對象:當您添加第二個儘快

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

With

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 

這是可能的事情g執行均爲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(); 
} 
+0

不能這樣(理論上)可以在來電者的喜歡的東西方消歧'使用HasBed;'? – Drew 2015-03-25 13:37:59

+0

我不知道編譯器的詳細信息,以便對啓用它有多複雜進行有根據的猜測。 – Shepmaster 2015-03-25 13:47:38

+1

@Drew問題是沒有實現類似的東西,它從語言設計角度尋找一個好的設計是可取的。並激勵它(這將是解決可以不同解決的問題的重要補充)。 – delnan 2015-03-25 16:42:29

12

我們可以在這裏使用關聯的項目。

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