Skip to content

Commit 4950789

Browse files
committed
feat: unstably support partial application
1 parent 9dd7a78 commit 4950789

File tree

5 files changed

+143
-4
lines changed

5 files changed

+143
-4
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ jobs:
3232
features:
3333
- ""
3434
- "implicit_map"
35+
- "unstable"
36+
- "unstable,implicit_map"
3537

3638
fail-fast: false
3739
steps:
@@ -42,7 +44,7 @@ jobs:
4244
toolchain: ${{ matrix.toolchain }}
4345

4446
- name: Skip doctests if not fully-featured
45-
if: ${{ matrix.features != 'implicit_map' }}
47+
if: ${{ !endsWith(matrix.features, 'implicit_map') }}
4648
run: echo "test_param=--lib --tests" >> $GITHUB_ENV
4749

4850
- name: cargo build

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ categories = ["rust-patterns", "no-std"]
1515
[features]
1616
default = ["implicit_map"]
1717
implicit_map = ["try_match_inner"]
18+
unstable = []
1819
std = []
1920

2021
[dependencies]

src/lib.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,113 @@
6868
//! let _ = try_match!(Var1((12, 34)), Var1((_0, _2)));
6969
//! ```
7070
//!
71+
//! # Unstable Features
72+
//!
73+
//! *Requires `unstable` Cargo feature, exempt from semver guarantees.*
74+
//!
75+
//! <details><summary><h3>Partial Application</h3></summary>
76+
//!
77+
//! Tracking issue: [#3](https://github.com/yvt/try_match-rs/issues/3)
78+
//!
79+
//! Omit the scrutinee expression to produce a closure:
80+
//!
81+
//! ```rust
82+
//! # {
83+
//! #![cfg(feature = "unstable")]
84+
//! # use try_match::try_match;
85+
//! # #[derive(Debug, PartialEq)] enum Enum<T> { Var1(T), Var2 }
86+
//! # use Enum::{Var1, Var2};
87+
//! assert_eq!(try_match!(Var1(42), Var1(x)), Ok(42));
88+
//! // ^^^^^^^^ -------
89+
//! assert_eq!(try_match!(, Var1(x))(Var1(42)), Ok(42));
90+
//! // ------- ^^^^^^^^
91+
//!
92+
//! // Equivalent to:
93+
//! assert_eq!((|x| try_match!(x, Var1(x)))(Var1(42)), Ok(42));
94+
//! // ------- ^^^^^^^^
95+
//! # }
96+
//! ```
97+
//!
98+
//! ```rust
99+
//! # {
100+
//! #![cfg(feature = "unstable")]
101+
//! # use try_match::try_match;
102+
//! # #[derive(Debug, PartialEq)] enum Enum<T> { Var1(T), Var2 }
103+
//! # use Enum::{Var1, Var2};
104+
//! let array = [Var1(42), Var2, Var1(10)];
105+
//! let filtered: Result<Vec<_>, _> = array
106+
//! .iter()
107+
//! .map(try_match!(, &Var1(_0) if _0 > 20))
108+
//! .collect();
109+
//!
110+
//! // `Var2` is the first value that doesn't match
111+
//! assert_eq!(filtered, Err(&Var2));
112+
//! # }
113+
//! ```
114+
//!
115+
//! *Caveat:* Since this mode is implemented by a closure,
116+
//! [the default binding mode][] ([RFC 2005][]), ownership, and control flow
117+
//! may work differently:
118+
//!
119+
//! ```rust
120+
//! # {
121+
//! # #![cfg(feature = "unstable")]
122+
//! # use try_match::try_match;
123+
//! try_match!(&Some(42), Some(_0));
124+
//! try_match!(&Some(42), &Some(ref _0));
125+
//! # }
126+
//! ```
127+
//!
128+
//! ```rust,compile_fail
129+
//! # use try_match::try_match;
130+
//! try_match!(, Some(_0))(&Some(42));
131+
//! // ERROR: expected enum `Option`, found reference
132+
//! ```
133+
//!
134+
//! ```rust
135+
//! # {
136+
//! # #![cfg(feature = "unstable")]
137+
//! # use try_match::try_match;
138+
//! use std::rc::Rc;
139+
//!
140+
//! // `rc2` is conditionally dropped
141+
//! let rc1 = Rc::new(());
142+
//! let rc2 = Rc::clone(&rc1);
143+
//! try_match!(None::<()>, Some(_) => drop(rc2));
144+
//! assert_eq!(Rc::strong_count(&rc1), 2);
145+
//!
146+
//! // `rc2` is unconditionally moved into a closure and dropped
147+
//! let rc1 = Rc::new(());
148+
//! let rc2 = Rc::clone(&rc1);
149+
//! try_match!(, Some(_) => drop(rc2))(None::<()>);
150+
//! assert_eq!(Rc::strong_count(&rc1), 1);
151+
//! # }
152+
//! ```
153+
//!
154+
//! ```rust
155+
//! # {
156+
//! # #![cfg(feature = "unstable")]
157+
//! # use try_match::try_match;
158+
//! fn func_uncurried() {
159+
//! try_match!((), () => return);
160+
//! unreachable!();
161+
//! }
162+
//!
163+
//! fn func_curried() -> i32 {
164+
//! try_match!(, () => return Ok(()))(());
165+
//! 42 // reachable
166+
//! }
167+
//!
168+
//! func_uncurried();
169+
//! func_curried();
170+
//! # }
171+
//! ```
172+
//!
173+
//! [the default binding mode]: https://doc.rust-lang.org/1.69.0/reference/patterns.html#binding-modes
174+
//! [RFC 2005]: https://rust-lang.github.io/rfcs/2005-match-ergonomics.html
175+
//!
176+
//! </details>
177+
//!
71178
//! # Quirks
72179
//!
73180
//! When using implicit mapping, bind variables defined inside macros are
@@ -218,6 +325,14 @@ macro_rules! try_match {
218325
($in:expr, $(|)? $($p:pat)|+ $(if $guard:expr)?) => {
219326
$crate::implicit_try_match!($in, $($p)|+ $(if $guard)?)
220327
};
328+
329+
// Partial application (requires `unstable` Cargo feature)
330+
(, $($pattern_and_rest:tt)*) => {
331+
$crate::assert_unstable!(
332+
["partial application"]
333+
|scrutinee| $crate::try_match!(scrutinee, $($pattern_and_rest)*)
334+
)
335+
}
221336
}
222337

