5

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]` 
6
  • > "I think it has something to do with the fact that the new function would have a different return type every time" Pretty close! The question to ask is "who decides what type T is?" You want the implementation of new to decide what closure to return, but generics let the caller decide what type T is. I'm pretty sure there's another question that addresses this... I'll look for it. Commented Jan 6, 2019 at 1:27
  • Ok I think that makes sense. Basically if the new function worked the type would be deciding for itself what T is, rather than the calling code deciding? Commented Jan 6, 2019 at 1:30
  • Yep! But that's also a useful thing to do sometimes, and you can do it with impl Trait. What is the correct way to return an Iterator (or any other trait)? is the other question I was looking for. Just change fn new() -> Struct<T> to fn new() -> Struct<impl Fn(&[u8])> and it will work. Commented Jan 6, 2019 at 1:35
  • Ok it's close. However, after your change I still can't actually call the new function. It gives "type annotations required: cannot resolve for<'r> <_ as std::ops::FnOnce<(&'r [u8],)>>::Output == ()" Commented Jan 6, 2019 at 2:03
  • The other answer has a lot of info but I'm not 100% sure it applies here. I'm specifically trying to set a type from within another type, not trying to return one. Although it's all tied together... Commented Jan 6, 2019 at 2:05

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the great explanation. That all makes sense on the surface, however when I tried to use this code I see that your changes allow the code to compile but will not allow me to use Struct::new() with the default closure. When I try, I get an error play.rust-lang.org/…. The error is cannot satisfy for<'r> <_ as FnOnce<(&'r [u8],)>>::Output == ()

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.