Assuming the following code is present
use core::any::Any;
enum Value {
Any(Box<dyn Any>),
Other, // placeholder, this code is adapted from mine
}
This code raises a diagnostic that I can't quite understand
impl<T: Any> TryFrom<Value> for T {
type Error = &'static str;
fn try_from(val: Value) -> Result<Self, Self::Error> {
if let Value::Any(any) = val {
if let Ok(down) = any.downcast::<T>() {
Ok(*down)
} else {
Err("incorrect type")
}
} else { Err("not an any") }
}
}
fn main() {
let res: Result<usize, &'static str> = Value::Any(Box::new(1usize)).try_into();
dbg!(res);
}
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Value`)
--> src/main.rs:9:6
|
9 | impl<T: Any> TryFrom<Value> for T {
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Value`)
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
= note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
I still don't quite understand what "must be covered by another type" means, nor "when it appears before the first local type".
However, if I modify the impl signature to target a single-element tuple containing T, the impl does not raise an error, and the code functions correctly:
impl<T: Any> TryFrom<Value> for (T,) {
type Error = &'static str;
fn try_from(val: Value) -> Result<Self, Self::Error> {
if let Value::Any(any) = val {
if let Ok(down) = any.downcast::<T>() {
Ok((*down,))
} else {
Err("incorrect type")
}
} else { Err("not an any") }
}
}
fn main() {
let res: Result<(usize,), &'static str> = Value::Any(Box::new(1usize)).try_into();
dbg!(res);
}
What purpose does the single-element tuple actually serve?
From RFC 2451:
Covered Type: A type which appears as a parameter to another type. For example,
T
is uncovered, but theT
inVec<T>
is covered. This is only relevant for type parameters.
It is important to note that the type T
does not equal the tuple type (T,)
. (T,)
can be considered equivalent to a hypothetical generic newtype/tuple struct struct Tuple1<T>(T)
defined in the standard library crate std
. With this analogy, impl<T: Any> TryFrom<Value> for (T,)
is equivalent to impl<T: Any> TryFrom<Value> for std::Tuple1<T>
.
Note that the covering type (in this case the single element tuple type, or in our analogy Tuple1
) need not be defined locally in the same crate. To put it simply, consider an impl<T> ForeignTrait<LocalType> for ForeignType<T>
:
ForeignType
has already been defined. So:ForeignTrait<LocalType>
can be implemented for
ForeignType<T>
outside of the current crate is through a generic
impl <S, T> ForeignTrait<S> for ForeignType<T>
(where S
covers
LocalType
).impl <S, T> ForeignTrait<S> for ForeignType<T>
that covers ForeignTrait<LocalType>
is only possible in the crate declaring ForeignType
.Hence it is impossible for a conflicting implementation of ForeignTrait<LocalType>
to exist for ForeignType<T>
outside of a) the local crate and b) the crate declaring ForeignType
, and so the impl
is allowed. The RFC discusses this in more detail.