I'm working on a system that's making extensive use of futures and tokio. This system has various subsystems which have fairly narrow APIs, and which may have multiple implementations. In other words, a good match for traits.
Other design points:
- a number of the methods return async results - ie, implement either
Future
orStream
- I'd like the traits to be usable as trait objects for when I need to either decouple the concrete type or have runtime dynamism
- the APIs are complex enough to need a number of related types
As a semi-hypothetical example:
trait Index {
type Error;
type Key;
type Value;
type Entry: Entry<Index=Self>;
fn put(&self, key: &Self::Key, val: Self::Value)
-> impl Future<Item=(), Error=Self::Error>; // placeholder syntax
fn get(&self, key: &Self::Key)
-> impl Future<Item=Option<Self::Value>, Error=Self::Error>;
fn entry(&self, key: &Self::Key)
-> impl Future<Item=Self::Entry, Error=Self::Error>;
// ...
}
trait Entry {
type Index: Index;
fn get(&self) -> EntryType<Self::Index>;
// ...
}
enum EntryType<Index: Index> {
Plain(Index::Value);
Subindex(Index);
// ...
}
This has several problems:
-
impl Trait
doesn't exist yet, and even if it did, it's not going to work on trait methods. - To make up for lack of
impl Trait
, it requires that every method needs to have a corresponding associated type:type GetReturn: Future<Item=Self::Value, ...>;
- which will get pretty cumbersome for traits that have more than a handful of methods. - The linked associated types that refer to
Self
make it non-object-safe
This last point prevents me from implementing something like:
struct BoxIndex<Idx: Index, K, V>(Idx, PhantomData<(K, V)>);
impl<Idx: Index, K, V> impl Index for BoxIndex<Idx, K, V> {
type Key = K;
type Value = V;
type Entry = BoxEntry<Self>;
// ...
}
which boxes up all the specific implementations of Index
to make them a uniform erased type (yet still having concrete types for K
and V
).
So two questions:
- is this the best way to handle traits whose methods return traits?
- is there a way I can box these things into type-erased trait objects?
Thoughts, suggestions, clues?