I'm writing a downloader for different hosts/providers and need to abstract over the different meta information they provide for files. For example, one host might return an MD5 sum, another an SHA1 sum or even no checksum at all. I wrote a Hoster
trait that can return a file (whose specific type should be unknown).
trait Hoster {
type File: Remote;
fn meta(&self, urls: &Vec<Url>) -> hyper::Result<Vec<Self::File>>;
// several other methods
}
The meta
function takes URLs and returns their meta information in the specific type. That type in turn may have its own functions.
When using Hoster
anywhere in my code, the compiler complains that I need to provide the specific File
trait type. I asked in IRC and kimundi offered me a solution which does work but is very much not what I had hoped/wanted. The general solution is this. Translated to my example:
pub trait Remote: std::fmt::Debug {
fn url(&self) -> String;
}
trait _Hoster {
type File: Remote + 'static;
fn id(&self) -> &'static str;
// Returns true if this host is responsible for this URL, false otherwise.
fn accepts(&self, url: &url::Url) -> bool;
// Returns meta information on given URLs.
fn meta(&self, url: &Vec<url::Url>) -> hyper::Result<Vec<Self::File>>;
}
struct Wrapper<T>(T);
use std::ops::Deref;
impl Remote for Box<Remote> {
fn url(&self) -> String {
self.deref().url()
}
}
impl<T: _Hoster> _Hoster for Wrapper<T> {
type File = Box<Remote>;
fn id(&self) -> &'static str {
self.0.id()
}
fn accepts(&self, url: &url::Url) -> bool {
self.0.accepts(url)
}
fn meta(&self, urls: &Vec<url::Url>) -> hyper::Result<Vec<Box<Remote>>> {
self.0.meta(urls)?.into_iter().map(|x| Box::new(x) as Box<Remote>).collect()
}
}
pub type Hoster = _Hoster<File = Box<Remote>>;
but as you can see I have to specify Box<Trait<Out=Box<Assoc>>>
everywhere in the code which can be solved by specifying my own type (as I did).
Some other things that are irritating:
- I have to duplicate all the
Wrapper
methods for every newRemote
method. - Most irritating of all: As you can see in my applied example, I have to write something like
self.0.foo(bar)?.into_iter().map(|x| Box::new(x) as Box<Remote>).collect()
in the wrapper just to turn allRemote
s inside aVec
intoBox<Remote>
s. While this is frustrating to type out, it is also very ill performing. - The same with the
Remote
trait. I have toimpl Remote for Box<Remote>
and import thestd::ops::Deref
trait and manually delegate all trait methods to the innards of the Box...
Is there another way?