Compiler does not seem to know that "impl Fn(Vector4<f64>) -> Vector4<f64>" implements the trait Fn(Vector4<f64>) -> Vector4<f64>

I have a function that returns an impl Fn(Vector4<f64>) -> Vector4<f64>. I would like to stick the result inside a Box<dyn Fn(Vector4<f64>) -> Vector4<f64>>, but the compiler gives me an error when I try to do this. Everything seems to work fine if I replace Vector4<f64> with u8. Is there a reason that this does not work with Vector4?

Example code:

extern crate nalgebra; // 0.21.1
use nalgebra::Vector4;

struct A {
    par: Box<dyn Fn(Vector4<f64>) -> Vector4<f64>>
}

fn b() -> impl Fn(Vector4<f64>) -> Vector4<f64> {
    |v| v
}

struct C {
    par2: Box<dyn Fn(u8) -> u8>
}

fn d() -> impl Fn(u8) -> u8 {
    |x| x
}

fn main() {
    let par = Box::new(b());
    // error
    let a = A { par };
    let par2 = Box::new(d());
    // okay
    let c = C { par2 };
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: expected a `std::ops::Fn<(nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, nalgebra::ArrayStorage<f64, nalgebra::U4, nalgebra::U1>>,)>` closure, found `impl std::ops::Fn<(nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, <nalgebra::DefaultAllocator as nalgebra::allocator::Allocator<f64, nalgebra::U4>>::Buffer>,)>`
  --> src/lib.rs:23:17
   |
23 |     let a = A { par };
   |                 ^^^ expected an `Fn<(nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, nalgebra::ArrayStorage<f64, nalgebra::U4, nalgebra::U1>>,)>` closure, found `impl std::ops::Fn<(nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, <nalgebra::DefaultAllocator as nalgebra::allocator::Allocator<f64, nalgebra::U4>>::Buffer>,)>`
   |
   = help: the trait `std::ops::Fn<(nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, nalgebra::ArrayStorage<f64, nalgebra::U4, nalgebra::U1>>,)>` is not implemented for `impl std::ops::Fn<(nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, <nalgebra::DefaultAllocator as nalgebra::allocator::Allocator<f64, nalgebra::U4>>::Buffer>,)>`
   = note: required for the cast to the object type `dyn std::ops::Fn(nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, nalgebra::ArrayStorage<f64, nalgebra::U4, nalgebra::U1>>) -> nalgebra::Matrix<f64, nalgebra::U4, nalgebra::U1, nalgebra::ArrayStorage<f64, nalgebra::U4, nalgebra::U1>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

Not sure what specifically prevents it. But it can be worked around by having b() return Box<dyn ...> directly.

Here are the two types that appear in the error message, with the paths shortened for readability. One uses ArrayStorage, while the other uses DefaultAllocator::Buffer:

Matrix<f64, U4, U1, ArrayStorage<f64, U4, U1>>
Matrix<f64, U4, U1, <DefaultAllocator as Allocator<f64, U4>>::Buffer>

It looks like maybe the compiler is getting confused by the two different impls of Allocator for DefaultAllocator, and is using the wrong one in one case.

2 Likes

Possibly related (?): This compiler bug where rustc panics when checking a closure that uses <DefaultAllocator as Allocator>::Buffer.

if you are using small dimension vectors (up to n = 6) you can use the static-math crate (which uses under the hood simple statics arrays) like so:

extern crate static_math;

use static_math::vector4::V4;

struct A {
    par: Box<dyn Fn(V4<f64>) -> V4<f64>>
}

fn b() -> impl Fn(V4<f64>) -> V4<f64> {
    |v| v
}

struct C {
    par2: Box<dyn Fn(u8) -> u8>
}

fn d() -> impl Fn(u8) -> u8 {
    |x| x
}

fn main() {
    let par = Box::new(b());
    // error
    let a = A { par };
    let par2 = Box::new(d());
    // okay
    let c = C { par2 };

}

Replacing Vector4<f64> with nalgebra::Matrix<f64, na::U4, na::U1, na::ArrayStorage<f64, na::U4, na::U1>> seems to have fixed the problem.

I ran cargo-bisect-rustc to see if the problem starting occurring at the same time as the compiler bug mentioned above. But it seems that no version of rustc from the past year compiles my code successfully.

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.