我是Rust的新手,我只是刚刚开始掌握一些核心概念。
在我的情况下,我需要返回一个盒装特征的向量。我想在函数式编程风格中这样做,但我无法编译它。
有人可以解释为什么下面的代码在我使用for
循环(命令式样式)时有效,但在使用迭代器(在这种情况下为filter_map
)使用函数式编程样式时不起作用?
注意:这是一个人为的例子,并且已经简化为我能写的最简单的形式,以证明我的误解。
#![allow(unused_variables)]
struct Foo {}
struct Bar {}
trait Thing {}
impl Thing for Foo {}
impl Thing for Bar {}
fn main() {
let things = get_things_using_for_loop(); // works!
let things = get_things_using_iterator(); // doesn't work! :-(
}
fn get_things_using_for_loop() -> Vec<Box<Thing>> {
let mut things: Vec<Box<Thing>> = vec![];
for t in ["foo".to_string(), "bar".to_string()].iter() {
if t == "foo" {
things.push(Box::new(Foo {}))
} else if t == "bar" {
things.push(Box::new(Bar {}))
}
}
things
}
fn get_things_using_iterator() -> Vec<Box<Thing>> {
["foo".to_string(), "bar".to_string()]
.iter()
.filter_map(|t| {
if t == "foo" {
Some(Box::new(Foo {}))
} else if t == "bar" {
Some(Box::new(Bar {}))
} else {
None
}
})
.collect()
}
函数get_things_using_for_loop()
编译,而函数get_things_using_iterator()
则不编译。
这是错误:
error: mismatched types [--explain E0308]
--> <anon>:23:31
|>
23 |> Some(Box::new(Bar {}))
|> ^^^^^^ expected struct `Foo`, found struct `Bar`
note: expected type `Foo`
note: found type `Bar`
答案 0 :(得分:6)
这与功能与命令无关。只是纯粹的类型:
struct Foo {}
struct Bar {}
trait Thing {}
impl Thing for Foo {}
impl Thing for Bar {}
fn main() {
let t = "quux";
if t == "foo" {
Some(Box::new(Foo {}))
} else if t == "bar" {
Some(Box::new(Bar {}))
} else {
None
};
}
if
/ else
返回的是哪种类型?第一个分支表示它是Option<Box<Foo>>
。第二个是Option<Box<Bar>>
。那些不是同一类型。如果您强制执行以下类型:
if t == "foo" {
Some(Box::new(Foo {}) as Box<Thing>)
} else if t == "bar" {
Some(Box::new(Bar {}) as Box<Thing>)
} else {
None
};
一切都会奏效。
您还可以指定值的类型:
let x: Option<Box<Thing>> = if t == "foo" {
Some(Box::new(Foo {}))
} else if t == "bar" {
Some(Box::new(Bar {}))
} else {
None
};
或者,就像你说的那样,通过注释闭包来指定类型:
.filter_map(|t| -> Option<Box<Thing>> {
答案 1 :(得分:5)
这实际上与这两种编程风格无关,但仅仅是你的代码的巧合。在第一个函数中,您明确说明了向量的类型:
let mut things: Vec<Box<Thing>> = vec![];
在你的第二个函数中,你永远不会提到你想要一个满载Box<Thing>
的向量。您只有一个if-else
构造,其案例返回:
Some(Box::new(Foo {}))
Some(Box::new(Bar {}))
None
编译器看到第一种情况并假设:“好的,程序员想要返回Option<Box<Foo>>
”。但是在接下来的例子中你有一个Option<Box<Bar>>
。现在编译器很困惑。但它不会搜索整个类型空间来查找由两种类型(Foo
和Bar
)实现的特征。另外:可能有多个特征都由Foo
和Bar
实现。我的观点:你有告诉编译器关于Thing
的一些事情!
为此,您有多种选择,例如:
if t == "foo" {
Some(Box::new(Foo {}) as Box<Thing>)
} else { /* ... */ }
只需将as Box<Thing>
添加到第一个案例已经足够了,编译器很满意。