Unable to infer fn item to fn ptr coercion when fn ptr nested within more than one level of generics
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.2k
- PR merge metrics
- PR metrics pending
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the coercion failure from the linked Playground using the reported rustc 1.77.0-nightly version and the example in src/lib.rs. Investigate how fn item-to-pointer coercion is inferred through nested iterator generics; done means the example compiles without the explicit as _ workaround and has a regression test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100