Bad type inference for the Vec of dynamic closures

Viewed 69

I am trying to get a list of partially applied functions, like so:

fn partially_applied() -> Vec<Box<dyn Fn(u32) -> u32>> {
    let xs = vec![1_u32, 2, 3];

    xs.into_iter().map(|x| Box::new(move |y| x + y)).collect() 
}

The compiler yields an error to me:

error[E0277]: a value of type std::vec::Vec<std::boxed::Box<dyn std::ops::Fn(u32) -> u32>> cannot be built from an iterator over elements of type std::boxed::Box<[closure@src/main.rs:4:41: 4:57]>


The subsequent attempts also did not work:

1.

fn partially_applied_with_explicit_collect_type() -> Vec<Box<dyn Fn(u32) -> u32>> {
    let xs = vec![1_u32, 2, 3];

    xs.into_iter().map(|x| Box::new(move |y| x + y)).collect::<Vec<Box<dyn Fn(u32) -> u32>>>() 
}
fn partially_applied_with_explicit_vec_type() -> Vec<Box<dyn Fn(u32) -> u32>> {
    let xs = vec![1_u32, 2, 3];

    let mut res: Vec<Box<dyn Fn(u32) -> u32>> = xs.into_iter().map(|x| Box::new(move |y| x + y)).collect(); 
    res 
}
fn partially_applied_with_explicit_push() -> Vec<Box<dyn Fn(u32) -> u32>> {
    let xs = vec![1_u32, 2, 3];

    let mut res = vec![];

    for x in xs { 
        res.push(Box::new(move |y| x + y)); 
    }

    res 
}

And only this attempt was finally succeeded!

fn partially_applied_with_explicit_push_and_vec_type() -> Vec<Box<dyn Fn(u32) -> u32>> {
    let xs = vec![1_u32, 2, 3];

    let mut res: Vec<Box<dyn Fn(u32) -> u32>> = vec![];

    for x in xs { 
        res.push(Box::new(move |y| x + y)); 
    }

    res 
}

Why the type inference is so bad when it comes to dynamic closures?

Why the collect did not work in the first attempts?

Is there a better way to utilize a type inference to express such an idea?

1 Answers

The underlying issue is that every closure gets its own unnamable type of which it is the sole member. Furthermore, while it the compiler can automatically cast a particular boxed closure into Box<dyn *closure's type*>, it cannot reach down deeper into generic types to perform the upcasting.

So the following is ok:

fn closure() -> Box<dyn Fn(u32) -> u32> {
    Box::new(|x| x)
}

But this is not:

fn partially_applied() -> Vec<Box<dyn Fn(u32) -> u32>> {
    let xs = vec![1_u32, 2, 3];
    xs.into_iter().map(|x| Box::new(move |y| x + y)).collect() 
}

Why not?

value of type `Vec<Box<dyn Fn(u32) -> u32>>` cannot be built from
`std::iter::Iterator<Item=Box<[closure@src/main.rs:12:37: 12:51]>>`

help: the trait `FromIterator<Box<[closure@src/main.rs:12:37: 12:51]>>`
is not implemented for `Vec<Box<dyn Fn(u32) -> u32>>`

It's because the trait bound in FromIterator for Vec is

impl<T> FromIterator<T> for Vec<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Vec<T> { ... }
}

and Box<[closure@src/main.rs]> is a distinct type from Box<dyn Fn(u32) -> u32>.

So the question is not "why is Rust's type inference failing here" but rather "why does Vec not implement the following":

impl<T, U> FromIterator<T> for Vec<U>
where T: Into<U> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Vec<U> { ... }
}

That, I don't know. I suspect this would lead to its own problems with type inference when there are multiple pairs of T,U that would make things work.

N.B. you can cast your boxes to the correct type as follows:

fn partially_applied() -> Vec<Box<dyn Fn(u32) -> u32>> {
    let xs = vec![1_u32, 2, 3];

    xs.into_iter()
        .map(|x| Box::new(move |y| x + y) as Box<dyn Fn(u32) -> u32>)
        .collect()
}
Related