From 679f6f347355726f9335fdcbf0d3b81b2e490c38 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 10 Jan 2021 11:36:45 +0100 Subject: [PATCH 1/4] Add `unwrap_unchecked()` methods for `Option` and `Result` In particular: - `unwrap_unchecked()` for `Option`. - `unwrap_unchecked()` and `unwrap_err_unchecked()` for `Result`. These complement other `*_unchecked()` methods in `core` etc. Currently there are a couple of places it may be used inside rustc (`LinkedList`, `BTree`). It is also easy to find other repositories with similar functionality. Fixes #48278. Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 31 +++++++++++++++++ library/core/src/result.rs | 64 +++++++++++++++++++++++++++++++++++- library/core/tests/lib.rs | 1 + library/core/tests/option.rs | 7 ++++ library/core/tests/result.rs | 12 +++++++ 5 files changed, 114 insertions(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 0051c9eede0..18b494b3175 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -428,6 +428,37 @@ pub fn unwrap_or_else T>(self, f: F) -> T { } } + /// Returns the contained [`Some`] value, consuming the `self` value, + /// without checking that the value is not [`None`]. + /// + /// # Safety + /// + /// Undefined behavior if the value is [`None`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x = Some("air"); + /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); + /// ``` + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Option<&str> = None; + /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); // Undefined behavior! + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + pub unsafe fn unwrap_unchecked(self) -> T { + debug_assert!(self.is_some()); + match self { + Some(val) => val, + None => unsafe { hint::unreachable_unchecked() }, + } + } + ///////////////////////////////////////////////////////////////////////// // Transforming contained values ///////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/result.rs b/library/core/src/result.rs index d6d17625729..a0f5c7746cc 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -229,7 +229,7 @@ use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; use crate::ops::{self, Deref, DerefMut}; -use crate::{convert, fmt}; +use crate::{convert, fmt, hint}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). /// @@ -821,6 +821,68 @@ pub fn unwrap_or_else T>(self, op: F) -> T { Err(e) => op(e), } } + + /// Returns the contained [`Ok`] value, consuming the `self` value, + /// without checking that the value is not an [`Err`]. + /// + /// # Safety + /// + /// Undefined behavior if the value is an [`Err`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Ok(2); + /// assert_eq!(unsafe { x.unwrap_unchecked() }, 2); + /// ``` + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Err("emergency failure"); + /// unsafe { x.unwrap_unchecked(); } // Undefined behavior! + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + pub unsafe fn unwrap_unchecked(self) -> T { + debug_assert!(self.is_ok()); + match self { + Ok(t) => t, + Err(_) => unsafe { hint::unreachable_unchecked() }, + } + } + + /// Returns the contained [`Err`] value, consuming the `self` value, + /// without checking that the value is not an [`Ok`]. + /// + /// # Safety + /// + /// Undefined behavior if the value is an [`Ok`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Ok(2); + /// unsafe { x.unwrap_err_unchecked() }; // Undefined behavior! + /// ``` + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Err("emergency failure"); + /// assert_eq!(unsafe { x.unwrap_err_unchecked() }, "emergency failure"); + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + pub unsafe fn unwrap_err_unchecked(self) -> E { + debug_assert!(self.is_err()); + match self { + Ok(_) => unsafe { hint::unreachable_unchecked() }, + Err(e) => e, + } + } } impl Result<&T, E> { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index e01aaa4cbf1..285e6cdfd39 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -62,6 +62,7 @@ #![feature(const_raw_ptr_deref)] #![feature(never_type)] #![feature(unwrap_infallible)] +#![feature(option_result_unwrap_unchecked)] #![feature(option_unwrap_none)] #![feature(peekable_next_if)] #![feature(peekable_peek_mut)] diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index 5388b475624..9470451278c 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -160,6 +160,13 @@ fn test_unwrap_or_else() { assert_eq!(x.unwrap_or_else(|| 2), 2); } +#[test] +fn test_unwrap_unchecked() { + assert_eq!(unsafe { Some(1).unwrap_unchecked() }, 1); + let s = unsafe { Some("hello".to_string()).unwrap_unchecked() }; + assert_eq!(s, "hello"); +} + #[test] fn test_iter() { let val = 5; diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index 81660870e95..7aa44c6e593 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -119,6 +119,18 @@ fn handler(msg: &'static str) -> isize { let _: isize = bad_err.unwrap_or_else(handler); } +#[test] +fn test_unwrap_unchecked() { + let ok: Result = Ok(100); + assert_eq!(unsafe { ok.unwrap_unchecked() }, 100); +} + +#[test] +fn test_unwrap_err_unchecked() { + let ok_err: Result = Err("Err"); + assert_eq!(unsafe { ok_err.unwrap_err_unchecked() }, "Err"); +} + #[test] pub fn test_expect_ok() { let ok: Result = Ok(100); From 76299b3f42a402aa896b76fccd725f52080f374d Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 10 Jan 2021 15:59:17 +0100 Subject: [PATCH 2/4] Add `SAFETY` annotations Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 1 + library/core/src/result.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 18b494b3175..5d34f5ca155 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -455,6 +455,7 @@ pub unsafe fn unwrap_unchecked(self) -> T { debug_assert!(self.is_some()); match self { Some(val) => val, + // SAFETY: the safety contract must be upheld by the caller. None => unsafe { hint::unreachable_unchecked() }, } } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index a0f5c7746cc..a357750b92f 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -849,6 +849,7 @@ pub unsafe fn unwrap_unchecked(self) -> T { debug_assert!(self.is_ok()); match self { Ok(t) => t, + // SAFETY: the safety contract must be upheld by the caller. Err(_) => unsafe { hint::unreachable_unchecked() }, } } @@ -879,6 +880,7 @@ pub unsafe fn unwrap_unchecked(self) -> T { pub unsafe fn unwrap_err_unchecked(self) -> E { debug_assert!(self.is_err()); match self { + // SAFETY: the safety contract must be upheld by the caller. Ok(_) => unsafe { hint::unreachable_unchecked() }, Err(e) => e, } From 0140dacabbc6b5a33f19f88c22b531f318ef8f37 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 25 Jan 2021 14:53:19 +0100 Subject: [PATCH 3/4] Link the reference about undefined behavior Suggested-by: Mara Bos Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 4 +++- library/core/src/result.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 5d34f5ca155..9f89bfd674a 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -433,7 +433,9 @@ pub fn unwrap_or_else T>(self, f: F) -> T { /// /// # Safety /// - /// Undefined behavior if the value is [`None`]. + /// Calling this method on [`None`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples /// diff --git a/library/core/src/result.rs b/library/core/src/result.rs index a357750b92f..436f4bf20c7 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -827,7 +827,9 @@ pub fn unwrap_or_else T>(self, op: F) -> T { /// /// # Safety /// - /// Undefined behavior if the value is an [`Err`]. + /// Calling this method on an [`Err`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples /// @@ -859,7 +861,9 @@ pub unsafe fn unwrap_unchecked(self) -> T { /// /// # Safety /// - /// Undefined behavior if the value is an [`Ok`]. + /// Calling this method on an [`Ok`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples /// From 01250fcec6c77552b0f7aac11ed833412294ccba Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 25 Jan 2021 14:58:09 +0100 Subject: [PATCH 4/4] Add tracking issue Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 2 +- library/core/src/result.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 9f89bfd674a..14e4e4da3b9 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -452,7 +452,7 @@ pub fn unwrap_or_else T>(self, f: F) -> T { /// ``` #[inline] #[track_caller] - #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] pub unsafe fn unwrap_unchecked(self) -> T { debug_assert!(self.is_some()); match self { diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 436f4bf20c7..a43ba5882ed 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -846,7 +846,7 @@ pub fn unwrap_or_else T>(self, op: F) -> T { /// ``` #[inline] #[track_caller] - #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] pub unsafe fn unwrap_unchecked(self) -> T { debug_assert!(self.is_ok()); match self { @@ -880,7 +880,7 @@ pub unsafe fn unwrap_unchecked(self) -> T { /// ``` #[inline] #[track_caller] - #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] pub unsafe fn unwrap_err_unchecked(self) -> E { debug_assert!(self.is_err()); match self {