rust-lang / rust-lang/rust

`Fn` closure with elided higher-ranked lifetimes rejected as "not general enough" through a `[F; 1]` wrapper

Open
#158,652 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug needs-triage
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code

This program compiles - it is rustc's own UI test tests/ui/closure-expected-type/issue-24421.rs (directive //@ check-pass), a closure whose argument types are inferred from an Fn bound with elided (higher-ranked) lifetimes:

fn test<F: Fn(&u64, &u64)>(f: F) {}

fn main() {
    test(|x, y| {});
}

Wrapping the f parameter in a one-element array [F; 1] (destructured in the body with let [f] = _f;) and wrapping the call-site closure in [ ... ] makes inference fail:

fn test<F: Fn(&u64, &u64)>(_f: [F; 1]) {
    let [f] = _f;
}

fn main() {
    test([|x, y| {}]); // error: implementation of `Fn` is not general enough
}
Expected

Both versions compile. The bound F: Fn(&u64, &u64) should behave the same as F: for<'a, 'b> Fn(&'a u64, &'b u64) (writing the bound that explicit way compiles identically) - a higher-ranked trait bound requiring F to implement Fn for any pair of lifetimes on the arguments. The rewritten program gives the compiler the same information: the argument has expected type [F; 1], so its single element has expected type F, still carrying the bound F: for<'a, 'b> Fn(&'a u64, &'b u64). The array literal adds one layer of structure but no ambiguity. The closure |x, y| {} accepts any (&u64, &u64) pair regardless of lifetime, so both programs are equivalent and both should compile.

Actual

The array-wrapped version is rejected:

error: implementation of `Fn` is not general enough
 --> src/main.rs:6:5
  |
6 |     test([|x, y| {}]);
  |     ^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
  |
  = note: closure with signature `fn(&'2 u64, &u64)` must implement `Fn<(&'1 u64, &u64)>`, for any lifetime `'1`...
  = note: ...but it actually implements `Fn<(&'2 u64, &u64)>`, for some specific lifetime `'2`

error: implementation of `Fn` is not general enough
 --> src/main.rs:6:5
  |
6 |     test([|x, y| {}]);
  |     ^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
  |
  = note: closure with signature `fn(&u64, &'2 u64)` must implement `Fn<(&u64, &'1 u64)>`, for any lifetime `'1`...
  = note: ...but it actually implements `Fn<(&u64, &'2 u64)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:6:5
  |
6 |     test([|x, y| {}]);
  |     ^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: closure with signature `fn(&'2 u64, &u64)` must implement `FnOnce<(&'1 u64, &u64)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 u64, &u64)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:6:5
  |
6 |     test([|x, y| {}]);
  |     ^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: closure with signature `fn(&u64, &'2 u64)` must implement `FnOnce<(&u64, &'1 u64)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&u64, &'2 u64)>`, for some specific lifetime `'2`

error: aborting due to 4 previous errors
Wrapper variants

The failure is not universal across indirect wrappers. Each row keeps the F: Fn(&u64, &u64) bound and an empty body, varying only how F is wrapped:

Wrapper Call site Result
none (baseline) test(|x, y| {}) yes - compiles
&F test(&|x, y| {}) yes - compiles
(F,) test((|x, y| {},)) yes - compiles
[F; 1] test([|x, y| {}]) no - not general enough
Option<F> test(Some(|x, y| {})) no - not general enough

The direct argument, a reference, and a tuple field compile; the array literal and the enum variant constructor (Some(...)) produce "not general enough." This is the same set of working forms (F, &F, (F,)) observed for a related E0282 inference split with the same [F; 1] wrapper, filed separately.

Partial annotation

Annotating a subset of the closure parameters fixes only those parameters:

Call site Result
test([|x, y| {}]) no - both x and y fail
test([|x:&u64, y:&u64| {}]) yes - compiles
test([|x:&u64, y| {}]) no - only y fails
test([|x, y:&u64| {}]) no - only x fails

Annotating a closure parameter with an explicit &u64 makes that parameter compile; an unannotated parameter in the same closure still fails. The failure therefore appears only for a parameter whose reference type has no source other than the Fn bound reaching the closure through the wrapper.

Notes

The same "not general enough" error appears in existing issues #70263, #99991, and #87079.
But no existing issue combines the array-literal surface trigger with this error.

Meta
rustc 1.96.0 (ac68faa20 2026-05-25)
binary: rustc
commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96
commit-date: 2026-05-25
host: aarch64-apple-darwin
release: 1.96.0
LLVM version: 22.1.2

Tested on stable 1.96.0 only.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the array-wrapped and baseline examples with rustc 1.96, then compare the wrapper variants and partial annotations described in the issue. Start with the existing passing test tests/ui/closure-expected-type/issue-24421.rs and related issues #70263, #99991, and #87079; done means the array-wrapped closure compiles without weakening the higher-ranked bound.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.