Writing down the `Fn` type of a generic function?


#1

I’m wondering how we could create the Fn type for a generic function?

I’m looking to take functions (and closures) of the form:

fn writeme<W: Write>(W) {...}

But I don’t see how I can express this type to accept it as an argument (or store it in a struct). Something like

struct Writme(Box<dyn Fn<W: Write>(W)>);

does not compile. Is there some syntax for closure that accepts a generic type which I could use?


#2

One way to handle this would be to make the wrapper struct itself have a type parameter:

struct Writme<W: Write>(Box<dyn Fn(W)>);

In general, I think you’d have to introduce the type parameter from somewhere else (e.g. the function you’re passing it to, or the struct you’re storing it in). That said, there might be a way of doing this I’m just not aware of :slight_smile:


#3

Currently, in Rust—which does not have higher kinded types—there is no such thing as the “type” of a generic function. Only monomorphized functions have types. For instance, writeme::<Vec<u8>> has a type; its type is just like any other fn foo(Vec<u8>) (which is to say it is an anonymous unit struct that impls Fn(Vec<u8>) and can be coerced into fn(Vec<u8>)).

In contrast, the type of writeme by its lonesome without any further information is simply Error: Type annotations needed. :stuck_out_tongue:

If you want to pass a generic function around in Rust, you need to simulate it using a trait with a generic associated method:

trait WriteFn {
    fn call<W: Write>(dest: W);
}

// (note: this still won't compile, for reasons discussed below!)
struct Writme(Box<dyn WriteFn>);

and it will not be possible to call it with fn items or closures; users need to manually create their own type and implement the trait.


If you try this, you shall soon run into your next issue, which is that WriteFn isn’t object-safe! That’s right, the particular thing you’re trying to do (using dynamic polymorphism) could never be possible, because the vtable would have to store an unbounded number of functions for different Write impls (some of which may be defined in crates that depend on yours!).

In this manner, I often describe dynamic polymorphism as “infectious.” If you want to use your writeme in a dynamic context, then the argument type has to be dynamic as well (or at least, you need to single out one specific Write type, so you might as well pick dyn Write!):

struct Writme(Box<dyn Fn(&mut dyn Write)>);