Dave Gauer 4 лет назад
Родитель
Сommit
c70fa5f58f
8 измененных файлов с 271 добавлено и 5 удалено
  1. 25 0
      27_defer.zig
  2. 29 0
      28_defer2.zig
  3. 60 0
      29_errdefer.zig
  4. 55 0
      30_switch.zig
  5. 42 0
      31_switch2.zig
  6. 49 0
      32_iferror.zig
  7. 3 4
      README.md
  8. 8 1
      ziglings

+ 25 - 0
27_defer.zig

@@ -0,0 +1,25 @@
+//
+// You can assign some code to run _after_ a block of code exits by
+// deferring it with a "defer" statement:
+//
+//     {
+//         defer runLater();
+//         runNow();
+//     }
+//
+// In the example above, runLater() will run when the block ({...})
+// is finished. So the code above will run in the following order:
+//
+//     runNow();
+//     runLater();
+//
+// This feature seems strange at first, but we'll see how it could be
+// useful in the next exercise.
+const std = @import("std");
+
+pub fn main() void {
+    // Without changing anything else, please add a 'defer' statement
+    // to this code so that our program prints "One Two\n":
+    std.debug.print("Two\n", .{});
+    std.debug.print("One ", .{});
+}

+ 29 - 0
28_defer2.zig

@@ -0,0 +1,29 @@
+//
+// Now that you know how "defer" works, let's do something more
+// interesting with it.
+//
+const std = @import("std");
+
+pub fn main() void {
+    const animals = [_]u8{ 'g', 'c', 'd', 'd', 'g', 'z' };
+
+    for (animals) |a| printAnimal(a);
+
+    
+    std.debug.print("done.\n", .{});
+}
+
+// This function is _supposed_ to print an animal name in parentheses
+// like "(Goat) ", but we somehow need to print the end parenthesis
+// even though this function can return in four different places!
+fn printAnimal(animal: u8) void {
+    std.debug.print("(", .{});
+
+    std.debug.print(") ", .{}); // <---- how!?
+
+    if (animal == 'g'){ std.debug.print("Goat", .{}); return; }
+    if (animal == 'c'){ std.debug.print("Cat", .{}); return; }
+    if (animal == 'd'){ std.debug.print("Dog", .{}); return; }
+
+    std.debug.print("Unknown", .{});
+}

+ 60 - 0
29_errdefer.zig

@@ -0,0 +1,60 @@
+//
+// Another common problem is a block of code that could exit in multiple
+// places due to an error - but that needs to run do something before it
+// exits (typically to clean up after itself).
+//
+// An "errdefer" is a defer that only runs if the block exits with an error:
+//
+//     {
+//         errdefer cleanup();
+//         try canFail();
+//     }
+//
+// The cleanup() function is called ONLY if the "try" statement returns an
+// error produced by canFail().
+//
+const std = @import("std");
+
+//
+var counter: u32 = 0;
+
+const MyErr = error{ GetFail, IncFail };
+
+pub fn main() void {
+    // We simply quit the entire program if we fail to get a number:
+    var a: u32 = makeNumber() catch return;
+    var b: u32 = makeNumber() catch return;
+
+    std.debug.print("Numbers: {}, {}\n", .{a,b});
+}    
+
+fn makeNumber() MyErr!u32 {
+    std.debug.print("Getting number...", .{});
+
+    // Please make the "failed" message print ONLY if the makeNumber()
+    // function exits with an error:
+    std.debug.print("failed!\n", .{});
+
+    var num = try getNumber();     // <-- This could fail!
+
+    num = try increaseNumber(num); // <-- This could ALSO fail!
+
+    std.debug.print("got {}. ", .{num});
+
+    return num;
+}
+
+fn getNumber() MyErr!u32 {
+    // I _could_ fail...but I don't!
+    return 4;
+}
+
+fn increaseNumber(n: u32) MyErr!u32 {
+    // I fail after the first time you run me!
+    if (counter > 0) return MyErr.IncFail;
+    
+    // Sneaky, weird global stuff.
+    counter += 1;
+
+    return n + 1;
+}

+ 55 - 0
30_switch.zig

@@ -0,0 +1,55 @@
+//
+// The "switch" statement lets you match the possible values of an
+// expression and perform a different action for each.
+//
+// This switch:
+//
+//     switch (players) {
+//         1 => startOnePlayerGame(),
+//         2 => startTwoPlayerGame(),
+//         else => {
+//             alert();
+//             return GameError.TooManyPlayers;
+//         }
+//     }
+//
+// Is equivalent to this if/else:
+//
+//     if (players == 1) startOnePlayerGame();
+//     else if (players == 2) startTwoPlayerGame();
+//     else {
+//         alert();
+//         return GameError.TooManyPlayers;
+//     }
+//     
+//
+//
+const std = @import("std");
+
+pub fn main() void {
+    const lang_chars = [_]u8{ 26, 9, 7, 42 };
+
+    for (lang_chars) |c| {
+        switch (c) {
+            1  => std.debug.print("A", .{}),
+            2  => std.debug.print("B", .{}),
+            3  => std.debug.print("C", .{}),
+            4  => std.debug.print("D", .{}),
+            5  => std.debug.print("E", .{}),
+            6  => std.debug.print("F", .{}),
+            7  => std.debug.print("G", .{}),
+            8  => std.debug.print("H", .{}),
+            9  => std.debug.print("I", .{}),
+            10 => std.debug.print("J", .{}),
+            // ... we don't need everything in between ...
+            25 => std.debug.print("Y", .{}),
+            26 => std.debug.print("Z", .{}),
+            // Switch statements must be "exhaustive" (there must be a
+            // match for every possible value).  Please add an "else"
+            // to this switch to print a question mark "?" when c is
+            // not one of the existing matches.
+        }
+    }
+
+    std.debug.print("\n", .{});
+}

