How can I define a constructor that takes a trait object or the bare type itself?


#1

Basically, I’d like to do:

let w = TMockWriter::new();
let w = TBufferedWriter::new(w); // <---- takes the unboxed type

let w = TMockWriter::new();
let w = TBufferedWriter::new(Box::new(w) as Box<Write + Send>); // <---- takes a trait object

Is this possible?


#2

I can’t figure out a way to not require type annotations in the Box case, but using a couple of levels of indirection via Borrow seem to work for storing it at least (https://is.gd/mevUzD)

use std::io;
use std::borrow::Borrow;
use std::marker::PhantomData;

struct Foo<T: io::Read + ?Sized = io::Read, U: Borrow<T> = Box<io::Read>> {
    foo: U,
    marker: PhantomData<T>,
}

impl<T: io::Read + ?Sized, U: Borrow<T>> Foo<T, U> {
    fn new(foo: U) -> Foo<T, U> {
        Foo {
            foo: foo,
            marker: PhantomData,
        }
    }
}

fn main() {
    let a = io::Cursor::new(Vec::new());
    let b = Foo::new(a.clone());
    let c = Foo::<io::Read, Box<io::Read>>::new(Box::new(a) as Box<io::Read>);
}

How does this constructor work?
#3

Oh dear - that looks pretty ugly. I guess what I’m asking isn’t a common use case. FWIW, I’ve a situation where a vast majority of the time the caller will specify the bare type to a new(...) directly, but once in a while they’ll have to use a Box (*).

(*) You may want to take a command-line argument indicating what concrete writer (tcp, unix stream, in-memory buffer etc.) to use.


#4

not sure that this is what you want but it compiles

https://is.gd/LnitW3

use std::io::Write;

struct Foo<T: Write> {
    foo: T,
}


impl<T: Write> Foo<T> {
    fn new(foo: T) -> Foo<T> {
        Foo {
            foo: foo,
        }
    }
}

fn main() {
    let a = Vec::new();
    let b = Foo::new(a.clone());
    let c = Foo::new(Box::new(a));
}