我有两个独立的库(不同的 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
用于转换,但这没有用,因为它们都是非标量类型。
我该如何解决这个问题(不涉及 Foo
和 Bar
的实现)?我觉得我正在尝试将经典 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/