4

Is there any real difference between FromStr and TryFrom<&str>?

From the definitions in the documentation, they look identical once you substitute &str for T in TryFrom:

pub trait FromStr {
    type Err;
    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

pub trait TryFrom<T> {
    type Error;
    fn try_from(value: T) -> Result<Self, Self::Error>;
}

Then there is parse:

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

But if we look at its implementation, we see that it does not do anything beyond providing a slightly shorter way of calling from_str:

pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
    FromStr::from_str(self)
}

If you look at TryFrom's library implementations, then it is almost exclusively composed of numerical conversions, though there is also:

impl<'_, T, const N: usize> TryFrom<&'_ [T]> for [T; N] where
    T: Copy, 
type Error = TryFromSliceError

which is reminiscent of

impl FromStr for String {
    type Err = core::convert::Infallible;
    #[inline]
    fn from_str(s: &str) -> Result<String, Self::Err> {
        Ok(String::from(s))
    }
}

Converting the example implementation of FromStr from the docs to TryFrom is trivial.

There was a similar question on Reddit to which the best answer seems to be "just implement both".

Finally there is this similar question though it uses the wrong type and the Serde context is a distraction.

So:

  1. Is there any real difference between FromStr and TryFrom<&str>?
  2. What is current best practice regarding their use?
  3. Is there any plan for the future towards improving the situation?
hkBst
  • 2,818
  • 10
  • 29
  • Does this answer your question? [What is the difference between the FromStr and TryFrom traits?](https://stackoverflow.com/questions/67385956/what-is-the-difference-between-the-fromstr-and-tryfromstring-traits) – Leonardo Dagnino Mar 15 '22 at 18:42
  • 1
    @LeonardoDagnino The OP referred this question explicitly. – Chayim Friedman Mar 15 '22 at 18:58
  • Sorry, didn't notice that. Does seem like the correct explanation to me. – Leonardo Dagnino Mar 15 '22 at 19:22
  • *"Is there any plan for the future towards improving the situation?"* - I'm not sure anything needs to be changed. In general, there are cases where different traits will come down to the same implementation: see the overlap of `AsRef`, `Borrow`, and `Deref` for example. So just because the code looks the same doesn't mean they aren't designed for different generic use-cases. I think this particular case, `FromStr` probably *could* be subsumed by `TryFrom<&str>` in its entirety, but trying to change it now would probably be more effort than its worth. – kmdreko Mar 16 '22 at 06:41
  • @kmdreko AsRef, Borrow, and Deref have somewhat different use cases though. `FromStr` v `TryFrom` largely comes down to historical / timeline: `TryFrom` was added in 1.34.0 while `FromStr` is a 1.0 API (which can also be seen from `TryFrom::Err`). – Masklinn Mar 17 '22 at 08:22

1 Answers1

2

FromStr provides more type information to the compiler, and thus may help inference. It is also more specific, and thus helps the reader. When the intent is to parse the string, I would use FromStr, because it can be used via the parse() method. If this is just for conversion, I would implement TryFrom<&str>.

Also remember that as stated in What is the difference between the FromStr and TryFrom<String> traits?, FromStr preceeded TryFrom.

Is there any plan for the future towards improving the situation?

Nothing that I'm aware of.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77