Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working with trait objects requiring sized

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?

like image 390
nathansizemore Avatar asked Jan 13 '16 04:01

nathansizemore


People also ask

What is sized trait Rust?

The Sized trait in Rust is an auto trait and a marker trait. Auto traits are traits that get automatically implemented for a type if it passes certain conditions. Marker traits are traits that mark a type as having a certain property.

What is a trait object?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.

What does Dyn mean Rust?

dyn is a prefix of a trait object's type. The dyn keyword is used to highlight that calls to methods on the associated Trait are dynamically dispatched. To use the trait this way, it must be 'object safe'. Unlike generic parameters or impl Trait , the compiler does not know the concrete type that is being passed.

What is object safety?

Object SafetyOnly traits that are object-safe can be made into trait objects. A trait is object-safe if both of these are true: the trait does not require that Self: Sized. all of its methods are object-safe.


2 Answers

Ok, there are a few problems here. Working down the list of compiler errors:

<anon>:15:53: 15:68 error: the trait `core::marker::Sized` is not implemented for the type `HStream` [E0277]
<anon>:15     let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
                                                              ^~~~~~~~~~~~~~~
<anon>:15:53: 15:68 help: see the detailed explanation for E0277
<anon>:15:53: 15:68 note: `HStream` does not have a constant size known at compile-time
<anon>:15:53: 15:68 note: required by `Stream`

Because HStream does not have a compile-time computable size, it cannot be substituted for the type parameter T. 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:

<T: ?Sized + HStream>

<anon>:15:53: 15:68 error: the trait `HStream` is not implemented for the type `HStream` [E0277]
<anon>:15     let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
                                                              ^~~~~~~~~~~~~~~
<anon>:15:53: 15:68 help: see the detailed explanation for E0277
<anon>:15:53: 15:68 note: required by `Stream`

A trait doesn't implement itself. You're asking for a type which implements HStream, but HStream doesn't implement itself (how would it?)

You have to provide a type which does.

<anon>:15:53: 15:68 error: the trait `HStream` cannot be made into an object [E0038]
<anon>:15     let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
                                                              ^~~~~~~~~~~~~~~
<anon>:15:53: 15:68 help: see the detailed explanation for E0038
<anon>:15:53: 15:68 note: the trait cannot require that `Self : Sized`

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 the Clone 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):

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 + CloneHStream {}

pub trait CloneHStream { fn clone_h_stream(&self) -> Box<HStream>; }

impl<T> CloneHStream for T where T: 'static + Clone + HStream {
    fn clone_h_stream(&self) -> Box<HStream> {
        Box::new(self.clone())
    }
}

pub struct Stream {
    pub inner: Box<HStream>
}

pub type StreamList = Arc<Mutex<LinkedList<Stream>>>;

fn main() {
    let mut list = Arc::new(Mutex::new(LinkedList::<Stream>::new()));
}
like image 165
DK. Avatar answered Oct 22 '22 12:10

DK.


You cannot use the HStream type directly; it doesn't represent anything. It's only used to construct derived pointer types, such as &HStream and Box<HStream>.

The simplest solution would be to have a LinkedList of Stream<Box<HStream>>.

fn main() {
    let mut list = Arc::new(Mutex::new(LinkedList::<Stream<Box<HStream>>>::new()));
}

Then, we just have to implement HStream for Box<HStream>.

impl<'a> HRecv for Box<HStream + 'a> {}
impl<'a> HSend for Box<HStream + 'a> {}
impl<'a> AsRawFd for Box<HStream + 'a> {
    fn as_raw_fd(&self) -> RawFd { (&**self).as_raw_fd() }
}
impl<'a> HStream for Box<HStream + 'a> {}

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 or Box<Clone>. Clone is not object-safe because its clone method returns Self, 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 of Clone's implementors!).

Since HStream is a subtrait of Clone, HStream is not object-safe either. The consequence is that we can't implement Clone at all, and types like Box<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.

pub trait BoxedHStreamClone {
    fn boxed_clone(&self) -> Box<HStream>;
}

// Implementation for all types that implement HStream and Clone and don't hold any borrows
impl<T: HStream + Clone + 'static> BoxedHStreamClone for T {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new(self.clone()) as Box<HStream>
    }
}

// Implementation for Box<HStream + 'a>, which cannot implement Clone
impl<'a> BoxedHStreamClone for Box<HStream + 'a> {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new((&**self).boxed_clone()) as Box<HStream>
    }
}

Replace the Clone trait bound on HStream with BoxedHStreamClone and you're good to go!

pub trait HStream: HRecv + HSend + AsRawFd + BoxedHStreamClone {}

Here's the final code:

use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::os::unix::io::{RawFd, AsRawFd};

pub trait BoxedHStreamClone {
    fn boxed_clone(&self) -> Box<HStream>;
}

impl<T: HStream + Clone + 'static> BoxedHStreamClone for T {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new(self.clone()) as Box<HStream>
    }
}

pub trait HRecv {}
pub trait HSend {}
pub trait HStream: HRecv + HSend + AsRawFd + BoxedHStreamClone {}
pub struct Stream<T: HStream> {
    pub inner: T
}

pub type StreamList = Arc<Mutex<LinkedList<Stream<Box<HStream>>>>>;

impl<'a> HRecv for Box<HStream + 'a> {}
impl<'a> HSend for Box<HStream + 'a> {}

impl<'a> AsRawFd for Box<HStream + 'a> {
    fn as_raw_fd(&self) -> RawFd { (&**self).as_raw_fd() }
}

impl<'a> BoxedHStreamClone for Box<HStream + 'a> {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new((&**self).boxed_clone()) as Box<HStream>
    }
}

impl<'a> HStream for Box<HStream + 'a> {}

fn main() {
    let mut list = Arc::new(Mutex::new(LinkedList::<Stream<Box<HStream>>>::new()));
}
like image 42
Francis Gagné Avatar answered Oct 22 '22 13:10

Francis Gagné