2016-08-07 37 views
12

我的目標是做最後兩行的代碼編譯,最後斷言的經過:允許函數接受`T`或任何`FnMut(T) - > T`

struct State { 
    string: String 
} 

impl State { 
    fn string<F: FnMut(String) -> String>(mut self, mut f: F) -> Self { 
     self.string = f(self.string); 
     self 
    } 
} 

fn main() { 
    let state = State { string: String::from("foo") }; 
    assert_eq!(state.string, "foo"); 
    let state = state.string(|old| old + "bar"); 
    assert_eq!(state.string, "foobar"); 
    // let state = state.string(String::from("baz")); 
    // assert_eq!(state.string, "baz"); 
} 

我認爲這將是可能的特質和專業化,但下面的代碼:

#![feature(specialization)] 

trait Get<T> { 
    fn get(self, old: T) -> T; 
} 

impl<T> Get<T> for T { 
    default fn get(self, _: T) -> T { 
     self 
    } 
} 

impl<T, F> Get<T> for F where F: FnMut(T) -> T { 
    fn get(mut self, old: T) -> T { 
     self(old) 
    } 
} 

struct State { 
    string: String 
} 

impl State { 
    fn string<G: Get<String>>(mut self, g: G) -> Self { 
     self.string = g.get(self.string); 
     self 
    } 
} 

拋出這個錯誤(live):

error[E0119]: conflicting implementations of trait `Get<_>`: 
    --> <anon>:13:1 
    | 
13 | impl<T, F> Get<T> for F where F: FnMut(T) -> T { 
    |^
    | 
note: conflicting implementation is here: 
    --> <anon>:7:1 
    | 
7 | impl<T> Get<T> for T { 
    |^

error: aborting due to previous error 

所以我的問題是,爲什麼第二個impl沒有獲得比第一個更具體的「特定」,並且在當前的stable或每晚的Rust中有沒有辦法讓我的原始代碼工作?

編輯:我知道只爲一種類型實現一個特性是可行的,但我想爲任何類型的通用解決方案,因爲我希望能夠使用此結構的任何任意字段。

+0

如果什麼'T'實現'FnMut( T) - > T'? – CodesInChaos

+0

@CodesInChaos我希望專業化選擇'FnMut(T) - > T'的實現,但我會很高興與任何一個,因爲我不打算與這種類型的使用它。 – Dogbert

回答

6

爲了您的具體問題,你不需要專業化:

struct State { 
    string: String, 
} 

impl State { 
    fn string<F>(mut self, mut f: F) -> Self 
     where F: Thing 
    { 
     self.string = f.thing(self.string); 
     self 
    } 
} 

trait Thing { 
    fn thing(&mut self, s: String) -> String; 
} 

impl Thing for String { 
    fn thing(&mut self, _s: String) -> String { 
     self.clone() 
    } 
} 

impl<F> Thing for F 
    where F: FnMut(String) -> String 
{ 
    fn thing(&mut self, s: String) -> String { 
     (self)(s) 
    } 
} 

fn main() { 
    let state = State { string: String::from("foo") }; 
    assert_eq!(state.string, "foo"); 
    let state = state.string(|old| old + "bar"); 
    assert_eq!(state.string, "foobar"); 
    let state = state.string(String::from("baz")); 
    assert_eq!(state.string, "baz"); 
} 

您既可以想要求FnOnce或實現一個&str的特質。目前,String的分配未被使用,導致一些效率低下。

然後,您可以多次實施的特質對有趣的類型:

struct State { 
    string: String, 
    vec: Vec<u8>, 
} 

impl State { 
    fn string<F>(mut self, mut f: F) -> Self 
     where F: Thing<String> 
    { 
     self.string = f.thing(self.string); 
     self 
    } 

    fn vec<F>(mut self, mut f: F) -> Self 
     where F: Thing<Vec<u8>> 
    { 
     self.vec = f.thing(self.vec); 
     self 
    } 
} 

trait Thing<T> { 
    fn thing(&mut self, s: T) -> T; 
} 

impl Thing<String> for String { 
    fn thing(&mut self, _s: String) -> String { 
     self.clone() 
    } 
} 

impl<F> Thing<String> for F 
    where F: FnMut(String) -> String 
{ 
    fn thing(&mut self, s: String) -> String { 
     (self)(s) 
    } 
} 

impl Thing<Vec<u8>> for Vec<u8> { 
    fn thing(&mut self, _s: Vec<u8>) -> Vec<u8> { 
     self.clone() 
    } 
} 

impl<F> Thing<Vec<u8>> for F 
    where F: FnMut(Vec<u8>) -> Vec<u8> 
{ 
    fn thing(&mut self, s: Vec<u8>) -> Vec<u8> { 
     (self)(s) 
    } 
} 

fn main() { 
    let state = State { string: String::from("foo"), vec: vec![1] }; 

    assert_eq!(state.string, "foo"); 
    let state = state.string(|old| old + "bar"); 
    assert_eq!(state.string, "foobar"); 
    let state = state.string(String::from("baz")); 
    assert_eq!(state.string, "baz"); 

    assert_eq!(state.vec, [1]); 
    let state = state.vec(|mut old: Vec<u8>| { 
     old.push(2); 
     old 
    }); 
    assert_eq!(state.vec, [1, 2]); 
    let state = state.vec(vec![3]); 
    assert_eq!(state.vec, [3]); 
} 

我相信,重複可以通過宏來處理:

macro_rules! thing { 
    ($t: ty) => { 
     impl Thing<$t> for $t { 
      default fn thing(&mut self, _val: $t) -> $t { 
       self.clone() 
      } 
     } 

     impl<F> Thing<$t> for F 
      where F: FnMut($t) -> $t 
     { 
      fn thing(&mut self, val: $t) -> $t { 
       (self)(val) 
      } 
     } 
    } 
} 

thing!(String); 
thing!(Vec<u8>); 
+0

感謝您的代碼!我想這是現​​在最好的方式,如果韋德拉克說專業化是正確的。 – Dogbert

4

專業化在這裏不起作用,因爲專業化只適用於連鎖店。也就是說,存在滿足impl

impl<T, F> Get<T> for F where F: FnMut(T) -> T 

但不

impl<T> Get<T> for T 

使後者不能專注前者的功能。

解決這個問題的最簡單方法是隻寫一個GetString特徵而不是Get<T>特徵;這樣你就不必考慮專門化這種malarkey了。

+0

對不起,我應該提到我需要一個通用的解決方案,因爲結構可以包含任意字段,我想爲它們創建一個類似的setter方法。那可能嗎? – Dogbert

+0

@Dogbert你認爲只是*不*這樣做?如果有解決方案(我懷疑是否存在),這不會令人愉快。你有沒有理由不能使用兩種不同的功能? – Veedrac

+0

我想最終爲具有宏的任何結構生成這些函數,並且'concat_idents!'不能用於生成從傳入的標識符稍微修改的名稱,例如,如果用戶傳遞了'string','set_string()'和'update_string()'。此外,只是'string()'看起來會更乾淨。無論如何,這是一個實驗,如果它使最終用戶的類型錯誤變得更糟,我可能不會在嚴肅的項目中使用它。 – Dogbert

相關問題