aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--21_errors.zig46
-rw-r--r--22_errors2.zig30
-rw-r--r--23_errors3.zig28
-rw-r--r--24_errors4.zig68
-rw-r--r--25_errors5.zig40
-rw-r--r--26_hello2.zig23
-rwxr-xr-xziglings6
7 files changed, 241 insertions, 0 deletions
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 " __ __ _ "