1

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() {}

Playground

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`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Phil Lord
  • 2,917
  • 1
  • 20
  • 31
  • 2
    Are the macros **required** to reproduce this problem? Is `Axiom` required to reproduce the problem? Please review how to create a [MCVE], emphasis on **minimal**. – Shepmaster Oct 08 '18 at 16:33

1 Answers1

3

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 Renderable 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();
    }
}

Working Playground

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
E_net4
  • 27,810
  • 13
  • 101
  • 139
  • As a second question, how did you work this out? I struggled hard with this, but couldn't find a way to get the compiler to tell me why the tuple wasn't a subclass of the thing I expected. – Phil Lord Oct 08 '18 at 16:56
  • @PhilLord Just observation with a sufficiently experienced eye. Note that the compiler was already telling you the missing constraint: `(&std::collections::BTreeSet,) : Render` was unsatisfied, which would either mean that the trait was not implemented for `(&T,)` or that `BTreeSet` was not implementing it. – E_net4 Oct 08 '18 at 17:00
  • Yeah, guessed it was experience. Problem with the compiler is I knew what it was missing, but I couldn't see why, given there was a `impl` for `&BTreeSet`. Anyway, thanks for the help. A bit more learned about rust. – Phil Lord Oct 08 '18 at 18:03