Compare commits

..

No commits in common. "main" and "v0.12.0" have entirely different histories.

62 changed files with 382 additions and 1303 deletions

View File

@ -1,5 +0,0 @@
Ziglings is a progressive learning series — each exercise builds on previous ones.
Before opening an issue, please ensure you've followed the path and read the instructions carefully.
Respectful and constructive feedback is always welcome.

6
.gitignore vendored
View File

@ -1,9 +1,5 @@
/.zig-cache/
/zig-cache/
/zig-out/
/answers/
/patches/healed/
/output/
.progress.txt
# Leave this in here for older zig versions
/zig-cache/

View File

@ -1,9 +1,8 @@
steps:
- name: eowyn
image: ziglings/ziglang:latest
pull: true
image: ziglings/ziglang
commands:
- sh ./patches/eowyn.sh
when:
event: [pull_request, push, cron]
event: [push, cron]
cron: daily*

View File

@ -40,7 +40,6 @@ fit for one reason or another.
## Platforms and Zig Versions
Because it uses the Zig build system, Ziglings should work
wherever Zig does.
@ -76,15 +75,6 @@ interface. Specifically:
eternal Ziglings contributor glory is yours!
## Licence
If you submit your contribution to the repository/project,
you agree that your contribution will be licensed under
the license of this repository/this project.
Please note, it does not change your rights to use your own
contribution for any other purpose.
## The Secrets
If you want to peek at the secrets, take a look at the `patches/`

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Dave Gauer, Chris Boesch
Copyright (c) 2021 Dave Gauer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -10,10 +10,11 @@ Those broken programs need your help! (You'll also save the
planet from evil aliens and help some friendly elephants stick
together, which is very sweet of you.)
This project was initiated by [Dave Gauer](https://ratfactor.com/) and is directly inspired
by the brilliant and fun [rustlings](https://github.com/rust-lang/rustlings) project.
Indirect inspiration comes from [Ruby Koans](http://rubykoans.com/) and the Little LISPer/Little
Schemer series of books.
This project was directly inspired by the brilliant and fun
[rustlings](https://github.com/rust-lang/rustlings)
project for the [Rust](https://www.rust-lang.org/) language.
Indirect inspiration comes from [Ruby Koans](http://rubykoans.com/)
and the Little LISPer/Little Schemer series of books.
## Intended Audience
@ -45,20 +46,20 @@ Verify the installation and build number of `zig` like so:
```
$ zig version
0.15.0-dev.xxxx+xxxxxxxxx
0.12.0-dev.xxxx+xxxxxxxxx
```
Clone this repository with Git:
```
git clone https://codeberg.org/ziglings/exercises.git ziglings
cd ziglings
$ git clone https://ziglings.org
$ cd ziglings.org
```
Then run `zig build` and follow the instructions to begin!
```
zig build
$ zig build
```
Note: The output of Ziglings is the unaltered output from the Zig
@ -68,13 +69,13 @@ reading these.
## A Note About Versions
**Hint:** To check out Ziglings for a stable release of Zig, you can use
the appropriate tag.
the appropriate tag.
The Zig language is under very active development. In order to be
current, Ziglings tracks **development** builds of the Zig
compiler rather than versioned **release** builds. The last
stable release was `0.14.1`, but Ziglings needs a dev build with
pre-release version "0.15.0" and a build number at least as high
stable release was `0.11.0`, but Ziglings needs a dev build with
pre-release version "0.12.0" and a build number at least as high
as that shown in the example version check above.
It is likely that you'll download a build which is _greater_ than
@ -87,14 +88,7 @@ that if you update one, you may need to also update the other.
### Version Changes
Version-0.15.0-dev.1092
* *2025-07-22* zig 0.15.0-dev.1092 - various changes due to new I/O API, see [#24488](https://github.com/ziglang/zig/pull/24488)
* *2024-09-16* zig 0.14.0-dev.1573 - introduction of labeled switch, see [#21257](https://github.com/ziglang/zig/pull/21257)
* *2024-09-02* zig 0.14.0-dev.1409 - several changes in std.builtin, see [#21225](https://github.com/ziglang/zig/pull/21225)
* *2024-08-04* zig 0.14.0-dev.1224 - several changes in build system, see [#21115](https://github.com/ziglang/zig/pull/21115)
* *2024-08-04* zig 0.14.0-dev.839 - several changes in build system, see [#20580](https://github.com/ziglang/zig/pull/20580), [#20600](https://github.com/ziglang/zig/issues/20600)
* *2024-06-17* zig 0.14.0-dev.42 - changes in `std.mem.split and tokenize` - see [#15579](https://github.com/ziglang/zig/pull/15579)
* *2024-05-29* zig 0.13.0-dev.339 - rework std.Progress - see [#20059](https://github.com/ziglang/zig/pull/20059)
Version-0.12.0-dev.3518
* *2024-03-21* zig 0.12.0-dev.3518 - change to @fieldParentPtr - see [#19470](https://github.com/ziglang/zig/pull/19470)
* *2024-03-21* zig 0.12.0-dev.3397 - rename std.os to std.posix - see [#5019](https://github.com/ziglang/zig/issues/5019)
* *2024-03-14* zig 0.12.0-dev.3302 - changes in `std.fmt` - floating-point formatting implementation - see [#19229](https://github.com/ziglang/zig/pull/19229)
@ -131,18 +125,6 @@ It can be handy to check just a single exercise:
zig build -Dn=19
```
Or run all exercises, starting from a specific one:
```
zig build -Ds=27
```
Or let Ziglings pick an exercise for you:
```
zig build -Drandom
```
You can also run without checking for correctness:
```
@ -174,11 +156,6 @@ zig build -Dn=19 -l
...
```
To reset the progress (have it run all the exercises that have already been completed):
```
zig build -Dreset
```
## What's Covered
The primary goal for Ziglings is to cover the core Zig language.
@ -234,10 +211,7 @@ Zig Core Language
* [X] Interfaces
* [X] Bit manipulation
* [X] Working with C
* [ ] Opaque types (anyopaque)
* [X] Threading
* [x] Labeled switch
* [x] Vector operations (SIMD)
Zig Standard Library

213
build.zig
View File

@ -15,7 +15,7 @@ const print = std.debug.print;
// 1) Getting Started
// 2) Version Changes
comptime {
const required_zig = "0.15.0-dev.1092";
const required_zig = "0.12.0-dev.3518";
const current_zig = builtin.zig_version;
const min_zig = std.SemanticVersion.parse(required_zig) catch unreachable;
if (current_zig.order(min_zig) == .lt) {
@ -103,8 +103,6 @@ const Mode = enum {
normal,
/// Named build mode: `zig build -Dn=n`
named,
/// Random build mode: `zig build -Drandom`
random,
};
pub const logo =
@ -120,24 +118,23 @@ pub const logo =
\\
;
const progress_filename = ".progress.txt";
pub fn build(b: *Build) !void {
if (!validate_exercises()) std.process.exit(2);
use_color_escapes = false;
if (std.fs.File.stderr().supportsAnsiEscapeCodes()) {
if (std.io.getStdErr().supportsAnsiEscapeCodes()) {
use_color_escapes = true;
} else if (builtin.os.tag == .windows) {
const w32 = struct {
const WINAPI = std.os.windows.WINAPI;
const DWORD = std.os.windows.DWORD;
const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
const STD_ERROR_HANDLE: DWORD = @bitCast(@as(i32, -12));
const GetStdHandle = std.os.windows.kernel32.GetStdHandle;
const GetConsoleMode = std.os.windows.kernel32.GetConsoleMode;
const SetConsoleMode = std.os.windows.kernel32.SetConsoleMode;
extern "kernel32" fn GetStdHandle(id: DWORD) callconv(WINAPI) ?*anyopaque;
extern "kernel32" fn GetConsoleMode(console: ?*anyopaque, out_mode: *DWORD) callconv(WINAPI) u32;
extern "kernel32" fn SetConsoleMode(console: ?*anyopaque, mode: DWORD) callconv(WINAPI) u32;
};
const handle = w32.GetStdHandle(w32.STD_ERROR_HANDLE).?;
const handle = w32.GetStdHandle(w32.STD_ERROR_HANDLE);
var mode: w32.DWORD = 0;
if (w32.GetConsoleMode(handle, &mode) != 0) {
mode |= w32.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
@ -161,9 +158,6 @@ pub fn build(b: *Build) !void {
false;
const override_healed_path = b.option([]const u8, "healed-path", "Override healed path");
const exno: ?usize = b.option(usize, "n", "Select exercise");
const rand: ?bool = b.option(bool, "random", "Select random exercise");
const start: ?usize = b.option(usize, "s", "Start at exercise");
const reset: ?bool = b.option(bool, "reset", "Reset exercise progress");
const sep = std.fs.path.sep_str;
const healed_path = if (override_healed_path) |path|
@ -197,108 +191,17 @@ pub fn build(b: *Build) !void {
return;
}
if (rand) |_| {
// Random build mode: verifies one random exercise.
// like for 'exno' but chooses a random exercise number.
print("work in progress: check a random exercise\n", .{});
var prng = std.Random.DefaultPrng.init(blk: {
var seed: u64 = undefined;
try std.posix.getrandom(std.mem.asBytes(&seed));
break :blk seed;
});
const rnd = prng.random();
const ex = exercises[rnd.intRangeLessThan(usize, 0, exercises.len)];
print("random exercise: {s}\n", .{ex.main_file});
const zigling_step = b.step(
"random",
b.fmt("Check the solution of {s}", .{ex.main_file}),
);
b.default_step = zigling_step;
zigling_step.dependOn(&header_step.step);
const verify_step = ZiglingStep.create(b, ex, work_path, .random);
verify_step.step.dependOn(&header_step.step);
zigling_step.dependOn(&verify_step.step);
return;
}
if (start) |s| {
if (s == 0 or s > exercises.len - 1) {
print("unknown exercise number: {}\n", .{s});
std.process.exit(2);
}
const first = exercises[s - 1];
const ziglings_step = b.step("ziglings", b.fmt("Check ziglings starting with {s}", .{first.main_file}));
b.default_step = ziglings_step;
var prev_step = &header_step.step;
for (exercises[(s - 1)..]) |ex| {
const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal);
verify_stepn.step.dependOn(prev_step);
prev_step = &verify_stepn.step;
}
ziglings_step.dependOn(prev_step);
return;
}
if (reset) |_| {
std.fs.cwd().deleteFile(progress_filename) catch |err| {
switch (err) {
std.fs.Dir.DeleteFileError.FileNotFound => {},
else => {
print("Unable to remove progress file, Error: {}\n", .{err});
return err;
},
}
};
print("Progress reset, {s} removed.\n", .{progress_filename});
std.process.exit(0);
}
// Normal build mode: verifies all exercises according to the recommended
// order.
const ziglings_step = b.step("ziglings", "Check all ziglings");
b.default_step = ziglings_step;
var prev_step = &header_step.step;
var starting_exercise: u32 = 0;
if (std.fs.cwd().openFile(progress_filename, .{})) |progress_file| {
defer progress_file.close();
const progress_file_size = try progress_file.getEndPos();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const contents = try progress_file.readToEndAlloc(allocator, progress_file_size);
defer allocator.free(contents);
starting_exercise = try std.fmt.parseInt(u32, contents, 10);
} else |err| {
switch (err) {
std.fs.File.OpenError.FileNotFound => {
// This is fine, may be the first time tests are run or progress have been reset
},
else => {
print("Unable to open {s}: {}\n", .{ progress_filename, err });
return err;
},
}
}
for (exercises) |ex| {
if (starting_exercise < ex.number()) {
const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal);
verify_stepn.step.dependOn(prev_step);
const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal);
verify_stepn.step.dependOn(prev_step);
prev_step = &verify_stepn.step;
}
prev_step = &verify_stepn.step;
}
ziglings_step.dependOn(prev_step);
@ -341,7 +244,7 @@ const ZiglingStep = struct {
return self;
}
fn make(step: *Step, options: Step.MakeOptions) !void {
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
// NOTE: Using exit code 2 will prevent the Zig compiler to print the message:
// "error: the following build command failed with exit code 1:..."
const self: *ZiglingStep = @alignCast(@fieldParentPtr("step", step));
@ -352,7 +255,7 @@ const ZiglingStep = struct {
return;
}
const exe_path = self.compile(options.progress_node) catch {
const exe_path = self.compile(prog_node) catch {
self.printErrors();
if (self.exercise.hint) |hint|
@ -362,7 +265,7 @@ const ZiglingStep = struct {
std.process.exit(2);
};
self.run(exe_path, options.progress_node) catch {
self.run(exe_path.?, prog_node) catch {
self.printErrors();
if (self.exercise.hint) |hint|
@ -376,7 +279,7 @@ const ZiglingStep = struct {
self.printErrors();
}
fn run(self: *ZiglingStep, exe_path: []const u8, _: std.Progress.Node) !void {
fn run(self: *ZiglingStep, exe_path: []const u8, _: *std.Progress.Node) !void {
resetLine();
print("Checking: {s}\n", .{self.exercise.main_file});
@ -449,18 +352,6 @@ const ZiglingStep = struct {
, .{ red, reset, exercise_output, red, reset, output, red, reset });
}
const progress = try std.fmt.allocPrint(b.allocator, "{d}", .{self.exercise.number()});
defer b.allocator.free(progress);
const file = try std.fs.cwd().createFile(
progress_filename,
.{ .read = true, .truncate = true },
);
defer file.close();
try file.writeAll(progress);
try file.sync();
print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text });
}
@ -484,7 +375,7 @@ const ZiglingStep = struct {
print("{s}PASSED{s}\n\n", .{ green_text, reset_text });
}
fn compile(self: *ZiglingStep, prog_node: std.Progress.Node) ![]const u8 {
fn compile(self: *ZiglingStep, prog_node: *std.Progress.Node) !?[]const u8 {
print("Compiling: {s}\n", .{self.exercise.main_file});
const b = self.step.owner;
@ -515,21 +406,7 @@ const ZiglingStep = struct {
zig_args.append("--listen=-") catch @panic("OOM");
//
// NOTE: After many changes in zig build system, we need to create the cache path manually.
// See https://github.com/ziglang/zig/pull/21115
// Maybe there is a better way (in the future).
const exe_dir = try self.step.evalZigProcess(zig_args.items, prog_node, false);
const exe_name = switch (self.exercise.kind) {
.exe => self.exercise.name(),
.@"test" => "test",
};
const sep = std.fs.path.sep_str;
const root_path = exe_dir.?.root_dir.path.?;
const sub_path = exe_dir.?.subPathOrDot();
const exe_path = b.fmt("{s}{s}{s}{s}{s}", .{ root_path, sep, sub_path, sep, exe_name });
return exe_path;
return try self.step.evalZigProcess(zig_args.items, prog_node);
}
fn help(self: *ZiglingStep) void {
@ -540,7 +417,6 @@ const ZiglingStep = struct {
const cmd = switch (self.mode) {
.normal => "zig build",
.named => b.fmt("zig build -Dn={s}", .{key}),
.random => "zig build -Drandom",
};
print("\n{s}Edit exercises/{s} and run '{s}' again.{s}\n", .{
@ -583,7 +459,7 @@ fn resetLine() void {
pub fn trimLines(allocator: std.mem.Allocator, buf: []const u8) ![]const u8 {
var list = try std.ArrayList(u8).initCapacity(allocator, buf.len);
var iter = std.mem.splitSequence(u8, buf, " \n");
var iter = std.mem.split(u8, buf, " \n");
while (iter.next()) |line| {
// TODO: trimming CR characters is probably not necessary.
const data = std.mem.trimRight(u8, line, " \r");
@ -618,7 +494,7 @@ const PrintStep = struct {
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
fn make(step: *Step, _: *std.Progress.Node) !void {
const self: *PrintStep = @alignCast(@fieldParentPtr("step", step));
print("{s}", .{self.message});
}
@ -645,7 +521,7 @@ fn validate_exercises() bool {
return false;
}
var iter = std.mem.splitScalar(u8, ex.output, '\n');
var iter = std.mem.split(u8, ex.output, "\n");
while (iter.next()) |line| {
const output = std.mem.trimRight(u8, line, " \r");
if (output.len != line.len) {
@ -962,7 +838,7 @@ const exercises = [_]Exercise{
},
.{
.main_file = "060_floats.zig",
.output = "Shuttle liftoff weight: 2032 metric tons",
.output = "Shuttle liftoff weight: 2032092kg",
},
.{
.main_file = "061_coercions.zig",
@ -1028,7 +904,6 @@ const exercises = [_]Exercise{
.{
.main_file = "074_comptime9.zig",
.output = "My llama value is 2.",
.skip = true,
},
.{
.main_file = "075_quiz8.zig",
@ -1063,7 +938,7 @@ const exercises = [_]Exercise{
.{
.main_file = "082_anonymous_structs3.zig",
.output =
\\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592
\\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592e0
,
.hint = "This one is a challenge! But you have everything you need.",
},
@ -1145,7 +1020,7 @@ const exercises = [_]Exercise{
},
.{
.main_file = "097_bit_manipulation.zig",
.output = "x = 1011; y = 1101",
.output = "x = 0; y = 1",
},
.{
.main_file = "098_bit_manipulation2.zig",
@ -1253,51 +1128,9 @@ const exercises = [_]Exercise{
.main_file = "107_files2.zig",
.output =
\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
\\Successfully Read 18 bytes: It's zigling time!
\\Successfully Read 18 byte: It's zigling time!
,
},
.{
.main_file = "108_labeled_switch.zig",
.output = "The pull request has been merged.",
},
.{
.main_file = "109_vectors.zig",
.output =
\\Max difference (old fn): 0.014
\\Max difference (new fn): 0.014
,
},
.{ .main_file = "110_quiz9.zig", .output =
\\Toggle pins with XOR on PORTB
\\-----------------------------
\\ 1100 // (initial state of PORTB)
\\^ 0101 // (bitmask)
\\= 1001
\\
\\ 1100 // (initial state of PORTB)
\\^ 0011 // (bitmask)
\\= 1111
\\
\\Set pins with OR on PORTB
\\-------------------------
\\ 1001 // (initial state of PORTB)
\\| 0100 // (bitmask)
\\= 1101
\\
\\ 1001 // (reset state)
\\| 0100 // (bitmask)
\\= 1101
\\
\\Clear pins with AND and NOT on PORTB
\\------------------------------------
\\ 1110 // (initial state of PORTB)
\\& 1011 // (bitmask)
\\= 1010
\\
\\ 0111 // (reset state)
\\& 1110 // (bitmask)
\\= 0110
},
.{
.main_file = "999_the_end.zig",
.output =

View File

@ -16,12 +16,12 @@ const std = @import("std");
//
pub fn main() !void {
// We get a Writer for Standard Out so we can print() to it.
var stdout = std.fs.File.stdout().writer(&.{});
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.interface.print("Hello world!\n", .{});
stdout.print("Hello world!\n", .{});
}

View File

@ -17,7 +17,7 @@
//
// if (foo) |value| {
// ...
// } else |err| switch (err) {
// } else |err| switch(err) {
// ...
// }
//

View File

@ -10,11 +10,11 @@ const std = @import("std");
const NumError = error{IllegalNumber};
pub fn main() void {
var stdout = std.fs.File.stdout().writer(&.{});
const stdout = std.io.getStdOut().writer();
const my_num: u32 = getNumber();
try stdout.interface.print("my_num={}\n", .{my_num});
try stdout.print("my_num={}\n", .{my_num});
}
// This function is obviously weird and non-functional. But you will not be changing it for this quiz.

View File

@ -5,7 +5,7 @@
// linked to the first elephant. This is because we had NO CONCEPT
// of a tail that didn't point to another elephant!
//
// We also introduce the handy `.?` shortcut:
// We also introduce the handy ".?" shortcut:
//
// const foo = bar.?;
//
@ -13,8 +13,7 @@
//
// const foo = bar orelse unreachable;
//
// Check out where we use this shortcut below to change control flow
// based on if an optional value exists.
// See if you can find where we use this shortcut below.
//
// Now let's make those elephant tails optional!
//
@ -32,25 +31,14 @@ pub fn main() void {
var elephantC = Elephant{ .letter = 'C' };
// Link the elephants so that each tail "points" to the next.
linkElephants(&elephantA, &elephantB);
linkElephants(&elephantB, &elephantC);
// `linkElephants` will stop the program if you try and link an
// elephant that doesn't exist! Uncomment and see what happens.
// const missingElephant: ?*Elephant = null;
// linkElephants(&elephantC, missingElephant);
elephantA.tail = &elephantB;
elephantB.tail = &elephantC;
visitElephants(&elephantA);
std.debug.print("\n", .{});
}
// If e1 and e2 are valid pointers to elephants,
// this function links the elephants so that e1's tail "points" to e2.
fn linkElephants(e1: ?*Elephant, e2: ?*Elephant) void {
e1.?.tail = e2.?;
}
// This function visits all elephants once, starting with the
// first elephant and following the tails to the next elephant.
fn visitElephants(first_elephant: *Elephant) void {
@ -63,9 +51,8 @@ fn visitElephants(first_elephant: *Elephant) void {
// We should stop once we encounter a tail that
// does NOT point to another element. What can
// we put here to make that happen?
if (e.tail == null) ???;
// HINT: We want something similar to what `.?` does,
// but instead of ending the program, we want to exit the loop...
e = e.tail ???
e = e.tail.?;
}
}

View File

@ -40,7 +40,7 @@
// Okay, you're armed.
//
// Now, please zap the alien structs until they're all gone or
// the Earth will be doomed!
// Earth will be doomed!
//
const std = @import("std");

View File

@ -43,7 +43,7 @@
//
// "void" is a _type_, not a value. It is the most popular of the
// Zero Bit Types (those types which take up absolutely no space
// and have only a semantic value). When compiled to executable
// and have only a semantic value. When compiled to executable
// code, zero bit types generate no code at all. The above example
// shows a variable foo of type void which is assigned the value
// of an empty expression. It's much more common to see void as

View File

@ -190,7 +190,7 @@ const TripItem = union(enum) {
fn printMe(self: TripItem) void {
switch (self) {
// Oops! The hermit forgot how to capture the union values
// in a switch statement. Please capture each value as
// in a switch statement. Please capture both values as
// 'p' so the print statements work!
.place => print("{s}", .{p.name}),
.path => print("--{}->", .{p.dist}),

View File

@ -41,14 +41,14 @@ pub fn main() void {
// The approximate weight of the Space Shuttle upon liftoff
// (including boosters and fuel tank) was 4,480,000 lb.
//
// We'll convert this weight from pounds to metric units at a
// conversion of 0.453592 kg to the pound.
const shuttle_weight: f16 = 0.453592 * 4480e3;
// We'll convert this weight from pound to kilograms at a
// conversion of 0.453592kg to the pound.
const shuttle_weight: f16 = 0.453592 * 4480e6;
// By default, float values are formatted in scientific
// notation. Try experimenting with '{d}' and '{d:.3}' to see
// how decimal formatting works.
print("Shuttle liftoff weight: {d:.0} metric tons\n", .{shuttle_weight});
print("Shuttle liftoff weight: {d:.0}kg\n", .{shuttle_weight});
}
// Floating further:

View File

@ -94,35 +94,31 @@ pub fn main() void {
print("He has room in his heart for:", .{});
// A StructFields array
const fields = @typeInfo(Narcissus).@"struct".fields;
const fields = @typeInfo(Narcissus).Struct.fields;
// 'fields' is a slice of StructFields. Here's the declaration:
//
// pub const StructField = struct {
// name: [:0]const u8,
// name: []const u8,
// type: type,
// default_value_ptr: ?*const anyopaque,
// default_value: anytype,
// is_comptime: bool,
// alignment: comptime_int,
//
// defaultValue() ?sf.type // Function that loads the
// // field's default value from
// // `default_value_ptr`
// };
//
// Please complete these 'if' statements so that the field
// name will not be printed if the field is of type 'void'
// (which is a zero-bit type that takes up no space at all!):
if (fields[0].??? != void) {
print(" {s}", .{fields[0].name});
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[0].name});
}
if (fields[1].??? != void) {
print(" {s}", .{fields[1].name});
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[1].name});
}
if (fields[2].??? != void) {
print(" {s}", .{fields[2].name});
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[2].name});
}
// Yuck, look at all that repeated code above! I don't know
@ -140,16 +136,14 @@ pub fn main() void {
// But a change after Zig 0.10.0 added the source file name to the
// type. "Narcissus" became "065_builtins2.Narcissus".
//
// To fix this, we've added this function to strip the filename from
// the front of the type name. (It returns a slice of the type name
// starting at the index + 1 of character ".")
// To fix this, I've added this function to strip the filename from
// the front of the type name in the dumbest way possible. (It returns
// a slice of the type name starting at character 14 (assuming
// single-byte characters).
//
// We'll be seeing @typeName again in Exercise 070. For now, you can
// see that it takes a Type and returns a u8 "string".
fn maximumNarcissism(myType: anytype) []const u8 {
const indexOf = @import("std").mem.indexOf;
// Turn "065_builtins2.Narcissus" into "Narcissus"
const name = @typeName(myType);
return name[indexOf(u8, name, ".").? + 1 ..];
// Turn '065_builtins2.Narcissus' into 'Narcissus'
return @typeName(myType)[14..];
}

View File

@ -7,7 +7,7 @@
// doing this work.
//
// An 'inline for' is performed at compile time, allowing you to
// programmatically loop through a series of items in situations
// programatically loop through a series of items in situations
// like those mentioned above where a regular runtime 'for' loop
// wouldn't be allowed:
//
@ -38,7 +38,7 @@ pub fn main() void {
// Please use an 'inline for' to implement the block below
// for each field in the slice 'fields'!
const fields = @typeInfo(Narcissus).@"struct".fields;
const fields = @typeInfo(Narcissus).Struct.fields;
??? {
if (field.type != void) {

View File

@ -78,7 +78,7 @@ fn printSequence(my_seq: anytype) void {
// a switch to handle printing the Array or Pointer fields,
// depending on which type of my_seq was passed in:
switch (my_typeinfo) {
.array => {
.Array => {
print("Array:", .{});
// Loop through the items in my_seq.
@ -86,7 +86,7 @@ fn printSequence(my_seq: anytype) void {
print("{}", .{s});
}
},
.pointer => {
.Pointer => {
// Check this out - it's pretty cool:
const my_sentinel = sentinel(@TypeOf(my_seq));
print("Many-item pointer:", .{});

View File

@ -19,12 +19,12 @@
// const MyBar = Bar(); // store the struct type
// const bar = Bar() {}; // create instance of the struct
//
// * The value of @typeName(Bar()) is "<filename>.Bar()".
// * The value of @typeName(MyBar) is "<filename>.Bar()".
// * The value of @typeName(@TypeOf(bar)) is "<filename>.Bar()".
// * The value of @typeName(Bar()) is "Bar()".
// * The value of @typeName(MyBar) is "Bar()".
// * The value of @typeName(@TypeOf(bar)) is "Bar()".
//
// You can also have completely anonymous structs. The value
// of @typeName(struct {}) is "<filename>.<function>__struct_<nnn>".
// of @typeName(struct {}) is "struct:<position in source>".
//
const print = @import("std").debug.print;

View File

@ -75,11 +75,11 @@ fn printTuple(tuple: anytype) void {
// with fields specific to that type.
//
// The list of a struct type's fields can be found in
// TypeInfo's @"struct".fields.
// TypeInfo's Struct.fields.
//
// Example:
//
// @typeInfo(Circle).@"struct".fields
// @typeInfo(Circle).Struct.fields
//
// This will be an array of StructFields.
const fields = ???;
@ -95,15 +95,13 @@ fn printTuple(tuple: anytype) void {
// Each 'field' in this loop is one of these:
//
// pub const StructField = struct {
// name: [:0]const u8,
// name: []const u8,
// type: type,
// default_value_ptr: ?*const anyopaque,
// default_value: anytype,
// is_comptime: bool,
// alignment: comptime_int,
// };
//
// Note we will learn about 'anyopaque' type later
//
// You'll need this builtin:
//
// @field(lhs: anytype, comptime field_name: []const u8)

View File

@ -13,7 +13,7 @@
// How could we do that? A good method is to use the modulo function.
// But if we write "765.2 % 360", it only works with float values
// that are known at compile time.
// In Zig, we would use @mod(a, b) instead.
// In Zig, we would use %mod(a, b) instead.
//
// Let us now assume that we cannot do this in Zig, but only with
// a C function from the standard library. In the library "math",

View File

@ -28,7 +28,7 @@
// 0..10 is a range from 0 to 9
// 1..4 is a range from 1 to 3
//
// At the moment, ranges in loops are only supported in 'for' loops.
// At the moment, ranges are only supported in 'for' loops.
//
// Perhaps you recall Exercise 13? We were printing a numeric
// sequence like so:

View File

@ -30,9 +30,9 @@
// std.debug.print("slice_ptr={*}\n", .{slice_ptr});
// }
// Instead of a simple integer or a slice with a constant size,
// this program requires allocating a slice that is the same size
// as an input array.
// Instead of a simple integer or a constant sized slice, this
// program requires a slice to be allocated that is the same size as
// an input array.
// Given a series of numbers, take the running average. In other
// words, each item N should contain the average of the last N

View File

@ -1,5 +1,5 @@
//
// Bit manipulation is a very powerful tool, also from Zig.
// Bit manipulations is a very powerful tool just also from Zig.
// Since the dawn of the computer age, numerous algorithms have been
// developed that solve tasks solely by moving, setting, or logically
// combining bits.
@ -8,10 +8,10 @@
// functions where possible. And it is often possible with calculations
// based on integers.
//
// At first glance, it is often not easy to understand what exactly these
// Often it is not easy to understand at first glance what exactly these
// algorithms do when only "numbers" in memory areas change outwardly.
// However, it should never be forgotten that the numbers only represent
// the interpretation of the bit sequences.
// But it must never be forgotten that the numbers only represent the
// interpretation of the bit sequences.
//
// Quasi the reversed case we have otherwise, namely that we represent
// numbers in bit sequences.
@ -21,7 +21,7 @@
// Zig provides all the necessary functions to change the bits inside
// a variable. It is distinguished whether the bit change leads to an
// overflow or not. The details are in the Zig documentation in section
// "Table of Operators".
// 10.1 "Table of Operators".
//
// Here are some examples of how the bits of variables can be changed:
//
@ -71,9 +71,9 @@ const print = std.debug.print;
pub fn main() !void {
// Let us use 1101 and 1011 as values for x and y
var x: u8 = 0b1101;
var y: u8 = 0b1011;
// As in the example above, we use 1 and 0 as values for x and y
var x: u8 = 1;
var y: u8 = 0;
// Now we swap the values of the two variables by doing xor on them
x ^= y;
@ -82,7 +82,7 @@ pub fn main() !void {
// What must be written here?
???;
print("x = {b}; y = {b}\n", .{ x, y });
print("x = {d}; y = {d}\n", .{ x, y });
}
// This variable swap takes advantage of the fact that the value resulting

View File

@ -1,5 +1,5 @@
//
// Another useful application for bit manipulation is setting bits as flags.
// Another useful practice for bit manipulation is setting bits as flags.
// This is especially useful when processing lists of something and storing
// the states of the entries, e.g. a list of numbers and for each prime
// number a flag is set.
@ -19,9 +19,9 @@
// For example, you could take an array of bool and set the value to 'true'
// for each letter in the order of the alphabet (a=0; b=1; etc.) found in
// the sentence. However, this is neither memory efficient nor particularly
// fast. Instead we choose a simpler approach that is very similar in principle:
// We define a variable with at least 26 bits (e.g. u32) and set the bit for
// each letter that is found in the corresponding position.
// fast. Instead we take a simpler way, very similar in principle, we define
// a variable with at least 26 bits (e.g. u32) and also set the bit for each
// letter found at the corresponding position.
//
// Zig provides functions for this in the standard library, but we prefer to
// solve it without these extras, after all we want to learn something.
@ -32,20 +32,20 @@ const print = std.debug.print;
pub fn main() !void {
// let's check the pangram
print("Is this a pangram? {}!\n", .{isPangram("The quick brown fox jumps over the lazy dog.")});
print("Is this a pangram? {?}!\n", .{isPangram("The quick brown fox jumps over the lazy dog.")});
}
fn isPangram(str: []const u8) bool {
// first we check if the string has at least 26 characters
if (str.len < 26) return false;
// we use a 32 bit variable of which we need 26 bits
// we uses a 32 bit variable of which we need 26 bits
var bits: u32 = 0;
// loop about all characters in the string
for (str) |c| {
// if the character is an alphabetical character
if (ascii.isAscii(c) and ascii.isAlphabetic(c)) {
if (ascii.isASCII(c) and ascii.isAlphabetic(c)) {
// then we set the bit at the position
//
// to do this, we use a little trick:

View File

@ -16,13 +16,13 @@
// Therefore, the comments for the format() function are the only
// way to definitively learn how to format strings in Zig:
//
// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L33
// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L29
//
// Zig already has a very nice selection of formatting options.
// These can be used in different ways, but generally to convert
// numerical values into various text representations. The results
// can be used for direct output to a terminal or stored for
// later use or written to a file. The latter is useful when
// These can be used in different ways, but typically to convert
// numerical values into various text representations. The
// results can be used for direct output to a terminal or stored
// for later use or written to a file. The latter is useful when
// large amounts of data are to be processed by other programs.
//
// In Ziglings, we are concerned with the output to the console.
@ -45,7 +45,7 @@
// output. Escape sequences can also be written one after the
// other, e.g. "\n\n" will cause two line feeds.
//
// By the way, the result of these escape sequences is passed
// By the way, the result of these escape sequences are passed
// directly to the terminal program. Other than translating them
// into control codes, escape sequences have nothing to do with
// Zig. Zig knows nothing about "line feeds" or "tabs" or
@ -60,7 +60,7 @@
// variety of formatting instructions. It's basically a tiny
// language of its own. Here's a numeric example:
//
// print("Catch-0x{x:0>4}.", .{twenty_two});
// print("Catch-{x:0>4}.", .{twenty_two});
//
// This formatting instruction outputs a hexadecimal number with
// leading zeros:
@ -95,7 +95,7 @@
// ...
//
// Without string formatting, this would be a more challenging
// assignment because the number of digits in the numbers varies
// assignment because the number of digits in the numbers vary
// from 1 to 3. But formatting can help us with that.
//
const std = @import("std");

View File

@ -41,12 +41,12 @@ pub fn main() void {
for (hex_nums, ???) |hn, ???| {
if (hn != dn) {
print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
return;
}
}
print("Arrays match!\n", .{});
std.debug.print("Arrays match!\n", .{});
}
//
// You are perhaps wondering what happens if one of the two lists

View File

@ -119,9 +119,9 @@
// after all we need some practice. Suppose we want to count the words
// of this little poem:
//
// My name is Ozymandias, King of Kings;
// Look on my Works, ye Mighty, and despair!
// by Percy Bysshe Shelley
// My name is Ozymandias, King of Kings;
// Look on my Works, ye Mighty, and despair!
// by Percy Bysshe Shelley
//
//
const std = @import("std");

View File

@ -4,8 +4,8 @@
// one possibility, namely asynchronous processes, in Exercises 84-91.
//
// However, the computing power of the processor is only distributed to
// the started and running tasks, which always reaches its limits when
// pure computing power is called up.
// the started tasks, which always reaches its limits when pure computing
// power is called up.
//
// For example, in blockchains based on proof of work, the miners have
// to find a nonce for a certain character string so that the first m bits
@ -106,7 +106,7 @@ pub fn main() !void {
// After the threads have been started,
// they run in parallel and we can still do some work in between.
std.Thread.sleep(1500 * std.time.ns_per_ms);
std.time.sleep(1500 * std.time.ns_per_ms);
std.debug.print("Some weird stuff, after starting the threads.\n", .{});
}
// After we have left the closed area, we wait until
@ -117,12 +117,12 @@ pub fn main() !void {
// This function is started with every thread that we set up.
// In our example, we pass the number of the thread as a parameter.
fn thread_function(num: usize) !void {
std.Thread.sleep(200 * num * std.time.ns_per_ms);
std.time.sleep(200 * num * std.time.ns_per_ms);
std.debug.print("thread {d}: {s}\n", .{ num, "started." });
// This timer simulates the work of the thread.
const work_time = 3 * ((5 - num % 3) - 2);
std.Thread.sleep(work_time * std.time.ns_per_s);
std.time.sleep(work_time * std.time.ns_per_s);
std.debug.print("thread {d}: {s}\n", .{ num, "finished." });
}

View File

@ -1,6 +1,6 @@
//
// Now that we are familiar with the principles of multi-threading,
// let's boldly venture into a practical example from mathematics.
// Now that we are familiar with the principles of multi threading, we
// boldly venture into a practical example from mathematics.
// We will determine the circle number PI with sufficient accuracy.
//
// There are different methods for this, and some of them are several
@ -21,9 +21,9 @@
// There were the Scottish mathematician Gregory and the German
// mathematician Leibniz, and even a few hundred years earlier the Indian
// mathematician Madhava. All of them independently developed the same
// formula, which was published by Leibniz in 1682 in the journal
// formula, which was published by Leibnitz in 1682 in the journal
// "Acta Eruditorum".
// This is why this method has become known as the "Leibniz series",
// This is why this method has become known as the "Leibnitz series",
// although the other names are also often used today.
// We will not go into the formula and its derivation in detail, but
// will deal with the series straight away:
@ -39,7 +39,7 @@
// in practice. Because either you don't need the precision, or you use a
// calculator in which the number is stored as a very precise constant.
// But at some point this constant was calculated and we are doing the same
// now. The question at this point is, how many partial values do we have
// now.The question at this point is, how many partial values do we have
// to calculate for which accuracy?
//
// The answer is chewing, to get 8 digits after the decimal point we need
@ -50,7 +50,7 @@
// enough for us for now, because we want to understand the principle and
// nothing more, right?
//
// As we have already discovered, the Leibniz series is a series with a
// As we have already discovered, the Leibnitz series is a series with a
// fixed distance of 2 between the individual partial values. This makes
// it easy to apply a simple loop to it, because if we start with n = 1
// (which is not necessarily useful now) we always have to add 2 in each
@ -104,4 +104,4 @@ fn thread_pi(pi: *f64, begin: u64, end: u64) !void {
// to such an extent that seconds become minutes during execution.
//
// And you should remove the formatting restriction in "print",
// otherwise you will not be able to see the additional digits.
// otherwise you will not be able to see the additional diggits.

View File

@ -1,22 +1,22 @@
//
// Until now, we've only been printing our output in the console,
// which is good enough for fighting aliens and hermit bookkeeping.
// which is good enough for fighting alien and hermit bookkeeping.
//
// However, many other tasks require some interaction with the file system,
// However, many other task require some interaction with the file system,
// which is the underlying structure for organizing files on your computer.
//
// The file system provides a hierarchical structure for storing files
// by organizing them into directories, which hold files and other directories,
// thus creating a tree structure that can be navigated.
// The File System provide a hierarchical structure for storing files
// by organizing files into directories, which hold files and other directories,
// thus creating a tree structure for navigating.
//
// Fortunately, the Zig Standard Library provides a simple API for interacting
// with the file system, see the detail documentation here:
// Fortunately, zig standard library provide a simple api for interacting
// with the file system, see the detail documentation here
//
// https://ziglang.org/documentation/master/std/#std.fs
//
// In this exercise, we'll try to:
// - create a new directory,
// - open a file in the directory,
// In this exercise, we'll try to
// - create a new directory
// - open a file in the directory
// - write to the file.
//
// import std as always
@ -27,43 +27,43 @@ pub fn main() !void {
const cwd: std.fs.Dir = std.fs.cwd();
// then we'll try to make a new directory /output/
// to store our output files.
// to put our output files.
cwd.makeDir("output") catch |e| switch (e) {
// there is a chance you might want to run this
// there are chance you might want to run this
// program more than once and the path might already
// have been created, so we'll have to handle this error
// been created, so we'll have to handle this error
// by doing nothing
//
// we want to catch error.PathAlreadyExists and do nothing
??? => {},
// if there's any other unexpected error we just propagate it through
// if is any other unexpected error we just propagate it through
else => return e,
};
// then we'll try to open our freshly created directory
// wait a minute...
// wait a minute
// opening a directory might fail!
// what should we do here?
var output_dir: std.fs.Dir = cwd.openDir("output", .{});
defer output_dir.close();
// we try to open the file `zigling.txt`,
// and propagate any error up
// and propagate the error up if there are any errors
const file: std.fs.File = try output_dir.createFile("zigling.txt", .{});
// it is a good habit to close a file after you are done with it
// so that other programs can read it and prevent data corruption
// it is a good habit to close a file after you are done with
// so that other program can read it and prevent data corruption
// but here we are not yet done writing to the file
// if only there were a keyword in Zig that
// allowed you to "defer" code execution to the end of the scope...
// if only there are a keyword in zig that
// allow you "defer" code execute to the end of scope...
file.close();
// you are not allowed to move these two lines above the file closing line!
// !you are not allow to switch this two lines to before file closing line!
const byte_written = try file.write("It's zigling time!");
std.debug.print("Successfully wrote {d} bytes.\n", .{byte_written});
}
// to check if you actually write to the file, you can either,
// 1. open the file in your text editor, or
// 2. print the content of the file in the console with the following command
// 1. open the file on your text editor, or
// 2. print the content of the file in the console with command
// >> cat ./output/zigling.txt
//
//
@ -86,7 +86,7 @@ pub fn main() !void {
//
// Question:
// - what should you do if you want to also read the file after opening it?
// - go to the documentation of the struct `std.fs.Dir` here:
// - go to documentation of the struct `std.fs.Dir` here
// https://ziglang.org/documentation/master/std/#std.fs.Dir
// - can you find a function for opening a file? how about deleting a file?
// - what kind of options can you use with those functions?
// - what kind of option can you uses with those function?

View File

@ -4,17 +4,17 @@
// - create a file {project_root}/output/zigling.txt
// with content `It's zigling time!`(18 byte total)
//
// Now there's no point in writing to a file if we don't read from it, am I right?
// Let's write a program to read the content of the file that we just created.
// Now there no point in writing to a file if we don't read from it am I right?
// let's wrote a program to read the content of the file that we just created.
//
// I am assuming that you've created the appropriate files for this to work.
// I am assuming you've created the appropriate files for this to work.
//
// Alright, bud, lean in close. Here's the game plan.
// Alright, bud, lean in close here's the game plan.
// - First, we open the {project_root}/output/ directory
// - Secondly, we open file `zigling.txt` in that directory
// - Then, we initialize an array of characters with all letter 'A', and print it
// - After that, we read the content of the file into the array
// - Finally, we print out the content we just read
// - then, we initalize an array of character with all letter 'A', and print it
// - After that, we read the content of the file to the array
// - Finally, we print out the read content
const std = @import("std");
@ -30,23 +30,23 @@ pub fn main() !void {
const file = try output_dir.openFile("zigling.txt", .{});
defer file.close();
// initialize an array of u8 with all letter 'A'
// we need to pick the size of the array, 64 seems like a good number
// fix the initialization below
// initalize an array of u8 with all letter 'A'.
// we need to pick a size of the array, 64 seems like a good number.
// fix the initalization below
var content = ['A']*64;
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content});
// okay, seems like a threat of violence is not the answer in this case
// can you go here to find a way to read the content?
// okay, seem like threat of violence is not the answer in this case
// can you go here to find a way to read the content ?
// https://ziglang.org/documentation/master/std/#std.fs.File
// hint: you might find two answers that are both valid in this case
const bytes_read = zig_read_the_file_or_i_will_fight_you(&content);
// hint: you might find two answer that are both vaild in this case
const byte_read = zig_read_the_file_or_i_will_fight_you(&content);
// Woah, too screamy. I know you're excited for zigling time but tone it down a bit.
// Can you print only what we read from the file?
std.debug.print("Successfully Read {d} bytes: {s}\n", .{
bytes_read,
// Woah, too screamy, I know you're excited for zigling time but tone it down a bit
// Can you print only what we read from the file ?
std.debug.print("Successfully Read {d} byte: {s}\n", .{
byte_read,
content, // change this line only
});
}

View File

@ -1,79 +0,0 @@
//
// You've heard of while loops in exercises 011,012,013 and 014
// You've also heard of switch expressions in exercises 030 and 31.
// You've also seen how labels can be used in exercise 063.
//
// By combining while loops and switch statements with continue and break statements
// one can create very concise State Machines.
//
// One such example would be:
//
// pub fn main() void {
// var op: u8 = 1;
// while (true) {
// switch (op) {
// 1 => { op = 2; continue; },
// 2 => { op = 3; continue; },
// 3 => return,
// else => {},
// }
// break;
// }
// std.debug.print("This statement cannot be reached\n", .{});
// }
//
// By combining all we've learned so far, we can now proceed with a labeled switch.
//
// A labeled switch is some extra syntactic sugar, which comes with all sorts of
// candy (performance benefits). Don't believe me? Directly to source https://github.com/ziglang/zig/pull/21367
//
// Here is the previous excerpt implemented as a labeled switch instead:
//
// pub fn main() void {
// foo: switch (@as(u8, 1)) {
// 1 => continue :foo 2,
// 2 => continue :foo 3,
// 3 => return,
// else => {},
// }
// std.debug.print("This statement cannot be reached\n", .{});
// }
//
// The flow of execution on this second case is:
// 1. The switch starts with value '1';
// 2. The switch evaluates to case '1' which in turn uses the continue statement
// to re-evaluate the labeled switch again, now providing the value '2';
// 3. In the case '2' we repeat the same pattern as case '1'
// but instead the value to be evaluated is now '3';
// 4. Finally we get to case '3', where we return from the function as a whole,
// so the debug statement is never executed.
// 5. In this example, since the input does not have clear, exhaustive patterns and
// can essentially be any 'u8' integer, we need to handle all cases not explicitly
// covered by using the 'else => {}' branch as the default case.
//
//
const std = @import("std");
const PullRequestState = enum(u8) {
Draft,
InReview,
Approved,
Rejected,
Merged,
};
pub fn main() void {
// Oh no, your pull request keeps being rejected,
// how would you fix it?
pr: switch (PullRequestState.Draft) {
PullRequestState.Draft => continue :pr PullRequestState.InReview,
PullRequestState.InReview => continue :pr PullRequestState.Rejected,
PullRequestState.Approved => continue :pr PullRequestState.Merged,
PullRequestState.Rejected => {
std.debug.print("The pull request has been rejected.\n", .{});
return;
},
PullRequestState.Merged => break, // Would you know where to break to?
}
std.debug.print("The pull request has been merged.\n", .{});
}

View File

@ -1,147 +0,0 @@
// So far in Ziglings, we've seen how for loops can be used to
// repeat calculations across an array in several ways.
//
// For loops are generally great for this kind of task, but
// sometimes they don't fully utilize the capabilities of the
// CPU.
//
// Most modern CPUs can execute instructions in which SEVERAL
// calculations are performed WITHIN registers at the SAME TIME.
// These are known as "single instruction, multiple data" (SIMD)
// instructions. SIMD instructions can make code significantly
// more performant.
//
// To see why, imagine we have a program in which we take the
// square root of four (changing) f32 floats.
//
// A simple compiler would take the program and produce machine code
// which calculates each square root sequentially. Most registers on
// modern CPUs have 64 bits, so we could imagine that each float moves
// into a 64-bit register, and the following happens four times:
//
// 32 bits 32 bits
// +-------------------+
// register | 0 | x |
// +-------------------+
//
// |
// [SQRT instruction]
// V
//
// +-------------------+
// | 0 | sqrt(x) |
// +-------------------+
//
// Notice that half of the register contains blank data to which
// nothing happened. What a waste! What if we were able to use
// that space instead? This is the idea at the core of SIMD.
//
// Most modern CPUs contain specialized registers with at least 128 bits
// for performing SIMD instructions. On a machine with 128-bit SIMD
// registers, a smart compiler would probably NOT issue four sqrt
// instructions as above, but instead pack the floats into a single
// 128-bit register, then execute a single "packed" sqrt
// instruction to do ALL the square root calculations at once.
//
// For example:
//
//
// 32 bits 32 bits 32 bits 32 bits
// +---------------------------------------+
// register | 4.0 | 9.0 | 25.0 | 49.0 |
// +---------------------------------------+
//
// |
// [SIMD SQRT instruction]
// V
//
// +---------------------------------------+
// register | 2.0 | 3.0 | 5.0 | 7.0 |
// +---------------------------------------+
//
// Pretty cool, right?
//
// Code with SIMD instructions is usually more performant than code
// without SIMD instructions. Zig cares a lot about performance,
// so it has built-in support for SIMD! It has a data structure that
// directly supports SIMD instructions:
//
// +-----------+
// | Vectors |
// +-----------+
//
// Operations performed on vectors in Zig will be done in parallel using
// SIMD instructions, whenever possible.
//
// Defining vectors in Zig is straightforwards. No library import is needed.
const v1 = @Vector(3, i32){ 1, 10, 100 };
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };
// Vectors support the same builtin operators as their underlying base types.
const v3 = v1 + v1; // { 2, 20, 200};
const v4 = v2 * v2; // { 4.0, 9.0, 25.0};
// Intrinsics that apply to base types usually extend to vectors.
const v5: @Vector(3, f32) = @floatFromInt(v3); // { 2.0, 20.0, 200.0}
const v6 = v4 - v5; // { 2.0, -11.0, -175.0}
const v7 = @abs(v6); // { 2.0, 11.0, 175.0}
// We can make constant vectors, and reduce vectors.
const v8: @Vector(4, u8) = @splat(2); // { 2, 2, 2, 2}
const v8_sum = @reduce(.Add, v8); // 8
const v8_min = @reduce(.Min, v8); // 2
// Fixed-length arrays can be automatically assigned to vectors (and vice-versa).
const single_digit_primes = [4]i8{ 2, 3, 5, 7 };
const prime_vector: @Vector(4, i8) = single_digit_primes;
// Now let's use vectors to simplify and optimize some code!
//
// Ewa is writing a program in which they frequently want to compare
// two lists of four f32s. Ewa expects the lists to be similar, and
// wants to determine the largest pairwise difference between the lists.
//
// Ewa wrote the following function to figure this out.
fn calcMaxPairwiseDiffOld(list1: [4]f32, list2: [4]f32) f32 {
var max_diff: f32 = 0;
for (list1, list2) |n1, n2| {
const abs_diff = @abs(n1 - n2);
if (abs_diff > max_diff) {
max_diff = abs_diff;
}
}
return max_diff;
}
// Ewa heard about vectors in Zig, and started writing a new vector
// version of the function, but has got stuck!
//
// Help Ewa finish the vector version! The examples above should help.
const Vec4 = @Vector(4, f32);
fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
const abs_diff_vec = ???;
const max_diff = @reduce(???, abs_diff_vec);
return max_diff;
}
// Quite the simplification! We could even write the function in one line
// and it would still be readable.
//
// Since the entire function is now expressed in terms of vector operations,
// the Zig compiler will easily be able to compile it down to machine code
// which utilizes the all-powerful SIMD instructions and does a lot of the
// computation in parallel.
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
const l1 = [4]f32{ 3.141, 2.718, 0.577, 1.000 };
const l2 = [4]f32{ 3.154, 2.707, 0.591, 0.993 };
const mpd_old = calcMaxPairwiseDiffOld(l1, l2);
const mpd_new = calcMaxPairwiseDiffNew(l1, l2);
print("Max difference (old fn): {d: >5.3}\n", .{mpd_old});
print("Max difference (new fn): {d: >5.3}\n", .{mpd_new});
}

View File

@ -1,484 +0,0 @@
// ----------------------------------------------------------------------------
// Quiz Time: Toggling, Setting, and Clearing Bits
// ----------------------------------------------------------------------------
//
// Another exciting thing about Zig is its suitability for embedded
// programming. Your Zig code doesn't have to remain on your laptop; you can
// also deploy your code to microcontrollers! This means you can write Zig to
// drive your next robot or greenhouse climate control system! Ready to enter
// the exciting world of embedded programming? Let's get started!
//
// ----------------------------------------------------------------------------
// Some Background
// ----------------------------------------------------------------------------
//
// A common activity in microcontroller programming is setting and clearing
// bits on input and output pins. This lets you control LEDs, sensors, motors
// and more! In a previous exercise (097_bit_manipulation.zig) you learned how
// to swap two bytes using the ^ (XOR - exclusive or) operator. This quiz will
// test your knowledge of bit manipulation in Zig while giving you a taste of
// what it's like to control registers in a real microcontroller. Included at
// the end are some helper functions that demonstrate how we might make our
// code a little more readable.
//
// Below is a pinout diagram for the famous ATmega328 AVR microcontroller used
// as the primary microchip on popular microcontroller platforms like the
// Arduino UNO.
//
// ============ PINOUT DIAGRAM FOR ATMEGA328 MICROCONTROLLER ============
// _____ _____
// | U |
// (RESET) PC6 --| 1 28 |-- PC5
// PD0 --| 2 27 |-- PC4
// PD1 --| 3 26 |-- PC3
// PD2 --| 4 25 |-- PC2
// PD3 --| 5 24 |-- PC1
// PD4 --| 6 23 |-- PC0
// VCC --| 7 22 |-- GND
// GND --| 8 21 |-- AREF
// |-- PB6 --| 9 20 |-- AVCC
// |-- PB7 --| 10 19 |-- PB5 --|
// | PD5 --| 11 18 |-- PB4 --|
// | PD6 --| 12 17 |-- PB3 --|
// | PD7 --| 13 16 |-- PB2 --|
// |-- PB0 --| 14 15 |-- PB1 --|
// | |___________| |
// \_______________________________/
// |
// PORTB
//
// Drawing inspiration from this diagram, we'll use the pins for PORTB as our
// mental model for this quiz on bit manipulation. It should be noted that
// in the following problems we are using ordinary variables, one of which we
// have named PORTB, to simulate modifying the bits of real hardware registers.
// But in actual microcontroller code, PORTB would be defined something like
// this:
// pub const PORTB = @as(*volatile u8, @ptrFromInt(0x25));
//
// This lets the compiler know not to make any optimizations to PORTB so that
// the IO pins are properly mapped to our code.
//
// NOTE : To keep things simple, the following problems are given using type
// u4, so applying the output to PORTB would only affect the lower four pins
// PB0..PB3. Of course, there is nothing to prevent you from swapping the u4
// with a u8 so you can control all 8 of PORTB's IO pins.
const std = @import("std");
const print = std.debug.print;
const testing = std.testing;
pub fn main() !void {
var PORTB: u4 = 0b0000; // only 4 bits wide for simplicity
// ------------------------------------------------------------------------
// Quiz
// ------------------------------------------------------------------------
// See if you can solve the following problems. The last two problems throw
// you a bit of a curve ball. Try solving them on your own. If you need
// help, scroll to the bottom of main to see some in depth explanations on
// toggling, setting, and clearing bits in Zig.
print("Toggle pins with XOR on PORTB\n", .{});
print("-----------------------------\n", .{});
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0101});
PORTB ^= (1 << 1) | (1 << 0); // What's wrong here?
checkAnswer(0b1001, PORTB);
newline();
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0011});
PORTB ^= (1 << 1) & (1 << 0); // What's wrong here?
checkAnswer(0b1111, PORTB);
newline();
print("Set pins with OR on PORTB\n", .{});
print("-------------------------\n", .{});
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
PORTB = PORTB ??? (1 << 2); // What's missing here?
checkAnswer(0b1101, PORTB);
newline();
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
PORTB ??? (1 << 2); // What's missing here?
checkAnswer(0b1101, PORTB);
newline();
print("Clear pins with AND and NOT on PORTB\n", .{});
print("------------------------------------\n", .{});
PORTB = 0b1110; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1011});
PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here?
checkAnswer(0b1010, PORTB);
newline();
PORTB = 0b0111; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1110});
PORTB &= ~(1 << 0); // What's missing here?
checkAnswer(0b0110, PORTB);
newline();
newline();
}
// ************************************************************************
// IN-DEPTH EXPLANATIONS BELOW
// ************************************************************************
//
//
//
//
//
//
//
//
//
//
//
// ------------------------------------------------------------------------
// Toggling bits with XOR:
// ------------------------------------------------------------------------
// XOR stands for "exclusive or". We can toggle bits with the ^ (XOR)
// bitwise operator, like so:
//
//
// In order to output a 1, the logic of an XOR operation requires that the
// two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will
// both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior
// of outputting a 0 when both inputs are 1s is what makes it different from
// the OR operator; it also gives us the ability to toggle bits by putting
// 1s into our bitmask.
//
// - 1s in our bitmask operand, can be thought of as causing the
// corresponding bits in the other operand to flip to the opposite value.
// - 0s cause no change.
//
// The 0s in our bitmask preserve these values
// -XOR op- ---expanded--- in the output.
// _______________/
// / /
// 1100 1 1 0 0
// ^ 0101 0 1 0 1 (bitmask)
// ------ - - - -
// = 1001 1 0 0 1 <- This bit was already cleared.
// \_______\
// \
// We can think of these bits having flipped
// because of the presence of 1s in those columns
// of our bitmask.
//
// Now let's take a look at setting bits with the | operator.
//
//
//
//
//
// ------------------------------------------------------------------------
// Setting bits with OR:
// ------------------------------------------------------------------------
// We can set bits on PORTB with the | (OR) operator, like so:
//
// var PORTB: u4 = 0b1001;
// PORTB = PORTB | 0b0010;
// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011
//
// -OR op- ---expanded---
// _ Set only this bit.
// /
// 1001 1 0 0 1
// | 0010 0 0 1 0 (bitmask)
// ------ - - - -
// = 1011 1 0 1 1
// \___\_______\
// \
// These bits remain untouched because OR-ing with
// a 0 effects no change.
//
// ------------------------------------------------------------------------
// To create a bitmask like 0b0010 used above:
//
// 1. First, shift the value 1 over one place with the bitwise << (shift
// left) operator as indicated below:
// 1 << 0 -> 0001
// 1 << 1 -> 0010 <-- Shift 1 one place to the left
// 1 << 2 -> 0100
// 1 << 3 -> 1000
//
// This allows us to rewrite the above code like this:
//
// var PORTB: u4 = 0b1001;
// PORTB = PORTB | (1 << 1);
// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011
//
// Finally, as in the C language, Zig allows us to use the |= operator, so
// we can rewrite our code again in an even more compact and idiomatic
// form: PORTB |= (1 << 1)
// So now we've covered how to toggle and set bits. What about clearing
// them? Well, this is where Zig throws us a curve ball. Don't worry we'll
// go through it step by step.
//
//
//
//
//
// ------------------------------------------------------------------------
// Clearing bits with AND and NOT:
// ------------------------------------------------------------------------
// We can clear bits with the & (AND) bitwise operator, like so:
// PORTB = 0b1110; // reset PORTB
// PORTB = PORTB & 0b1011;
// print("PORTB: {b:0>4}\n", .{PORTB}); // output -> 1010
//
// - 0s clear bits when used in conjunction with a bitwise AND.
// - 1s do nothing, thus preserving the original bits.
//
// -AND op- ---expanded---
// __________ Clear only this bit.
// /
// 1110 1 1 1 0
// & 1011 1 0 1 1 (bitmask)
// ------ - - - -
// = 1010 1 0 1 0 <- This bit was already cleared.
// \_______\
// \
// These bits remain untouched because AND-ing with a
// 1 preserves the original bit value whether 0 or 1.
//
// ------------------------------------------------------------------------
// We can use the ~ (NOT) operator to easily create a bitmask like 1011:
//
// 1. First, shift the value 1 over two places with the bit-wise << (shift
// left) operator as indicated below:
// 1 << 0 -> 0001
// 1 << 1 -> 0010
// 1 << 2 -> 0100 <- The 1 has been shifted two places to the left
// 1 << 3 -> 1000
//
// 2. The second step in creating our bitmask is to invert the bits
// ~0100 -> 1011
// in C we would write this as:
// ~(1 << 2) -> 1011
//
// But if we try to compile ~(1 << 2) in Zig, we'll get an error:
// unable to perform binary not operation on type 'comptime_int'
//
// Before Zig can invert our bits, it needs to know the number of
// bits it's being asked to invert.
//
// We do this with the @as (cast as) built-in like this:
// @as(u4, 1 << 2) -> 0100
//
// Finally, we can invert our new mask by placing the NOT ~ operator
// before our expression, like this:
// ~@as(u4, 1 << 2) -> 1011
//
// If you are offput by the fact that you can't simply invert bits like
// you can in languages such as C without casting to a particular size
// of integer, you're not alone. However, this is actually another
// instance where Zig is really helpful because it protects you from
// difficult to debug integer overflow bugs that can have you tearing
// your hair out. In the interest of keeping things sane, Zig requires
// you simply to tell it the size of number you are inverting. In the
// words of Andrew Kelley, "If you want to invert the bits of an
// integer, zig has to know how many bits there are."
//
// For more insight into the Zig team's position on why the language
// takes the approach it does with the ~ operator, take a look at
// Andrew's comments on the following github issue:
// https://github.com/ziglang/zig/issues/1382#issuecomment-414459529
//
// Whew, so after all that what we end up with is:
// PORTB = PORTB & ~@as(u4, 1 << 2);
//
// We can shorten this with the &= combined AND and assignment operator,
// which applies the AND operator on PORTB and then reassigns PORTB. Here's
// what that looks like:
// PORTB &= ~@as(u4, 1 << 2);
//
// ------------------------------------------------------------------------
// Conclusion
// ------------------------------------------------------------------------
//
// While the examples in this quiz have used only 4-bit wide variables,
// working with 8 bits is no different. Here's an example where we set
// every other bit beginning with the two's place:
// var PORTD: u8 = 0b0000_0000;
// print("PORTD: {b:0>8}\n", .{PORTD});
// PORTD |= (1 << 1);
// PORTD = setBit(u8, PORTD, 3);
// PORTD |= (1 << 5) | (1 << 7);
// print("PORTD: {b:0>8} // set every other bit\n", .{PORTD});
// PORTD = ~PORTD;
// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD});
// newline();
//
// // Here we clear every other bit beginning with the two's place.
//
// PORTD = 0b1111_1111;
// print("PORTD: {b:0>8}\n", .{PORTD});
// PORTD &= ~@as(u8, 1 << 1);
// PORTD = clearBit(u8, PORTD, 3);
// PORTD &= ~@as(u8, (1 << 5) | (1 << 7));
// print("PORTD: {b:0>8} // clear every other bit\n", .{PORTD});
// PORTD = ~PORTD;
// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD});
// newline();
// ----------------------------------------------------------------------------
// Here are some helper functions for manipulating bits
// ----------------------------------------------------------------------------
// Functions for setting, clearing, and toggling a single bit
fn setBit(comptime T: type, byte: T, comptime bit_pos: T) !T {
return byte | (1 << bit_pos);
}
test "setBit" {
try testing.expectEqual(setBit(u8, 0b0000_0000, 3), 0b0000_1000);
}
fn clearBit(comptime T: type, byte: T, comptime bit_pos: T) T {
return byte & ~@as(T, (1 << bit_pos));
}
test "clearBit" {
try testing.expectEqual(clearBit(u8, 0b1111_1111, 0), 0b1111_1110);
}
fn toggleBit(comptime T: type, byte: T, comptime bit_pos: T) T {
return byte ^ (1 << bit_pos);
}
test "toggleBit" {
var byte = toggleBit(u8, 0b0000_0000, 0);
try testing.expectEqual(byte, 0b0000_0001);
byte = toggleBit(u8, byte, 0);
try testing.expectEqual(byte, 0b0000_0000);
}
// ----------------------------------------------------------------------------
// Some additional functions for setting, clearing, and toggling multiple bits
// at once with a tuple because, hey, why not?
// ----------------------------------------------------------------------------
//
fn createBitmask(comptime T: type, comptime bits: anytype) !T {
comptime var bitmask: T = 0;
inline for (bits) |bit| {
if (bit >= @bitSizeOf(T)) return error.BitPosTooLarge;
if (bit < 0) return error.BitPosTooSmall;
bitmask |= (1 << bit);
}
return bitmask;
}
test "creating bitmasks from a tuple" {
try testing.expectEqual(createBitmask(u8, .{0}), 0b0000_0001);
try testing.expectEqual(createBitmask(u8, .{1}), 0b0000_0010);
try testing.expectEqual(createBitmask(u8, .{2}), 0b0000_0100);
try testing.expectEqual(createBitmask(u8, .{3}), 0b0000_1000);
//
try testing.expectEqual(createBitmask(u8, .{ 0, 4 }), 0b0001_0001);
try testing.expectEqual(createBitmask(u8, .{ 1, 5 }), 0b0010_0010);
try testing.expectEqual(createBitmask(u8, .{ 2, 6 }), 0b0100_0100);
try testing.expectEqual(createBitmask(u8, .{ 3, 7 }), 0b1000_1000);
try testing.expectError(error.BitPosTooLarge, createBitmask(u4, .{4}));
}
fn setBits(byte: u8, bits: anytype) !u8 {
const bitmask = try createBitmask(u8, bits);
return byte | bitmask;
}
test "setBits" {
try testing.expectEqual(setBits(0b0000_0000, .{0}), 0b0000_0001);
try testing.expectEqual(setBits(0b0000_0000, .{7}), 0b1000_0000);
try testing.expectEqual(setBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
try testing.expectEqual(setBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
try testing.expectEqual(setBits(0b0000_0000, .{ 2, 3, 4, 5 }), 0b0011_1100);
try testing.expectError(error.BitPosTooLarge, setBits(0b1111_1111, .{8}));
try testing.expectError(error.BitPosTooSmall, setBits(0b1111_1111, .{-1}));
}
fn clearBits(comptime byte: u8, comptime bits: anytype) !u8 {
const bitmask: u8 = try createBitmask(u8, bits);
return byte & ~@as(u8, bitmask);
}
test "clearBits" {
try testing.expectEqual(clearBits(0b1111_1111, .{0}), 0b1111_1110);
try testing.expectEqual(clearBits(0b1111_1111, .{7}), 0b0111_1111);
try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
try testing.expectEqual(clearBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 6, 7 }), 0b0011_1100);
try testing.expectError(error.BitPosTooLarge, clearBits(0b1111_1111, .{8}));
try testing.expectError(error.BitPosTooSmall, clearBits(0b1111_1111, .{-1}));
}
fn toggleBits(comptime byte: u8, comptime bits: anytype) !u8 {
const bitmask = try createBitmask(u8, bits);
return byte ^ bitmask;
}
test "toggleBits" {
try testing.expectEqual(toggleBits(0b0000_0000, .{0}), 0b0000_0001);
try testing.expectEqual(toggleBits(0b0000_0000, .{7}), 0b1000_0000);
try testing.expectEqual(toggleBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_0000);
try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3 }), 0b0000_0000);
try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 2, 4, 6 }), 0b0101_0101);
try testing.expectError(error.BitPosTooLarge, toggleBits(0b1111_1111, .{8}));
try testing.expectError(error.BitPosTooSmall, toggleBits(0b1111_1111, .{-1}));
}
// ----------------------------------------------------------------------------
// Utility functions
// ----------------------------------------------------------------------------
fn newline() void {
print("\n", .{});
}
fn checkAnswer(expected: u4, answer: u4) void {
if (expected != answer) {
print("*************************************************************\n", .{});
print("= {b:0>4} <- INCORRECT! THE EXPECTED OUTPUT IS {b:0>4}\n", .{ answer, expected });
print("*************************************************************\n", .{});
} else {
print("= {b:0>4}", .{answer});
}
newline();
}

