What's the difference between these two types?

It appears in an error message

error[E0308]: mismatched types
  --> irisia-core\src\prop_test.rs:89:17
   |
88 |             equality_matters &= <fn(&str) -> Gender as HelpUpdate<Gender, T5>>::update(
   |                                 ------------------------------------------------------ arguments to this function are incorrect
89 |                 &Gender::from_str,
   |                 ^^^^^^^^^^^^^^^^^ expected `&fn(&str) -> Gender`, found `&fn(&str) -> Gender {Gender::from_str}`
   |
   = note: expected reference `&for<'a> fn(&'a str) -> prop_test::Gender`
              found reference `&for<'a> fn(&'a str) -> prop_test::Gender {prop_test::Gender::from_str}`
note: method defined here
  --> irisia-core\src\element\props\traits.rs:14:8
   |
14 |     fn update(&self, source: &mut S, maybe_init: T, equality_matters: bool) -> bool;
   |        ^^^^^^

For more information about this error, try `rustc --explain E0308`.

What's the difference between

&for<'a> fn(&'a str) -> prop_test::Gender

and

&for<'a> fn(&'a str) -> prop_test::Gender {prop_test::Gender::from_str}

the outside function has the trait bound

fn(&str) -> Gender: HelpUpdate<Gender, T5>

the call site is

<fn(&str) -> Gender as HelpUpdate<Gender, T5>>::update(
    &Gender::from_str,
    &mut self.gender,
    updater.gender,
    equality_matters,
)

Gender definition is

#[derive(Clone, Copy, PartialEq)]
enum Gender {
    Male,
    Female,
    Unknown,
}

impl Gender {
    fn from_str(string: &str) -> Gender {
        // the details have been omitted
        todo!()
    }
}

HelpUpdate definition is

impl<S, T> HelpUpdate<S, (T,)> for fn(T) -> S
where
    S: PartialEq<S>,
{
    fn update(&self, source: &mut S, maybe_init: (T,), equality_matters: bool) -> bool {
        // the details have been omitted
        todo!();
    }
}

Why has this error been thrown?

Here's a neat, (pretty new), video explanation for function types:

1 Like

The latter is a function item type, which actually isn't nameable (similar to closure types). It's the type of the function item prop_test::Gender::from_str.

You might be able to coerce it to a function pointer, but there's probably a better way. For example if this works (untested):

impl<S, T, F> HelpUpdate<S, (T,)> for F
where
    F: Fn(T) -> S,
    S: PartialEq<S>,

Oh I forgot this, thanks. But why does the type of the function item not directly be function pointer?

Why isn't it literally a function pointer? Functions are more general than function pointers (can be generic over types, can be generic over lifetimes that appear only in the return type, etc); function items are zero-sized types and don't incur indirection to call and are presumably easier to inline; probably other reasons.

Why didn't the function item coerce to a function pointer automatically in this case? Because it has to coerce before it's behind the & I guess.

So that not every function call incurs the overhead of a dynamic dispatch.

Oh, that should probably be

F: ?Sized + Fn(T) -> S

so that it includes dyn '_ + Fn(T) -> S.

I see.

But why a function literal with generic types not implement multiple Fn(T)s? Just like this

Because generic function is not a function, strictly speaking, it's a function constructor. Just like a generic type like Vec is not a type - it's a type constructor. In your example, a::<u32> and a::<i32> are different functions, possibly even stored in different places in the binary.

3 Likes

That could work for some signatures, but not others. There's probably other downsides around not being able to turbofish functions and so on. But whatever arguments there are for doing things differently, it would be a breaking change now.

Oh yeah, stuff like this. (All type parameters today are early-bound; the version where the generic is only in the trait implementation and not a parameter of the function item type is late-bound.)

This theoretically can be done but not in my case, because if so I cannot write the trait bound.

The coercion to function pointer is necessary.