safe_unwrap/
lib.rs

1#![no_std]
2
3//! Annotate unwraps as manually checked
4//!
5//! The `safe_unwrap` macros allows unwrapping and annotating that the unwrap
6//! will never fail.
7//!
8//! An example:
9//!
10//! ```
11//! #[macro_use]
12//! extern crate safe_unwrap;
13//!
14//! fn main() {
15//!    let res = Some(42);
16//!
17//!    // we know that unwrapping res will never fail, so it is safe to call unwrap
18//!    let val = safe_unwrap!("is constant value", res);
19//!
20//!    assert_eq!(val, 42);
21//! }
22//! ```
23//!
24//! In release builds, `safe_unwrap!(expr)` is equivalent to `expr.unwrap()`;
25//! in debug builds, `expect()` will be called with a message indicating that
26//! the assumed invariant has been violated.
27//!
28//! Alternative, for `Result` and `Option` types, you can risk a small bit of
29//! overhead in exchange for nicer syntax:
30//!
31//! ```
32//! extern crate safe_unwrap;
33//! use safe_unwrap::SafeUnwrap;
34//!
35//! fn main() {
36//!     let res = Some(42);
37//!
38//!     // works only for Result and Option types
39//!     let val = res.safe_unwrap("is constant value");
40//!
41//!     assert_eq!(val, 42);
42//!
43//!     #[cfg(feature = "std")]
44//!     {
45//!         // With `std`, two additional methods are available.
46//!         let val = res.unwrap_or_abort("is constant value");
47//!         assert_eq!(val, 42);
48//!         let val = res.unwrap_or_exit("is constant value");
49//!         assert_eq!(val, 42);
50//!     }
51//! }
52//! ```
53//!
54//! The semantics of `.safe_unwrap` are otherwise the same as the
55//! `safe_unwrap!` macro. The tradeoff here is that you are at the mercy of the
56//! LLVM optimizer to remove the unused static string `"is constant value"`
57//! from the resulting executable (often works in release mode).
58//!
59//!
60//! ## `std` support
61//!
62//! By default, `no_std` is supported. With the `std` feature, `SafeUnwrap` has
63//! two additional methods, which require the standard library. They work the
64//! same way as `safe_unwrap`, but:
65//!
66//! * `unwrap_or_abort` aborts the process instead of panicking.
67//! * `unwrap_or_exit` exits with code 1 instead of panicking.
68
69#[cfg(feature = "std")]
70extern crate std;
71
72#[cfg(feature = "std")]
73use std::io::Write;
74
75// TODO: replace `cfg(debug_assertions)` with something cleaner using a build
76//       script
77#[macro_export]
78#[cfg(not(debug_assertions))]
79macro_rules! safe_unwrap {
80    ($reason:expr, $e:expr) => ($e.unwrap())
81}
82
83#[macro_export]
84#[cfg(debug_assertions)]
85macro_rules! safe_unwrap {
86    ($reason:expr, $e:expr) => (
87        $e.expect(concat!("[BUG] violated: ",
88        $reason))
89    )
90}
91
92pub trait SafeUnwrap<T> {
93    fn safe_unwrap(self, msg: &str) -> T;
94    #[cfg(feature = "std")]
95    fn unwrap_or_abort(self, msg: &str) -> T;
96    #[cfg(feature = "std")]
97    fn unwrap_or_exit(self, msg: &str) -> T;
98}
99
100#[cfg(not(debug_assertions))]
101impl<T, E: core::fmt::Debug> SafeUnwrap<T> for Result<T, E> {
102    #[inline]
103    fn safe_unwrap(self, _: &str) -> T {
104        self.unwrap()
105    }
106
107    #[cfg(feature = "std")]
108    #[inline]
109    fn unwrap_or_abort(self, _: &str) -> T {
110        self.unwrap_or_else(|_| std::process::abort())
111    }
112
113    #[cfg(feature = "std")]
114    #[inline]
115    fn unwrap_or_exit(self, _: &str) -> T {
116        self.unwrap_or_else(|_| std::process::exit(1))
117    }
118}
119
120#[cfg(not(debug_assertions))]
121impl<T> SafeUnwrap<T> for Option<T> {
122    #[inline]
123    fn safe_unwrap(self, _: &str) -> T {
124        self.unwrap()
125    }
126
127    #[cfg(feature = "std")]
128    #[inline]
129    fn unwrap_or_abort(self, _: &str) -> T {
130        self.unwrap_or_else(|| std::process::abort())
131    }
132
133    #[cfg(feature = "std")]
134    #[inline]
135    fn unwrap_or_exit(self, _: &str) -> T {
136        self.unwrap_or_else(|| std::process::exit(1))
137    }
138}
139
140#[cfg(debug_assertions)]
141impl<T, E: core::fmt::Debug> SafeUnwrap<T> for Result<T, E> {
142    #[inline]
143    fn safe_unwrap(self, msg: &str) -> T {
144        self.expect(msg)
145    }
146
147    #[cfg(feature = "std")]
148    #[inline]
149    fn unwrap_or_abort(self, msg: &str) -> T {
150        self.unwrap_or_else(|_| {
151            let _ = writeln!(std::io::stderr(), "{}", msg);
152            std::process::abort()
153        })
154    }
155
156    #[cfg(feature = "std")]
157    #[inline]
158    fn unwrap_or_exit(self, msg: &str) -> T {
159        self.unwrap_or_else(|_| {
160            let _ = writeln!(std::io::stderr(), "{}", msg);
161            std::process::exit(1)
162        })
163    }
164}
165
166#[cfg(debug_assertions)]
167impl<T> SafeUnwrap<T> for Option<T> {
168    #[inline]
169    fn safe_unwrap(self, msg: &str) -> T {
170        self.expect(msg)
171    }
172
173    #[cfg(feature = "std")]
174    #[inline]
175    fn unwrap_or_abort(self, msg: &str) -> T {
176        self.unwrap_or_else(|| {
177            let _ = writeln!(std::io::stderr(), "{}", msg);
178            std::process::abort()
179        })
180    }
181
182    #[cfg(feature = "std")]
183    #[inline]
184    fn unwrap_or_exit(self, msg: &str) -> T {
185        self.unwrap_or_else(|| {
186            let _ = writeln!(std::io::stderr(), "{}", msg);
187            std::process::exit(1)
188        })
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::SafeUnwrap;
195
196    #[test]
197    fn works_when_ok() {
198        let x = safe_unwrap!("this comment is meaningless", Some(42));
199        assert_eq!(x, 42);
200    }
201
202    #[test]
203    #[should_panic]
204    fn doesnt_work_when_err() {
205        let _: Option<()> = safe_unwrap!("should fail", None);
206    }
207
208    #[test]
209    fn trait_works_when_ok() {
210        let x = Some(42).safe_unwrap("meaningless comment");
211        assert_eq!(x, 42);
212
213        let r: Result<usize, ()> = Ok(42);
214        let y = r.safe_unwrap("meaningless comment");
215        assert_eq!(y, 42);
216    }
217
218    #[test]
219    #[should_panic]
220    fn trait_doesnt_work_when_err() {
221        let _: Option<()> = None.safe_unwrap("should fail");
222        let _: Result<(), ()> = Err(()).safe_unwrap("should fail");
223    }
224
225}