View File

@ -12,12 +12,6 @@
# using the patches in this directory and convey them
# to convalesce in the healed directory.
#
delete_progress() {
progress_file=".progress.txt"
if [ -f $progress_file ]; then
rm $progress_file
fi
}
set -e
# We check ourselves before we wreck ourselves.
@ -29,12 +23,9 @@ fi
# Which version we have?
echo "Zig version" $(zig version)
echo "Eowyn version 25.1.9, let's try our magic power."
echo "Eowyn version 23.10.5.1, let's try our magic power."
echo ""
# Remove progress file
delete_progress
# Create directory of healing if it doesn't already exist.
mkdir -p patches/healed
@ -63,6 +54,3 @@ zig fmt --check patches/healed
# Test the healed exercises. May the compiler have mercy upon us.
zig build -Dhealed
# Remove progress file again
delete_progress

View File

@ -1,9 +1,9 @@
--- exercises/026_hello2.zig 2025-07-22 09:55:51.337832401 +0200
+++ answers/026_hello2.zig 2025-07-22 10:00:11.233348058 +0200
--- exercises/026_hello2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/026_hello2.zig 2023-10-05 20:04:06.959431737 +0200
@@ -23,5 +23,5 @@
// 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.interface.print("Hello world!\n", .{});
+ try stdout.interface.print("Hello world!\n", .{});
- stdout.print("Hello world!\n", .{});
+ try stdout.print("Hello world!\n", .{});
}

