Skip to content

Rust incorrectly concludes that trait is not implemented #82252

Description

@survived

Suppose I have a function:

fn cast<T>(n: U64) -> T // U64 is my own defined type
where 
    for<'a> T: TryFrom<&'a U64>,
    for<'a> <T as TryFrom<&'a U64>>::Error: Debug,
{
    T::try_from(&n).unwrap()
}

Here, for some reason, Rust refuses to infer that <T as TryFrom<&'a U64>>::Error implements Debug. Can be checked by adding more code:

struct U32(u32);
struct U64(u64);
#[derive(Debug)]
struct TooLargeErr;

impl TryFrom<&U64> for U32 {
    type Error = TooLargeErr;
    fn try_from(n: &U64) -> Result<U32, Self::Error> {
        u32::try_from(n.0).map(U32).map_err(|_| TooLargeErr)
    }
}

fn main() {
    // This fails to compile because: `<_ as TryFrom<&'a U64>>::Error` ... doesn't implement `Debug`
    // However, `TooLargeErr` does implement Debug.
    let n: U32 = cast(U64(1));
    assert_eq!(n.0, 1);
}

If we replace main function with:

fn main() {
    let n = U32::try_from(&U64(1)).unwrap();
    assert_eq!(n.0, 1);
}

then it will be compiled, so Rust is able to infer that TooLargeErr implements Debug, but it doesn't do the same in generic bounds.

Here's a link on playground

Meta

Reproduces at Rust 1.50.0 stable, beta, nightly

Activity

  1. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    on Apr 5, 2023
  2. survived commented on Dec 18, 2023

    @survived
    Author

    Two year later I've came across this issue again, and I've got some more practical example where the type system breaks. I was trying to implement serde_with::DeserializeAs<T> trait for all T such that T: TryFrom<&[u8]>. Plus, I do need to know that <T as TryFrom<&[u8]>>::Error implements Display for better error reporting. Trait implementation was supposed to look something like that:

    impl<'de, T> serde_with::DeserializeAs<'de, T> for MyType
    where
        T: TryFrom<&[u8]>,
        <T as TryFrom<&[u8]>>::Error: Display
    { /* ... */ }

    However, here Rust forces me to use for<'a> syntax. The final (simplified) code:

    impl<'de, T> serde_with::DeserializeAs<'de, T> for MyType
    where
        for<'a> T: TryFrom<&'a [u8]>,
        for<'a> <T as TryFrom<&'a [u8]>>::Error: std::fmt::Display,
    { /* ... */ }

    The type system is capable of figuring out that MyType implements serde_with::DeserializeAs<'de, [u8; 32]>, for instance.

    fn it_implements_trait<'de, M, T>()
    where
        M: serde_with::DeserializeAs<'de, T>,
    {}
    it_implements_trait::<MyType, [u8; 32]>(); // this compiles
    it_implements_trait::<Option<MyType>, Option<[u8; 32]>>(); // and that compiles

    but it breaks when it's needed the most: inside serde::Deserialize macro which produces the code similar to:

    struct Foo([u8; 32]);
    impl<'de> serde::Deserialize<'de> for Foo {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de>,
        {
            let inner: [u8; 32] = serde_with::As::<MyType>::deserialize(deserializer)?;
            Ok(Self(inner))
        }
    }

    ...that causes a compilation error:

    error[E0277]: `<_ as TryFrom<&'a [u8]>>::Error` doesn't implement `std::fmt::Display`
       --> cggmp21/src/utils.rs:365:35
        |
    365 |             let inner: [u8; 32] = serde_with::As::<MyType>::deserialize(deserializer)?;
        |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<_ as TryFrom<&'a [u8]>>::Error` cannot be formatted with the default formatter
        |
        = help: the trait `for<'a> std::fmt::Display` is not implemented for `<_ as TryFrom<&'a [u8]>>::Error`
        = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
        = help: the trait `DeserializeAs<'de, T>` is implemented for `MyType`
    note: required for `MyType` to implement `DeserializeAs<'_, _>`
       --> cggmp21/src/utils.rs:277:18
        |
    277 |     impl<'de, T> serde_with::DeserializeAs<'de, T> for MyType
        |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^
    ...
    280 |         for<'a> <T as TryFrom<&'a [u8]>>::Error: std::fmt::Display,
        |                                                  ----------------- unsatisfied trait bound introduced here
    note: required by a bound in `serde_with::de::<impl As<T>>::deserialize`
       --> /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_with-2.3.3/src/de/mod.rs:153:12
        |
    151 |     pub fn deserialize<'de, D, I>(deserializer: D) -> Result<I, D::Error>
        |            ----------- required by a bound in this associated function
    152 |     where
    153 |         T: DeserializeAs<'de, I>,
        |            ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `serde_with::de::<impl As<T>>::deserialize`
    
    

    which is not true because <[u8; 32] as TryFrom<&[u8]>::Error is TryFromSliceError and it does implement Display.

  3. kadiwa4 commented on Mar 26, 2024

    @kadiwa4
    Contributor

    Bisection reveals this was fixed in nightly 2024-02-10 by #120584.
    @rustbot label: +E-needs-test

  4. added
    E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
    on Mar 26, 2024
  5. survived commented on May 17, 2024

    @survived
    Author

    The first example compiles on latest Rust, so I suppose it is indeed fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)A-trait-systemArea: Trait systemC-bugCategory: This is a bug.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions