From 28327ae3bcc5666cf332b56b39bfa75fcc856d5e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sat, 2 Nov 2019 11:14:33 +0200 Subject: [PATCH 1/6] RFC: unwrap_infallible --- text/0000-unwrap-infallible.md | 196 +++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 text/0000-unwrap-infallible.md diff --git a/text/0000-unwrap-infallible.md b/text/0000-unwrap-infallible.md new file mode 100644 index 00000000000..50b6d36fd7d --- /dev/null +++ b/text/0000-unwrap-infallible.md @@ -0,0 +1,196 @@ +- Feature Name: `unwrap_infallible` +- Start Date: 2019-11-01 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +Add method `Result::unwrap_infallible` to provide a convenient alternative +to `unwrap` for converting `Result` values with an uninhabitable `Err` type, +while ensuring infallibility at compile time. + +# Motivation +[motivation]: #motivation + +`Result`, soon to be equivalent to `Result`, +has been occurring quite often in recent code. The first instinct is to +use `unwrap` on it, knowing it can never panic, but herein lies a +maintainability hazard: if the error parameter type at such a use site is +later changed to an inhabitable one, the `unwrap` call quietly becomes liable +to panic. + +Therefore, it would make sense to add an alternative conversion method +to the standard library that would only be applicable to `Result` +with an uninhabitable error type. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Add a new method to `Result`, available when the `Err` type parameter is `!` +or is claimed to be infallibly convertible to `!` by a provided `From` impl: + +```rust +impl> Result { + pub fn unwrap_infallible(self) -> T { + match self { + Ok(x) => x, + Err(e) => e.into(), + } + } +} +``` + +This method should be used in preference to `Result::unwrap` when the user +wishes to ensure at compile time that an `Err` value cannot occur and therefore +this conversion can't fail (provided compliance by the `Into` impl of +the error type). + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The method `unwrap_infallible` is most readily avalable on `Result<_, !>`. +The failure branch is eliminated by the compiler even in the debug profile. + +Extending the error type to `E: Into` allows crate authors who have defined +their own uninhabitable types to benefit from this API without redefining +their "never-type" as an alias of `!`. Such "never-types", most prominently +`core::convert::Infallible` prior to `never_type` +[stabilization][never_type], serve the purpose of `!` before it is stabilized, +but aliasing the type to `!` when it is stabilized later +[can break][infallible-compat] pre-existing stable code in some corner cases. + +[never_type]: https://github.com/rust-lang/rust/pull/65355 +[infallible-compat]: https://doc.rust-lang.org/std/convert/enum.Infallible.html#future-compatibility + +To fully preserve backward compatibility, custom "never-types" can be made +convertible into `!`: + +```rust +enum MyNeverToken {} + +impl From for ! { + fn from(never: MyNeverToken) -> Self { + match never {} + } +} +``` + +The implementation of `unwrap_infallible`, as provided above, relies on +the `From` impl being divergent without panicking, which fits the general +contract of `From`/`Into`. + +# Drawbacks +[drawbacks]: #drawbacks + +Can't think of any. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +Adding an inherent method to `Result` is a backward-compatible way to +provide convenience in supporting a safe coding practice. +The method `unwrap_infallible` fits in nicely with other methods in the +`unwrap*` family and is easily discoverable in the documentation. + +## A blanket From impl + +Another possible way to provide a convenient conversion is adding a blanket +`From` impl to `libcore`: + +```rust +impl> From> for T { + fn from(res: Result) -> T { + match res { + Ok(x) => x, + Err(e) => e.into(), + } + } +} +``` + +Or, more restricted (shown implemented with the `exhaustive_patterns` compiler +feature for coolness): + +```rust +impl From> for T { + fn from(Ok(t): Result) -> T { t } +} +``` + +Either way, the `From` impl may overlap with impls defined in +other crates for their own types. + +## Going without + +Without a convenient conversion, developers conscious of the refactoring hazard +have to write verbose statements like +`res.unwrap_or_else(|never| match never {})`, or invent their own utilities +for a shorthand. Others may never learn of the pitfall and use `unwrap` as +the quickest suitable way to write a conversion found in the documentation of +`Result`. + +## Third-party crate + +The crate [unwrap-infallible][ext-crate] provides the `unwrap_infallible` +method in an extension trait. A third-party crate, though, is not as +discoverable as having a method available on `Result` in the standard library. + +[ext-crate]: https://crates.io/crates/unwrap-infallible + +## Exhaustive single variant pattern match + +It will be possible to irrefutably match enums by the single inhabitable variant +if the `exhaustive_patterns` feature +([rust-lang/rust#51085][exhaustive_patterns]) is stabilized: + +```rust +let Ok(x) = u64::try_from(x); +``` + +This may be more convenient in some cases like the argument pattern match +example above, but in other cases it is less ergonomic than a method call. +So, this can be considered a complementary solution. + +[exhaustive_patterns]: https://github.com/rust-lang/rust/issues/51085 + +## Question the infallible + +It's been [proposed][question-infallible] to make `Result<_, !>` work with +the `?` operator in any function: + +```rust +fn g(x: u32) { + let x = u64::try_from(x)?; + ... +} +``` + +[question-infallible]: https://internals.rust-lang.org/t/a-distinct-way-to-unwrap-result-t-e-where-e-into/11212/8 + +This would not completely eliminate the refactoring hazard that motivates +this proposal, but would in fact make it worse: the infallibility would be +conditional on both the return type of the containing function and +the error type of the expression under `?`, with possibility to change either +without compile-time breakage. + +Additionally, the usual semantics of `?` meaning "may return an error early +here" would obscure the intent to make use of infallibility. + +# Prior art +[prior-art]: #prior-art + +The original author is certain that the equivalent concern exists and may be +resolved more elegantly in the Haskell type system, but lacks detailed +knowledge of the language and spare time to research. Fill this in if necessary. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +None. + +# Future possibilities +[future-possibilities]: #future-possibilities + +Authors of custom never-types could find creative ways to implement `Into` +that will be invoked by this method. From aefc7bbe86f96ade5975b2a37d4a98a8184d4642 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sat, 2 Nov 2019 11:25:40 +0200 Subject: [PATCH 2/6] unwrap_infallible: Reference as RFC 2799 --- text/0000-unwrap-infallible.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unwrap-infallible.md b/text/0000-unwrap-infallible.md index 50b6d36fd7d..57eb1c7edfa 100644 --- a/text/0000-unwrap-infallible.md +++ b/text/0000-unwrap-infallible.md @@ -1,6 +1,6 @@ - Feature Name: `unwrap_infallible` - Start Date: 2019-11-01 -- RFC PR: +- RFC PR: [rust-lang/rfcs#2799](https://github.com/rust-lang/rfcs/pull/2799) - Rust Issue: # Summary From 59df1ae30a308bf8bb1913357a2e910376188ded Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sat, 2 Nov 2019 11:31:28 +0200 Subject: [PATCH 3/6] Proofreading: s/statements/expressions/ --- text/0000-unwrap-infallible.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unwrap-infallible.md b/text/0000-unwrap-infallible.md index 57eb1c7edfa..96f939449c1 100644 --- a/text/0000-unwrap-infallible.md +++ b/text/0000-unwrap-infallible.md @@ -124,7 +124,7 @@ other crates for their own types. ## Going without Without a convenient conversion, developers conscious of the refactoring hazard -have to write verbose statements like +have to write verbose expressions like `res.unwrap_or_else(|never| match never {})`, or invent their own utilities for a shorthand. Others may never learn of the pitfall and use `unwrap` as the quickest suitable way to write a conversion found in the documentation of From 1b93f13e43a98d050fb802371460cf2683048f30 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sat, 2 Nov 2019 16:49:11 +0200 Subject: [PATCH 4/6] Fix a typo in text/0000-unwrap-infallible.md Co-Authored-By: kennytm --- text/0000-unwrap-infallible.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unwrap-infallible.md b/text/0000-unwrap-infallible.md index 96f939449c1..9926674d011 100644 --- a/text/0000-unwrap-infallible.md +++ b/text/0000-unwrap-infallible.md @@ -49,7 +49,7 @@ the error type). # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -The method `unwrap_infallible` is most readily avalable on `Result<_, !>`. +The method `unwrap_infallible` is most readily available on `Result<_, !>`. The failure branch is eliminated by the compiler even in the debug profile. Extending the error type to `E: Into` allows crate authors who have defined From 10ee3b8ba5d53d51ddeead6144950698176b1d1c Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sat, 2 Nov 2019 17:20:00 +0200 Subject: [PATCH 5/6] unwrap_infallible: Link to a Rust issue Found a pre-existing Rust issue which proposed nearly exactly what I came up with. --- text/0000-unwrap-infallible.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unwrap-infallible.md b/text/0000-unwrap-infallible.md index 9926674d011..107eeff65bc 100644 --- a/text/0000-unwrap-infallible.md +++ b/text/0000-unwrap-infallible.md @@ -1,7 +1,7 @@ - Feature Name: `unwrap_infallible` - Start Date: 2019-11-01 - RFC PR: [rust-lang/rfcs#2799](https://github.com/rust-lang/rfcs/pull/2799) -- Rust Issue: +- Rust Issue: [rust-lang/rust#61695](https://github.com/rust-lang/rust/issues/61695) # Summary [summary]: #summary From 546f9b3b93ba58c6f169c9baea46e3d88871c472 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 4 Nov 2019 23:09:35 +0200 Subject: [PATCH 6/6] unwrap_infallible: Renamed the method to into_ok Bikeshedding in the RFC issue has been most useful. --- text/0000-unwrap-infallible.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/text/0000-unwrap-infallible.md b/text/0000-unwrap-infallible.md index 107eeff65bc..6e41ab6d172 100644 --- a/text/0000-unwrap-infallible.md +++ b/text/0000-unwrap-infallible.md @@ -6,7 +6,7 @@ # Summary [summary]: #summary -Add method `Result::unwrap_infallible` to provide a convenient alternative +Add method `Result::into_ok` to provide a convenient alternative to `unwrap` for converting `Result` values with an uninhabitable `Err` type, while ensuring infallibility at compile time. @@ -32,7 +32,7 @@ or is claimed to be infallibly convertible to `!` by a provided `From` impl: ```rust impl> Result { - pub fn unwrap_infallible(self) -> T { + pub fn into_ok(self) -> T { match self { Ok(x) => x, Err(e) => e.into(), @@ -49,7 +49,7 @@ the error type). # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -The method `unwrap_infallible` is most readily available on `Result<_, !>`. +The method `into_ok` is most readily available on `Result<_, !>`. The failure branch is eliminated by the compiler even in the debug profile. Extending the error type to `E: Into` allows crate authors who have defined @@ -76,7 +76,7 @@ impl From for ! { } ``` -The implementation of `unwrap_infallible`, as provided above, relies on +The implementation of `into_ok`, as provided above, relies on the `From` impl being divergent without panicking, which fits the general contract of `From`/`Into`. @@ -90,8 +90,10 @@ Can't think of any. Adding an inherent method to `Result` is a backward-compatible way to provide convenience in supporting a safe coding practice. -The method `unwrap_infallible` fits in nicely with other methods in the -`unwrap*` family and is easily discoverable in the documentation. +The method is easily discoverable in the documentation. +Its name, `into_ok`, is chosen to be short, yet distinct to facilitate +audit and refactoring. The conventional meaning of `into*` methods implies +an infallible conversion. ## A blanket From impl @@ -132,7 +134,7 @@ the quickest suitable way to write a conversion found in the documentation of ## Third-party crate -The crate [unwrap-infallible][ext-crate] provides the `unwrap_infallible` +The crate [unwrap-infallible][ext-crate] provides a similar `unwrap_infallible` method in an extension trait. A third-party crate, though, is not as discoverable as having a method available on `Result` in the standard library.