在`struct Abc <foo>`中保存`trait Bar:Foo {}`

时间:2016-06-26 11:36:31

标签: rust

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特征; abc.bared(Baz) Baz Foo实施BarBaz,但在Abc中保存<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:baselineAligned="false" android:orientation="horizontal" > <fragment android:id="@+id/list_Fragment1xml_mainAct_ID" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight=".08" class="com.knowledgeflex.lpm_qrt.Menu_Fragment1" > </fragment> <fragment android:id="@+id/detail_Fragmentxml_mainAct_ID" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight=".28" class="com.knowledgeflex.lpm_qrt.Option_Fragment3"> </fragment> <fragment android:id="@+id/map_Fragmentxml_mainAct_ID" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight=".92" class="com.knowledgeflex.lpm_qrt.Map_Fragment4"> </fragment> </LinearLayout> 时出现类型不匹配错误。如何解决?

3 个答案:

答案 0 :(得分:2)

ZxcBaz是不相关的类型,您不能将其中一个分配给另一个。

如果您希望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不同,特征对象可能不是最佳解决方案。也许你应该展示你想要解决的实际问题。

答案 1 :(得分:1)

您尚未声明可以存储任何实现Foo的内容的类型;你已经宣布了一种用于制作类型的工厂,它可以存储实现Foo类型的任何特定对象。

完成一些代码:

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

这大致翻译为“给我一个实现F的{​​{1}}类型”,我将创建一个存储一个“{1}}的类型。”

使用时:

Foo

添加回编译器推断的类型:

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>之前,你不能创建具体的对象。)

现在应该清楚为什么不能将它与Abc一起使用。

现在出现实际错误:

F

错误实际上不是对Baz的调用;这是定义:

<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

此方法表示它采用任何类型abc.bared,它实现// In the impl of Abc<F> fn bared<B: Bar + 'static>(&mut self, b: B) { self.f = Box::new(b); } 但可能与B完全无关,并将其存储在Bar中,这是F类型为self.f。您无法将Box<F>分配给Box<B>,因为它们的类型不同。

正如@kennytm所说,你可以通过使字段成为特征对象(Box<F>)来存储不同的类型,这与其他语言中的基类指针更相似。

答案 2 :(得分:0)

除了其他答案之外,如果您尝试构建构建器模式,则可能需要更改bared方法以按值获取构建器,然后返回新类型:

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

这会在调用Abc<Zxc>时将具体类型从Abc<Baz>更改为bared

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