Regarding type inference of numeric types with closures


#1

(I made this post on reddit and received a suggestion to repost it here.)

I noticed yesterday a curious property of type inference in Rust. The following does not work:

Box::new(|| 1) as Box<Fn() -> u32>

Rust first infers the closure return type as i32, creates a box of that, then attempts to convert it into a trait object with incorrect return type u32.

However, the following does work:

fn box_fn<F, R>(f: F) -> Box<Fn() -> R> where F: Fn() -> R + 'static {
    Box::new(f) as Box<Fn() -> R>
}
fn requires_u32(f: Box<Fn() -> u32>) -> u32 {
    f()
}
requires_u32(box_fn(|| 1));

Now the very generic closure wrapper needs to get its types straight first, which it does by figuring the calling expression needs a Fn() -> u32, matching the R type as such. Then the lambda is created within this explicitly u32 return value requiring context, making it all work out.

Could such inferrence be extended to type conversions such as in the first bit of code? Such that the conversion would be evaluated first as requiring something convertable into a specific type, which could be used to guide type inferrence on the left side?


#2

Another thing that works

let _: Box<Fn() -> u32> = Box::new(|| 1);

Sadly #![feature(type_ascription)] doesn’t work, since it can’t coerce (yet?):

error[E0308]: mismatched types
 --> src/main.rs:3:5
  |
3 |     Box::new(|| 1): Box<Fn() -> u32>;
  |     ^^^^^^^^^^^^^^ expected trait std::ops::Fn, found closure
  |
  = note: expected type `std::boxed::Box<std::ops::Fn() -> u32>`
             found type `std::boxed::Box<[closure@src/main.rs:3:14: 3:18]>`

#3

I’ve been fiddling with this stuff. Right now we do numeric fallback to i32 before checking cast coercions. This is backwards incompatible to change, consider this:

   let cap = 512 * 512;
  cap as u8;
  // Assert `cap` did not get inferred to `u8` and overflowed.
  assert_ne!(cap, 0);

If we changed this behaviour we’d infer cap to u8 which might not be desirable. So even though it’s not always ideal it seems unlikely that we will want to change this behaviour.


#4

That’s a really good point! Though I suppose as doesn’t need to actually change the inferrence as long as the left side is castable to the right side? So it more should restrict the types the left side can be rather than dicate what it should be?