View File

@ -1,15 +1,15 @@
--- exercises/034_quiz4.zig 2025-07-22 09:55:51.337832401 +0200
+++ answers/034_quiz4.zig 2025-07-22 10:05:08.320323184 +0200
--- exercises/034_quiz4.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/034_quiz4.zig 2023-10-05 20:04:06.996099091 +0200
@@ -9,10 +9,10 @@
const NumError = error{IllegalNumber};
-pub fn main() void {
+pub fn main() !void {
var stdout = std.fs.File.stdout().writer(&.{});
const stdout = std.io.getStdOut().writer();
- const my_num: u32 = getNumber();
+ const my_num: u32 = try getNumber();
try stdout.interface.print("my_num={}\n", .{my_num});
try stdout.print("my_num={}\n", .{my_num});
}

View File

@ -1,19 +1,20 @@
--- exercises/046_optionals2.zig 2024-11-08 22:46:25.592875338 +0100
+++ answers/046_optionals2.zig 2024-11-08 22:46:20.699447951 +0100
@@ -22,7 +22,7 @@
--- exercises/046_optionals2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/046_optionals2.zig 2023-10-05 20:04:07.049433424 +0200
@@ -21,7 +21,7 @@
const Elephant = struct {
letter: u8,
- tail: *Elephant = null, // Hmm... tail needs something...
+ tail: ?*Elephant = null, // Hmm... tail needs something...
+ tail: ?*Elephant = null, // <---- make this optional!
visited: bool = false,
};
@@ -66,6 +66,6 @@
@@ -51,7 +51,7 @@
// We should stop once we encounter a tail that
// does NOT point to another element. What can
// we put here to make that happen?
- if (e.tail == null) ???;
+ if (e.tail == null) break;
// HINT: We want something similar to what `.?` does,
// but instead of ending the program, we want to exit the loop...
- e = e.tail ???
+ e = e.tail orelse break;
e = e.tail.?;
}
}

