8

When working with the Serde crate in Rust I tried to add a #[serde(try_from = String)] container attribute to a type that implements FromStr and thus can be parsed from string. Unfortunately this seems not enough for Serde, from the compiler error message it becomes obvious that I have to manually implement TryFrom<String>, too.

Why is TryFrom<String> not implemented automatically for all types that implement FromStr? And why is there a separate trait for fallible conversion from strings? What is the difference between these two traits?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
blerontin
  • 2,892
  • 5
  • 34
  • 60
  • 1
    I think the answer is an average "for historical reasons": AFAIK `FromStr` is much older and `TryFrom` was added only later. However, the trait autoimplementation that you expected sounds logical, maybe there were technical reasons and it could not be easily done. – Zólyomi István May 04 '21 at 13:44
  • 2
    You might find it useful that `serde_with` allows you to [automatically (de)serialize using `FromStr` and `Display`](https://docs.rs/serde_with/1.8.1/serde_with/guide/serde_as/index.html#deserialize-with-fromstr-and-display), and has many other convenience features – trent May 04 '21 at 14:04
  • Thanks for the link to `serde_with`. I will take a look, but as my project targets WASM I'm rather sensitive to additional dependencies in case they increase output size. – blerontin May 04 '21 at 14:41

2 Answers2

10

FromStr was stable in Rust 1.0, TryFrom was stablized in Rust 1.34; it would be very hard to use the trait before it existed.

Why is TryFrom<String> not implemented automatically for all types that implement FromStr?

This would cause ambiguity with types that have already implemented both TryFrom and FromStr. An advanced form of specialization would be needed to allow this.

why is there a separate trait for fallible conversion from strings?

FromStr creates an owned value from a borrowed string slice (&str), so it's semantically different from taking ownership of a String. TryFrom<String> would allow you to reuse the memory allocated for the String.

The closer analog would be impl<'a> TryFrom<&'a str> for ..., but having the lifetime in the trait makes using it more complicated in "simple" cases of parsing a value out of a string.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    I should have noticed that both traits have different lifetime requirements. Implementing `TryFrom<&str>` and using `#[serde(try_from = "&str")]` seems to work fine, though. – blerontin May 05 '21 at 15:10
1

TryFrom is a generic trait, which can be implemented for "any" type. From the documentation:

Simple and safe type conversions that may fail in a controlled way under some circumstances.

Type -> Type

It also provides an auto implementation of TryInto.

FromStr is used mostly to parse types, also as per the documentation:

FromStr's from_str method is often used implicitly, through str's parse method.

str -> Type

Implement it to be used by str::parse

Netwave
  • 40,134
  • 6
  • 50
  • 93