From 2de8a8c54d7090dd063bed8b6b283c2fcb452e43 Mon Sep 17 00:00:00 2001 From: Dave Gauer Date: Sat, 30 Jan 2021 20:00:32 -0500 Subject: [PATCH] Added ex 21-26 for error handling --- 21_errors.zig | 46 ++++++++++++++++++++++++++++++++++ 22_errors2.zig | 30 ++++++++++++++++++++++ 23_errors3.zig | 28 +++++++++++++++++++++ 24_errors4.zig | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 25_errors5.zig | 40 +++++++++++++++++++++++++++++ 26_hello2.zig | 23 +++++++++++++++++ ziglings | 6 +++++ 7 files changed, 241 insertions(+) create mode 100644 21_errors.zig create mode 100644 22_errors2.zig create mode 100644 23_errors3.zig create mode 100644 24_errors4.zig create mode 100644 25_errors5.zig create mode 100644 26_hello2.zig diff --git a/21_errors.zig b/21_errors.zig new file mode 100644 index 0000000..34c5e18 --- /dev/null +++ b/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; +} diff --git a/22_errors2.zig b/22_errors2.zig new file mode 100644 index 0000000..fcfd391 --- /dev/null +++ b/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!", .{}); +} + diff --git a/23_errors3.zig b/23_errors3.zig new file mode 100644 index 0000000..6060bf1 --- /dev/null +++ b/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; + } +} diff --git a/24_errors4.zig b/24_errors4.zig new file mode 100644 index 0000000..b60cc2d --- /dev/null +++ b/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; +} + diff --git a/25_errors5.zig b/25_errors5.zig new file mode 100644 index 0000000..d9e9ce1 --- /dev/null +++ b/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; +} + diff --git a/26_hello2.zig b/26_hello2.zig new file mode 100644 index 0000000..237d27c --- /dev/null +++ b/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", .{}); +} diff --git a/ziglings b/ziglings index b1caa5c..26e2ee3 100755 --- a/ziglings +++ b/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 19_functions2.zig "2 4 8 16" 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 " __ __ _ "