2

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:

  1. I have to duplicate all the Wrapper methods for every new Remote method.
  2. 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 all Remotes inside a Vec into Box<Remote>s. While this is frustrating to type out, it is also very ill performing.
  3. The same with the Remote trait. I have to impl Remote for Box<Remote> and import the std::ops::Deref trait and manually delegate all trait methods to the innards of the Box...

Is there another way?

Danyel
  • 2,180
  • 18
  • 32

0 Answers0