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}