Impl foreign trait for type bound by local trait

I'm currently trying to reduce code duplication and ran into a little problem:
I have a few (local) types containing some &Foo and &Bar. Now lets say I want to implement the foreign trait core::ops::Index for all these types and the implementation only relies on the value of &Foo and &Bar.
The solution I thought of was creating two traits trait GetFoo and trait GetBar which contain a function to get the value &Foo or &Bar from &self.
Now I thought I could implement core::ops::Index for T where T: GetFoo + GetBar, but the compiler gives me the error message: "type parameter T must be used as the type parameter for some local type".
So here my two questions:
I do understand why its not possible to implement foreign traits for arbitrary types, but shouldn't this case be fine, since the type parameter T is bound by a local, non public Trait?
Is there any way to implement this without having to write all functions for every type (and possibly delegating them somewhere)?

I asked this question on the discord also (thanks everyone there) and someone had the idea that I could try to implement Deref<Target=Foo> and Deref<Target=Bar> for my types and use this as a trait bound. This failed in my actual use case with the error message of conflicting implemetations of Index for the type Vec (the type T was obviously not bound to be from my crate anymore).

playground

You could implement your local trait for non-local types.

impl GetFoo for i32 {
    fn get_foo(&self) -> &Foo { unimplemented!() }
}

impl GetBar for i32 {
    fn get_bar(&self) -> &Bar { unimplemented!() }
}

Now, i32 would satisfy the condition for Index, but it is a foreign type, so you shouldn't be able to implement Index for it.

1 Like

But why would that be a Problem? No one outsidr my crate could even know that the traits exist (they are not pub) so there shouldn't be any problem in implementing it for whatever I like right? I can't think of any situation where this would lead to conflicting implementations in different crates, but I guess I'm missing something?

Rust likes to be conservative, right now the trait resolution algorithm will try and play as safe as possible. This also has the slight benefit of being automatically forward compatible, you can always add a trait impl without breaking other code.

In addition to simply being conservative, it’s also undesirable to make an impl’s legal-ness depend on whether certain things are public. That makes the trait system more complex, less orthogonal, risks discouraging people from breaking up large modules, etc. That’s not to say some kind of “private impls” feature can’t work, but it’s a difficult and subtle design problem, and it’s not clear if such a feature would pull its weight.

Imagine the following:

  • You implement GetFoo for i32.
  • You implement Index for T where T: GetFoo.
  • Then, later, authors of the standard library implement Index for i32.
  • Someone pulls in both your crate and the std.

Which Index implementation should win? Or should your crate be broken by this change in std and refuse to compile, due to conflicting implementations?

2 Likes

Did you find an alternate approach to the issue?

No I didn't find an alternative approach yet. I'm thinking about using macros, which might work but I didn't have time to try that out yet.
If someone knows a simpler solution (I don't think macros are that complicated, but they add an additional layer of complexity), I would be glad to hear it :slight_smile:

I mean if you are okay with writing a HasFoo and HasBar impl for each type with one of them, why not just write the Index impl instead? Is it has a large shared body, you could factor that out into it's own free-standing function and pass the Foo reference to that.

It wouldn't be just the index trait but some more. I could still factor out the common code but would require a lot of delegate functions. I hoped I could prevent this (there is a delegate crate which seems to do something similar, maybe that's a solution for me)

The best solution I found for now is using a macro that gets a type and the identifiers and constructs the trait implementation based on these identifiers:
playground
Still it seems odd to me that this can't be described by the type system, but this solutions will work for me.

Thanks for the help from everyone :slight_smile:

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.