Description
I've run into a case where rustc is not able to infer a fn item to fn pointer coercion when the resulting type is sufficiently complex.
use std::iter;
pub struct A;
pub struct B<'a>(&'a A);
pub fn mapper(a: &A) -> B<'_> {
B(a)
}
type MapIterator<'a> = iter::Map<std::slice::Iter<'a, A>, fn(&'a A) -> B<'a>>;
type CompoundIterator<'a> = iter::Chain<std::option::IntoIter<B<'a>>, MapIterator<'a>>;
// compiles
pub fn make_map_iterator<'a>(vec: &'a Vec<A>) -> MapIterator<'a> {
vec.iter().map(mapper)
}
// doesn't compile
pub fn make_chain_iterator<'a>(option: Option<B<'a>>, vec: &'a Vec<A>) -> CompoundIterator<'a> {
option.into_iter().chain(vec.iter().map(mapper))
}
The error I see is as follows:
error[E0308]: mismatched types
--> src/lib.rs:20:5
|
19 | pub fn make_chain_iterator<'a>(option: &'a Option<A>, vec: &'a Vec<A>) -> CompoundIterator<'a> {
| -------------------- expected `std::iter::Chain<std::option::IntoIter<B<'a>>, Map<std::slice::Iter<'a, A>, fn(&'a A) -> B<'a>>>` because of return type
20 | option.as_ref().map(mapper).into_iter().chain(vec.iter().map(mapper))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found fn item
|
= note: expected struct `std::iter::Chain<std::option::IntoIter<B<'a>>, Map<std::slice::Iter<'a, _>, fn(&'a A) -> B<'a>>>`
found struct `std::iter::Chain<std::option::IntoIter<B<'_>>, Map<std::slice::Iter<'_, _>, for<'a> fn(&'a A) -> B<'a> {mapper}>>`
The first example correctly infers that mapper
needs to be coerced to fn ptr for the call to map
to return the correct type. However, once the chain
is introduced, the compiler no longer recognizes that mapper
can (and should) be coerced to fn ptr. Once the fn ptr is buried in that second level of generics, the coercion is no longer inferred.
Changing .map(mapper)
to .map(mapper as _)
makes it coerce correctly, but it seems weird to me that it would be necessary to include that. It's as if the type inference isn't realizing coercion is an option here without being explicitly hinted.
The issue still occurs if I separate the 'a
lifetime from the function pointer type using the for<'_>
syntax suggested by the diagnostic. These two facts make me think this is not a lifetime issue.
I know this is goofy code and it's a mouthful to name these compound iterator types explicitly. This is reduced from code I wrote before RPITIT was stabilized and I needed to give these types names. At this point, for my application, I could replace this with RPITIT or use the as _
workaround, but I figured this was surprising enough to be worth asking about, and might uncover a bug.
Meta
rustc --version --verbose
:
rustc 1.77.0-nightly (f688dd684 2024-01-04)
binary: rustc
commit-hash: f688dd684faca5b31b156fac2c6e0ae81fc9bc90
commit-date: 2024-01-04
host: x86_64-unknown-linux-gnu
release: 1.77.0-nightly
LLVM version: 17.0.6