Generic type doesn't compile

This below doesn't compile? WTF??!?
Every tutorial I have read says that when working with generics this is how you do the implementation. Apparently not.

pub struct ThreadPool<F>
where
F: FnOnce(std::net::TcpStream) + Send + 'static,
{
 func: F,
}

impl<F> ThreadPool<F>
^^^^^^^^^^^^^ `F` cannot be sent between threads safely
{
    
}

Your impl block is missing the trait bound. Adding this to the structure definition is only one half of the story.

Wait, I'm not implementing a trait. I simply want to provide implementation for a ThreadPool.

The compiler output tells you how to solve this error: Add bounds to the impl's type parameter, to match the bounds on the type definition.

In most cases it's actually sufficient to have the bounds only on the impl, so you don't need to repeat them:

pub struct ThreadPool<F> {
 func: F,
}

impl<F> ThreadPool<F>
where
    F: FnOnce(std::net::TcpStream) + Send + 'static,
{
}

When RFC 2089 is implemented, your original code will compile, too.

1 Like

The traits involved are FnOnce and Send. Your code uses them as “trait bounds” on the type parameter F.

Yes, but there is a different syntax for implementing a trait and different for providing an implementation AFAIC.
So why on earth would I bother implementing a trait?
Sorry, this does not makes sense. It simply doesn't make sense.

So basically the surplus repetition will be gone. Good. I mean... seriously...

This isn't implementing a trait. It's just moving a where clause from the struct item to the impl item.

The where clause places restrictions on the generic parameters. The reason the word “trait” was used is just because those restrictions are mostly expressed using traits like Send and FnOnce. Such restrictions are called “trait bounds.”

Yes, RFC 2089 will eliminate the need for repetition in some cases. Though note that in this particular case, you can already avoid repetition by removing the bounds from your struct definition.

Two points:

  1. By removing the bounds from struct you mean doing this:?
pub struct ThreadPool<F>
{
}

Would that mean that I won't be able to 'Send' F between threads? If so, this is not an option because I need to be able to send F between threads.
Second point, you've mentioned that compiler already tells me how to solve it:

error[E0277]: `F` cannot be sent between threads safely
  --> src/thread_pool.rs:20:9
   |
8  | pub struct ThreadPool<F>
   |            ---------- required by a bound in this
9  | where
10 | F: FnOnce(std::net::TcpStream) + Send + 'static,
   |                                  ---- required by this bound in `thread_pool::ThreadPool`
...
20 | impl<F> ThreadPool<F>
   |         ^^^^^^^^^^^^^ `F` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `F`
help: consider restricting type parameter `F`
   |
20 | impl<F: std::marker::Send> ThreadPool<F>
   |       ^^^^^^^^^^^^^^^^^^^

Really? Does it really tell me? Because I've actually tried to do what the compiler tells me and it didn't work.

No, F still automatically implements Send as long as all its data does. So any type that was sendable before will still be sendable.

This change does make it possible to construct ThreadPool<F> for cases where F is non-sendable, but generally one prevents by putting the public constructor function, or other required functions, in an impl block with appropriate bounds. Programs that try to use ThreadPool with a non-Send type will still end up getting the same basic error message as before.

The compiler prints two different error messages for the original code, with a “help” suggestion for each error:

help: consider restricting type parameter `F`
  |
9 | impl<F: std::ops::FnOnce<(std::net::TcpStream,)>> ThreadPool<F>
  |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

and:

help: consider restricting type parameter `F`
  |
9 | impl<F: std::marker::Send> ThreadPool<F>
  |       ^^^^^^^^^^^^^^^^^^^

If you apply just one of the suggested fixes, then you'll get a suggestion for how to apply the second one too:

help: consider further restricting this bound
  |
9 | impl<F: FnOnce(std::net::TcpStream) + std::marker::Send> ThreadPool<F>
  |                                     ^^^^^^^^^^^^^^^^^^^

After applying both suggestions, you end up with this code, which compiles.

Fair enough, that worked. Thanks

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.