From adc2547baa84fe5288dce7f94b22e02c98bfe346 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 10 Mar 2016 21:33:59 -0800 Subject: [PATCH] Add TryFrom and TryInto traits --- text/0000-try-from.md | 165 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 text/0000-try-from.md diff --git a/text/0000-try-from.md b/text/0000-try-from.md new file mode 100644 index 00000000000..a08c9e09d46 --- /dev/null +++ b/text/0000-try-from.md @@ -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: Sized { + type Err; + + fn try_from(t: T) -> Result; +} + +pub trait TryInto: Sized { + type Err; + + fn try_into(self) -> Result; +} +``` + +In a fashion similar to `From` and `Into`, a blanket implementation of `TryInto` +is provided for all `TryFrom` implementations: + +```rust +impl TryInto for T where U: TryFrom { + type Error = U::Err; + + fn try_into(self) -> Result { + 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 for u8 { + type Err = TryFromIntError; + + fn try_from(t: usize) -> Result { + // ... + } +} + +// ... +``` + +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 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 From for T`. We could similarly provide a "lifting" +implementation of `TryFrom`: + +```rust +impl> TryFrom for U { + type Err = Void; + + fn try_from(t: T) -> Result { + 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. + +# 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 +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