Option map won't compile while match works well
Consider this snippet:
fn xxx() -> Option<&'static str> {
Some("word1 word2 word3")
}
fn to_words<'a>() -> Option<Box<Iterator<Item = &'a str> + 'a>> {
xxx().map(|x|Box::new(x.split(' ')))
}
fn main() {
println!("{}", to_words().unwrap().count());
}
it won't compiles with the error:
error[E0308]: mismatched types
--> src/main.rs:6:5
|
5 | fn to_words<'a>() -> Option<Box<Iterator<Item = &'a str> + 'a>> {
| ------------------------------------------ expected `std::option::Option<std::boxed::Box<std::iter::Iterator<Item=&'a str> + 'a>>` because of return type
6 | xxx().map(|x|Box::new(x.split(' ')))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait std::iter::Iterator, found struct `std::str::Split`
|
= note: expected type `std::option::Option<std::boxed::Box<std::iter::Iterator<Item=&'a str> + 'a>>`
found type `std::option::Option<std::boxed::Box<std::str::Split<'_, char>>>`
but match in exactly same case compiles well:
fn xxx() -> Option<&'static str> {
Some("word1 word2 word3")
}
fn to_words<'a>() -> Option<Box<Iterator<Item = &'a str> + 'a>> {
match xxx() {
Some(x) => Some(Box::new(x.split(' '))),
None => None
}
}
fn main() {
println!("{}", to_words().unwrap().count());
}
And if I explicitly define closure's argument type and returning value type it also compiles well:
fn xxx() -> Option<&'static str> {
Some("word1 word2 word3")
}
fn to_words<'a>() -> Option<Box<Iterator<Item = &'a str> + 'a>> {
let f: fn(x: &'a str) -> Box<Iterator<Item = &'a str>> = |x| {
Box::new(x.split(' '))
};
xxx().map(f)
}
fn main() {
println!("{}", to_words().unwrap().count());
}
I am sure there should not be differences between match and map at all.
This issue is reproduceble on rust stable and nightly.
There is a difference -- you need a coercion to convert from the concrete type of some boxed split iterator
Box<Split<..>intoBox<Iterator<..>>(unsizing coercion). The.map()version does not support this, or rather, a value inside themap's closure will not coerce due to the expected type outside the map. I haven't found an open or closed issue on this topic, but there might be one already.bluss at 2018-01-05 22:42:07
For completeness: the simplest explicit way to get this to compile is
fn to_words<'a>() -> Option<Box<Iterator<Item = &'a str> + 'a>> { xxx().map(|x| Box::new(x.split(' ')) as Box<Iterator<Item = _>>) }It would be awesome if the compiler could infer casts like this through closure boundaries though!
Dabo at 2018-01-07 05:07:50
Triage: no change
Maayan Hanin at 2022-03-22 07:28:56