+ 42 - 0
31_switch2.zig

@@ -0,0 +1,42 @@
+//
+// What's really nice is that you can use a switch statement as an
+// expression to return a value.
+//
+//     var a = switch (x) {
+//         1 => 9,
+//         2 => 16,
+//         3 => 7,
+//         ...
+//     }
+//
+const std = @import("std");
+
+pub fn main() void {
+    const lang_chars = [_]u8{ 26, 9, 7, 42 };
+
+    for (lang_chars) |c| {
+        var real_char: u8 = switch (c) {
+            1  => 'A',
+            2  => 'B',
+            3  => 'C',
+            4  => 'D',
+            5  => 'E',
+            6  => 'F',
+            7  => 'G',
+            8  => 'H',
+            9  => 'I',
+            10 => 'J',
+            // ...
+            25 => 'Y',
+            26 => 'Z',
+            // As in the last exercise, please add the "else" clause
+            // and this time, have it return an exclamation mark "!".
+        };
+
+        std.debug.print("{c}", .{real_char});
+        // Note: "{c}" forces print() to display the value as a character.
+        // Can you guess what happens if you remove the "c"? Try it!
+    }
+
+    std.debug.print("\n", .{});
+}

+ 49 - 0
32_iferror.zig

@@ -0,0 +1,49 @@
+//
+// Let's revisit the very first error exercise. This time, we're going to
+// look at a special error-handling type of the "if" statement.
+//
+//     if (foo) |value| {
+//
+//         // foo was NOT an error; value is the non-error value of foo
+//
+//     } else |err| {
+//
+//         // foo WAS an error; err is the error value of foo
+//
+//     }
+//
+// We'll take it even further and use a switch statement to handle
+// the error types.
+//
+const MyNumberError = error{
+    TooBig,
+    TooSmall,
+};
+
+const std = @import("std");
+
+pub fn main() void {
+    var nums = [_]u8{2,3,4,5,6};
+
+    for (nums) |num| {
+        std.debug.print("{}", .{num});
+
+        var n = numberMaybeFail(num);
+        if (n) |value| {
+            std.debug.print("=4. ", .{});
+        } else |err| switch (err) {
+            MyNumberError.TooBig   => std.debug.print(">4. ", .{}),
+            // Please add a match for TooSmall here and have it print: "<4. "
+        }
+    }
+
+    std.debug.print("\n", .{});
+}
+
+// This time we'll have numberMaybeFail() return an error union rather
+// than a straight error.
+fn numberMaybeFail(n: u8) MyNumberError!u8 {
+    if(n > 4) return MyNumberError.TooBig;
+    if(n < 4) return MyNumberError.TooSmall;
+    return n;
+}

+ 3 - 4
README.md

@@ -66,10 +66,9 @@ Planned exercises:
 * [x] While
 * [x] For
 * [x] Functions
-* [ ] Errors
-* [ ] Defer
-* [ ] Switch
-* [ ] Runtime safety
+* [x] Errors
+* [x] Defer
+* [x] Switch
 * [ ] Unreachable
 * [ ] Pointers
 * [ ] Pointer sized integers

+ 8 - 1
ziglings

@@ -54,7 +54,7 @@ function check_it {
     fi
 
     # Wildcards to be lenient with anything AROUND the correct output
-    if [[ "$result" == *$correct_output* ]]
+    if [[ "$result" == *"$correct_output"* ]]
     then
         printf "${fmt_yay}** PASSED **${fmt_off}\n"
     else
@@ -94,6 +94,13 @@ check_it 23_errors3.zig "a=64, b=22"
 check_it 24_errors4.zig "a=20, b=14, c=10"
 check_it 25_errors5.zig "a=0, b=19, c=0"
 check_it 26_hello2.zig "Hello world" "Try using a try!"
+check_it 27_defer.zig "One Two"
+check_it 28_defer2.zig "(Goat) (Cat) (Dog) (Dog) (Goat) (Unknown) done."
+check_it 29_errdefer.zig "Getting number...got 5. Getting number...failed!"
+check_it 30_switch.zig "ZIG?"
+check_it 31_switch2.zig "ZIG!"
+check_it 32_iferror.zig "2<4. 3<4. 4=4. 5>4. 6>4." "Seriously, what's the deal with fours?"
+#check_it 33_quiz4.zig "foo" "Can you make this work?"
 
 echo
 echo "    __   __          _ "