Skip to content

Commit 517053d

Browse files
authored
Merge pull request #1246 from sylbeth/match_class_no_last
`match_class!` fallback branch is now optional for `()`
2 parents 7137f2a + 03f45eb commit 517053d

File tree

2 files changed

+40
-16
lines changed

2 files changed

+40
-16
lines changed

godot-core/src/classes/match_class.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
/// The current implementation checks [`Gd::try_cast()`][crate::obj::Gd::try_cast] linearly with the number of branches.
1212
/// This may change in the future.
1313
///
14-
/// Requires a fallback branch, even if all direct known classes are handled. The reason for this is that there may be other subclasses which
15-
/// are not statically known by godot-rust (e.g. from a script or GDExtension). The fallback branch can either be `_` (discard object), or
16-
/// `variable` to access the original object inside the fallback arm.
14+
/// When none of the listed classes match, a _fallback branch_ acts as a catch-all and allows to retrieve the original `Gd` pointer.
15+
/// If the type of the `match_class!` expression is `()`, you can omit the fallback branch. For all other types, it is required, even if all
16+
/// direct subclasses are handled. The reason for this is that there may be other subclasses which are not statically known by godot-rust
17+
/// (e.g. from a script or GDExtension).
18+
///
19+
/// The fallback branch can either be `_` (discard object), or `variable` to access the original object inside the fallback arm.
1720
///
1821
/// # Example
1922
/// ```no_run
@@ -51,6 +54,7 @@
5154
/// original => 0,
5255
/// // Can also be used with mut:
5356
/// // mut original => 0,
57+
/// // If the match arms have type (), we can also omit the fallback branch.
5458
/// };
5559
///
5660
/// // event_type is now 0, 1, 2, or 3
@@ -75,14 +79,6 @@ macro_rules! match_class {
7579
#[doc(hidden)]
7680
#[macro_export]
7781
macro_rules! match_class_muncher {
78-
/*
79-
// If we want to support `variable @ _ => { ... }` syntax, use this branch first (to not match `_` as type).
80-
($subject:ident, $var:ident @ _ => $block:expr $(,)?) => {{
81-
let $var = $subject;
82-
$block
83-
}};
84-
*/
85-
8682
// mut variable @ Class => { ... }.
8783
($subject:ident, mut $var:ident @ $Ty:ty => $block:expr, $($rest:tt)*) => {{
8884
match $subject.try_cast::<$Ty>() {
@@ -115,8 +111,9 @@ macro_rules! match_class_muncher {
115111
$block
116112
}};
117113

118-
// _ => { ... }.
119-
($subject:ident, _ => $block:expr $(,)?) => {{
120-
$block
114+
// _ => { ... }
115+
// or nothing, if fallback is absent and overall expression being ().
116+
($subject:ident, $(_ => $block:expr $(,)?)?) => {{
117+
$($block)?
121118
}};
122119
}

itest/rust/src/engine_tests/match_class_test.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,9 @@ fn match_class_control_flow() {
148148

149149
let mut broken = false;
150150

151-
#[allow(clippy::never_loop)]
151+
#[expect(clippy::never_loop)]
152152
for _i in 0..1 {
153-
match_class! { obj.clone(),
153+
let _: i32 = match_class! { obj.clone(),
154154
_node @ Node => 1,
155155
_res @ Resource => {
156156
broken = true;
@@ -164,3 +164,30 @@ fn match_class_control_flow() {
164164

165165
assert!(broken, "break statement should have been executed");
166166
}
167+
168+
#[itest]
169+
fn match_class_unit_type() {
170+
let obj: Gd<Object> = Object::new_alloc();
171+
let to_free = obj.clone();
172+
let mut val = 0;
173+
174+
match_class! { obj,
175+
mut node @ Node2D => {
176+
require_mut_node2d(&mut node);
177+
val = 1;
178+
},
179+
node @ Node => {
180+
require_node(&node);
181+
val = 2;
182+
},
183+
// No need for _ branch since all branches return ().
184+
}
185+
186+
assert_eq!(val, 0);
187+
to_free.free();
188+
189+
// Special case: no branches at all. Also test unit type.
190+
let _: () = match_class! { RefCounted::new_gd(),
191+
// Nothing.
192+
};
193+
}

0 commit comments

Comments
 (0)