Browse Source

Added ex 21-26 for error handling

Dave Gauer 4 years ago
parent
commit
2de8a8c54d
7 changed files with 241 additions and 0 deletions
  1. 46 0
      21_errors.zig
  2. 30 0
      22_errors2.zig
  3. 28 0
      23_errors3.zig
  4. 68 0
      24_errors4.zig
  5. 40 0
      25_errors5.zig
  6. 23 0
      26_hello2.zig
  7. 6 0
      ziglings

+ 46 - 0
21_errors.zig

@@ -0,0 +1,46 @@
+//
+// Believe it or not, sometimes things to wrong in programs.
+//
+// In Zig, an error is a value. Errors are named so we can identify
+// things that can go wrong. Errors are created in "error sets", which
+// are just a collection of named errors.
+// 
+// We have the start of an error set, but we're missing the condition
+// "TooSmall". Please add it where needed!
+const MyNumberError = error{
+    TooBig,
+    ???,
+    TooFour,
+};
+
+const std = @import("std");
+
+pub fn main() void {
+    var nums = [_]u8{2,3,4,5,6};
+
+    for (nums) |n| {
+        std.debug.print("{}", .{n});
+
+        const number_error = numberFail(n);
+        
+        if (number_error == MyNumberError.TooBig) {
+            std.debug.print(">4. ", .{});
+        }
+        if (???) {
+            std.debug.print("<4. ", .{});
+        }
+        if (number_error == MyNumberError.TooFour) {
+            std.debug.print("=4. ", .{});
+        }
+    }
+
+    std.debug.print("\n", .{});
+}
+
+// Notice how this function can return any member of the MyNumberError
+// error set.
+fn numberFail(n: u8) MyNumberError {
+    if(n > 4) return MyNumberError.TooBig;
+    if(n < 4) return MyNumberError.TooSmall; // <---- this one is free!
+    return MyNumberError.TooFour;
+}

+ 30 - 0
22_errors2.zig

@@ -0,0 +1,30 @@
+//
+// A common case for errors is a situation where we're expecting to
+// have a value OR something has gone wrong. Take this example:
+//
+//     var text: Text = getText('foo.txt');
+//
+// What happens if getText() can't find 'foo.txt'?  How do we express
+// this in Zig?
+//
+// Zig let's us make what's called an "error union" which is a value
+// which could either be a regular value OR an error from a set:
+//
+//     var text: MyErrorSet!Text =  getText('foo.txt');
+//
+// For now, let's just see if we can try making an error union!
+//
+const std = @import("std");
+
+const MyNumberError = error{ TooSmall };
+
+pub fn main() void {
+    var my_number: ??? = 5;
+
+    // Looks like my_number will need to either store a number OR
+    // an error. Can you set the type correctly above?
+    my_number = MyNumberError.TooSmall;
+
+    std.debug.print("I compiled!", .{});
+}
+

+ 28 - 0
23_errors3.zig

@@ -0,0 +1,28 @@
+//
+// One way to deal with error unions is to "catch" any error and
+// replace it with a default value.
+//
+//     foo = canFail() catch 6;
+//
+// If canFail() fails, foo will equal 6.
+//
+const std = @import("std");
+
+const MyNumberError = error{ TooSmall };
+
+pub fn main() void {
+    var a: u32 = addTwenty(44) catch 22;
+    var b: u32 = addTwenty(4)  ???   22;
+
+    std.debug.print("a={}, b={}", .{a,b});
+}
+
+// Please provide the return type from this function.
+// Hint: it'll be an error union.
+fn addTwenty(n: u32) ??? {
+    if (n < 5) {
+        return MyNumberError.TooSmall;
+    } else {
+        return n + 20;
+    }
+}

+ 68 - 0
24_errors4.zig

