Currently rust has both the traits std::str::FromStr and std::convert::From<T>, the latter of which is often implemented for T: String even when std::str::FromStr is not.
Can implementing std::str::From<String>/std::str::From<&str> provide a free implementation of std::convert::FromStr so that this problem is avoided? From<&str> (being exception-free) can be used to implement FromStr, even if the converse isn't true.
FromStr represents a fallible conversion, and so would correspond more closely to TryFrom rather than From. However, the set of blanket implementations allowed for TryFrom prevent it form replacing FromStr in parse: https://github.com/rust-lang/rust/pull/44174
We're going to keep running into problems like this so long as rust doesn't have a way to resolve multiple implementation conflicts :/
Semantically, an _infallible_ conversion is just a special case of fallible conversions, really just a Result<T,!>, if you will. From should provide TryFrom and both should provide FromStr. If #1822 were to be resolved, rust-lang/rust#44174 would automatically take care of this particular issue, though.
Basically, replace FromStr with TryFrom<&str> and then the blanket TryFrom<T> derived from From<T> would provide FromStr in both the fallible and infallible cases.
Edit: corrected swapped From and TryFrom in text.
Semantically, an infallible conversion is just a special case of fallible conversions, really just a
Result<T,!>, if you will.TryFromshould provideFrom...
Perhaps I'm reading this wrong, but if From is really just a special case of TryFrom, shouldn't it be that From would provide TryFrom, instead of the other way around?
@DaseinPhaos Yes, that was a typo. Thanks for catching it - fixed.
That approach isn't really actionable without breaking backwards compatibility.
I'm not sure that's necessarily the case, @sfackler .
Step 1) Make sure every struct that implements std::str::FromStr implements either From or TryFrom
Step 2) Remove all manual impls of std::str::FromStr from base
Step 3) Rewrite str::parse() to use TryFrom
Step 4) Warn "FromStr has been deprecated" any time impl std::str::FromStr is called
Step 5) Create a new impl FromStr covering all TryFrom implementors, to be used only by legacy code.
The important thing is that this happens before TryFrom becomes stable, because you can guarantee that no one that has TryFrom also has FromStr (and vice versa), so there will be no conflicting implementations that rust has to resolve. Mark parse() as deprecated while you're at it, and just use into() and try_into() thereafter.
How is step 1 supposed to work? We don't own all Rust code in existence.
I think I'm over complicating things. Make sure all current FromStr impls in rust base (not in all rust code everywhere) implement TryFrom<str>. Mark both str::parse() and str::FromStr as deprecated but keep around until whenever we can dump them. Update all documentation and replace parse() with into() and FromStr to TryFrom<str>. Done.
An important distinction between impl FromStr for String and (Try)From<String> is that the former must allocate, while the latter is no-op.
If I had been allowed to travel back in time and change something except not adding TryFrom traits, I'd add from_string method with default, overridable implementation to FromStr to do conversions cheaply. :)
Also, I like parse() method because of the name. Rather than deprecating it, I'd prefer resolving this mess via specialization.
I'm guessing this is blocked until specialization is stabilized ?
Where something like this would be possible:
impl<T> TryFrom<String> for T
where
T: FromStr,
{
type Error = <T as FromStr>::Err;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
@malobre yes, requires specialization. The impl you suggest is still in conflict because String: FromStr, so when T is String you get ambiguity.
Most helpful comment
We're going to keep running into problems like this so long as rust doesn't have a way to resolve multiple implementation conflicts :/
Semantically, an _infallible_ conversion is just a special case of fallible conversions, really just a
Result<T,!>, if you will.Fromshould provideTryFromand both should provideFromStr. If #1822 were to be resolved, rust-lang/rust#44174 would automatically take care of this particular issue, though.Basically, replace
FromStrwithTryFrom<&str>and then the blanketTryFrom<T>derived fromFrom<T>would provideFromStrin both the fallible and infallible cases.Edit: corrected swapped
FromandTryFromin text.