How to make a new associated function with a struc

2020-02-14 04:45发布

问题:

I almost have an intuitive sense of why this code should NOT work, but I can't quite put my finger on it. I think it has something to do with the fact that the new function would have a different return type every time.

Why is that a problem? Why does the direct creation work?

struct Struct<T>
where
    T: Fn(&[u8]),
{
    func: T,
}

impl<T> Struct<T>
where
    T: Fn(&[u8]),
{
    fn new() -> Struct<T> {
        // this doesn't work
        Struct { func: |msg| {} }
    }
}

fn main() {
    // this works
    let s = Struct { func: |msg| {} };
}

The error is

error[E0308]: mismatched types
  --> src/main.rs:14:24
   |
14 |         Struct { func: |msg| {} }
   |                        ^^^^^^^^ expected type parameter, found closure
   |
   = note: expected type `T`
              found type `[closure@src/main.rs:14:24: 14:32]`

回答1:

tl;dr; You can do the following:

fn new() -> Struct<impl Fn(&[u8])> {
    Struct { func: |msg| {} }
}

More detailed:

Let's dissect your impl block:

impl<T> Struct<T>
where
    T: Fn(&[u8]),
{
    fn new() -> Struct<T> {
        // this doesn't work
        Struct { func: |msg| {} }
    }
}

We start with:

impl<T> Struct<T>
where
    T: Fn(&[u8]),

This tells the compiler that the whole impl block is "valid" for any T satisfying Fn(&[u8]).

Now:

fn new() -> Struct<T> {
    // this doesn't work
    Struct { func: |msg| {} }
}

You say that new returns a Struct<T>, and we are within the block stating that everything inside it works for any T satisfying Fn(&[u8]). However, you return one particular instance of Struct, namely the one parametrized by |msg| {} - thus, the return value can not be a Struct<T> for any T satisfying Fn(&[u8]).

However, you can modify it to do the following:

fn new() -> Struct<impl Fn(&[u8])> {
    Struct { func: |msg| {} }
}

This tells the compiler that new returns a Struct, whose parameter is known to satisfy Fn(&[u8]), so that the compiler should infer it. In particular it has no assumptions about T and returns one particular type.

In the direct initialization, however, we tell the compiler:

let s = Struct { func: |msg| {} };

The compiler sees that you want to create a Struct and knows that - in order to create it - it must infer the type for T res. func. It sees that you passed |msg| {} for func, infers the type for the closure and now knows a concrete type to put into the T.