try_blocks/lib.rs
1#![no_std]
2//! # Notice
3//!
4//! This crate has been moved/renamed to [tryvial](https://crates.io/crates/tryvial).
5//! You should use that instead.
6//!
7//! ---
8//!
9//! Macro for stable try blocks that performs Ok-wrapping, and otherwise tries to
10//! achieve feature parity with RFC 1859. The macro is compatible with any type
11//! that implements the unstable `Try` trait through the use of type magic.
12//!
13//! This crate is `no_std` compatible.
14
15/// Macro for ok-wrapping any `Try` type. This works on stable through dark type magic.
16///
17/// Note that type inference is very finicky; you should give this a type ascription ASAP.
18/// ```
19/// # use try_blocks::wrap_ok;
20/// let r: Result<_, ()> = wrap_ok!(1);
21/// assert_eq!(r, Ok(1));
22/// ```
23#[macro_export]
24macro_rules! wrap_ok {
25 ($e:expr) => {{
26 ::core::iter::empty().try_fold($e, |_, x: core::convert::Infallible| match x {})
27 }};
28}
29
30/// Macro for the recieving end of a `?` operation.
31/// Right now, type inference is quite finicky so you usually have to declare a concrete type somewhere.
32///
33/// ```
34/// # use try_blocks::try_block;
35/// // Note: this fails without explicitly specifying the error type.
36/// let y: Result<_, std::num::ParseIntError> = try_block! {
37/// "1".parse::<i32>()? + "2".parse::<i32>()?
38/// };
39/// # assert_eq!(y, Ok(3));
40/// ```
41/// If you know that a block will have a specific type, it may aid type inference to use
42/// the macros [`try_opt`], [`try_res`], and [`try_cf`].
43///
44/// ## Alternative
45/// The only other way to emulate try blocks is with closures, which is very ugly.
46///
47/// #### Before:
48/// ```ignore
49/// let result: Result<T, E> = (|| {
50/// let a = do_one(x)?;
51/// let b = do_two(a)?;
52/// Ok(b)
53/// })();
54/// ```
55///
56/// #### After:
57/// ```
58/// # use try_blocks::try_block;
59/// # type T = (); type E = ();
60/// # fn do_one((): T) -> Result<T, E> { Ok(()) }
61/// # fn do_two((): T) -> Result<T, E> { Ok(()) }
62/// # let x = ();
63/// let result: Result<T, E> = try_block! {
64/// let a = do_one(x)?;
65/// let b = do_two(a)?;
66/// b
67/// };
68/// ```
69#[macro_export]
70macro_rules! try_block {
71 { $($token:tt)* } => {{
72 #[allow(unused_mut)]
73 let mut f = || $crate::wrap_ok!({ $($token)* });
74 f()
75 }}
76}
77
78/// Like [`try_block`], but specificially for [`Option`]. This aids type inference.
79#[macro_export]
80macro_rules! try_opt {
81 { $($token:tt)* } => {{
82 #[allow(unused_mut)]
83 let mut f = || ::core::option::Option::Some({ $($token)* });
84 f()
85 }};
86}
87
88/// Like [`try_block`], but specificially for [`Result`]]. This aids type inference.
89#[macro_export]
90macro_rules! try_res {
91 { $($token:tt)* } => {{
92 #[allow(unused_mut)]
93 let mut f = || ::core::result::Result::Ok({ $($token)* });
94 f()
95 }};
96}
97
98/// Like [`try_block`], but specificially for [`ControlFlow`]. This aids type inference.
99#[macro_export]
100macro_rules! try_cf {
101 { $($token:tt)* } => {
102 #[allow(unused_mut)]
103 let mut f = || ::core::ops::ControlFlow::Continue({ $($token)* });
104 f()
105 };
106}
107
108#[cfg(test)]
109mod tests {
110 #[test]
111 fn parse_sum() {
112 let result: Result<_, core::num::ParseIntError> = try_block! {
113 let x = "1".parse::<i32>()?;
114 let x = "2".parse::<i32>()? + x * 10;
115 "3".parse::<i32>()? + x * 10
116 };
117 assert_eq!(result, Ok(123));
118 }
119
120 #[test]
121 fn option() {
122 assert_eq!(
123 Some(520),
124 try_block! {
125 "400".parse::<i32>().ok()? + "20".parse::<i32>().ok()? * "6".parse::<i32>().ok()?
126 },
127 );
128 }
129
130 #[test]
131 fn named() {
132 try_opt! {
133 let x = "400".parse::<i32>().ok()?;
134 let x = x.checked_add(6_900_000)?;
135 assert_eq!(x, 6_900_400);
136 }
137 .unwrap();
138
139 let res: Result<_, ()> = try_res! {
140 1
141 };
142 assert_eq!(res, Ok(1));
143 }
144}