0

I was playing around the visitor design pattern in Rust. My initial visitor didn't have any type parameter so I was able to do something like this:

impl<T> From<T> for Box<dyn VisitorElement>
where
    T: VisitorElement + 'static,
{
    fn from(elem: T) -> Box<dyn VisitorElement> {
        Box::new(elem)
    }
}

As my visitor was simply a f32 calculator, I wanted to make it more generic by replacing f32 by a type parameter. After passing the proper type parameter everywhere, my From implementation wouldn't work. Here's a complete example (playground):

/*
 * Visitor traits
 */
trait Visitor<T> {
    fn visit_literal(&mut self, literal: &Literal<T>);
}

trait VisitorElement<T> {
    fn accept(&self, visitor: &mut dyn Visitor<T>);
}
/*
 * Literal value
 */
struct Literal<T> {
    value: T,
}
impl<T> Literal<T> {
    fn new(value: T) -> Self {
        Self { value: value }
    }
}
impl<T> VisitorElement<T> for Literal<T> {
    fn accept(&self, visitor: &mut dyn Visitor<T>) {
        visitor.visit_literal(self)
    }
}

impl<K, T> From<K> for Box<dyn VisitorElement<T>>
where
    K: VisitorElement<T> + 'static,
{
    fn from(elem: K) -> Box<dyn VisitorElement<T>> {
        Box::new(elem)
    }
}

fn main() {
    let element = Literal::new(0.1);
    let boxed: Box<dyn VisitorElement<_>> = element.into();
}
error[E0119]: conflicting implementations of trait `std::convert::From<std::boxed::Box<(dyn VisitorElement<_> + 'static)>>` for type `std::boxed::Box<(dyn VisitorElement<_> + 'static)>`:
  --> src/main.rs:28:1
   |
28 | / impl<K, T> From<K> for Box<dyn VisitorElement<T>>
29 | | where
30 | |     K: VisitorElement<T> + 'static,
31 | | {
...  |
34 | |     }
35 | | }
   | |_^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T> std::convert::From<T> for T;
   = note: downstream crates may implement trait `VisitorElement<_>` for type `std::boxed::Box<(dyn VisitorElement<_> + 'static)>`

My goal was to allow auto boxing dynamic types in order to make code typing less verbose.

From How is there a conflicting implementation of `From` when using a generic type?, I can see that the type S can cause the trait to convert to itself but in my case the type K is constrained to VisitorElement but I stille end up with a conflict with a Box<dyn VisitorElement<_> + 'static>.

Is there a reason why the constraint on K: VisitorElement<T> + 'static allows a Box<dyn VisitorElement<T> + 'static> into itself but K: VisitorElement<f32> doesn't?

From my understanding, the type K can't be a Box because Box doesn't implement VisitorElement<T>, so I should never end up with a Box as K.

If the issue was caused by the Into being automatically implemented... I'd have From> for Literal<_> then Literal<_> into Box<dyn VisitorElement<_>, but that's true for any type. In my case it doesn't explicitly do that, unless the Box is a special type that implements the same trait as its content?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Loïc Faure-Lacroix
  • 13,220
  • 6
  • 67
  • 99
  • It's hard to answer multiple questions made in one post. Please separate them into multiple questions so that we can help you better and so that your questions will help others in the future that have one of the same questions as you! – Shepmaster May 27 '20 at 15:49
  • It's hard to answer your question because it doesn't include a [MRE], emphasis on the **minimal**. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster May 27 '20 at 15:49
  • It looks like your question might be answered by the answers of [How is there a conflicting implementation of `From` when using a generic type?](https://stackoverflow.com/q/37347311/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster May 27 '20 at 15:50
  • @trentcl not sure what you mean. Initially it was just `VisitorElement` then I made `VisitorElement` to pass a type parameter to allow for something else than f32. If the type is manually specified it works... but if it isn't it end up trying to convert to itself. – Loïc Faure-Lacroix May 27 '20 at 16:21
  • I edited the question. @Shepmaster I'm not sure it's the same issue as you linked or at least not as directly. – Loïc Faure-Lacroix May 27 '20 at 16:45
  • *because `Box` doesn't implement `VisitorElement`* — this doesn't matter. From the proposed duplicate (emphasis mine): *someone **may** implement [...] so that the `From` impl you have written overlaps with the impl in the standard library*. – Shepmaster May 27 '20 at 17:11
  • @Shepmaster yes but why doesn't From for `Box>` also overlap? – Loïc Faure-Lacroix May 27 '20 at 17:20
  • It **is** allowed (does not break the coherence rules) for a downstream crate to `struct Arg; impl VisitorElement for Box> {...}`, which would cause a conflict with `From for T`. It **is not** possible for a downstream crate to simply `impl VisitorElement for Box>`, because there are no local types in that `impl`. The interaction of coherence and generics is complex and subtle; I suggest reading [RFC #2451](https://github.com/rust-lang/rfcs/blob/master/text/2451-re-rebalancing-coherence.md) for an introduction. – trent May 27 '20 at 18:18
  • 1
    Are you *sure* that `VisitorElement` should be generic over `T`? Can you, for instance, usefully implement `VisitorElement` for `Literal`? If not, an associated type may be more correct. [Here's an example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9db2bcce565c7950b68a5854d27333e5). – trent May 27 '20 at 18:32

0 Answers0