1

I have a structure Foo which is composed of some other types and I implement a trait on some container of those various types (Rust playground):

struct MyType1 {}
struct MyType2 {}

struct Foo {
    field1: MyType1,
    field2: MyType2,
}

trait MyTrait {
    /// Just a simple default implementation to keep things simple
    fn do_something(&self) {
        println!("Hello world");
    }
}

struct MyContainer<T> {
    val: T,
}

// Use the default implementation for brevity.
impl MyTrait for MyContainer<Foo> {}
impl MyTrait for MyContainer<MyType1> {}
impl MyTrait for MyContainer<MyType2> {}

This should be fine.

Noting that Foo is really a composition of MyType1 and MyType2, what I actually want to do is this, which doesn't work:

impl MyTrait for MyContainer<Foo> {
    fn do_something(&self) {
        // clearly this won't work as MyType1 and MyType2 are not `Copy`, nor
        // do I want to clone them.
        MyContainer {
            val: self.val.field1,
        }
        .do_something();
        MyContainer {
            val: self.val.field2,
        }
        .do_something();
    }
}

I can create a blanket implementation that covers the reference case:

impl<'a, T> MyTrait for MyContainer<&'a T>
where
    MyContainer<T>: MyTrait,
{
    fn do_something(&self) {
        println!("Inside ref trait")
    }
}

such that I can create a MyContainer around the reference:

impl MyTrait for MyContainer<Foo> {
    fn do_something(&self) {
        MyContainer {
            val: &self.val.field1,
        }
        .do_something();
        MyContainer {
            val: &self.val.field2,
        }
        .do_something();
    }
}

This doesn't address my problem because I want to run the implementation on MyContainer<T>, not MyContainer<&T>. MyContainer is a verification marker structure that denotes correct completion of a verification method on T. That is, putting a T in MyContainer denotes that T is somehow "verified". The composing structure implements the necessary verification trait. It's really part of the API in which it's not overly onerous to implement new Ts, just a couple of traits is enough to have it work.

Since MyContainer is trivial, it seems to me that it ought to be possible to coerce this to create a transient representation of MyContainer<T> that does the right thing.

I'd prefer to do this with safe code, but unsafe might be the way to go. Is it possible to do what I want? Is there a better way to handle a wrapped composition structure?

This is different to other questions I've seen in which the trait is implemented on T and it is desired to implement it on &T without any extra effort. In this case, we have it implemented on MyContainer<T>, and I want it implemented on MyContainer<&T> to act like it was a MyContainer<T> without any extra effort on the part of the implementers of MyType1 and MyType2 and any future MyTypeN.

As per my understanding of the comments, I tried flipping the logic around, so that the implementation is made on the wrapped reference and the blanket implementation is imposed on the wrapped value, but I'm having trouble getting the trait bounds correct. The rust playground shows the compiler recursing infinitely before it gives up.

Henry Gomersall
  • 8,434
  • 3
  • 31
  • 54
  • It looks like your question might be answered by the answers of [Do I have to implement a trait twice when implementing it for both reference and non-reference types?](https://stackoverflow.com/q/56555910/155423); [When should I not implement a trait for references to implementors of that trait?](https://stackoverflow.com/q/28799372/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Aug 18 '20 at 15:05
  • @Shepmaster edited to clarify difference with linked questions – Henry Gomersall Aug 18 '20 at 15:16
  • And why is `MyContainer<&T>` invalid? It should indicate the same amount of verification. – Shepmaster Aug 18 '20 at 15:16
  • 1
    It's not invalid, it just doesn't implement `do_something()`. I can impose on implementers that they must implement `MyTrait` for both `MyContainer` and `MyContainer<&T>`, but it would be exactly the same code. – Henry Gomersall Aug 18 '20 at 15:19
  • Why not provide blanket implementations that treat `T` and `&T` the same, as I linked? Then the implementor of `MyTrait` doesn't need to do anything special. – Shepmaster Aug 18 '20 at 15:21
  • Ah, I think I understand. The point is to provide the specific implementations for `MyContainer<&T>`, and then provide a blanket implementation to cover the `MyContainer` case. This seems embarrassingly obvious now I say it! – Henry Gomersall Aug 18 '20 at 15:39
  • @Shepmaster I can't see what the trait bounds are to implement this for a wrapped struct. More info is added to the question. I'd really value your input! – Henry Gomersall Aug 18 '20 at 16:24

0 Answers0