Search code examples
rustcompiler-errorstrait-objects

What exactly is the requirement for "covering" a type & why does a single element tuple satisfy it?


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?

(Playground Link)


Solution

  • From RFC 2451:

    Covered Type: A type which appears as a parameter to another type. For example, T is uncovered, but the T in Vec<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>:

    1. The covering type ForeignType has already been defined. So:
    2. The only way 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).
    3. Because of these rules, an 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.