Problem with HRTBs and associated type bounds

After having had some struggles with TryFrom in the past, I tried again to use them. And I came across the following problem::

#![feature(generic_associated_types)]
#![allow(unused)]

trait Machine {
    type Datum<'a>
    where
        Self: 'a;
    fn get_datum<'a>(&'a self) -> Self::Datum<'a>;
}

struct ConversionError<T> {
    original: T,
}

impl<T> ConversionError<T> {
    fn explain(&self) {}
}

struct NongenericError {}

impl NongenericError {
    fn explain(&self) {}
}

fn foo<M>(machine: M)
where
    M: Machine,
    // This won't work:
    for<'a> String: TryFrom<
        <M as Machine>::Datum<'a>,
        Error = ConversionError<<M as Machine>::Datum<'a>>,
    >,
    // But this works:
    //for<'a> String: TryFrom<<M as Machine>::Datum<'a>, Error = NongenericError>,
{
    let result: Result<String, _> = machine.get_datum().try_into();
    if let Err(err) = result {
        err.explain();
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0582]: binding for associated type `Error` references lifetime `'a`, which does not appear in the trait input types
  --> src/lib.rs:31:9
   |
31 |         Error = ConversionError<<M as Machine>::Datum<'a>>,
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0582`.
error: could not compile `playground` due to previous error

Is it impossible what I attempt to do? What's wrong with:

    for<'a> String: TryFrom<
        <M as Machine>::Datum<'a>,
        Error = ConversionError<<M as Machine>::Datum<'a>>,
    >,

If I have a TryFrom::Error that doesn't depend on 'a it works fine.

#49601 I think.

1 Like

Workaround. Please choose a better name. Note that this isn't based on TryFrom directly anymore (in case you need that specifically for some reason...)

// TryIntoOrConversionErrorOfSelf
trait TIoCEoS<T>: TryInto<T, Error=ConversionError<Self>> {}
impl<T, U> TIoCEoS<T> for U where U: TryInto<T, Error=ConversionError<Self>> {}

fn foo<M>(machine: M)
where
    M: Machine,
    for<'a> <M as Machine>::Datum<'a>: TIoCEoS<String>,
{
    let result: Result<String, _> = machine.get_datum().try_into();
    if let Err(err) = result {
        err.explain();
    }
}
1 Like

I suspect trying to base this on TryFrom might cause issues with orphan rules? Or why did you use TryInto here?

I might need to make something like TIoCEoS anyway. Even with that name it feels more readable than for<'a> String: TryFrom<<M as Machine>::Datum<'a>, Error = ConversionError<<M as Machine>::Datum<'a>>> (edit: and I simplified the latter already for the sake of demonstrating the problem :sweat_smile:).

I wrote it in-line as TryInto to see if it helped (no). I saw that in this form, 'a is not a parameter of the trait and thought "Hmmm!", but that was a red herring. (Being on the left is still "an input to the trait", in the words of the linked issue.) But it also made the relationship clearer, by which I mean this part:

// When inline, Self is <M as Machine>::Datum<'a>
Self: TryInto<T, Error=ConversionError<Self>>

So I tried the helper trait. It worked, I posted it, translating to TryFrom is an exercise for the reader :wink:. (Now I'm picking away at the examples in the issue.)

Alright thanks, I'll try it with TryFrom… (Just wanted to know whether you knew whether it would be futile anyway.)

Looks like orphan rules pose no problem:

It works indeed with TryFrom too (Playground). (Note: I used the name X for that trait.)

2 Likes