Skip to content

Rust incorrectly concludes that trait is not implemented #82252

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
survived opened this issue Feb 18, 2021 · 3 comments
Closed

Rust incorrectly concludes that trait is not implemented #82252

survived opened this issue Feb 18, 2021 · 3 comments
Labels
A-associated-items Area: Associated items (types, constants & functions) A-trait-system Area: Trait system C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@survived
Copy link

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

@survived survived added the C-bug Category: This is a bug. label Feb 18, 2021
@jyn514 jyn514 added A-associated-items Area: Associated items (types, constants & functions) A-trait-system Area: Trait system labels Feb 18, 2021
@Noratrieb Noratrieb added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue. labels Apr 5, 2023
@survived
Copy link
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.

@kadiwa4
Copy link
Contributor

kadiwa4 commented Mar 26, 2024

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

@rustbot rustbot added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Mar 26, 2024
@survived
Copy link
Author

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

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-associated-items Area: Associated items (types, constants & functions) A-trait-system Area: Trait system C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants