use std::{convert::TryFrom, fmt};
#[derive(Debug)]
enum Foo {
A,
B,
}
impl TryFrom<&[u8]> for Foo {
type Error = String;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
todo!()
}
}
fn convert_byte<T>(byte: u8) -> Result<T, String>
where
T: for<'a> TryFrom<&'a [u8]>,
for<'a> <T as TryFrom<&'a [u8]>>::Error: fmt::Display,
{
let arr = [byte];
T::try_from(&arr).map_err(|err| err.to_string())
}
fn main() {
let foo: Foo = convert_byte(b'A').unwrap();
println!("Results: {foo:?}");
}
and got error:
error[E0277]: `<_ as TryFrom<&'a [u8]>>::Error` doesn't implement `std::fmt::Display`
--> src/main.rs:35:20
|
35 | let foo: Foo = convert_byte(b'A').unwrap();
| ^^^^^^^^^^^^ `<_ 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
note: required by a bound in `convert_byte`
--> src/main.rs:28:46
|
25 | fn convert_byte<T>(byte: u8) -> Result<T, String>
| ------------ required by a bound in this function
...
28 | for<'a> <T as TryFrom<&'a [u8]>>::Error: fmt::Display,
| ^^^^^^^^^^^^ required by this bound in `convert_byte`
but <_ as TryFrom<&'a [u8]>>::Error is String, and it obviously implement fmt::Display,
so what is the problem?
use std::{convert::TryFrom, fmt};
#[derive(Debug)]
enum Foo {
A,
B,
}
impl<'a> TryFrom<&'a [u8]> for Foo {
type Error = String;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
todo!()
}
}
fn convert_byte<T, E>(byte: u8) -> Result<T, String>
where
T: for<'a> TryFrom<&'a [u8], Error = E>,
E: fmt::Display,
{
let arr = [byte];
T::try_from(&arr).map_err(|err| err.to_string())
}
fn main() {
let foo: Foo = convert_byte(b'A').unwrap();
println!("Results: {foo:?}");
}
Since E is a single type parameter outside the HRTB, it can't vary with the HRTB lifetime which avoids all sorts of weird edge cases in the type system and diagnostics. It's obviously also not identical to your original definition so it's only a workaround, not a real fix.
for<'a> <T as TryFrom<&'a [u8]>>::Error: fmt::Display,
for<'a> <&'a [u8] as TryInto<T>>::Error: fmt::Display,
It goes about solving these different ways clearly, and after playing around a bit I think it's because it knows for sure who the implementer is in the second case (modulo lifetimes), &'_ [u8]. Where as for whatever reason, despite the type ascription, it has decided it doesn't know for sure what T is in the original example, and having T along with the HRTB makes it give up I guess.
error[E0277]: `<_ as TryFrom<&'a [u8]>>::Error` doesn't implement `std::fmt::Display`
--> src/main.rs:26:20
|
26 | let foo: Foo = convert_byte(b'A').unwrap();
| ^^^^^^^^^^^^ `<_ 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`
See all those <_ as TryFrom<&'a [u8]>>'s? It doesn't know it's looking for Foo as TryFrom.