Why can't the compiler infer the type of this argument?

Sorry in advance for the title, I couldn't find a better one.

Consider this very simple code:

pub fn box_it<M>(cl: impl FnMut(M) + 'static) -> Box<dyn FnMut(M)>
{
    Box::new(cl)
}

fn box_u32_manually() -> Box<dyn FnMut(u32)> {
    Box::new(move |arg| {
        println!("{:?}", arg);
    })
}

fn box_u32_v2() -> Box<dyn FnMut(u32)> {
    box_it(move |arg| {
        println!("{:?}", arg);
    })
}

fn main() {
    let b1 = box_u32_manually();
    let b2 = box_u32_v2();
}

The code compiles fine.

Consider now this modified version, which uses the New Type Idiom:

pub fn box_it<M>(cl: impl FnMut(M) + 'static) -> Box<dyn FnMut(M)>
{
    Box::new(cl)
}

struct S(u32);

fn box_s_manually() -> Box<dyn FnMut(S)> {
    Box::new(move |arg| {
        println!("{:?}", arg.0);
    })
}

fn box_s_v2() -> Box<dyn FnMut(S)> {
    box_it(move |arg| {
        println!("{:?}", arg.0);
    })
}

fn main() {
    let b1 = box_s_manually();
    let b2 = box_s_v2();
}

The code fails to compile with the following error:

error[E0282]: type annotations needed
  --> src/main.rs:15:18
   |
15 |     box_it(move |arg| {
   |                  ^
16 |         println!("{:?}", arg.0);
   |                          - type must be known at this point
   |
help: consider giving this closure parameter an explicit type
   |
15 |     box_it(move |arg: /* Type */| {
   |                   ++++++++++++

The source of the error is box_s_v2, which I can fix by manually typing the type parameter:

box_it::<S>(move |arg| {
   println!("{:?}", arg.0);
})

Am I right to be surprised that the compiler couldn't figure out such simple type parameter?

1 Like

It's because it's a field, and there's no traits for fields. So the inference fails when it's an unknown type and you try to look at a field or a non-train method call, because it doesn't know where to look.

I wouldn't say you're necessarily wrong to be surprised, but this is very normal.

For example, this fails https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ba99d9cbb6c38f334b4a53ad990f747a

struct S {
    foo: i32,
}

fn main() {
    let mut v = Vec::new();
    for i in 0..10 {
        if let Some(x) = v.pop() {
            dbg!(x.foo);
        }
        v.push(S { foo: i });
    }
}

because it tried to look at the field "before" it sees what the type is.

Whereas if you just put the two statements in the loop in the other order https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f1ec51ff2f50791bf3cdece0949a7fb6

fn main() {
    let mut v = Vec::new();
    for i in 0..10 {
        v.push(S { foo: i });
        if let Some(x) = v.pop() {
            dbg!(x.foo);
        }
    }
}

then inference resolves it successfully.

Will it get smarter in future? Maybe. But for now, this is how it works.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.