@@ -0,0 +1,68 @@
+//
+// Using `catch` to replace an error with a default value is a bit
+// of a blunt instrument since it doesn't matter what the error is.
+//
+// Catch lets us capture the error value and perform additional
+// actions with this form:
+//
+//     canFail() catch |err| {
+//         if (err == FishError.TunaMalfunction) {
+//             ...
+//         }
+//     };
+//
+const std = @import("std");
+
+const MyNumberError = error{
+    TooSmall,
+    TooBig,
+};
+
+pub fn main() void {
+    // The "catch 0" below is just our way of dealing with the fact
+    // that makeJustRight() returns a error union (for now).
+    var a: u32 = makeJustRight(44) catch 0;
+    var b: u32 = makeJustRight(14) catch 0;
+    var c: u32 = makeJustRight(4)  catch 0;
+
+    std.debug.print("a={}, b={}, c={}", .{a,b,c});
+}
+
+// In this silly example we've split the responsibility of making
+// a number just right into four (!) functions:
+//
+//     makeJustRight()   Calls fixTooBig(), cannot fix any errors.
+//     fixTooBig()       Calls fixTooSmall(), fixes TooBig errors.
+//     fixTooSmall()     Calls detectProblems(), fixes TooSmall errors.
+//     detectProblems()  Returns the number or an error.
+//
+fn makeJustRight(n: u32) MyNumberError!u32 {
+    return fixTooBig(n) catch |err| { return err; };
+}
+
+fn fixTooBig(n: u32) MyNumberError!u32 {
+    return fixTooSmall(n) catch |err| {
+        if (err == MyNumberError.TooBig) {
+            return 20;
+        }
+        
+        return err;
+    };
+}
+
+fn fixTooSmall(n: u32) MyNumberError!u32 {
+    // Oh dear, this is missing a lot! But don't worry, it's nearly
+    // identical to fixTooBig() above. 
+    //
+    // If we get a TooSmall error, we should return 10.
+    // If we get any other error, we should return that error.
+    // Otherwise, we return the u32 number.
+    return detectProblems(n) ??? 
+}
+
+fn detectProblems(n: u32) MyNumberError!u32 {
+    if (n < 10) return MyNumberError.TooSmall;
+    if (n > 20) return MyNumberError.TooBig;
+    return n;
+}
+

+ 40 - 0
25_errors5.zig

@@ -0,0 +1,40 @@
+//
+// Zig has a handy "try" shortcut for this common error handling pattern:
+//
+//     canFail() catch |err| return err;
+//
+// which can be more compactly written as:
+//
+//     try canFail();
+//
+const std = @import("std");
+
+const MyNumberError = error{
+    TooSmall,
+    TooBig,
+};
+
+pub fn main() void {
+    var a: u32 = addFive(44) catch 0;
+    var b: u32 = addFive(14) catch 0;
+    var c: u32 = addFive(4)  catch 0;
+
+    std.debug.print("a={}, b={}, c={}", .{a,b,c});
+}
+
+fn addFive(n: u32) MyNumberError!u32 {
+    //
+    // This function needs to return any error which might come back from fix().
+    // Please use a "try" statement rather than a "catch".
+    //
+    var x = detect(n);
+
+    return x + 5;
+}
+
+fn detect(n: u32) MyNumberError!u32 {
+    if (n < 10) return MyNumberError.TooSmall;
+    if (n > 20) return MyNumberError.TooBig;
+    return n;
+}
+

+ 23 - 0
26_hello2.zig

@@ -0,0 +1,23 @@
+//
+// Great news! Now we know enough to understand a "real" Hello World
+// program in Zig - one that uses the system Standard Out resource...which
+// can fail!
+//
+const std = @import("std");
+
+// Take note that this main() definition now returns "!void" rather
+// than just "void". Since there's no specific error type, this means
+// that Zig will infer the error type. This is appropriate in the case
+// of main(), but can have consequences elsewhere.
+pub fn main() !void {
+
+    // We get a Writer for Standard Out so we can print() to it.
+    const stdout = std.io.getStdOut().writer();
+
+    // Unlike std.debug.print(), the Standard Out writer can fail
+    // with an error. We don't care _what_ the error is, we want
+    // to be able to pass it up as a return value of main().
+    //
+    // We just learned of a single statement which can accomplish this.
+    stdout.print("Hello world!\n", .{});
+}

+ 6 - 0
ziglings

@@ -88,6 +88,12 @@ check_it 17_quiz2.zig "8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16" "This is a
 check_it 18_functions.zig "Question: 42" "Can you help write the function?"
 check_it 18_functions.zig "Question: 42" "Can you help write the function?"
 check_it 19_functions2.zig "2 4 8 16"
 check_it 19_functions2.zig "2 4 8 16"
 check_it 20_quiz3.zig "32 64 128 256" "Unexpected pop quiz! Help!"
 check_it 20_quiz3.zig "32 64 128 256" "Unexpected pop quiz! Help!"
+check_it 21_errors.zig "2<4. 3<4. 4=4. 5>4. 6>4." "What's the deal with fours?"
+check_it 22_errors2.zig "I compiled" "Get the error union type right to allow this to compile."
+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!"
 
 
 echo
 echo
 echo "    __   __          _ "
 echo "    __   __          _ "