From e394029a24caf0e5dfb76c1545ab130f2e409ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Krzy=C5=BCanowski?= Date: Sat, 31 Aug 2024 00:11:05 +0200 Subject: [PATCH] Add Zig Error Handling Example --- zig-error-handling/.gitignore | 2 + zig-error-handling/build.zig | 26 +++++++++++ zig-error-handling/src/main.zig | 77 +++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 zig-error-handling/.gitignore create mode 100644 zig-error-handling/build.zig create mode 100644 zig-error-handling/src/main.zig diff --git a/zig-error-handling/.gitignore b/zig-error-handling/.gitignore new file mode 100644 index 0000000..dca1103 --- /dev/null +++ b/zig-error-handling/.gitignore @@ -0,0 +1,2 @@ +zig-out/ +.zig-cache/ diff --git a/zig-error-handling/build.zig b/zig-error-handling/build.zig new file mode 100644 index 0000000..dedaf42 --- /dev/null +++ b/zig-error-handling/build.zig @@ -0,0 +1,26 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const exe = b.addExecutable(.{ + .name = "zig-error-handling", + .root_source_file = b.path("src/main.zig"), + .target = b.host, + }); + + b.installArtifact(exe); + + const run_exe = b.addRunArtifact(exe); + const run_step = b.step("run", "Run the application"); + + run_step.dependOn(&run_exe.step); + + const unit_tests = b.addTest(.{ + .root_source_file = b.path("src/main.zig"), + .target = b.host, + }); + + const test_step = b.step("test", "Run unit tests"); + const run_unit_tests = b.addRunArtifact(unit_tests); + + test_step.dependOn(&run_unit_tests.step); +} diff --git a/zig-error-handling/src/main.zig b/zig-error-handling/src/main.zig new file mode 100644 index 0000000..9d551bf --- /dev/null +++ b/zig-error-handling/src/main.zig @@ -0,0 +1,77 @@ +const std = @import("std"); + +const AddNumbersError = error{ + InequalLengths, + GuardOverflow, +}; + +// This functions performs saturated addition of two u8 (unsigned 8-bit integer) slices, +// but if any addition result is more than guard then it returns GuardOverflow error instead. +pub fn numbers_magic(allocator: std.mem.Allocator, a: []const u8, b: []const u8, guard: u8) ![]u8 { + if (a.len != b.len) { + return error.InequalLengths; + } + + var result = try allocator.alloc(u8, a.len); + // errdefer is called only when error is returned, so when there is no error normal result will + // be returned to the caller (it should not be deallocated in every case, because it would result + // in segmentation fault) + errdefer allocator.free(result); + + for (0.., result) |idx, _| { + // Saturated addition to avoid integer overflow panic. + result[idx] = a[idx] +| b[idx]; + + if (result[idx] > guard) { + return error.GuardOverflow; + } + } + + return result; +} + +pub fn main() !void { + std.debug.print( + "This example does not offer runnable code other than tests. Use zig build test to run them :)\n", + .{}, + ); +} + +// This numbers_magic call will not generate any error, so try is used. +// The result needs to be freed, because there was no error. +test "numers magic no error, no memory leak" { + const result = try numbers_magic( + std.testing.allocator, + &.{ 1, 2, 3, 4, 5, 6 }, + &.{ 2, 1, 3, 7, 4, 2 }, + 50, + ); + defer std.testing.allocator.free(result); +} + +// Numbers magic call is expected to return error (40 + 17 > 50), no deallocation is needed here. +// and it does not result in any memory leak, because of errdefer inside numbers_magic, +// which cleans up in that case. +test "numbers magic error, no memory leak" { + const result = numbers_magic( + std.testing.allocator, + &.{ 10, 20, 30, 40, 45, 43 }, + &.{ 12, 11, 13, 17, 14, 12 }, + 50, + ); + + try std.testing.expect(result == error.GuardOverflow); +} + +// Just to show that there will be memory leak if function succeeded (no error returned). +// Errdefer inside numbers_magic won't clean up in this case, so without using +// std.testing.allocator.free(...) this will leak memory. +// This test will fail, there should be information about this in output e.g. "1 leaked" +test "numbers magic no error, memory leak" { + _ = try numbers_magic( + std.testing.allocator, + &.{ 1, 2, 3, 4, 5, 6 }, + &.{ 2, 1, 3, 7, 4, 2 }, + 50, + ); +}