Reputation: 3057
I have the code as follows (part of a larger library). The compiler is telling me that a tuple does not implement a trait, but I have both an implementation for a tuple and for the one element of that tuple. And it works with another type of tuple.
Why does the tuple (BTreeSet<Annotation>)
not get matched here?
use std::collections::BTreeSet;
pub struct Axiom {}
pub struct Annotation {}
pub struct AnnotatedAxiom {
pub axiom: Axiom,
pub annotation: BTreeSet<Annotation>,
}
trait Render {
/// Render a entity to Write
fn render(&self);
}
impl<'a, T: Render> Render for &'a BTreeSet<T> {
fn render(&self) {}
}
impl<'a, A: Render> Render for (&'a A,) {
fn render(&self) {
(&self.0).render();
}
}
/// The types in `Render` are too long to type.
macro_rules! render {
($type:ty, $self:ident,
$body:tt) => {
impl Render for $type {
fn render(& $self)
$body
}
}
}
render!{
Annotation, self,
{
}
}
render!{
Axiom, self,
{
}
}
render!{
AnnotatedAxiom, self,
{
// Axiom implements Render
(&self.axiom).render();
// Annotation implements Render
(&self.annotation).render();
// A 1-element tuple of Axiom implements Render
(&self.axiom,).render();
// A 1-element tuple of Annotation does!?
(&self.annotation,).render();
}
}
fn main() {}
error[E0599]: no method named `render` found for type `(&std::collections::BTreeSet<Annotation>,)` in the current scope
--> src/main.rs:62:29
|
62 | (&self.annotation,).render();
| ^^^^^^
|
= note: the method `render` exists but the following trait bounds were not satisfied:
`(&std::collections::BTreeSet<Annotation>,) : Render`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `render`, perhaps you need to implement it:
candidate #1: `Render`
Upvotes: 1
Views: 229
Reputation: 29983
There is a gap in the implementation chain:
impl<'a, T: Render> Render for &'a BTreeSet<T> {
// ....
}
impl<'a, A: Render> Render for (&'a A,) {
// ...
}
The first impl
provides Render
for a reference to a BTreeSet
, whereas the second one provides an implementation for a tuple of a reference to something that implements Render
. Since BTreeSet
itself does not implement Render
(only a reference to it does!), the compiler will refuse to work.
This is a situation where it's more ergonomic to abstract away from references, since Render
appears to be fitting for any reference to another Render
able value. Implement this trait for all references &T
where T: Render
:
impl<'a, T> Render for &'a T
where
T: Render,
{
fn render(&self) {
(**self).render();
}
}
The remaining implementations become slightly simpler as a consequence of this:
impl<T> Render for BTreeSet<T>
where
T: Render,
{
fn render(&self) {}
}
impl<A> Render for (A,)
where
A: Render,
{
fn render(&self) {
(&self.0).render();
}
}
See also:
Upvotes: 3