https://www.rustexplorer.com/b/pg3wqr
trait A {}
struct B<T: A>(T);
// this is ok
impl<T: A> From<T> for B<T> {
fn from(t: T) -> Self { B(t) }
}
// this is not ok
impl<T: A> From<B<T>> for T {
fn from(b: B<T>) -> Self { b.0 }
}
I get:
error[E0210]: type parameter
T
must be covered by another type when it appears before the first local type (B<T>
)
This is not really helping (neither did searching this error message). What I wanted to achieve seems pretty straightforward. Why is this not allowed, and how can I work around the limitations?
https://stackoverflow.com/a/39186717/4876553 basically recommended the same thing, without the bound on the impl, which fails to compile in the same manner.
EDIT: I accidentally copy-pasted a WIP version of me trying to work around this; fixed back to the original example.
CodePudding user response:
Rust's orphan rules don't allow it. The exact covering semantics are complicated (if you're interested, this article is a good jumping-off point), but the basic idea is this:
Take all of the type and trait names passed to an impl
in order, starting with the trait itself, then to the structure or enum we're writing an impl
for, then any type arguments. So with the impl
written as
impl<T> MyTrait<Bar, Baz, T> for Foo
we would write MyTrait, Foo, Bar, Baz, T
(where T
is a type variable).
Now, in this list we've written, find the first name that is defined in our own crate. If the trait is defined in our own crate, then that's the name. If Foo
is, then take that one. Otherwise, keep going until you find one. If there are no names defined in the current crate, fail because we have an orphan instance. In this example, let's pretend MyTrait
is defined somewhere else but Foo
is our own. Then we have
MyTrait, Foo, Bar, Baz, T
^^^
Anything before the name we just found must be concrete, i.e. it can't be a generic type argument. In our example here, the only type argument is T
, and it occurs after Foo
, so it's fine.
(Note that I'm also glossing over covered types. That is, an impl on &Foo
is, for the orphan purposes, considered an impl on Foo
. This applies to references, Box
, and several other built-in Box
-like types; it never applies to types you define yourself)
Now let's take a look at your examples. Things get more complicated when we add generics, and I'm not going to go over all of the messy technical details here, so we'll try to keep it as simple as possible.
impl<T: A> From<T> for B<T>
Here, the trait is From
, the type we're implementing is B
, and our type argument to From
is T
. Our list is
From, B, T
Note that although we're implementing for B<T>
, we say T
is covered by the B
type, so we only take the top-level B
, not what's inside.
Now From
is standard library, so we don't own it, but B
is in our crate. So we have
From, B, T
^
Then we look for generic arguments. The only type argument here is T
, and it appears after the B
that we own, so we're good.
Now the other one.
impl<T: A> From<B<T>> for T
The trait is From
, as before. The first type is T
, a generic type argument. Then B<T>
is next.
From, T, B
Again, we write B
, not B<T>
, since we only care about uncovered types.
From
is standard library, and T
is a type argument, so B
is the only thing we own here.
From, T, B
^
But there's a type argument T
before the first thing we own! That's a problem, so this fails by Rust's orphan rules.
The reason the rules are written this way is to prevent two sibling crates (i.e. crates who are not aware of each other) from accidentally implementing conflicting instances. If we allowed
impl<T: A> From<B<T>> for T
then someone else might come along one day and write
impl<T> From<T> for C
for some type C
in their own crate. They're well within their rights to do so: C
is their own type and the only generic argument occurs after C
in their trait argument list. But then there are two candidate solutions for impl From<B<C>> for C
. (Note that trait bounds, i.e. T: A
are taken into consideration much later and are not considered at this point, since that would just make the already complex problem of trait resolution completely untenable).
CodePudding user response:
In your implementation you are using from(u: U)
. The way you have your code written U
isn't a generic type because you haven't declared it in the impl block. The only generic type available is T
.
In the function you need to use a parameter that is of type B. You have declared B as a tuple struct with its 1 and only field being of generic type T which must implement trait A. So to get that t back out of B you need to call b.0 which is the syntax to get the value out of a tuple struct.
You also need to make that field accessible so you need to change the struct definition to
struct B<T: A>(pub T);
and then change the impl block to this
impl<T: A> From<B<T>> for T {
fn from(b: B) -> T {
b.0
}
}
The function definition could also return Self
e.g. fn from(b: B) -> Self {...}
because in this impl block Self is the same as T.