Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot coerce closures to fn(...) -> ... in a slice if the slice contains only one function #48109

Closed
symphorien opened this issue Feb 9, 2018 · 6 comments · Fixed by #71599
Labels
A-closures Area: Closures (`|…| { … }`) A-coercions Area: implicit and explicit `expr as Type` coercions A-type-system Area: Type system C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@symphorien
Copy link
Contributor

fn useful(i: usize) -> usize {
    i
}

fn useful2(i: usize) -> usize {
    i
}

fn main() {
    for f in &[useful, useful2, |x| x] {
        println!("{}", f(6));
    }
}

compiles and runs fine. (see https://play.rust-lang.org/?gist=de9e7ce8fd0683e1dbdcf362a2e16dd4&version=stable)
but if I remove useful2 from the slice, type inference fails:

error[E0308]: mismatched types
  --> src/main.rs:10:24
   |
10 |     for f in &[useful, |x| x] {
   |                        ^^^^^ expected fn item, found closure
   |
   = note: expected type `fn(usize) -> usize {useful}`
              found type `[closure@src/main.rs:10:24: 10:29]`

(see https://play.rust-lang.org/?gist=428522c1c8e14eefaa690d5d09e67b82&version=stable )

If I insert useful twice (ie &[useful, useful, |x| x]) I still get this error.

@estebank estebank added A-type-system Area: Type system A-closures Area: Closures (`|…| { … }`) labels Feb 9, 2018
@Manishearth
Copy link
Member

This also depends on the order of items within the array. You must start with two different functions for it to work.

IIRC the fn item to fn pointer coercion is a bit more "loose" than other coercions (it triggers a bit more often), causing this. I wasn't aware closures also coerce.

@vitalyd
Copy link

vitalyd commented Feb 10, 2018

You don’t have to start with two different functions, but you must have (at least) 2 different ones before the closure is seen. That is, you can have &[useful, useful, useful2, <closure>] - that works.

@eddyb
Copy link
Member

eddyb commented Feb 10, 2018

This is because two functions will coerce to a function pointer, but a function and a closure won't.
Not just arrays, but if-else and match are also affected.

The only reason it works for functions, without a coercion target being inferred from anywhere, is for backwards-compatibility, since some items were incorrectly typed as fn pointers in 1.0.

cc @rust-lang/lang We could allow [|x| x, |y| y + 1] to have the type [fn(i32) -> i32] (just like [fn_1, fn_2] coerces both function types to fn pointers), do we want to?
If we do it then it would also work when mixing functions and closures - or do we only want that?

@nikomatsakis
Copy link
Contributor

@eddyb a good question. It feels like it would be useful, but might be quite confusing though, and perhaps problematic in the future if we ever get some form of union type thing going on, no?

@eddyb
Copy link
Member

eddyb commented Feb 12, 2018

@nikomatsakis Well, right now, the lack of it breaks the associativity of the "LUB coercion": LubCoerce(A, B) = C must mean either: CoerceTo(A, B) (C = B), CoerceTo(B, A) (C = A), or some special-cased C, for which CoerceTo(A, C) && CoerceTo(B, C).

We originally special-cased fn pointers to be LUB-coerced from two function types, and that means that if the final type is going to be a fn pointer type, any order of functions and fn pointer values will coerce into a single fn pointer type (assuming they all have the same signature).

Introducing more coercions (i.e. from closures) to a given special-cased C (i.e. fn pointers), without considering those types (closures) in the special-case makes LubCoerce non-associative, e.g. LubCoerce(LubCoerce(fun1, fun2), closure) works because it's effectively CoerceTo(closure, LubCoerce(fun1, fun2) = fn(...)), but LubCoerce(function, closure) doesn't.

And non-associative LUB-coerce is observable as order dependence of arrays, if-else/match arms, etc.

In other words, for an order-independent LUB-coerce, the special-cases aside from CoerceTo, should not be about coercion source types or "kinds of coercions", but about "guessing" coercion output types, and if we can guess a type O such that CoerceTo(A, O) works, we must be able to guess O for any B where CoerceTo(B, O) works.

@XAMPPRocky XAMPPRocky added C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Apr 26, 2018
@steveklabnik
Copy link
Member

Triage: no changes

@jonas-schievink jonas-schievink added the A-coercions Area: implicit and explicit `expr as Type` coercions label Apr 20, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue May 18, 2020
Support coercion between (FnDef | Closure) and (FnDef | Closure)

Fixes rust-lang#46742, fixes rust-lang#48109
Inject `Closure` into the `FnDef x FnDef` coercion special case, which makes coercion of `(FnDef | Closure) x (FnDef | Closure)` possible, where closures should be **non-capturing**.
@bors bors closed this as completed in 58e6447 May 19, 2020
@fmease fmease added A-type-system Area: Type system and removed A-type-system Area: Type system labels Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-coercions Area: implicit and explicit `expr as Type` coercions A-type-system Area: Type system C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants