I'm new to Rust, and I'm only just beginning to grasp some core concepts.
In my case, I need to return a vector of boxed traits. I'd like to do it in functional programming style, but I can't get it to compile.
Can someone explain why the below code works when I use a for loop (imperative style), but doesn't work when using a functional programming style using an iterator (filter_map in this case)?
Note: this is a contrived example and has been reduced to the simplest form I could write in order to demonstrate my misunderstanding.
#![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()
}
The function get_things_using_for_loop() compiles, while the function get_things_using_iterator() does not.
Here is the error:
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`
This has nothing to do with functional vs. imperative. Just pure types:
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
};
}
What type is returned from the if/else? The first branch says it's an Option<Box<Foo>>. The second says it's Option<Box<Bar>>. Those aren't the same type. If you coerce the types:
if t == "foo" {
Some(Box::new(Foo {}) as Box<Thing>)
} else if t == "bar" {
Some(Box::new(Bar {}) as Box<Thing>)
} else {
None
};
It will all work.
You can also specify the type of the value:
let x: Option<Box<Thing>> = if t == "foo" {
Some(Box::new(Foo {}))
} else if t == "bar" {
Some(Box::new(Bar {}))
} else {
None
};
Or, like you said, specify the type by annotating the closure:
.filter_map(|t| -> Option<Box<Thing>> {
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With