I have a generic trait that has two methods, one uses the type parameter while the other doesn't:
pub trait Map<E: Encoder> {
fn keys(&self) -> &[&str];
fn values(&self) -> &[&dyn Value<E>];
}
pub trait Value<E: Encoder> { /* ... */ }
I have another non-generic trait:
pub trait PrintKeys {
fn print(&self);
}
Finally, I have a type that I want to implement this trait for:
struct MapWrapper<M>(M);
I want to use Map::keys
in my implementation of PrintKeys
, like so:
impl<M> PrintKeys for MapWrapper<M> {
fn print(&self) {
for key in self.0.keys() {
println!(key);
}
}
}
But in order to be able to do this, I would need to put a M: Map
constraint somewhere, and in order to be able to do that I need to introduce another type parameter, which would be unbounded. Ideally I would like to use something like Java's generic wildcards:
struct MapWrapper<M: Map<?>>(M);
I have tried a few approaches and am not sure which one would be the most idiomatic.
First approach: constraint on MapWrapper
struct MapWrapper<E: Encoder, M: Map<E>>(M);
However this gives me an unbounded type parameter error. I considered using PhantomData
but I'm not sure that it's an idiomatic or good way to handle this problem.
Second approach: constraint on trait implementation
impl<E: Encoder, M: Map<E>> PrintKeys for MapWrapper<E> {
fn print(&self) {
for key in self.0.keys() {
println!(key);
}
}
}
This also gives me an unbounded type parameter error, only this time there is no way to suppress it.
Third approach: splitting the generic trait
I could split my Map
trait into two, one generic and another not:
pub trait MapKeys {
fn keys(&self) -> &[&str];
}
pub trait MapValues<E: Encoder> {
fn values(&self) -> &[&dyn Value<E>];
}
Then constraint either the MapWrapper
or the implementation of PrintKeys
for MapWrapper
with MapKeys
. However I don't like the idea of splitting this trait in two because it is part of a public interface and I think that it would make implementing it more confusing.
What would you recommend?