Reputation: 4316
I am trying to write a function that can descent into any kind of Value
and inform a Delegate
about the similarities it observes. The idea is to make this work across all kinds of Json/Yaml/YouNameIt values, generically.
Here is an MVCE to trigger the issue (playground link):
pub trait Value: PartialEq<Self> {
type Item;
type Key;
fn items<'a>(&'a self) -> Option<Box<Iterator<Item = (Self::Key, &'a Self::Item)> + 'a>>;
}
pub trait Delegate<'a, V> {
fn something(&mut self, _v: &'a V) {}
}
pub fn diff<'a, V, D>(l: &'a V, d: &'a mut D)
where V: Value,
<V as Value>::Item: Value,
D: Delegate<'a, V>
{
d.something(l);
let v = l.items().unwrap().next().unwrap();
d.something(v.1);
}
struct Recorder;
impl<'a, V> Delegate<'a, V> for Recorder {}
#[derive(PartialEq)]
struct RecursiveValue;
impl Value for RecursiveValue {
type Key = usize;
type Item = RecursiveValue;
fn items<'a>(&'a self) -> Option<Box<Iterator<Item = (Self::Key, &'a Self::Item)> + 'a>> {
None
}
}
fn main() {
let v = RecursiveValue;
let mut r = Recorder;
diff(&v, &mut r);
}
When attempting to compile the code, the following error is produced:
error[E0308]: mismatched types
--> <anon>:19:17
|
19 | d.something(v.1);
| ^^^ expected type parameter, found associated type
|
= note: expected type `&'a V`
= note: found type `&<V as Value>::Item`
I'm trying to say that the associated Item
type is of type V
too. Is there a way to make such an algorithm work generically?
Upvotes: 1
Views: 417
Reputation: 430290
Here's a further reduced example:
pub trait Value {
type Item;
fn items(&self) -> &Self::Item;
}
pub trait Delegate<V> {
fn something(&mut self, v: &V);
}
pub fn diff<V, D>(l: &V, d: &mut D)
where V: Value,
V::Item: Value,
D: Delegate<V>
{
let v = l.items();
d.something(v);
}
fn main() {}
The important thing to focus on are the restrictions on the generics of diff
:
pub fn diff<V, D>(l: &V, d: &mut D)
where V: Value,
V::Item: Value,
D: Delegate<V>
In words, this says:
V
can be any type so long as it implements the Value
trait.V::Item
can be any type so long as it implements the Value
trait.D
can be any type so long as it implements the Delegate<V>
trait.Nowhere in that list of requirements was listed "V
and V::Item
must be the same". In fact, it's a feature that they are not required to be the same.
In this reduction, another solution would be to say D: Delegate<V::Item>
. However, that wouldn't apply to the slightly larger reproduction:
pub fn diff<V, D>(l: &V, d: &mut D)
where V: Value,
V::Item: Value,
D: Delegate<V::Item>
{
d.something(l);
let v = l.items();
d.something(v);
}
As Matthieu M. has pointed out, you want to specify the associated type of the trait:
pub fn diff<V, D>(l: &V, d: &mut D)
where V: Value<Item = V>,
D: Delegate<V>
For further reading, check out Requiring implementation of Mul in generic function.
Upvotes: 1
Reputation: 299730
The answer lies at the very bottom of the Associated Types chapter of the Rust Book.
When using a generic type in a bound, as in V: Value
, it is possible to constrain one or several of its associated types to specific types by using the Generic<AssociatedType = SpecificType>
syntax.
In your case, it means constraining V
to Value<Item = V>
. This should also obsolete any reason to further constrain V::Item
since the bounds to V
are naturally available.
I do encourage you to read the book to help you learn Rust, or at least skim it to know what's available there and be able to refer to it when you have a difficulty.
Upvotes: 2