-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TryFrom and TryInto traits #1542
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
- Feature Name: try_from | ||
- Start Date: 2016-03-10 | ||
- RFC PR: | ||
- Rust Issue: | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
The standard library provides the `From` and `Into` traits as standard ways to | ||
convert between types. However, these traits only support *infallable* | ||
conversions. This RFC proposes the addition of `TryFrom` and `TryInto` traits | ||
to support these use cases in a standard way. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
Fallible conversions are fairly common, and a collection of ad-hoc traits has | ||
arisen to support them, both [within the standard library][from-str] and [in | ||
third party crates][into-connect-params]. A standardized set of traits | ||
following the pattern set by `From` and `Into` will ease these APIs by | ||
providing a standardized interface as we expand the set of fallible | ||
conversions. | ||
|
||
One specific avenue of expansion that has been frequently requested is fallible | ||
integer conversion traits. Conversions between integer types may currently be | ||
performed with the `as` operator, which will silently truncate the value if it | ||
is out of bounds of the target type. Code which needs to down-cast values must | ||
manually check that the cast will succeed, which is both tedious and error | ||
prone. A fallible conversion trait reduces code like this: | ||
|
||
```rust | ||
let value: isize = ...; | ||
|
||
let value: u32 = if value < 0 || value > u32::max_value() as isize { | ||
return Err(BogusCast); | ||
} else { | ||
value as u32 | ||
}; | ||
``` | ||
|
||
to simply: | ||
|
||
```rust | ||
let value: isize = ...; | ||
let value: u32 = try!(value.try_into()); | ||
``` | ||
|
||
# Detailed design | ||
[design]: #detailed-design | ||
|
||
Two traits will be added to the `core::convert` module: | ||
|
||
```rust | ||
pub trait TryFrom<T>: Sized { | ||
type Err; | ||
|
||
fn try_from(t: T) -> Result<Self, Self::Err>; | ||
} | ||
|
||
pub trait TryInto<T>: Sized { | ||
type Err; | ||
|
||
fn try_into(self) -> Result<T, Self::Err>; | ||
} | ||
``` | ||
|
||
In a fashion similar to `From` and `Into`, a blanket implementation of `TryInto` | ||
is provided for all `TryFrom` implementations: | ||
|
||
```rust | ||
impl<T, U> TryInto<U> for T where U: TryFrom<T> { | ||
type Error = U::Err; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: |
||
|
||
fn try_into(self) -> Result<U, Self::Err> { | ||
U::try_from(self) | ||
} | ||
} | ||
``` | ||
|
||
In addition, implementations of `TryFrom` will be provided to convert between | ||
*all combinations* of integer types: | ||
|
||
```rust | ||
#[derive(Debug)] | ||
pub struct TryFromIntError(()); | ||
|
||
impl fmt::Display for TryFromIntError { | ||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
fmt.write_str(self.description()) | ||
} | ||
} | ||
|
||
impl Error for TryFromIntError { | ||
fn description(&self) -> &str { | ||
"out of range integral type conversion attempted" | ||
} | ||
} | ||
|
||
impl TryFrom<usize> for u8 { | ||
type Err = TryFromIntError; | ||
|
||
fn try_from(t: usize) -> Result<u8, TryFromIntError> { | ||
// ... | ||
} | ||
} | ||
|
||
// ... | ||
``` | ||
|
||
This notably includes implementations that are actually infallible, including | ||
implementations between a type and itself. A common use case for these kinds | ||
of conversions is when interacting with a C API and converting, for example, | ||
from a `u64` to a `libc::c_long`. `c_long` may be `u32` on some platforms but | ||
`u64` on others, so having an `impl TryFrom<u64> for u64` ensures that | ||
conversions using these traits will compile on all architectures. Similarly, a | ||
conversion from `usize` to `u32` may or may not be fallible depending on the | ||
target architecture. | ||
|
||
The standard library provides a reflexive implementation of the `From` trait | ||
for all types: `impl<T> From<T> for T`. We could similarly provide a "lifting" | ||
implementation of `TryFrom`: | ||
|
||
```rust | ||
impl<T, U: From<T>> TryFrom<T> for U { | ||
type Err = Void; | ||
|
||
fn try_from(t: T) -> Result<U, Void> { | ||
Ok(U::from(t)) | ||
} | ||
} | ||
``` | ||
|
||
However, this implementation would directly conflict with our goal of having | ||
uniform `TryFrom` implementations between all combinations of integer types. In | ||
addition, it's not clear what value such an implementation would actually | ||
provide, so this RFC does *not* propose its addition. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a very important implementation to add. I write code like this all the time:
which then can be used with anything that implements
and be able to pass in anything implementing plain old The coherence problem could’ve been a reason for not considering this at the time of writing, but since specialisation has been implemented, perhaps it can be made to work? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not believe this can be done in the current implementation of specialization. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, IIRC this requires the "lattice rule". (I expect something with that level of expressiveness to be available eventually, but it will be a while.) |
||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
It is unclear if existing fallible conversion traits can backwards-compatibly | ||
be subsumed into `TryFrom` and `TryInto`, which may result in an awkward mix of | ||
ad-hoc traits in addition to `TryFrom` and `TryInto`. | ||
|
||
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
We could avoid general traits and continue making distinct conversion traits for | ||
each use case. | ||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
Are `TryFrom` and `TryInto` the right names? There is some precedent for the | ||
`try_` prefix: `TcpStream::try_clone`, `Mutex::try_lock`, etc. | ||
|
||
What should be done about `FromStr`, `ToSocketAddrs`, and other ad-hoc fallible | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dislike the tendency of replacing concrete methods with meaningless generic implementations. Is anyone going to write generic code which would be applicable to both I'm not against There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don’t know what should be concluded from this, but note that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @petrochenkov Why do you feel that It's also worth noting that the fate of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't feel that way with respect to |
||
conversion traits? An upgrade path may exist in the future with specialization, | ||
but it is probably too early to say definitively. | ||
|
||
Should `TryFrom` and `TryInto` be added to the prelude? This would be the first | ||
prelude addition since the 1.0 release. | ||
|
||
[from-str]: https://doc.rust-lang.org/1.7.0/std/str/trait.FromStr.html | ||
[into-connect-params]: http://sfackler.github.io/rust-postgres/doc/v0.11.4/postgres/trait.IntoConnectParams.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example is pretty untypical.
These overflows, like arithmetic overflows, are very rarely handled, so most of the time people will have to deal with
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not all that uncommon in my experience:
https://github.com/sfackler/rust-postgres/blob/master/src/types/mod.rs#L700
https://github.com/sfackler/rust-postgres-large-object/blob/master/src/lib.rs#L220
But in any case, what you do with the
Err
once you get it doesn't really have much to do with this RFC - it's focused on how you get thatErr
.