View File

@ -1,8 +1,8 @@
--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100
+++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100
--- exercises/058_quiz7.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/058_quiz7.zig 2023-10-05 20:04:07.106101152 +0200
@@ -192,8 +192,8 @@
// Oops! The hermit forgot how to capture the union values
// in a switch statement. Please capture each value as
// in a switch statement. Please capture both values as
// 'p' so the print statements work!
- .place => print("{s}", .{p.name}),
- .path => print("--{}->", .{p.dist}),

View File

@ -1,11 +1,11 @@
--- exercises/060_floats.zig 2025-03-03 20:23:40.255443963 +0400
+++ answers/060_floats.zig 2025-03-03 20:29:58.554854977 +0400
--- exercises/060_floats.zig 2023-11-06 19:45:03.609687304 +0100
+++ answers/060_floats.zig 2023-11-06 19:44:49.249419994 +0100
@@ -43,7 +43,7 @@
//
// We'll convert this weight from pounds to metric units at a
// conversion of 0.453592 kg to the pound.
- const shuttle_weight: f16 = 0.453592 * 4480e3;
+ const shuttle_weight: f32 = 0.453592 * 4.480e3;
// We'll convert this weight from pound to kilograms at a
// conversion of 0.453592kg to the pound.
- const shuttle_weight: f16 = 0.453592 * 4480e6;
+ const shuttle_weight: f32 = 0.453592 * 4.480e6;
// By default, float values are formatted in scientific
// notation. Try experimenting with '{d}' and '{d:.3}' to see

