generics - 泛化两个结构

标签 generics types rust

我有两个独立的库(不同的 crates),每个库都定义了一个结构和一个函数来构建这样的结构,如果可能的话(为简单起见,我将所有内容都放在一个模块中):

struct Foo {}
struct Bar {}

impl Foo {
    fn make_foo(a: &str) -> Option<Foo> {
        Some(Foo {})
    }
}

impl Bar {
    fn make_bar(b: &str) -> Option<Bar> {
        Some(Bar {})
    }
}

我现在想以相同的方式处理这两个结构,而不管我是否真的有一个 Foo。或 Bar :

trait SomeTrait {
    fn do_something() -> ();
}
impl SomeTrait for Foo {
    fn do_something() -> () {
        ()
    }
}
impl SomeTrait for Bar {
    fn do_something() -> () {
        ()
    }
}

由于SomeTrait的大小在编译时是未知的,我不能简单地 let a: Option<SomeTrait> = Foo::make_foo("abc") .所以我试着把它包在 Box 里(解压 Option 之前):

fn main() {
    let f: Option<Box<SomeTrait>> = Foo::make_foo("abc").map(Box::new)
}

但编译器仍然报错:

error: mismatched types [E0308]
    let b: Option<Box<SomeTrait>> = Foo::new().map(Box::new);
                                    ^~~~~~~~~~~~~~~~~~~~~~~~
help: run `rustc --explain E0308` to see a detailed explanation
note: expected type `std::option::Option<Box<SomeTrait>>`
note:    found type `std::option::Option<Box<Foo>>`

我尝试使用 as用于转换,但这没有用,因为它们都是非标量类型。

我该如何解决这个问题(不涉及 FooBar 的实现)?我觉得我正在尝试将经典 OOP 语言的模式应用于 Rust 的特征系统。

最后,我希望能够做类似的事情:

fn main() {
    let arg = 5;
    let x: Option<Box<SomeTrait>> = match arg {
        4 => Foo::make_foo("abc").map(Box::new),
        _ => Bar::make_bar("abc").map(Box::new),
    };

    x.do_something()
}

完整示例:

// Library part
struct Foo {}
struct Bar {}

impl Foo {
    fn new() -> Option<Foo> {
        Some(Foo {})
    }
}
impl Bar {
    fn new() -> Option<Bar> {
        Some(Bar {})
    }
}

// My Part
trait SomeTrait {
    fn do_something(&self);
}
impl SomeTrait for Foo {
    fn do_something(&self) {
        println!("foo")
    }
}
impl SomeTrait for Bar {
    fn do_something(&self) {
        println!("bar")
    }
}

fn main() {
    let b: Option<Box<SomeTrait>> = match "x" {
        "x" => Foo::new().map(Box::new),
        _ => Bar::new().map(Box::new),
    };
    b.unwrap().do_something();
}

最佳答案

这有两个普遍的问题。首先是您需要将对象表示为 Option<Box<SomeTrait>>所以设置你的变量绑定(bind)需要像这样let f: Option<Box<SomeTrait>> = Some(Box::new(Foo::make_foo("abc")));

并且您需要从 make 方法中删除选项部分,以便稍后可以正确包装它。

impl Foo {
    fn make_foo(a: &str) -> Foo {
        Foo {}
    }
}

这里的顺序很重要。选项不能包含未调整大小的特征,因此它需要包装盒子。

其次,如果特征具有不引用 &self 的方法,则特征不能成为特征对象。

除 rust 错误文档之外的快速显示

Method has no receiver

Methods that do not take a self parameter can't be called since there won't be a way to get a pointer to the method table for them.

所以对于你的特征方法,你需要

trait SomeTrait {
    fn do_something(&self);
}
impl SomeTrait for Foo {
    fn do_something(&self) {  }
}
impl SomeTrait for Bar {
    fn do_something(&self) {  }
}

编辑:

由于您无法更改 make 函数,另一种正确映射它们的方法是向 lambda 添加返回类型,以便它知道如何帮助编译器同意该类型。

let f: Option<Box<SomeTrait>> = Foo::make_foo("abc").and_then(|foo| -> Option<Box<SomeTrait>> {
    Some(Box::new(foo))
});

关于generics - 泛化两个结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39213311/

相关文章:

java - 有界通配符和函数参数的关系

java - 泛型和ArrayList

c++ - 强制函数只接受一组特定的数据类型

c++ - 如何转换 C++ 模板参数?

c# - 无法转换通用对象

c# - 比较类型是否可为空

file-io - 文本文件输入/输出期间的行尾转换

file - 从文件中获取数字并放入 Vec<i32> 但不断出现错误

rust - 如何让 Rust BTreeMap::get 返回一个字符串?

Java 类定义泛型