So I can absolutely do FromStr::<ItemId>::from_str(s) to get an ItemId, but serde compilation fails for the above as TryFrom<&str> is not implemented:
the trait bound `ItemId: TryFrom<&str>` is not satisfied [E0277]
Help: the trait `From<&str>` is not implemented for `ItemId`
Note: required for `&str` to implement `Into<ItemId>`
Note: required for `ItemId` to implement `TryFrom<&str>`
If I manually implement TryFrom<&str> and use FromStr::from_str as the implementation, it all works.
Due to the orphan rules, I can't do this:
impl<T, E> TryFrom<&str> for T where T: FromStr<Err = E> {
type Error = E;
fn try_from(value: &str) -> Result<Self, Self::Error> {
FromStr::from_str(value)
}
}
Do I need to manually create all of these TryFrom implementations for all of my ResourceId types? Why does FromStr which is a fallible conversion from &str, not automatically imply TryFrom<&str>?
FromStr came with 1.0 and TryFrom didn't stabilize until 1.34. In the meanwhile, you could write your own implementations for FromStr and From/Into. When TryFrom came along, it came with its blanket implementation based on Into. Adding another blanket implementation would create conflicts with those pre-existing implementations.
Or in shorter form: adding a blanket implementation for your trait at some point later than the trait was introduced is a breaking change.
I didn't try to find a better way for serde specifically, but if you want or need the implementations, yes. (Which is the point where I reach for macros.)
I seem to remember coming across frameworks for better UX for making derive macros, does anyone remember what these are? Every time I've tried in the past I've gotten stuck and overwhelmed.
I don’t think the version history is relevant. The conflict comes from these impls:
impl<T, U> TryFrom<U> for T
where
U: Into<T>
impl<T, U> Into<U> for T
where
U: From<T>
// proposed
impl<T, E> TryFrom<&str> for T where T: FromStr<Err = E>
This would mean that if some type implemented both FromStr and From<&str>, then TryFrom would have two conflicting implementations for that type. Rust always rejects blanket impls that would allow this type of conflict to occur:
trait Foo {}
trait Bar {}
trait Baz {}
impl<T: Foo> Baz for T {}
impl<T: Bar> Baz for T {}
error[E0119]: conflicting implementations of trait `Baz`
--> src/lib.rs:6:1
|
5 | impl<T: Foo> Baz for T {}
| ---------------------- first implementation here
6 | impl<T: Bar> Baz for T {}
| ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
for simple cases, you can use macro_rules to generate the derived impls, with the help of macro_rules_attribute, see the examples in the documentation: