2016-06-26 7 views
0
trait Foo {} 
trait Bar: Foo {} 

struct Zxc; 
impl Foo for Zxc {} 

struct Baz; 
impl Bar for Baz {} 
impl Foo for Baz {}  

struct Abc<F: Foo> { 
    f: F 
} 
impl<F: Foo> Abc<F> { 
    fn bared<B: Bar>(&mut self, b: B) { 
     self.f = b; 
    } 
} 

fn main() { 
    let mut abc = Abc { f: Zxc }; 
    abc.bared(Baz); 
} 

Try it on the playgroundです。`構造体Abc <Foo>` `trait Bar:Foo {}`を保存します。

AbcFoo形質を保存する。 abc.bared(Baz)Bazとし、FooBarを実装していますが、をAbcに保存するとタイプミスマッチエラーが発生します。それを修正するには?

答えて

2

ZxcおよびBazは関連性のないタイプです。一方を他方に割り当てることはできません。

Abcに「基本クラス」Fooを使用して両方を保存できるようにする場合は、trait objectを使用します。

struct Abc { 
    f: Box<Foo> 
} 
//^Abc is not a template. 

impl Abc { 
    fn bared<B: Bar + 'static>(&mut self, b: B) { 
     self.f = Box::new(b); 
    } 
    //^maybe you want to change it to take `b: Box<Bar>` 
    // it depends on how you want to expose the API. 
} 

fn main() { 
    let mut abc = Abc { f: Box::new(Zxc) }; 
    abc.bared(Baz); 
} 

しかし、RustのOOPパラダイムはJavaのものと異なり、特性オブジェクトは最良の解決策ではない可能性があります。おそらく、あなたは解決したい実際の問題を示すべきです。

+0

たぶん 'unsafe'を使用してのようなもの? – Lupe

+2

ここで 'unsafe'を使わないことを100%推奨します。私はそれが必要だとは思わないし、それを使用するとメモリエラーにつながる可能性が高い。 – Shepmaster

+0

良いAPIを作成するための@Shepmaster Crutch – Lupe

1

Fooを実装するものを格納できる型は宣言していません。 Fooを実装する型の特定のオブジェクトを格納できる型を作成するための一種のファクトリを宣言しました。

struct Abc<F: Foo> { 
    f: F 
} 

これは、「私にFooを実装タイプFを与える、と私は1つ格納タイプAbc<F>作成します」をとほぼ翻訳:あなたのコードの一部を通って行く

あなたがそれを使用します。

let mut abc = Abc { f: Box::new(Zxc) }; 

コンパイラによって推論種類に戻って追加:

let mut abc: Abc<Zxc> = Abc { f: Box::new(Zxc) }; 

のでabc.fのタイプはBox<Zxc>ある - ないBox<Foo>

だから今はAbc<Zxc>を持っている - (あなたがタイプパラメータFを指定するまで、あなたは具体的なオブジェクトを作成することはできません)Abc汎用的ではありません。

Bazと一緒に使用できない理由は明らかです。さて、実際のエラーになって

:エラーがabc.baredへの呼び出しには、実際にはない

<anon>:17:18: 17:19 error: mismatched types: expected `F`, 
    found `B` (expected type parameter, 
    found a different type parameter) [E0308] <anon>:17   self.f = b; 
         ^<anon>:17:18: 17:19 help: see the detailed explanation for E0308 error: aborting due to previous error 

// In the impl of Abc<F> 
fn bared<B: Bar + 'static>(&mut self, b: B) { 
    self.f = Box::new(b); 
} 

この方法は、それがBarを実装していますが、全くFに関連していなくてもよい任意の型Bを取ると言い、そしてself.fに格納し、タイプBox<F>である:それは定義です。をBox<F>に割り当てることはできません。

@kennytmによれば、フィールドを特性オブジェクト(Box<Foo>)にすることで、他の言語の基本クラスポインタに似ています。

impl<F: Foo> Abc<F> { 
    fn bared<B: Bar>(self, b: B) -> Abc<B> { 
     Abc { f: b } 
    } 
} 

この:あなたはBuilderパターンを構築しようとしている場合は他の回答に加えて

0

、あなたは値によってビルダーを取るし、新しい型を返すためにあなたのbared方法を変更することもできますbaredへの呼び出しでAbc<Baz>からAbc<Zxc>からの具体的なタイプを変更します。

let abc = Abc { f: Zxc }; 
let def = abc.bared(Baz); 
+0

'Abc 'はサイズが0バイトです。これは、コンパイラがそのようなビルダーを最適化し、呼び出しの代わりに 'Abc 'をインライン化することを意味しますか? – Lupe

+0

@Lupeコンパイラが以前作成したビルダーで行ったインライン展開のレベルで、[私は非常に感銘を受けました](https://twitter.com/JakeGoulding/status/738568045623431168)。 – Shepmaster

+0

'fn foo(mut self) - > Self'と書いて、変更が必要であることをユーザーに示しますか? – Lupe

関連する問題