I'd like to have a LinkedList
of trait object wrapper structs. The inner would be a stream type for either an Ssl or Non-Ssl stream. My hope was to pass the struct wrapper around, and as long as the inner conformed to the same trait, everything would be OK regardless of inner stream type being used.
Simple example:
use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::os::unix::io::{RawFd, AsRawFd};
pub trait HRecv {}
pub trait HSend {}
pub trait HStream: HRecv + HSend + AsRawFd + Clone {}
pub struct Stream<T: HStream> {
pub inner: T
}
pub type StreamList = Arc<Mutex<LinkedList<Stream<HStream>>>>;
fn main() {
let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
}
Produces the following error:
error: the trait 'core::marker::Sized' is not implemented for the type 'HStream' [E0277]
let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
^~~~~~~~~~~~~~~
I've tried adding + Sized
to the definition of HStream
, as well as making inner
a Box<T>
, both produce the same error.
Is it currently possible to do this with Rust? If so, what would the syntax be?
Ok, there are a few problems here. Working down the list of compiler errors:
Because
HStream
does not have a compile-time computable size, it cannot be substituted for the type parameterT
. All type parameters implicitly require the substituted type to be compile-time sized. If you want to allow dynamically sized types, you need to explicitly opt-out of this implicit bound by saying something like:A trait doesn't implement itself. You're asking for a type which implements
HStream
, butHStream
doesn't implement itself (how would it?)You have to provide a type which does.
And here's the K-O problem:
HStream
cannot be used with dynamic dispatch, period. It's not object safe. This is most likely because of theClone
requirement.The "fix" to all of the above is to redesign your types so that the problem doesn't exist. What that entails is impossible to know because there isn't enough context here to tell what you're trying to do.
At a blind stab, though, here's what it might look like without generics (which you don't appear to be using, anyway):
You cannot use the
HStream
type directly; it doesn't represent anything. It's only used to construct derived pointer types, such as&HStream
andBox<HStream>
.The simplest solution would be to have a
LinkedList
ofStream<Box<HStream>>
.Then, we just have to implement
HStream
forBox<HStream>
.Note that this is missing a trait...
Clone
.Clone
is not object-safe, which means that it's not possible to create trait object types for that trait, such as&Clone
orBox<Clone>
.Clone
is not object-safe because itsclone
method returnsSelf
, which represents the concrete type of the implementor. If you used this method through a trait object, the compiler wouldn't be able to know in advance the type of the result (it could be any ofClone
's implementors!).Since
HStream
is a subtrait ofClone
,HStream
is not object-safe either. The consequence is that we can't implementClone
at all, and types likeBox<HStream>
are not legal to use.However, we can work around this by making our own, object-safe trait. We can even automatically implement it on types that implement the standard
Clone
trait.Replace the
Clone
trait bound onHStream
withBoxedHStreamClone
and you're good to go!Here's the final code: