How to use Traits as Generic type parameters?

Although in this example it is just a matter of "style preference", the problem does indeed arrive when unnameable types are involved: let's image someone wants to have:

struct Session<F : Fn()> {
    f: F,
}

impl Session<?> {
    fn new () -> Session<impl Fn()>
    {
        Session { f: || println!("Demo"), }
    }
}

In that case, the clean solution would be to use existential types:

type WebSocketStream = impl Stream...;
// attach the actual type with a constructor:
fn mk_websocket_stream (...) -> WebSocketStream
{
    connect_secure(...)
    /* could be refactored as
    connect_insecure(...) // */
}

/// And now we can do:
struct Session { // <- no longer generic!
    client: WebSocketStream.
}

impl Session {
    fn new (...) -> Self
    {
        Self { client: mk_websocket_stream(...) }
    }
}

Now, on stable we don't have this, so we need to be a bit more hacky creative :smile::

The idea will be to have Session::new() return a Session<impl Stream>, although this return type cannot be Self, since there is no way to impl for ...<impl Stream> on stable Rust.

This isn't a problem, as long as we don't introduce an unused generic type parameter.

So, for instance, we can do:

pub
struct Session<T> { // <- no trait bound **yet**
    client: T,
}

impl Session</* what do we put here? */> {
    pub
    fn new (...) -> Session<impl Stream>
    {
        let client = ...;
        Self { client }
    }
}

/// CLASSIC IMPLS HERE:
// generic impl just to be able to cover the above `impl Stream` unnameable type.
impl<T : Stream> Session<T> {
    ...
}

So, the only question remaining is:

what type do we use in /* what do we put here */?

And the answer is: whatever you want, as long as it is not generic:

/// Even a type as dumb as `()` works.
impl Session<()> {
    fn new (...) -> Session<impl Stream> { ... }
}

/// Using `dyn Trait` for something that becomes `impl Trait` may "read / look better"
impl Session<dyn Stream> { // <- But you need to be able to use a `?Sized` type param
   ...
}

// The longer but better looking solution
use private::impl_Stream;
mod private {
    #![allow(nonstandard_style)]
    pub
    struct impl_Stream;

    /// If you have added a `: Stream` boubnd even at the struct definition
    /// you will need a dummy impl of Stream for this dummy type;
    impl Stream for impl_Stream { ... unreachable!() ... unreachable!() ... }
}
/// so that you can do:
impl Session<impl_Stream> {
    fn new (...) -> Stream<impl Stream> { ... }
}
6 Likes