223338
#[cfg(feature = "implicit_map")]
@@ -241,6 +356,24 @@ macro_rules! implicit_try_match {
241356
};
242357
}
243358

359+
#[cfg(feature = "unstable")]
360+
#[macro_export]
361+
#[doc(hidden)]
362+
macro_rules! assert_unstable {
363+
([$($msg:tt)*] $($tt:tt)*) => {
364+
$($tt)*
365+
}
366+
}
367+
368+
#[cfg(not(feature = "unstable"))]
369+
#[macro_export]
370+
#[doc(hidden)]
371+
macro_rules! assert_unstable {
372+
([$($msg:tt)*] $($tt:tt)*) => {
373+
compile_error!(concat!($($msg)*, " requires `unstable` Cargo feature"))
374+
}
375+
}
376+
244377
/// Pattern: `$p:pat`
245378
///
246379
/// The produced expression evaluates to `Ok(_)` using bound variables on a
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
error: no rules expected the token `,`
2-
--> tests/compile-fail/reserved/try_match_partial.rs:4:35
1+
error: partial application requires `unstable` Cargo feature
2+
--> tests/compile-fail/reserved/try_match_partial.rs:4:13
33
|
44
4 | let _ = try_match::try_match!(, Some(_0))(Some(42));
5-
| ^ no rules expected this token in macro call
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this error originates in the macro `$crate::assert_unstable` which comes from the expansion of the macro `try_match::try_match` (in Nightly builds, run with -Z macro-backtrace for more info)

tests/compile.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn fail_no_implicit_map() {
1212

1313
#[test]
1414
#[cfg(feature = "implicit_map")]
15+
#[cfg(not(feature = "unstable"))]
1516
fn fail_reserved() {
1617
trybuild::TestCases::new().compile_fail("tests/compile-fail/reserved/**/*.rs");
1718
}

0 commit comments

Comments
 (0)