View File

@ -1,5 +1,5 @@
--- exercises/065_builtins2.zig 2025-06-17 13:58:07.857258167 +0200
+++ answers/065_builtins2.zig 2025-06-17 13:56:36.630415938 +0200
--- exercises/065_builtins2.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/065_builtins2.zig 2023-10-05 20:04:07.136101712 +0200
@@ -58,7 +58,7 @@
// Oops! We cannot leave the 'me' and 'myself' fields
// undefined. Please set them here:
@ -18,22 +18,22 @@
// Now we print a pithy statement about Narcissus.
print("A {s} loves all {s}es. ", .{
@@ -113,15 +113,15 @@
@@ -109,15 +109,15 @@
// Please complete these 'if' statements so that the field
// name will not be printed if the field is of type 'void'
// (which is a zero-bit type that takes up no space at all!):
- if (fields[0].??? != void) {
+ if (fields[0].type != void) {
print(" {s}", .{fields[0].name});
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[0].name});
}
- if (fields[1].??? != void) {
+ if (fields[1].type != void) {
print(" {s}", .{fields[1].name});
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[1].name});
}
- if (fields[2].??? != void) {
+ if (fields[2].type != void) {
print(" {s}", .{fields[2].name});
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[2].name});
}

View File

@ -1,8 +1,8 @@
--- exercises/071_comptime6.zig 2024-09-02 19:21:50.250454978 +0200
+++ answers/071_comptime6.zig 2024-09-02 19:21:23.553250563 +0200
--- exercises/071_comptime6.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/071_comptime6.zig 2023-10-05 20:04:07.162768879 +0200
@@ -40,7 +40,7 @@
const fields = @typeInfo(Narcissus).@"struct".fields;
const fields = @typeInfo(Narcissus).Struct.fields;
- ??? {
+ inline for (fields) |field| {

View File

@ -1,5 +1,5 @@
--- exercises/076_sentinels.zig 2024-09-02 19:27:04.336781039 +0200
+++ answers/076_sentinels.zig 2024-09-02 19:26:15.709134934 +0200
--- exercises/076_sentinels.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/076_sentinels.zig 2023-10-05 20:04:07.186102649 +0200
@@ -82,7 +82,7 @@
print("Array:", .{});

View File

@ -1,11 +1,11 @@
--- exercises/082_anonymous_structs3.zig 2025-03-14 16:41:17.892873287 +0200
+++ answers/082_anonymous_structs3.zig 2025-03-14 16:40:56.043829543 +0200
--- exercises/082_anonymous_structs3.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/082_anonymous_structs3.zig 2023-10-05 20:04:07.212769813 +0200
@@ -82,14 +82,14 @@
// @typeInfo(Circle).@"struct".fields
// @typeInfo(Circle).Struct.fields
//
// This will be an array of StructFields.
- const fields = ???;
+ const fields = @typeInfo(@TypeOf(tuple)).@"struct".fields;
+ const fields = @typeInfo(@TypeOf(tuple)).Struct.fields;
// 2. Loop through each field. This must be done at compile
// time.
@ -17,7 +17,7 @@
// 3. Print the field's name, type, and value.
//
// Each 'field' in this loop is one of these:
@@ -119,9 +119,9 @@
@@ -117,9 +117,9 @@
//
// The first field should print as: "0"(bool):true
print("\"{s}\"({any}):{any} ", .{

View File

@ -0,0 +1,11 @@
--- exercises/084_async.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/084_async.zig 2023-10-05 20:04:07.219436606 +0200
@@ -48,7 +48,7 @@
pub fn main() void {
// Additional Hint: you can assign things to '_' when you
// don't intend to do anything with them.
- foo();
+ _ = async foo();
}
fn foo() void {

View File

@ -0,0 +1,10 @@
--- exercises/085_async2.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/085_async2.zig 2023-10-05 20:04:07.226103397 +0200
@@ -19,6 +19,7 @@
pub fn main() void {
var foo_frame = async foo();
+ resume foo_frame;
}
fn foo() void {

View File

@ -0,0 +1,16 @@
--- exercises/086_async3.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/086_async3.zig 2023-10-05 20:04:07.229436793 +0200
@@ -13,7 +13,12 @@
const n = 5;
var foo_frame = async foo(n);
- ???
+ // Silly solution. You can also use a loop.
+ resume foo_frame;
+ resume foo_frame;
+ resume foo_frame;
+ resume foo_frame;
+ resume foo_frame;
print("\n", .{});
}

View File

@ -0,0 +1,21 @@
--- exercises/087_async4.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/087_async4.zig 2023-10-05 20:04:07.236103584 +0200
@@ -16,7 +16,7 @@
while (global_counter <= 5) {
print("{} ", .{global_counter});
- ???
+ resume foo_frame;
}
print("\n", .{});
@@ -24,7 +24,7 @@
fn foo() void {
while (true) {
- ???
- ???
+ global_counter += 1;
+ suspend {}
}
}

View File

@ -0,0 +1,11 @@
--- exercises/088_async5.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/088_async5.zig 2023-10-05 20:04:07.239436980 +0200
@@ -36,7 +36,7 @@
pub fn main() void {
var myframe = async getPageTitle("http://example.com");
- var value = ???
+ var value = await myframe;
print("{s}\n", .{value});
}

View File

@ -0,0 +1,13 @@
--- exercises/089_async6.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/089_async6.zig 2023-10-05 20:04:07.242770376 +0200
@@ -41,8 +41,8 @@
var com_frame = async getPageTitle("http://example.com");
var org_frame = async getPageTitle("http://example.org");
- var com_title = com_frame;
- var org_title = org_frame;
+ var com_title = await com_frame;
+ var org_title = await org_frame;
print(".com: {s}, .org: {s}.\n", .{ com_title, org_title });
}

View File

@ -0,0 +1,11 @@
--- exercises/090_async7.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/090_async7.zig 2023-10-05 20:04:07.249437167 +0200
@@ -29,7 +29,7 @@
// The main() function can not be async. But we know
// getBeef() will not suspend with this particular
// invocation. Please make this okay:
- var my_beef = getBeef(0);
+ var my_beef = nosuspend getBeef(0);
print("beef? {X}!\n", .{my_beef});
}

View File

@ -0,0 +1,26 @@
--- exercises/091_async8.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/091_async8.zig 2023-10-05 20:04:07.252770563 +0200
@@ -17,7 +17,7 @@
var frame = async suspendable();
- print("X", .{});
+ print("D", .{});
resume frame;
@@ -25,11 +25,11 @@
}
fn suspendable() void {
- print("X", .{});
+ print("B", .{});
suspend {
- print("X", .{});
+ print("C", .{});
}
- print("X", .{});
+ print("E", .{});
}

View File

@ -1,5 +1,5 @@
--- exercises/097_bit_manipulation.zig 2025-05-12 21:25:03.395385743 +0200
+++ answers/097_bit_manipulation.zig 2025-05-12 21:22:57.472986976 +0200
--- exercises/097_bit_manipulation.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/097_bit_manipulation.zig 2023-10-05 20:04:07.282771124 +0200
@@ -80,7 +80,7 @@
y ^= x;
@ -7,5 +7,5 @@
- ???;
+ x ^= y;
print("x = {b}; y = {b}\n", .{ x, y });
print("x = {d}; y = {d}\n", .{ x, y });
}

View File

@ -1,5 +1,5 @@
--- exercises/099_formatting.zig 2024-11-07 21:45:10.459123650 +0100
+++ answers/099_formatting.zig 2024-11-07 21:43:55.154345991 +0100
--- exercises/099_formatting.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/099_formatting.zig 2023-10-05 20:04:07.292771311 +0200
@@ -131,7 +131,7 @@
for (0..size) |b| {
// What formatting is needed here to make our columns

View File

@ -7,5 +7,5 @@
- for (hex_nums, ???) |hn, ???| {
+ for (hex_nums, dec_nums) |hn, dn| {
if (hn != dn) {
print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
return;

View File

@ -1,16 +1,16 @@
--- exercises/106_files.zig 2025-03-13 15:26:59.532367792 +0200
+++ answers/106_files.zig 2025-03-14 22:04:52.243435159 +0200
--- exercises/106_files.zig 2024-03-27 16:52:05.660910200 +0800
+++ answers/106_files.zig 2024-03-27 16:52:09.649422200 +0800
@@ -35,7 +35,7 @@
// by doing nothing
//
// we want to catch error.PathAlreadyExists and do nothing
- ??? => {},
+ error.PathAlreadyExists => {},
// if there's any other unexpected error we just propagate it through
// if is any other unexpected error we just propagate it through
else => return e,
};
@@ -44,7 +44,7 @@
// wait a minute...
// wait a minute
// opening a directory might fail!
// what should we do here?
- var output_dir: std.fs.Dir = cwd.openDir("output", .{});
@ -20,10 +20,10 @@
// we try to open the file `zigling.txt`,
@@ -55,7 +55,7 @@
// but here we are not yet done writing to the file
// if only there were a keyword in Zig that
// allowed you to "defer" code execution to the end of the scope...
// if only there are a keyword in zig that
// allow you "defer" code execute to the end of scope...
- file.close();
+ defer file.close();
// you are not allowed to move these two lines above the file closing line!
// !you are not allow to switch this two lines to before file closing line!
const byte_written = try file.write("It's zigling time!");

View File

@ -1,26 +1,26 @@
--- exercises/107_files2.zig 2025-03-13 15:26:59.532367792 +0200
+++ answers/107_files2.zig 2025-03-14 22:08:35.167953736 +0200
--- exercises/107_files2.zig 2024-03-27 16:51:56.199719600 +0800
+++ answers/107_files2.zig 2024-03-27 16:52:01.650935300 +0800
@@ -33,7 +33,7 @@
// initialize an array of u8 with all letter 'A'
// we need to pick the size of the array, 64 seems like a good number
// fix the initialization below
// initalize an array of u8 with all letter 'A'.
// we need to pick a size of the array, 64 seems like a good number.
// fix the initalization below
- var content = ['A']*64;
+ var content = [_]u8{'A'} ** 64;
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content});
@@ -41,12 +41,12 @@
// can you go here to find a way to read the content?
// can you go here to find a way to read the content ?
// https://ziglang.org/documentation/master/std/#std.fs.File
// hint: you might find two answers that are both valid in this case
- const bytes_read = zig_read_the_file_or_i_will_fight_you(&content);
+ const bytes_read = try file.read(&content);
// hint: you might find two answer that are both vaild in this case
- const byte_read = zig_read_the_file_or_i_will_fight_you(&content);
+ const byte_read = try file.read(&content);
// Woah, too screamy. I know you're excited for zigling time but tone it down a bit.
// Can you print only what we read from the file?
std.debug.print("Successfully Read {d} bytes: {s}\n", .{
bytes_read,
// Woah, too screamy, I know you're excited for zigling time but tone it down a bit
// Can you print only what we read from the file ?
std.debug.print("Successfully Read {d} byte: {s}\n", .{
byte_read,
- content, // change this line only
+ content[0..bytes_read], // change this line only
+ content[0..byte_read], // change this line only
});
}

View File

@ -1,18 +0,0 @@
--- exercises/108_labeled_switch.zig 2024-09-20 12:09:24.370066539 +0200
+++ answers/108_labeled_switch.zig 2024-09-20 12:09:06.499711739 +0200
@@ -65,13 +65,13 @@
// how would you fix it?
pr: switch (PullRequestState.Draft) {
PullRequestState.Draft => continue :pr PullRequestState.InReview,
- PullRequestState.InReview => continue :pr PullRequestState.Rejected,
+ PullRequestState.InReview => continue :pr PullRequestState.Approved,
PullRequestState.Approved => continue :pr PullRequestState.Merged,
PullRequestState.Rejected => {
std.debug.print("The pull request has been rejected.\n", .{});
return;
},
- PullRequestState.Merged => break, // Would you know where to break to?
+ PullRequestState.Merged => break :pr, // Would you know where to break to?
}
std.debug.print("The pull request has been merged.\n", .{});
}

View File

@ -1,13 +0,0 @@
--- exercises/109_vectors.zig 2024-11-07 14:57:09.673383618 +0100
+++ answers/109_vectors.zig 2024-11-07 14:22:59.069150138 +0100
@@ -121,8 +121,8 @@
const Vec4 = @Vector(4, f32);
fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
- const abs_diff_vec = ???;
- const max_diff = @reduce(???, abs_diff_vec);
+ const abs_diff_vec = @abs(a - b);
+ const max_diff = @reduce(.Max, abs_diff_vec);
return max_diff;
}

View File

@ -1,56 +0,0 @@
--- exercises/110_quiz9.zig 2025-02-08 13:19:48.522641785 -0800
+++ answers/110_quiz9.zig 2025-02-10 17:42:04.525004335 -0800
@@ -108,7 +108,7 @@
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0101});
- PORTB ^= (1 << 1) | (1 << 0); // What's wrong here?
+ PORTB ^= (1 << 2) | (1 << 0);
checkAnswer(0b1001, PORTB);
newline();
@@ -116,7 +116,7 @@
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0011});
- PORTB ^= (1 << 1) & (1 << 0); // What's wrong here?
+ PORTB ^= (1 << 1) | (1 << 0);
checkAnswer(0b1111, PORTB);
newline();
@@ -170,7 +170,7 @@
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
- PORTB = PORTB ??? (1 << 2); // What's missing here?
+ PORTB = PORTB | (1 << 2);
checkAnswer(0b1101, PORTB);
newline();
@@ -178,7 +178,7 @@
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
- PORTB ??? (1 << 2); // What's missing here?
+ PORTB |= (1 << 2);
checkAnswer(0b1101, PORTB);
newline();
@@ -269,7 +269,7 @@
PORTB = 0b1110; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1011});
- PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here?
+ PORTB = PORTB & ~@as(u4, 1 << 2);
checkAnswer(0b1010, PORTB);
newline();
@@ -277,7 +277,7 @@
PORTB = 0b0111; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1110});
- PORTB &= ~(1 << 0); // What's missing here?
+ PORTB &= ~@as(u4, 1 << 0);
checkAnswer(0b0110, PORTB);
newline();

