fn-item to fn-pointer coercion fails with E0271 when the fn item is wrapped with array + closure
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- Rust
- Estrellas
- 119k
- Forks
- 16.2k
- Métricas de merge de PR
- Métricas de PR pendientes
Descripción
Code
This program compiles and runs - it is rustc's own UI test tests/ui/functions-closures/fn-bare-spawn.rs:
fn spawn<T: Send>(val: T, f: fn(T)) { f(val); }
fn f(i: isize) { assert_eq!(i, 100); }
pub fn main() {
spawn(100, f); // fn item `f` is coerced to the fn pointer `fn(isize)` at the argument site
}
The argument f is an fn item of fn(isize) {f}; it is coerced to the fn pointer fn(isize) because the parameter's expected type is fn(T).
This coercion is driven by the expected type.
Now route the same fn item through a closure F: FnOnce() -> fn(T) and wrap that closure in a one-element array [F; 1] (destructured in the body with let [f0] = _f0;, call-site closure wrapped in [ … ]).
The expected type fn(T) is still there but inference no longer performs the coercion:
fn spawn<T: Send, F: FnOnce() -> fn(T)>(val: T, _f0: [F; 1]) {
let [f0] = _f0;
let f = f0();
f(val);
}
fn f(i: isize) { assert_eq!(i, 100); }
pub fn main() {
spawn(100, [move || f]); // <-- error[E0271]
}
Expected
Both versions compile.
The compiler has exactly the same information, as the expected type still reaches f:
- the array argument has expected type
[F; 1], so its sole element has expected typeF. Fstill carries the boundF: FnOnce() -> fn(T), which fixes the closure's return type tofn(T).- so the closure body
fhas expected typefn(T).
Actual
The array-wrapped version is rejected with a type mismatch between the fn item and the fn pointer it should have been coerced to:
error[E0271]: expected `{closure@src/main.rs:10:17}` to return `fn({integer})`, but it returns `fn(isize) {f}`
--> src/main.rs:10:25
|
10 | spawn(100, [move || f]);
| ----- ------- ^ expected fn pointer, found fn item
| | |
| | this closure
| required by a bound introduced by this call
|
= note: expected fn pointer `fn({integer})`
found fn item `fn(isize) {f}`
note: required by a bound in `spawn`
--> src/main.rs:1:34
|
1 | fn spawn<T: Send, F: FnOnce() -> fn(T)>(val: T, _f0: [F; 1]) {
| ^^^^^ required by this bound in `spawn`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.
The closure is inferred to return the fn item's own type fn(isize) {f} instead of being coerced to the expected fn(isize) pointer - i.e. the expected return type fn(T) did not reach the closure body through the array element.
Testing with different wrapper types
// direct F - compiles
fn spawn<T: Send, F: FnOnce() -> fn(T)>(val: T, _f0: F) { let f = _f0(); f(val); }
// (F,) - compiles
fn spawn<T: Send, F: FnOnce() -> fn(T)>(val: T, _f0: (F,)) { let f = (_f0.0)(); f(val); }
// [F; 1] - fails E0271
fn spawn<T: Send, F: FnOnce() -> fn(T)>(val: T, _f0: [F; 1]) { let [f0] = _f0; let f = f0(); f(val); }
fn f(i: isize) { assert_eq!(i, 100); }
// main, respectively:
// spawn(100, move || f);
// spawn(100, (move || f,));
// spawn(100, [move || f]);
A reference &F also compiles (with the bound relaxed to F: Fn() -> fn(T), since a FnOnce cannot be called through &F).
With explicit type annotations, the wrapped form compiles
The same [F; 1]-wrapped form compiles once the closure's return type is supplied explicitly, by either of two routes:
// concrete array element type --> the coercion fires through the array literal + closure body
fn f(i: isize) { assert_eq!(i, 100); }
fn main() {
let _x: [fn() -> fn(isize); 1] = [move || f]; // compiles (the coercion fires through the annotation)
let _ = _x;
}
// explicit closure return annotation supplies the expected type by hand
fn spawn<T: Send, F: FnOnce() -> fn(T)>(val: T, _f0: [F; 1]) {
let [f0] = _f0; let f = f0(); f(val);
}
fn f(i: isize) { assert_eq!(i, 100); }
fn main() {
spawn(100isize, [move || -> fn(isize) { f }]); // compiles
}
Both compile. The failure appears only when the closure's return type has no source other than being inferred through the [F; 1] element from the callee bound.
Notes
Possibly related to #157468, another [F; 1]-wrapping case involving a callee Fn/FnOnce bound, which surfaces as E0282. I am filing this as a separate issue because the symptom differs - the error code here is E0271 rather than E0282.
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.
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Empieza reproduciendo los ejemplos mínimos directo, tupla y [F; 1], comparándolos con tests/ui/functions-closures/fn-bare-spawn.rs. Usa los casos de anotación explícita para aislar el comportamiento de coerción que falta. Se considera terminado cuando el closure envuelto en un array compila igual que las formas directa y de tupla, con cobertura de regresión para el caso E0271.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- rust
- Área
- compilers
- Tipo de issue
- Error
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Estado de actividad
- Tranquilo
- Claridad
- Bastante claro
- Aptitud para principiantes
- 45/100