108_labeled_switch.zig 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. //
  2. // You've heard of while loops in exercises 011,012,013 and 014
  3. // You've also heard of switch expressions in exercises 030 and 31.
  4. // You've also seen how labels can be used in exercise 063.
  5. //
  6. // By combining while loops and switch statements with continue and break statements
  7. // one can create very concise State Machines.
  8. //
  9. // One such example would be:
  10. //
  11. // pub fn main() void {
  12. // var op: u8 = 1;
  13. // while (true) {
  14. // switch (op) {
  15. // 1 => { op = 2; continue; },
  16. // 2 => { op = 3; continue; },
  17. // 3 => return,
  18. // 4 => {},
  19. // }
  20. // break;
  21. // }
  22. // std.debug.print("This statement cannot be reached");
  23. // }
  24. //
  25. // By combining all we've learned so far, we can now proceed with a labeled switch
  26. //
  27. // A labeled switch is some extra syntatic sugar, which comes with all sorts of
  28. // candy (performance benefits). Don't believe me? Directly to source https://github.com/ziglang/zig/pull/21367
  29. //
  30. // Here is the previous excerpt implemented as a labeled switch instead:
  31. //
  32. // pub fn main() void {
  33. // foo: switch (@as(u8, 1)) {
  34. // 1 => continue :foo 2,
  35. // 2 => continue :foo 3,
  36. // 3 => return,
  37. // 4 => {},
  38. // else => {},
  39. // }
  40. // std.debug.print("This statement cannot be reached");
  41. // }
  42. //
  43. // The flow of execution on this second case is:
  44. // 1. The switch starts with value '1';
  45. // 2. The switch evaluates to case '1' which in turn uses the continue statement
  46. // to re-evaluate the labeled switch again, now providing the value '2';
  47. // 3. In the case '2' we repeat the same pattern as case '1'
  48. // but instead the value to be evaluated is now '3';
  49. // 4. Finally we get to case '3', where we return from the function as a whole.
  50. // 5. In this example as the input has no clear exhaustive patterns but a essentially
  51. // any u8 integer, we need do need to handle any case that is not explicitly handled
  52. // by using the `else => {}` branch as a default case.
  53. //
  54. // Since step 4 or a break stament do not exist in this switch, the debug statement is
  55. // never executed
  56. //
  57. const std = @import("std");
  58. const PullRequestState = enum(u8) {
  59. Draft,
  60. InReview,
  61. Approved,
  62. Rejected,
  63. Merged,
  64. };
  65. pub fn main() void {
  66. // Oh no, your pull request keeps being rejected,
  67. // how would you fix it?
  68. pr: switch (PullRequestState.Draft) {
  69. PullRequestState.Draft => continue :pr PullRequestState.InReview,
  70. PullRequestState.InReview => continue :pr PullRequestState.Rejected,
  71. PullRequestState.Approved => continue :pr PullRequestState.Merged,
  72. PullRequestState.Rejected => {
  73. std.debug.print("The pull request has been rejected.\n", .{});
  74. return;
  75. },
  76. PullRequestState.Merged => break, // Would you know where to break to?
  77. }
  78. std.debug.print("The pull request has been merged.\n", .{});
  79. }