View File

@ -50,7 +50,7 @@ pub fn addCliTests(b: *std.Build, exercises: []const Exercise) *Step {
case_step.dependOn(&verify.step);
}
const cleanup = b.addRemoveDirTree(.{ .src_path = .{ .owner = b, .sub_path = tmp_path } });
const cleanup = b.addRemoveDirTree(tmp_path);
cleanup.step.dependOn(case_step);
step.dependOn(&cleanup.step);
@ -82,7 +82,7 @@ pub fn addCliTests(b: *std.Build, exercises: []const Exercise) *Step {
const verify = CheckStep.create(b, exercises, stderr);
verify.step.dependOn(&cmd.step);
const cleanup = b.addRemoveDirTree(.{ .src_path = .{ .owner = b, .sub_path = tmp_path } });
const cleanup = b.addRemoveDirTree(tmp_path);
cleanup.step.dependOn(&verify.step);
step.dependOn(&cleanup.step);
@ -150,7 +150,7 @@ const CheckNamedStep = struct {
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner;
const self: *CheckNamedStep = @alignCast(@fieldParentPtr("step", step));
const ex = self.exercise;
@ -161,7 +161,7 @@ const CheckNamedStep = struct {
);
defer stderr_file.close();
var stderr = stderr_file.readerStreaming(&.{});
const stderr = stderr_file.reader();
{
// Skip the logo.
const nlines = mem.count(u8, root.logo, "\n");
@ -169,10 +169,10 @@ const CheckNamedStep = struct {
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf);
_ = try readLine(stderr, &buf);
}
}
try check_output(step, ex, &stderr);
try check_output(step, ex, stderr);
}
};
@ -202,7 +202,7 @@ const CheckStep = struct {
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner;
const self: *CheckStep = @alignCast(@fieldParentPtr("step", step));
const exercises = self.exercises;
@ -213,7 +213,7 @@ const CheckStep = struct {
);
defer stderr_file.close();
var stderr = stderr_file.readerStreaming(&.{});
const stderr = stderr_file.reader();
for (exercises) |ex| {
if (ex.number() == 1) {
// Skip the logo.
@ -222,15 +222,15 @@ const CheckStep = struct {
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf);
_ = try readLine(stderr, &buf);
}
}
try check_output(step, ex, &stderr);
try check_output(step, ex, stderr);
}
}
};
fn check_output(step: *Step, exercise: Exercise, reader: *Reader) !void {
fn check_output(step: *Step, exercise: Exercise, reader: Reader) !void {
const b = step.owner;
var buf: [1024]u8 = undefined;
@ -297,9 +297,12 @@ fn check(
}
}
fn readLine(reader: *fs.File.Reader, buf: []u8) !?[]const u8 {
try reader.interface.readSliceAll(buf);
return mem.trimRight(u8, buf, " \r\n");
fn readLine(reader: fs.File.Reader, buf: []u8) !?[]const u8 {
if (try reader.readUntilDelimiterOrEof(buf, '\n')) |line| {
return mem.trimRight(u8, line, " \r\n");
}
return null;
}
/// Fails with a custom error message.
@ -322,7 +325,7 @@ const FailStep = struct {
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner;
const self: *FailStep = @alignCast(@fieldParentPtr("step", step));
@ -365,7 +368,7 @@ const HealStep = struct {
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner;
const self: *HealStep = @alignCast(@fieldParentPtr("step", step));
@ -402,8 +405,7 @@ fn heal(allocator: Allocator, exercises: []const Exercise, work_path: []const u8
/// difference that returns an error when the temp path cannot be created.
pub fn makeTempPath(b: *Build) ![]const u8 {
const rand_int = std.crypto.random.int(u64);
const rand_hex64 = std.fmt.hex(rand_int);
const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ rand_hex64;
const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Build.hex64(rand_int);
const path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch
@panic("OOM");
try b.cache_root.handle.makePath(tmp_dir_sub_path);