summaryrefslogtreecommitdiff
path: root/exercises/111_labeled_switch.zig
diff options
context:
space:
mode:
authorChris Boesch <chrboesch@noreply.codeberg.org>2026-04-03 13:35:56 +0200
committerChris Boesch <chrboesch@noreply.codeberg.org>2026-04-03 13:35:56 +0200
commite0259f43a726f61da14686de802021fcdb9aacd0 (patch)
tree57359102e5e898289b91ada2d65ca742ec118c8b /exercises/111_labeled_switch.zig
parentffde357f303e7459a12cfe4b785ae9e8ef9ebe30 (diff)
Insert space for additional async exercises
Diffstat (limited to 'exercises/111_labeled_switch.zig')
-rw-r--r--exercises/111_labeled_switch.zig79
1 files changed, 79 insertions, 0 deletions
diff --git a/exercises/111_labeled_switch.zig b/exercises/111_labeled_switch.zig
new file mode 100644
index 0000000..897fcf5
--- /dev/null
+++ b/exercises/111_labeled_switch.zig
@@ -0,0 +1,79 @@
+//
+// You've heard of while loops in exercises 011,012,013 and 014
+// You've also heard of switch expressions in exercises 030 and 31.
+// You've also seen how labels can be used in exercise 063.
+//
+// By combining while loops and switch statements with continue and break statements
+// one can create very concise State Machines.
+//
+// One such example would be:
+//
+// pub fn main() void {
+// var op: u8 = 1;
+// while (true) {
+// switch (op) {
+// 1 => { op = 2; continue; },
+// 2 => { op = 3; continue; },
+// 3 => return,
+// else => {},
+// }
+// break;
+// }
+// std.debug.print("This statement cannot be reached\n", .{});
+// }
+//
+// By combining all we've learned so far, we can now proceed with a labeled switch.
+//
+// A labeled switch is some extra syntactic sugar, which comes with all sorts of
+// candy (performance benefits). Don't believe me? Directly to source https://github.com/ziglang/zig/pull/21367
+//
+// Here is the previous excerpt implemented as a labeled switch instead:
+//
+// pub fn main() void {
+// foo: switch (@as(u8, 1)) {
+// 1 => continue :foo 2,
+// 2 => continue :foo 3,
+// 3 => return,
+// else => {},
+// }
+// std.debug.print("This statement cannot be reached\n", .{});
+// }
+//
+// The flow of execution on this second case is:
+// 1. The switch starts with value '1';
+// 2. The switch evaluates to case '1' which in turn uses the continue statement
+// to re-evaluate the labeled switch again, now providing the value '2';
+// 3. In the case '2' we repeat the same pattern as case '1'
+// but instead the value to be evaluated is now '3';
+// 4. Finally we get to case '3', where we return from the function as a whole,
+// so the debug statement is never executed.
+// 5. In this example, since the input does not have clear, exhaustive patterns and
+// can essentially be any 'u8' integer, we need to handle all cases not explicitly
+// covered by using the 'else => {}' branch as the default case.
+//
+//
+const std = @import("std");
+
+const PullRequestState = enum(u8) {
+ Draft,
+ InReview,
+ Approved,
+ Rejected,
+ Merged,
+};
+
+pub fn main() void {
+ // Oh no, your pull request keeps being rejected,
+ // how would you fix it?
+ pr: switch (PullRequestState.Draft) {
+ PullRequestState.Draft => continue :pr PullRequestState.InReview,
+ PullRequestState.InReview => continue :pr PullRequestState.Rejected,
+ PullRequestState.Approved => continue :pr PullRequestState.Merged,
+ PullRequestState.Rejected => {
+ std.debug.print("The pull request has been rejected.\n", .{});
+ return;
+ },
+ PullRequestState.Merged => break, // Would you know where to break to?
+ }
+ std.debug.print("The pull request has been merged.\n", .{});
+}