64 Commits

Author SHA1 Message Date
Chris Boesch
83a89702b2 Merge pull request 'Improve command line style' (#451) from manliop/exercises:improve-command-line-style into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/451
2026-06-06 22:11:53 +02:00
Manlio Perillo
72a441a6bd Improve command line style
Parse the command line following the code from the Zig compiler cli.

Use the args iterator instead of slice, and use while instead of for
loop.

Use the new std.mem.cutPrefix instead of the custom function prefix.

Move Context struct to the top.
2026-06-06 17:39:42 +02:00
Chris Boesch
6aaf048935 Merge pull request 'removed unnecessary shell prompt' (#449) from readme-fixes into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/449
2026-06-04 20:17:48 +02:00
Chris Boesch
febc5e8bd2 removed unnecessary shell prompt 2026-06-04 20:16:35 +02:00
Chris Boesch
b7ff71cb9e Merge pull request 'Fix exercises 65, 71, 82 due to the build system update' (#443) from mark2185/exercises:fix-build-update into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/443
2026-06-03 18:37:25 +02:00
Chris Boesch
a403436fe8 Merge branch 'main' into fix-build-update 2026-06-03 17:39:58 +02:00
Chris Boesch
beeca8d510 Merge pull request 'Elrond has moved to Rivendell (where he lives)' (#447) from rivendell into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/447
2026-06-02 20:26:16 +02:00
Chris Boesch
01ad296114 Elrond has moved to Rivendell (where he lives) 2026-06-02 20:17:48 +02:00
Chris Boesch
9b87962595 Merge pull request 'fixed patch file path' (#446) from contribute-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/446
2026-06-02 20:05:42 +02:00
Chris Boesch
9d6ef1f5ba fixed patch file path 2026-06-02 20:03:18 +02:00
Chris Boesch
fadd2ea4c7 Merge pull request 'fixed minor issues' (#444) from elrond-fixes into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/444
2026-06-02 12:10:11 +02:00
Chris Boesch
e69a865ce3 fixed minor issues 2026-06-02 11:56:34 +02:00
Chris Boesch
f6dda8181a Merge pull request 'Add another defer exercise' (#438) from mark2185/exercises:defer3 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/438
2026-06-02 08:56:50 +02:00
Luka Markušić
bd55c4ac5a fixup! Add another defer exercise 2026-06-02 06:12:06 +02:00
Luka Markušić
e61bedaa25 Add another defer exercise 2026-06-01 22:32:11 +02:00
Chris Boesch
ac9f96459c Merge pull request 'fix removed array multiplication' (#436) from mark2185/exercises:array_mult into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/436
2026-06-01 20:50:08 +02:00
Luka Markušić
014560c3f5 fix removed array multiplication 2026-06-01 16:05:36 +02:00
Luka Markušić
3b865a0c17 Fix 082_anonymous_structs3.zig because of new build system 2026-06-01 16:00:27 +02:00
Luka Markušić
63c798637c Fix 071_comptime6.zig because of new build system 2026-06-01 15:37:05 +02:00
Luka Markušić
4480762e83 Fix 065_builtins2.zig because of new build system 2026-06-01 15:34:01 +02:00
Chris Boesch
4ce3ed23f3 Merge pull request 'updated version in history' (#439) from build-system-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/439
2026-06-01 00:23:49 +02:00
Chris Boesch
8db82ef84e updated version in history 2026-06-01 00:22:32 +02:00
Chris Boesch
6d23c876d6 Merge pull request 'started fix for new build system' (#437) from build-system-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/437
2026-06-01 00:20:31 +02:00
Chris Boesch
fa36a4520f removed ziglings logic from build file 2026-05-31 23:54:20 +02:00
Chris Boesch
7889d15b84 moved ziglings logic to elrond 2026-05-31 23:48:17 +02:00
Chris Boesch
8a0077e83b Merge pull request 'fix typos' (#435) from mark2185/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/435
2026-05-31 21:12:39 +02:00
Chris Boesch
762d8b8fa5 started fix for new build system 2026-05-31 20:00:50 +02:00
Luka Markušić
7300e87028 fix contributing instructions for patches 2026-05-31 19:10:30 +02:00
Luka Markušić
1c3238d619 fix typos 2026-05-31 19:10:30 +02:00
Chris Boesch
000e9448d5 Merge pull request '051: add a comma' (#429) from xiaolizhi/exercises:comma-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/429
2026-05-25 12:49:37 +02:00
xiaolizhi
f434dbfc01 051: add a comma 2026-05-25 10:37:05 +02:00
Chris Boesch
8468040a8e Merge pull request 'added expalantion for const pointer' (#428) from pointer into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/428
2026-05-22 22:27:15 +02:00
Chris Boesch
1d965491c1 added expalantion for const pointer 2026-05-22 21:58:19 +02:00
Chris Boesch
926fc04d81 Merge pull request 'Instruct user not to worry about sentinel syntax in 065_builtins2' (#425) from nickgrim/exercises:sentinel_warning into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/425
2026-05-18 19:50:18 +02:00
Chris Boesch
6b8bbfd980 Merge pull request 'Minor grammar fix to comment in 005_arrays2' (#426) from nickgrim/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/426
2026-05-18 19:49:52 +02:00
Nick Grimshaw
e96ce4da8e Minor grammar fix to comment in 005_arrays2 2026-05-18 17:38:04 +01:00
Nick Grimshaw
42a417f9a5 Instruct user not to worry about sentinel syntax in 065_builtins2 2026-05-18 17:29:17 +01:00
Chris Boesch
dde51b3126 Merge pull request 'fixed removed array multiplication' (#423) from array_mult into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/423
2026-05-04 17:30:31 +02:00
Chris Boesch
8af3372cf2 fixed removed array multiplication 2026-05-04 17:15:30 +02:00
Chris Boesch
1ba1e301a8 Merge pull request 'adjusted comment to zig 0.16' (#422) from i421 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/422
2026-05-02 23:08:18 +02:00
Chris Boesch
1c897e1951 adjusted comment to zig 0.16 2026-05-02 19:25:52 +02:00
Chris Boesch
6048e6ef21 Merge pull request 'fixed compileError printing' (#419) from i418 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/419
2026-04-30 19:53:56 +02:00
Chris Boesch
3a782a96d5 fixed compileError printing 2026-04-30 19:30:27 +02:00
Chris Boesch
4bdcf195b9 Merge pull request 'docs: update wrong comment reference to prior exercise in 110_files2' (#416) from kwyse/exercises:fix-110_files2 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/416
2026-04-23 10:20:48 +02:00
kwyse
656d6824d9 docs: update wrong reference to prior exercise
The comment should reference exercise 109, not 106.
2026-04-22 22:49:10 +02:00
Chris Boesch
0d9652d867 Merge pull request 'fix: move exercise 100 celebration comment to the right file' (#413) from Frost-Phoenix/exercises:fix_comment into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/413
2026-04-21 22:25:41 +02:00
Chris Boesch
945d9b84b0 Merge branch 'main' into fix_comment 2026-04-21 22:13:57 +02:00
Chris Boesch
c7afea6ef5 Merge pull request 'added skip to the C exercises' (#414) from fixing_cimport into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/414
2026-04-20 20:20:14 +02:00
Chris Boesch
6c2531b824 added skip to the C exercises 2026-04-20 19:38:31 +02:00
Chris Boesch
42db1f05a7 Merge pull request 'docs: fix wrong prerequisite in 110_files2.zig' (#412) from Kercy/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/412
2026-04-20 19:28:22 +02:00
Frost-Phoenix
333ca33e78 fix: move exercise 100 celebration comment to the right file 2026-04-20 17:40:49 +02:00
KercyDing
5eb9a30abe docs: fix wrong prerequisite in 110_files2.zig 2026-04-20 04:22:21 +08:00
Chris Boesch
0603b66cc7 Merge pull request 'fixed readme' (#411) from readme into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/411
2026-04-19 21:29:03 +02:00
Chris Boesch
a79e923768 fixed readme 2026-04-19 21:26:05 +02:00
Chris Boesch
234e829b15 Merge pull request 'fixed readme' (#409) from readme into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/409
2026-04-19 15:01:03 +02:00
Chris Boesch
823b4987f7 fixed readme 2026-04-19 14:49:29 +02:00
Chris Boesch
ca8aee84ad Merge pull request 'Demonstrate the benefit of a mutex' (#408) from MatthijsBlom/ziglings:matthijsblom-patch-1 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/408
2026-04-19 14:21:24 +02:00
MatthijsBlom
c4cb76ea8e Update patches/patches/091_async7.patch 2026-04-18 23:50:13 +02:00
MatthijsBlom
e0470c4f45 demonstrate the benefit of the mutex 2026-04-18 23:46:59 +02:00
Chris Boesch
f35c9419d2 Merge pull request 'fix: mutex unlock call in async example' (#407) from gabelluardo/ziglings:fix-async91 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/407
2026-04-18 21:50:49 +02:00
Gabriele Belluardo
656e1ba1e7 fix: mutex unlock call in async example 2026-04-18 11:55:49 +02:00
Chris Boesch
8e2cbf7b5a Merge pull request 'Some deprecated functions removed and a progress bar added.' (#403) from fixes_v016 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/403
2026-04-15 18:39:30 +02:00
Chris Boesch
d918dbae59 Some deprecated functions removed and a progress bar added. 2026-04-15 18:21:21 +02:00
Chris Boesch
4746ccc1c8 v0.16 2026-04-15 14:33:47 +02:00
40 changed files with 1507 additions and 2078 deletions

View File

@@ -111,7 +111,7 @@ directory.
Every Ziglings exercise contains mistakes on purpose. Every Ziglings exercise contains mistakes on purpose.
To keep our automated tests happy, each exercise also To keep our automated tests happy, each exercise also
has a patch in `patches/healed` that “heals” it. has a patch in `patches/patches` that “heals” it.
When you change an exercise, you will usually need to update When you change an exercise, you will usually need to update
its patch too. Thats where our little helper Gollum comes in: its patch too. Thats where our little helper Gollum comes in:
@@ -119,12 +119,12 @@ its patch too. Thats where our little helper Gollum comes in:
1. In the project root, create a folder called `answers/` 1. In the project root, create a folder called `answers/`
2. Put your solved version of the exercise file in there 2. Put your solved version of the exercise file in there
3. Back in the root, run: 3. Back in the root, run:
`./patches/gollum <exercise-number>`<br> `./patches/gollum.sh <exercise-number>`<br>
For example: `./patches/gollum 106` For example: `./patches/gollum.sh 106`
This will generate a shiny new patch. This will generate a shiny new patch.
Double-check everything by asking the magical Eowyn: Double-check everything by asking the magical Eowyn:
`./patches/eowyn`<br> `./patches/eowyn.sh`<br>
If all tests pass: You are done! If all tests pass: You are done!
Dont forget to commit the patch file. Dont forget to commit the patch file.

View File

@@ -1,7 +1,7 @@
# Ziglings # Ziglings
Welcome to Ziglings! This project contains a series of tiny Welcome to Ziglings! This project contains a series of tiny
broken programs (and one nasty surprise). By fixing them, you'll broken programs (and one nasty surprise). By fixing them, you'll
learn how to read and write [Zig](https://ziglang.org/) code. learn how to read and write [Zig](https://ziglang.org/) code.
![Ziglings](images/ziglings_dark.jpg "Ziglings") ![Ziglings](images/ziglings_dark.jpg "Ziglings")
@@ -44,8 +44,8 @@ in that case, you can download it directly from the [build directory](https://zi
Verify the installation and build number of `zig` like so: Verify the installation and build number of `zig` like so:
``` ```
$ zig version zig version
0.16.0-dev.xxxx+xxxxxxxxx 0.17.0-dev.xxxx+xxxxxxxxx
``` ```
Clone this repository with Git: Clone this repository with Git:
@@ -73,11 +73,11 @@ the appropriate tag.
The Zig language is under very active development. In order to be The Zig language is under very active development. In order to be
current, Ziglings tracks **development** builds of the Zig current, Ziglings tracks **development** builds of the Zig
compiler rather than versioned **release** builds. The last compiler rather than versioned **release** builds. The last
stable release was `0.15.2`, but Ziglings needs a dev build with stable release was `0.16`, but Ziglings needs a dev build with
pre-release version "0.16.0" and a build number at least as high pre-release version "0.17.0-dev" and a build number at least as high
as that shown in the example version check above. as that shown in the example version check above.
**Hint**: You can find a version change summary at the end of this README. **Hint**: You can find a summary of breaking changes at the end of this README.
It is likely that you'll download a build which is _greater_ than It is likely that you'll download a build which is _greater_ than
the minimum. the minimum.
@@ -106,12 +106,6 @@ Or let Ziglings pick an exercise for you:
zig build -Drandom zig build -Drandom
``` ```
You can also run without checking for correctness:
```
zig build -Dn=19 test
```
Or skip the build system entirely and interact directly with the Or skip the build system entirely and interact directly with the
compiler if you're into that sort of thing: compiler if you're into that sort of thing:
@@ -119,22 +113,10 @@ compiler if you're into that sort of thing:
zig run exercises/001_hello.zig zig run exercises/001_hello.zig
``` ```
Calling all wizards: To prepare an executable for debugging,
install it to zig-cache/bin with:
```
zig build -Dn=19 install
```
To get a list of all possible options, run: To get a list of all possible options, run:
``` ```
zig build -Dn=19 -l zig build -h
install Install 019_functions2.zig to prefix path
uninstall Uninstall 019_functions2.zig from prefix path
test Run 019_functions2.zig without checking output
...
``` ```
To reset the progress (have it run all the exercises that have already been completed): To reset the progress (have it run all the exercises that have already been completed):
@@ -211,6 +193,7 @@ Zig Standard Library
### Version Changes ### Version Changes
* 2026-05-31 zig 0.17.0-dev.607 - zig build: separate the maker process from the configurer process, see[#35428](https://codeberg.org/ziglang/zig/pulls/35428)
* 2026-03-20 zig 0.16.0-dev.2915 - `GeneralPurposeAllocator` was changed to `DebugAllocator` * 2026-03-20 zig 0.16.0-dev.2915 - `GeneralPurposeAllocator` was changed to `DebugAllocator`
* 2026-02-04 zig 0.16.0-dev.2471 - added process.Child.Cwd, see [#31090](https://codeberg.org/ziglang/zig/pulls/31090) * 2026-02-04 zig 0.16.0-dev.2471 - added process.Child.Cwd, see [#31090](https://codeberg.org/ziglang/zig/pulls/31090)
* 2026-01-09 zig 0.16.0-dev.2075 - move randomness API to `std.Io`, see [#30709](https://codeberg.org/ziglang/zig/pulls/30709) * 2026-01-09 zig 0.16.0-dev.2075 - move randomness API to `std.Io`, see [#30709](https://codeberg.org/ziglang/zig/pulls/30709)

1376
build.zig

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
// //
// Zig has some fun array operators. // Zig has one array operator.
// //
// You can use '++' to concatenate two arrays: // You can use '++' to concatenate two arrays:
// //
@@ -7,12 +7,8 @@
// const b = [_]u8{ 3,4 }; // const b = [_]u8{ 3,4 };
// const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5 // const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
// //
// You can use '**' to repeat an array: // Note that '++' only operates on arrays while your program is
// // _being compiled_. This special time is known in Zig
// const d = [_]u8{ 1,2,3 } ** 2; // equals 1 2 3 1 2 3
//
// Note that both '++' and '**' only operate on arrays while your
// program is _being compiled_. This special time is known in Zig
// parlance as "comptime" and we'll learn plenty more about that // parlance as "comptime" and we'll learn plenty more about that
// later. // later.
// //
@@ -30,7 +26,8 @@ pub fn main() void {
// (Problem 2) // (Problem 2)
// Please set this array using repetition. // Please set this array using repetition.
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1 // It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
const bit_pattern = [_]u8{ ??? } ** 3; const bit_pattern_unit = [_]u8{ ??? };
const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit)));
// Okay, that's all of the problems. Let's see the results. // Okay, that's all of the problems. Let's see the results.
// //

View File

@@ -27,10 +27,6 @@ pub fn main() void {
const d: u8 = ziggy[???]; const d: u8 = ziggy[???];
// (Problem 2) // (Problem 2)
// Use the array repeat '**' operator to make "ha ha ha ".
const laugh = "ha " ???;
// (Problem 3)
// Use the array concatenation '++' operator to make "Major Tom". // Use the array concatenation '++' operator to make "Major Tom".
// (You'll need to add a space as well!) // (You'll need to add a space as well!)
const major = "Major"; const major = "Major";
@@ -38,7 +34,7 @@ pub fn main() void {
const major_tom = major ??? tom; const major_tom = major ??? tom;
// That's all the problems. Let's see our results: // That's all the problems. Let's see our results:
std.debug.print("d={u} {s}{s}\n", .{ d, laugh, major_tom }); std.debug.print("d={u} {s}\n", .{ d, major_tom });
// Keen eyes will notice that we've put 'u' and 's' inside the '{}' // Keen eyes will notice that we've put 'u' and 's' inside the '{}'
// placeholders in the format string above. This tells the // placeholders in the format string above. This tells the
// print() function to format the values as a UTF-8 character and // print() function to format the values as a UTF-8 character and

View File

@@ -10,6 +10,8 @@ pub fn main() void {
for (animals) |a| printAnimal(a); for (animals) |a| printAnimal(a);
std.debug.print("done.\n", .{}); std.debug.print("done.\n", .{});
std.debug.print("Answer to everything? {d}\n", .{calculateTheUltimateQuestionOfLife()});
} }
// This function is _supposed_ to print an animal name in parentheses // This function is _supposed_ to print an animal name in parentheses
@@ -35,3 +37,24 @@ fn printAnimal(animal: u8) void {
std.debug.print("Unknown", .{}); std.debug.print("Unknown", .{});
} }
// This function is supposed to calculate the answer to the
// ultimate question of life, the universe, and everything,
// but it needs to be deferred as far in the future as possible,
// in order to gather more data.
//
// When there are multiple defers in a single block, they are executed in reverse order.
// This example might seem silly, but it's important to know when e.g.
// deinitializing containers whose elements need to be deinitialized first.
fn calculateTheUltimateQuestionOfLife() u32 {
var x: u32 = 100;
// Try reordering the statements to get the answer 42
{
defer x = x / 10;
defer x = x + 11;
defer x = x * 2;
}
return x;
}

View File

@@ -27,3 +27,19 @@ pub fn main() void {
std.debug.print("a: {}, b: {}\n", .{ a, b.* }); std.debug.print("a: {}, b: {}\n", .{ a, b.* });
} }
//
// A look into the future:
// When you allocate memory, you store the returned address in
// a const var. The pointer itself never changes — it always
// refers to the same allocation — but you can still read and
// write the data it points to.
//
// Example:
//
// const buf = try allocator.alloc(u8, 1024);
// buf[0] = 42; // fine: the *contents* are mutable
//
// Note:
// Passing this pointer to a function is cheap: it's just an address
// copied on the stack. The caller can work with the data without
// needing to know where it came from or how it was allocated.

View File

@@ -54,7 +54,7 @@ var global_wizard = Character{};
// an extremely efficient place for memory storage. // an extremely efficient place for memory storage.
// //
// Also, when a function executes, the input arguments are often // Also, when a function executes, the input arguments are often
// loaded into the beating heart of the CPU itself in registers. // loaded into the beating heart of the CPU itself, in registers.
// //
// Our main() function here has no input parameters, but it will have // Our main() function here has no input parameters, but it will have
// a stack entry (called a "frame"). // a stack entry (called a "frame").

View File

@@ -224,11 +224,10 @@ const NotebookEntry = struct {
// +---+----------------+----------------+----------+ // +---+----------------+----------------+----------+
// //
const HermitsNotebook = struct { const HermitsNotebook = struct {
// Remember the array repetition operator `**`? It is no mere // Remember the array repetition function @splat()? It is a great way
// novelty, it's also a great way to assign multiple items in an // to assign multiple items in an array without having to list them
// array without having to list them one by one. Here we use it to // one by one. Here we use it to initialize an array with null values.
// initialize an array with null values. entries: [place_count]?NotebookEntry = @splat(null),
entries: [place_count]?NotebookEntry = .{null} ** place_count,
// The next entry keeps track of where we are in our "todo" list. // The next entry keeps track of where we are in our "todo" list.
next_entry: u8 = 0, next_entry: u8 = 0,
@@ -409,7 +408,7 @@ pub fn main() void {
// aside memory for the trip and have the hermit's notebook fill // aside memory for the trip and have the hermit's notebook fill
// in the trip from the destination back to the path. Note that // in the trip from the destination back to the path. Note that
// this is the first time we've actually used the destination! // this is the first time we've actually used the destination!
var trip = [_]?TripItem{null} ** (place_count * 2); var trip: [place_count * 2]?TripItem = @splat(null);
notebook.getTripTo(trip[0..], destination) catch |err| { notebook.getTripTo(trip[0..], destination) catch |err| {
print("Oh no! {}\n", .{err}); print("Oh no! {}\n", .{err});

View File

@@ -27,10 +27,17 @@
// the types match. Zig does not perform unsafe type coercions // the types match. Zig does not perform unsafe type coercions
// behind your back: // behind your back:
// //
// var foo: f16 = 5; // NO ERROR // var foo: f16 = 5; // NO ERROR
//
// A runtime value can coerce to a different type,
// as long as the value is losslessly representable:
//
// var foo: u16 = 5;
// var bar: f16 = foo; // NO ERROR (5 fits in f16)
//
// var foo: u16 = 49876;
// var bar: f16 = foo; // ERROR (49876 not representable in f16)
// //
// var foo: u16 = 5; // A literal of a different type
// var bar: f16 = foo; // ERROR
// //
// Please fix the two float problems with this program and // Please fix the two float problems with this program and
// display the result as a whole number. // display the result as a whole number.

View File

@@ -93,36 +93,25 @@ pub fn main() void {
print("He has room in his heart for:", .{}); print("He has room in his heart for:", .{});
// A StructFields array // `field_names` is a slice of strings and it holds the names of the struct's fields
const fields = @typeInfo(Narcissus).@"struct".fields; // `field_types` is a slice of strings and it holds the types of the struct's fields,
// it is guaranteed to be the same length as `field_names`
const field_names = @typeInfo(Narcissus).@"struct".field_names;
const field_types = @typeInfo(Narcissus).@"struct".field_types;
// 'fields' is a slice of StructFields. Here's the declaration:
//
// pub const StructField = struct {
// name: [:0]const u8,
// type: type,
// default_value_ptr: ?*const anyopaque,
// 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 // Please complete these 'if' statements so that the field
// name will not be printed if the field is of type 'void' // 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!): // (which is a zero-bit type that takes up no space at all!):
if (fields[0].??? != void) { if (field_???[???] != void) {
print(" {s}", .{fields[0].name}); print(" {s}", .{field_???[???]});
} }
if (fields[1].??? != void) { if (field_???[???] != void) {
print(" {s}", .{fields[1].name}); print(" {s}", .{field_???[???]});
} }
if (fields[2].??? != void) { if (field_???[???] != void) {
print(" {s}", .{fields[2].name}); print(" {s}", .{field_???[???]});
} }
// Yuck, look at all that repeated code above! I don't know // Yuck, look at all that repeated code above! I don't know

View File

@@ -39,16 +39,16 @@ pub fn main() void {
var count = 0; var count = 0;
count += 1; count += 1;
const a1: [count]u8 = .{'A'} ** count; const a1: [count]u8 = @splat('A');
count += 1; count += 1;
const a2: [count]u8 = .{'B'} ** count; const a2: [count]u8 = @splat('B');
count += 1; count += 1;
const a3: [count]u8 = .{'C'} ** count; const a3: [count]u8 = @splat('C');
count += 1; count += 1;
const a4: [count]u8 = .{'D'} ** count; const a4: [count]u8 = @splat('D');
print("{s} {s} {s} {s}\n", .{ a1, a2, a3, a4 }); print("{s} {s} {s} {s}\n", .{ a1, a2, a3, a4 });

View File

@@ -36,13 +36,14 @@ pub fn main() void {
// statement was repeated three times almost verbatim. Yuck! // statement was repeated three times almost verbatim. Yuck!
// //
// Please use an 'inline for' to implement the block below // Please use an 'inline for' to implement the block below
// for each field in the slice 'fields'! // for each field in the corresponding slices (they're of the same length)!
const fields = @typeInfo(Narcissus).@"struct".fields; const field_names = @typeInfo(Narcissus).@"struct".field_names;
const field_types = @typeInfo(Narcissus).@"struct".field_types;
??? { ??? {
if (field.type != void) { if (field_type != void) {
print(" {s}", .{field.name}); print(" {s}", .{field_name});
} }
} }

View File

@@ -1,4 +1,5 @@
const print = @import("std").debug.print; const std = @import("std");
const print = std.debug.print;
// We're going to (ab)use the power of Zig to make animal hybrid creatures! // We're going to (ab)use the power of Zig to make animal hybrid creatures!
// What do you think a GatorMouse would look like? Eek. // What do you think a GatorMouse would look like? Eek.
@@ -31,7 +32,7 @@ fn makeCreature(comptime count: usize, comptime fmt: []const u8) [count]Animal {
// We return an array of animals representing the creature. (This is why we // We return an array of animals representing the creature. (This is why we
// really needed the 'count' parameter. Arrays need a size.) // really needed the 'count' parameter. Arrays need a size.)
var animals: [count]Animal = .{undefined} ** count; var animals: [count]Animal = undefined;
var next_animal: usize = 0; var next_animal: usize = 0;
inline for (fmt) |char| { inline for (fmt) |char| {
@@ -58,7 +59,7 @@ fn makeCreature(comptime count: usize, comptime fmt: []const u8) [count]Animal {
// other animals or is this an error? // other animals or is this an error?
'g' => ???, 'g' => ???,
else => @compileError("No animal starts with '" ++ char ++ "'!"), else => @compileError(std.fmt.comptimePrint("No animal starts with '{c}'!", .{char})),
}, },
.l => switch (char) { .l => switch (char) {

View File

@@ -48,9 +48,7 @@ const Path = struct {
// instead. // instead.
// //
// Please fill in the body of this function! // Please fill in the body of this function!
fn makePath(from: *Place, to: *Place, dist: u8) Path { fn makePath(from: *Place, to: *Place, dist: u8) Path {}
}
// Using our new function, these path definitions take up considerably less // Using our new function, these path definitions take up considerably less
// space in our program now! // space in our program now!
@@ -97,7 +95,7 @@ const NotebookEntry = struct {
}; };
const HermitsNotebook = struct { const HermitsNotebook = struct {
entries: [place_count]?NotebookEntry = .{null} ** place_count, entries: [place_count]?NotebookEntry = @splat(null),
next_entry: u8 = 0, next_entry: u8 = 0,
end_of_entries: u8 = 0, end_of_entries: u8 = 0,
@@ -193,7 +191,7 @@ pub fn main() void {
} }
} }
var trip = [_]?TripItem{null} ** (place_count * 2); var trip: [place_count * 2]?TripItem = @splat(null);
notebook.getTripTo(trip[0..], destination) catch |err| { notebook.getTripTo(trip[0..], destination) catch |err| {
print("Oh no! {}\n", .{err}); print("Oh no! {}\n", .{err});

View File

@@ -74,36 +74,27 @@ fn printTuple(tuple: anytype) void {
// @typeInfo() - takes a type, returns a TypeInfo union // @typeInfo() - takes a type, returns a TypeInfo union
// with fields specific to that type. // with fields specific to that type.
// //
// The list of a struct type's fields can be found in // The list of a struct type's field types can be found in
// TypeInfo's @"struct".fields. // TypeInfo's @"struct".field_types.
// //
// Example: // Example:
// //
// @typeInfo(Circle).@"struct".fields // @typeInfo(Circle).@"struct".field_types
// //
// This will be an array of StructFields. // This will be an array of field types.
const fields = ???; const field_types = ???;
// This will be an array of field names.
const field_names = ???;
// 2. Loop through each field. This must be done at compile // 2. Loop through each field. This must be done at compile
// time. // time.
// //
// Hint: remember 'inline' loops? // Hint: remember 'inline' loops?
// //
for (fields) |field| { for (???, ???) |???, ???| {
// 3. Print the field's name, type, and value. // 3. Print the field's name, type, and value.
// //
// Each 'field' in this loop is one of these:
//
// pub const StructField = struct {
// name: [:0]const u8,
// type: type,
// default_value_ptr: ?*const anyopaque,
// is_comptime: bool,
// alignment: comptime_int,
// };
//
// Note we will learn about 'anyopaque' type later
//
// You'll need this builtin: // You'll need this builtin:
// //
// @field(lhs: anytype, comptime field_name: []const u8) // @field(lhs: anytype, comptime field_name: []const u8)
@@ -123,8 +114,8 @@ fn printTuple(tuple: anytype) void {
// for declarations. If it's a value, it looks for data. // for declarations. If it's a value, it looks for data.
// //
print("\"{s}\"({any}):{any} ", .{ print("\"{s}\"({any}):{any} ", .{
field.???, field_name,
field.???, field_type,
???, ???,
}); });
} }

View File

@@ -6,7 +6,7 @@
// //
// // In a task: // // In a task:
// try mutex.lock(io); // blocks until lock is acquired // try mutex.lock(io); // blocks until lock is acquired
// defer mutex.unlock(); // defer mutex.unlock(io);
// // ... critical section: safe to modify shared data ... // // ... critical section: safe to modify shared data ...
// //
// Without the mutex, concurrent tasks could read and write the // Without the mutex, concurrent tasks could read and write the
@@ -52,6 +52,12 @@ fn increment(io: std.Io, state: *SharedState, times: u32) void {
state.mutex.??? catch return; state.mutex.??? catch return;
defer state.mutex.unlock(); // <-- what's missing here? defer state.mutex.unlock(); // <-- what's missing here?
// Sleep to give the other tasks a chance to run in the meantime.
// We do this here only to make nondeterminism more visible.
io.sleep(std.Io.Duration.fromMilliseconds(1), .awake) catch {};
// What happens if you neglect to lock the mutex?
state.counter += 1; state.counter += 1;
} }
} }

View File

@@ -93,3 +93,12 @@ pub fn main() !void {
// //
// For Crypto it is better not to use this, but in sorting algorithms like // For Crypto it is better not to use this, but in sorting algorithms like
// Bubble Sort it works very well. // Bubble Sort it works very well.
//
// By the way, congratulations for making it to Exercise 100!
//
// +-------------+
// | Celebration |
// | Area * * * |
// +-------------+
//
// Please keep your celebrating within the area provided.

View File

@@ -51,12 +51,3 @@ pub fn main() void {
// //
// You are perhaps wondering what happens if one of the two lists // You are perhaps wondering what happens if one of the two lists
// is longer than the other? Try it! // is longer than the other? Try it!
//
// By the way, congratulations for making it to Exercise 100!
//
// +-------------+
// | Celebration |
// | Area * * * |
// +-------------+
//
// Please keep your celebrating within the area provided.

View File

@@ -14,7 +14,7 @@
// exercises. // exercises.
// //
// A nice example of this has been published on the Zig homepage, // A nice example of this has been published on the Zig homepage,
// replacing the somewhat dusty 'Hello world! // replacing the somewhat dusty 'Hello world!'
// //
// Nothing against 'Hello world!', but it just doesn't do justice // Nothing against 'Hello world!', but it just doesn't do justice
// to the elegance of Zig and that's a pity, if someone takes a short, // to the elegance of Zig and that's a pity, if someone takes a short,

View File

@@ -1,6 +1,6 @@
// //
// Prerequisite : // Prerequisite :
// - exercise/106_files.zig, or // - exercise/109_files.zig, or
// - create a file {project_root}/output/zigling.txt // - create a file {project_root}/output/zigling.txt
// with content `It's zigling time!`(18 bytes total) // with content `It's zigling time!`(18 bytes total)
// //
@@ -28,7 +28,7 @@ pub fn main(init: std.process.Init) !void {
// Get the current working directory // Get the current working directory
const cwd = std.Io.Dir.cwd(); const cwd = std.Io.Dir.cwd();
// try to open ./output assuming you did your 106_files exercise // try to open ./output assuming you did your 109_files exercise
var output_dir = try cwd.openDir(io, "output", .{}); var output_dir = try cwd.openDir(io, "output", .{});
defer output_dir.close(io); defer output_dir.close(io);
@@ -36,10 +36,10 @@ pub fn main(init: std.process.Init) !void {
const file = try output_dir.openFile(io, "zigling.txt", .{}); const file = try output_dir.openFile(io, "zigling.txt", .{});
defer file.close(io); defer file.close(io);
// initialize an array of u8 with all letter 'A' // initialize an array of u8 entirely with the letter 'A'
// we need to pick the size of the array, 64 seems like a good number // we need to pick the size of the array, 64 seems like a good number
// fix the initialization below // do you remember the array repetition function?
var content = ['A']*64; var content: ??? = ???('A');
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA` // this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content}); std.debug.print("{s}\n", .{content});

View File

@@ -73,7 +73,7 @@
// Operations performed on vectors in Zig will be done in parallel using // Operations performed on vectors in Zig will be done in parallel using
// SIMD instructions, whenever possible. // SIMD instructions, whenever possible.
// //
// Defining vectors in Zig is straightforwards. No library import is needed. // Defining vectors in Zig is straightforward. No library import is needed.
const v1 = @Vector(3, i32){ 1, 10, 100 }; const v1 = @Vector(3, i32){ 1, 10, 100 };
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 }; const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };

View File

@@ -1,9 +1,9 @@
// //
// We've already learned plenty about bit manipulation using bitwise operations // We've already learned plenty about bit manipulation using bitwise operations
// in exercices 097 and 098 and in quiz 110. The techniques we already know work // in exercises 097 and 098 and in quiz 110. The techniques we already know work
// just fine, but creating masks and shifting individual bits around can become // just fine, but creating masks and shifting individual bits around can become
// quite tedious and unwieldy pretty quickly. // quite tedious and unwieldy pretty quickly.
// What if there was a better, a more convenient way to control invidivual bits? // What if there was a better, a more convenient way to control individual bits?
// //
// Luckily, Zig has a keyword for exactly this purpose: // Luckily, Zig has a keyword for exactly this purpose:
// //

View File

@@ -1,6 +1,6 @@
--- exercises/005_arrays2.zig 2023-10-03 22:15:22.122241138 +0200 --- exercises/005_arrays2.zig 2026-05-04 16:26:32.778330847 +0200
+++ answers/005_arrays2.zig 2023-10-05 20:04:06.862763262 +0200 +++ answers/005_arrays2.zig 2026-05-04 16:26:13.082917974 +0200
@@ -25,12 +25,12 @@ @@ -21,12 +21,12 @@
// (Problem 1) // (Problem 1)
// Please set this array concatenating the two arrays above. // Please set this array concatenating the two arrays above.
// It should result in: 1 3 3 7 // It should result in: 1 3 3 7
@@ -10,8 +10,8 @@
// (Problem 2) // (Problem 2)
// Please set this array using repetition. // Please set this array using repetition.
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1 // It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
- const bit_pattern = [_]u8{ ??? } ** 3; - const bit_pattern_unit = [_]u8{ ??? };
+ const bit_pattern = [_]u8{ 1, 0, 0, 1 } ** 3; + const bit_pattern_unit = [_]u8{ 1, 0, 0, 1 };
const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit)));
// Okay, that's all of the problems. Let's see the results. // Okay, that's all of the problems. Let's see the results.
//

View File

@@ -1,6 +1,6 @@
--- exercises/006_strings.zig 2023-10-03 22:15:22.122241138 +0200 --- exercises/006_strings.zig 2026-05-04 17:04:31.763821070 +0200
+++ answers/006_strings.zig 2023-10-05 20:04:06.869430053 +0200 +++ answers/006_strings.zig 2026-05-04 17:02:11.672866263 +0200
@@ -24,18 +24,18 @@ @@ -24,14 +24,14 @@
// (Problem 1) // (Problem 1)
// Use array square bracket syntax to get the letter 'd' from // Use array square bracket syntax to get the letter 'd' from
// the string "stardust" above. // the string "stardust" above.
@@ -8,11 +8,6 @@
+ const d: u8 = ziggy[4]; + const d: u8 = ziggy[4];
// (Problem 2) // (Problem 2)
// Use the array repeat '**' operator to make "ha ha ha ".
- const laugh = "ha " ???;
+ const laugh = "ha " ** 3;
// (Problem 3)
// Use the array concatenation '++' operator to make "Major Tom". // Use the array concatenation '++' operator to make "Major Tom".
// (You'll need to add a space as well!) // (You'll need to add a space as well!)
const major = "Major"; const major = "Major";
@@ -21,4 +16,4 @@
+ const major_tom = major ++ " " ++ tom; + const major_tom = major ++ " " ++ tom;
// That's all the problems. Let's see our results: // That's all the problems. Let's see our results:
std.debug.print("d={u} {s}{s}\n", .{ d, laugh, major_tom }); std.debug.print("d={u} {s}\n", .{ d, major_tom });

View File

@@ -1,6 +1,6 @@
--- exercises/028_defer2.zig 2023-10-03 22:15:22.122241138 +0200 --- exercises/028_defer2.zig 2026-06-02 06:08:12.713672612 +0200
+++ answers/028_defer2.zig 2023-10-05 20:04:06.966098530 +0200 +++ answers/028_defer2.zig 2026-06-02 06:08:43.262234023 +0200
@@ -18,7 +18,7 @@ @@ -20,7 +20,7 @@
fn printAnimal(animal: u8) void { fn printAnimal(animal: u8) void {
std.debug.print("(", .{}); std.debug.print("(", .{});
@@ -9,3 +9,15 @@
if (animal == 'g') { if (animal == 'g') {
std.debug.print("Goat", .{}); std.debug.print("Goat", .{});
@@ -51,9 +51,9 @@
// Try reordering the statements to get the answer 42
{
- defer x = x / 10;
- defer x = x + 11;
defer x = x * 2;
+ defer x = x + 11;
+ defer x = x / 10;
}
return x;

View File

@@ -1,5 +1,5 @@
--- exercises/040_pointers2.zig 2023-10-03 22:15:22.122241138 +0200 --- exercises/040_pointers2.zig 2026-05-22 21:57:28.601255748 +0200
+++ answers/040_pointers2.zig 2023-10-05 20:04:07.022766257 +0200 +++ answers/040_pointers2.zig 2026-05-22 21:57:27.672235943 +0200
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
pub fn main() void { pub fn main() void {

View File

@@ -1,5 +1,5 @@
--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100 --- exercises/058_quiz7.zig 2026-05-04 16:34:31.692458399 +0200
+++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100 +++ answers/058_quiz7.zig 2026-05-04 16:34:29.239406323 +0200
@@ -192,8 +192,8 @@ @@ -192,8 +192,8 @@
// Oops! The hermit forgot how to capture the union values // 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 each value as
@@ -11,7 +11,7 @@
} }
} }
}; };
@@ -255,7 +255,7 @@ @@ -254,7 +254,7 @@
// dereference and optional value "unwrapping" look // dereference and optional value "unwrapping" look
// together. Remember that you return the address with the // together. Remember that you return the address with the
// "&" operator. // "&" operator.
@@ -20,7 +20,7 @@
// Try to make your answer this long:__________; // Try to make your answer this long:__________;
} }
return null; return null;
@@ -309,7 +309,7 @@ @@ -308,7 +308,7 @@
// //
// Looks like the hermit forgot something in the return value of // Looks like the hermit forgot something in the return value of
// this function. What could that be? // this function. What could that be?

View File

@@ -1,6 +1,6 @@
--- exercises/060_floats.zig 2026-04-03 14:40:17.582344768 +0200 --- exercises/060_floats.zig 2026-05-02 19:22:46.225370223 +0200
+++ answers/060_floats.zig 2026-04-03 14:49:26.997886326 +0200 +++ answers/060_floats.zig 2026-05-02 19:22:47.523142218 +0200
@@ -43,7 +43,7 @@ @@ -50,7 +50,7 @@
// //
// We'll convert this weight from pounds to metric units at a // We'll convert this weight from pounds to metric units at a
// conversion of 0.453592 kg to the pound. // conversion of 0.453592 kg to the pound.
@@ -9,7 +9,7 @@
// By default, float values are formatted in standard decimal // By default, float values are formatted in standard decimal
// notation. Experiment with '{d}' and '{d:.3}' to see how // notation. Experiment with '{d}' and '{d:.3}' to see how
@@ -51,7 +51,7 @@ @@ -58,7 +58,7 @@
// scientific notation. // scientific notation.
// NOTE: The weight of the shuttle is a huge number, a scientific notation // NOTE: The weight of the shuttle is a huge number, a scientific notation
// may be more appropriate. // may be more appropriate.

View File

@@ -1,5 +1,5 @@
--- exercises/065_builtins2.zig 2026-02-27 13:10:36 --- exercises/065_builtins2.zig 2026-06-01 15:33:16.617432671 +0200
+++ answers/065_builtins2.zig 2026-02-27 13:10:52 +++ answers/065_builtins2.zig 2026-06-01 15:33:31.104018108 +0200
@@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
// Oops! We cannot leave the 'me' and 'myself' fields // Oops! We cannot leave the 'me' and 'myself' fields
// undefined. Please set them here: // undefined. Please set them here:
@@ -18,22 +18,26 @@
// Now we print a pithy statement about Narcissus. // Now we print a pithy statement about Narcissus.
print("A {s} loves all {s}es. ", .{ print("A {s} loves all {s}es. ", .{
@@ -113,15 +113,15 @@ @@ -102,16 +102,16 @@
// Please complete these 'if' statements so that the field // Please complete these 'if' statements so that the field
// name will not be printed if the field is of type 'void' // 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!): // (which is a zero-bit type that takes up no space at all!):
- if (fields[0].??? != void) { - if (field_???[???] != void) {
+ if (fields[0].type != void) { - print(" {s}", .{field_???[???]});
print(" {s}", .{fields[0].name}); + if (field_types[0] != void) {
+ print(" {s}", .{field_names[0]});
} }
- if (fields[1].??? != void) { - if (field_???[???] != void) {
+ if (fields[1].type != void) { - print(" {s}", .{field_???[???]});
print(" {s}", .{fields[1].name}); + if (field_types[1] != void) {
+ print(" {s}", .{field_names[1]});
} }
- if (fields[2].??? != void) { - if (field_???[???] != void) {
+ if (fields[2].type != void) { - print(" {s}", .{field_???[???]});
print(" {s}", .{fields[2].name}); + if (field_types[2] != void) {
+ print(" {s}", .{field_names[2]});
} }
// Yuck, look at all that repeated code above! I don't know

View File

@@ -1,5 +1,5 @@
--- exercises/067_comptime2.zig 2025-12-07 22:51:24.396031248 +0100 --- exercises/067_comptime2.zig 2026-05-04 15:38:52.565144012 +0200
+++ answers/067_comptime2.zig 2025-12-07 22:50:18.721616293 +0100 +++ answers/067_comptime2.zig 2026-05-04 15:37:20.257213463 +0200
@@ -36,7 +36,7 @@ @@ -36,7 +36,7 @@
// In this contrived example, we've decided to allocate some // In this contrived example, we've decided to allocate some
// arrays using a variable count! But something's missing... // arrays using a variable count! But something's missing...
@@ -8,4 +8,4 @@
+ comptime var count = 0; + comptime var count = 0;
count += 1; count += 1;
const a1: [count]u8 = .{'A'} ** count; const a1: [count]u8 = @splat('A');

View File

@@ -1,11 +1,11 @@
--- exercises/071_comptime6.zig 2024-09-02 19:21:50.250454978 +0200 --- exercises/071_comptime6.zig 2026-06-01 15:35:27.223400223 +0200
+++ answers/071_comptime6.zig 2024-09-02 19:21:23.553250563 +0200 +++ answers/071_comptime6.zig 2026-06-01 15:36:35.349728561 +0200
@@ -40,7 +40,7 @@ @@ -41,7 +41,7 @@
const field_names = @typeInfo(Narcissus).@"struct".field_names;
const fields = @typeInfo(Narcissus).@"struct".fields; const field_types = @typeInfo(Narcissus).@"struct".field_types;
- ??? { - ??? {
+ inline for (fields) |field| { + inline for (field_names, field_types) |field_name, field_type| {
if (field.type != void) { if (field_type != void) {
print(" {s}", .{field.name}); print(" {s}", .{field_name});
} }

View File

@@ -1,6 +1,6 @@
--- exercises/074_comptime9.zig 2026-04-11 21:35:08.132459373 -0700 --- exercises/074_comptime9.zig 2026-05-04 17:11:05.144118157 +0200
+++ answers/074_comptime9.zig 2026-04-12 07:13:37.971010827 -0700 +++ answers/074_comptime9.zig 2026-05-04 17:10:36.778519877 +0200
@@ -27,12 +27,12 @@ @@ -28,12 +28,12 @@
start, // Ready to start a new animal. start, // Ready to start a new animal.
l, // This means we've seen an "l", so if we see an "m", we know it's a Llama. l, // This means we've seen an "l", so if we see an "m", we know it's a Llama.
}; };
@@ -9,22 +9,22 @@
// We return an array of animals representing the creature. (This is why we // We return an array of animals representing the creature. (This is why we
// really needed the 'count' parameter. Arrays need a size.) // really needed the 'count' parameter. Arrays need a size.)
var animals: [count]Animal = .{undefined} ** count; var animals: [count]Animal = undefined;
- var next_animal: usize = 0; - var next_animal: usize = 0;
+ comptime var next_animal: usize = 0; + comptime var next_animal: usize = 0;
inline for (fmt) |char| { inline for (fmt) |char| {
@@ -56,7 +56,7 @@ @@ -57,7 +57,7 @@
// //
// What do you think happens with Gators? Do they join with // What do you think happens with Gators? Do they join with
// other animals or is this an error? // other animals or is this an error?
- 'g' => ???, - 'g' => ???,
+ 'g' => @compileError("Gators refuse to join with other animals."), + 'g' => @compileError("Gators refuse to join with other animals."),
else => @compileError("No animal starts with '" ++ char ++ "'!"), else => @compileError(std.fmt.comptimePrint("No animal starts with '{c}'!", .{char})),
}, },
@@ -68,7 +68,7 @@ @@ -69,7 +69,7 @@
next_animal += 1; next_animal += 1;
// Something is missing here. After we finish a Llama, we // Something is missing here. After we finish a Llama, we
// need to be ready to _start_ over with a new animal... // need to be ready to _start_ over with a new animal...

View File

@@ -1,15 +1,17 @@
--- exercises/075_quiz8.zig 2023-11-21 14:48:15.440702720 +0100 --- exercises/075_quiz8.zig 2026-05-04 15:51:48.254371574 +0200
+++ answers/075_quiz8.zig 2023-11-21 14:50:23.453311616 +0100 +++ answers/075_quiz8.zig 2026-05-04 15:49:28.426445382 +0200
@@ -49,7 +49,11 @@ @@ -48,7 +48,13 @@
// instead.
// //
// Please fill in the body of this function! // Please fill in the body of this function!
fn makePath(from: *Place, to: *Place, dist: u8) Path { -fn makePath(from: *Place, to: *Place, dist: u8) Path {}
- +fn makePath(from: *Place, to: *Place, dist: u8) Path {
+ return Path{ + return Path{
+ .from = from, + .from = from,
+ .to = to, + .to = to,
+ .dist = dist, + .dist = dist,
+ }; + };
} +}
// Using our new function, these path definitions take up considerably less // Using our new function, these path definitions take up considerably less
// space in our program now!

View File

@@ -1,32 +1,32 @@
--- exercises/082_anonymous_structs3.zig 2026-02-27 13:05:46 --- exercises/082_anonymous_structs3.zig 2026-06-01 15:59:11.872467805 +0200
+++ answers/082_anonymous_structs3.zig 2026-02-27 13:07:22 +++ answers/082_anonymous_structs3.zig 2026-06-01 15:58:38.004730144 +0200
@@ -82,14 +82,14 @@ @@ -82,17 +82,17 @@
// @typeInfo(Circle).@"struct".fields // @typeInfo(Circle).@"struct".field_types
// //
// This will be an array of StructFields. // This will be an array of field types.
- const fields = ???; - const field_types = ???;
+ const fields = @typeInfo(@TypeOf(tuple)).@"struct".fields; + const field_types = @typeInfo(@TypeOf(tuple)).@"struct".field_types;
// This will be an array of field names.
- const field_names = ???;
+ const field_names = @typeInfo(@TypeOf(tuple)).@"struct".field_names;
// 2. Loop through each field. This must be done at compile // 2. Loop through each field. This must be done at compile
// time. // time.
// //
// Hint: remember 'inline' loops? // Hint: remember 'inline' loops?
// //
- for (fields) |field| { - for (???, ???) |???, ???| {
+ inline for (fields) |field| { + inline for (field_types, field_names) |field_type, field_name| {
// 3. Print the field's name, type, and value. // 3. Print the field's name, type, and value.
// //
// Each 'field' in this loop is one of these: // You'll need this builtin:
@@ -123,9 +123,9 @@ @@ -116,7 +116,7 @@
// for declarations. If it's a value, it looks for data.
//
print("\"{s}\"({any}):{any} ", .{ print("\"{s}\"({any}):{any} ", .{
- field.???, field_name,
- field.???, field_type,
- ???, - ???,
+ field.name, + @field(tuple, field_name),
+ field.type,
+ @field(tuple, field.name),
}); });
} }
} }

View File

@@ -1,5 +1,5 @@
--- exercises/091_async7.zig 2026-04-02 10:50:08.142508099 +0200 --- exercises/091_async7.zig 2026-04-18 23:30:40.963951835 +0200
+++ answers/091_async7.zig 2026-04-02 10:49:59.629341593 +0200 +++ answers/091_async7.zig 2026-04-18 23:33:47.313340585 +0200
@@ -49,8 +49,8 @@ @@ -49,8 +49,8 @@
for (0..times) |_| { for (0..times) |_| {
// Acquire the lock before modifying shared state. // Acquire the lock before modifying shared state.
@@ -9,5 +9,5 @@
+ state.mutex.lock(io) catch return; + state.mutex.lock(io) catch return;
+ defer state.mutex.unlock(io); + defer state.mutex.unlock(io);
state.counter += 1; // Sleep to give the other tasks a chance to run in the meantime.
} // We do this here only to make nondeterminism more visible.

View File

@@ -1,11 +1,11 @@
--- exercises/110_files2.zig 2026-03-20 19:23:48.874150121 +0100 --- exercises/110_files2.zig 2026-05-31 14:54:55.061019159 +0200
+++ answers/110_files2.zig 2026-04-02 10:51:15.815831734 +0200 +++ answers/110_files2.zig 2026-05-31 14:54:42.655691238 +0200
@@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
// initialize an array of u8 with all letter 'A' // initialize an array of u8 entirely with the letter 'A'
// we need to pick the size of the array, 64 seems like a good number // we need to pick the size of the array, 64 seems like a good number
// fix the initialization below // do you remember the array repetition function?
- var content = ['A']*64; - var content: ??? = ???('A');
+ var content = [_]u8{'A'} ** 64; + var content: [64]u8 = @splat('A');
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA` // this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content}); std.debug.print("{s}\n", .{content});

1224
rivendell/elrond.zig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,413 +0,0 @@
const std = @import("std");
const root = @import("../build.zig");
const debug = std.debug;
const fmt = std.fmt;
const mem = std.mem;
const Allocator = std.mem.Allocator;
const Process = std.process;
const Build = std.Build;
const Step = Build.Step;
const RunStep = Build.RunStep;
const LazyPath = Build.LazyPath;
const Exercise = root.Exercise;
pub fn addCliTests(b: *std.Build, exercises: []const Exercise) *Step {
const step = b.step("test-cli", "Test the command line interface");
{
// Test that `zig build -Dhealed -Dn=n` selects the nth exercise.
const case_step = createCase(b, "case-1");
const tmp_path = createTempPath(b) catch |err| {
return fail(step, "unable to make tmp path: {s}\n", .{@errorName(err)});
};
defer deleteTmpPath(b, tmp_path);
const heal_step = HealStep.create(b, exercises, tmp_path);
for (exercises[0 .. exercises.len - 1]) |ex| {
const n = ex.number();
const cmd = b.addSystemCommand(&.{
b.graph.zig_exe,
"build",
"-Dhealed",
b.fmt("-Dhealed-path={s}", .{tmp_path}),
b.fmt("-Dn={}", .{n}),
});
cmd.setName(b.fmt("zig build -Dhealed -Dn={}", .{n}));
cmd.expectExitCode(0);
cmd.step.dependOn(&heal_step.step);
const stderr = cmd.captureStdErr(.{});
const verify = CheckNamedStep.create(b, ex, stderr);
verify.step.dependOn(&cmd.step);
case_step.dependOn(&verify.step);
}
}
{
// Test that `zig build -Dhealed` processes all the exercises in order.
const case_step = createCase(b, "case-2");
const tmp_path = createTempPath(b) catch |err| {
return fail(step, "unable to make tmp path: {s}\n", .{@errorName(err)});
};
defer deleteTmpPath(b, tmp_path);
const heal_step = HealStep.create(b, exercises, tmp_path);
heal_step.step.dependOn(case_step);
// TODO: when an exercise is modified, the cache is not invalidated.
const cmd = b.addSystemCommand(&.{
b.graph.zig_exe,
"build",
"-Dhealed",
b.fmt("-Dhealed-path={s}", .{tmp_path}),
});
cmd.setName("zig build -Dhealed");
cmd.expectExitCode(0);
cmd.step.dependOn(&heal_step.step);
const stderr = cmd.captureStdErr(.{});
const verify = CheckStep.create(b, exercises, stderr);
verify.step.dependOn(&cmd.step);
}
{
// Test that `zig build -Dn=n` prints the hint.
const case_step = createCase(b, "case-3");
for (exercises[0 .. exercises.len - 1]) |ex| {
if (ex.skip) continue;
if (ex.hint) |hint| {
const n = ex.number();
const cmd = b.addSystemCommand(&.{
b.graph.zig_exe,
"build",
b.fmt("-Dn={}", .{n}),
});
cmd.setName(b.fmt("zig build -Dn={}", .{n}));
cmd.expectExitCode(2);
cmd.addCheck(.{ .expect_stderr_match = hint });
case_step.dependOn(&cmd.step);
}
}
step.dependOn(case_step);
}
return step;
}
fn createCase(b: *Build, name: []const u8) *Step {
const case_step = b.allocator.create(Step) catch @panic("OOM");
case_step.* = Step.init(.{
.id = .custom,
.name = name,
.owner = b,
});
return case_step;
}
/// Checks the output of `zig build -Dn=n`.
const CheckNamedStep = struct {
step: Step,
exercise: Exercise,
stderr: LazyPath,
pub fn create(owner: *Build, exercise: Exercise, stderr: LazyPath) *CheckNamedStep {
const self = owner.allocator.create(CheckNamedStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "check-named",
.owner = owner,
.makeFn = make,
}),
.exercise = exercise,
.stderr = stderr,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const io = b.graph.io;
const self: *CheckNamedStep = @alignCast(@fieldParentPtr("step", step));
const ex = self.exercise;
const stderr_file = try std.Io.Dir.cwd().openFile(
io,
self.stderr.getPath(b),
.{ .mode = .read_only },
);
defer stderr_file.close(io);
var stderr = stderr_file.readerStreaming(io, &.{});
{
// Skip the logo.
const nlines = mem.count(u8, root.logo, "\n");
var buf: [80]u8 = undefined;
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf);
}
}
try check_output(step, ex, &stderr);
}
};
/// Checks the output of `zig build`.
const CheckStep = struct {
step: Step,
exercises: []const Exercise,
stderr: LazyPath,
pub fn create(
owner: *Build,
exercises: []const Exercise,
stderr: LazyPath,
) *CheckStep {
const self = owner.allocator.create(CheckStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "check",
.owner = owner,
.makeFn = make,
}),
.exercises = exercises,
.stderr = stderr,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const io = b.graph.io;
const self: *CheckStep = @alignCast(@fieldParentPtr("step", step));
const exercises = self.exercises;
const stderr_file = try std.Io.Dir.cwd().openFile(
io,
self.stderr.getPath(b),
.{ .mode = .read_only },
);
defer stderr_file.close(io);
var stderr = stderr_file.readerStreaming(io, &.{});
for (exercises) |ex| {
if (ex.number() == 1) {
// Skip the logo.
const nlines = mem.count(u8, root.logo, "\n");
var buf: [80]u8 = undefined;
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf);
}
}
try check_output(step, ex, &stderr);
}
}
};
fn check_output(step: *Step, exercise: Exercise, reader: *std.Io.File.Reader) !void {
const b = step.owner;
var buf: [1024]u8 = undefined;
if (exercise.skip) {
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = b.fmt("Skipping {s}", .{exercise.main_file});
try check(step, exercise, expect, actual);
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
try check(step, exercise, "", actual);
}
return;
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = b.fmt("Compiling {s}...", .{exercise.main_file});
try check(step, exercise, expect, actual);
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = b.fmt("Checking {s}...", .{exercise.main_file});
try check(step, exercise, expect, actual);
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = switch (exercise.kind) {
.exe => "PASSED:",
.@"test" => "PASSED",
};
try check(step, exercise, expect, actual);
}
// Skip the exercise output.
const nlines = switch (exercise.kind) {
.exe => 1 + mem.count(u8, exercise.output, "\n") + 1,
.@"test" => 1,
};
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(reader, &buf) orelse @panic("EOF");
}
}
fn check(
step: *Step,
exercise: Exercise,
expect: []const u8,
actual: []const u8,
) !void {
if (!mem.eql(u8, expect, actual)) {
return step.fail("{s}: expected to see \"{s}\", found \"{s}\"", .{
exercise.main_file,
expect,
actual,
});
}
}
fn readLine(reader: *std.Io.File.Reader, buf: []u8) !?[]const u8 {
try reader.interface.readSliceAll(buf);
return mem.trimEnd(u8, buf, " \r\n");
}
/// Fails with a custom error message.
const FailStep = struct {
step: Step,
error_msg: []const u8,
pub fn create(owner: *Build, error_msg: []const u8) *FailStep {
const self = owner.allocator.create(FailStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "fail",
.owner = owner,
.makeFn = make,
}),
.error_msg = error_msg,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const self: *FailStep = @alignCast(@fieldParentPtr("step", step));
try step.result_error_msgs.append(b.allocator, self.error_msg);
return error.MakeFailed;
}
};
/// A variant of `std.Build.Step.fail` that does not return an error so that it
/// can be used in the configuration phase. It returns a FailStep, so that the
/// error will be cleanly handled by the build runner.
fn fail(step: *Step, comptime format: []const u8, args: anytype) *Step {
const b = step.owner;
const fail_step = FailStep.create(b, b.fmt(format, args));
step.dependOn(&fail_step.step);
return step;
}
/// Heals the exercises.
const HealStep = struct {
step: Step,
exercises: []const Exercise,
work_path: []const u8,
pub fn create(owner: *Build, exercises: []const Exercise, work_path: []const u8) *HealStep {
const self = owner.allocator.create(HealStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "heal",
.owner = owner,
.makeFn = make,
}),
.exercises = exercises,
.work_path = work_path,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const self: *HealStep = @alignCast(@fieldParentPtr("step", step));
return heal(b.allocator, self.exercises, self.work_path);
}
};
/// Heals all the exercises.
fn heal(allocator: Allocator, exercises: []const Exercise, work_path: []const u8) !void {
const io = std.Options.debug_io;
const sep = std.Io.Dir.path.sep_str;
const join = std.Io.Dir.path.join;
const exercises_path = "exercises";
const patches_path = "patches" ++ sep ++ "patches";
for (exercises) |ex| {
const name = ex.name();
const file = try join(allocator, &.{ exercises_path, ex.main_file });
const patch = b: {
const patch_name = try fmt.allocPrint(allocator, "{s}.patch", .{name});
break :b try join(allocator, &.{ patches_path, patch_name });
};
const output = try join(allocator, &.{ work_path, ex.main_file });
const argv = &.{ "patch", "-i", patch, "-o", output, "-s", file };
_ = try Process.run(allocator, io, .{ .argv = argv });
}
}
fn createTempPath(b: *Build) ![]const u8 {
const io = b.graph.io;
const rand_int = r: {
var x: u64 = undefined;
io.random(@ptrCast(&x));
break :r x;
};
const tmp_dir_sub_path = "tmp" ++ std.Io.Dir.path.sep_str ++ std.fmt.hex(rand_int);
const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
try b.cache_root.handle.createDirPath(io, tmp_dir_sub_path);
return result_path;
}
fn deleteTmpPath(b: *Build, path: []const u8) void {
const io = b.graph.io;
std.Io.Dir.cwd().deleteTree(io, path) catch |err| {
std.log.warn("failed to delete {s}: {t}", .{ path, err });
};
}

View File

@@ -1,108 +0,0 @@
const std = @import("std");
const print = std.debug.print;
const string = []const u8;
const cwd = std.fs.cwd();
const Dir = std.fs.Dir;
const Allocator = std.mem.Allocator;
const EXERCISES_PATH = "exercises";
const HEALED_PATH = "patches/healed";
const TEMP_PATH = "patches/healed/tmp";
const PATCHES_PATH = "patches/patches";
// Heals all the exercises.
fn heal(alloc: Allocator) !void {
try cwd.makePath(HEALED_PATH);
const org_path = try cwd.realpathAlloc(alloc, EXERCISES_PATH);
const patch_path = try cwd.realpathAlloc(alloc, PATCHES_PATH);
const healed_path = try cwd.realpathAlloc(alloc, HEALED_PATH);
var idir = try cwd.openIterableDir(EXERCISES_PATH, Dir.OpenDirOptions{});
defer idir.close();
var it = idir.iterate();
while (try it.next()) |entry| {
// create filenames
const healed_file = try concat(alloc, &.{ healed_path, "/", entry.name });
const patch_file = try concat(alloc, &.{ patch_path, "/", try patch_name(alloc, entry.name) });
// patch file
const result = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "patch", "-i", patch_file, "-o", healed_file, entry.name },
.cwd = org_path,
});
print("{s}", .{result.stderr});
}
}
// Yields all the healed exercises that are not correctly formatted.
fn check_healed(alloc: Allocator) !void {
try cwd.makePath(TEMP_PATH);
const temp_path = try cwd.realpathAlloc(alloc, TEMP_PATH);
const healed_path = try cwd.realpathAlloc(alloc, HEALED_PATH);
var idir = try cwd.openIterableDir(HEALED_PATH, Dir.OpenDirOptions{});
defer idir.close();
var it = idir.iterate();
while (try it.next()) |entry| {
// Check the healed file
const result = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "zig", "fmt", "--check", entry.name },
.cwd = healed_path,
});
// Is there something to fix?
if (result.stdout.len > 0) {
const temp_file = try concat(alloc, &.{ temp_path, "/", entry.name });
const healed_file = try concat(alloc, &.{ healed_path, "/", entry.name });
try std.fs.copyFileAbsolute(healed_file, temp_file, std.fs.CopyFileOptions{});
// Formats the temp file
_ = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "zig", "fmt", entry.name },
.cwd = temp_path,
});
// Show the differences
const diff = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "diff", "-c", healed_file, entry.name },
.cwd = temp_path,
});
print("{s}", .{diff.stdout});
try std.fs.deleteFileAbsolute(temp_file);
}
}
}
fn concat(alloc: Allocator, slices: []const string) !string {
const buf = try std.mem.concat(alloc, u8, slices);
return buf;
}
fn patch_name(alloc: Allocator, path: string) !string {
var filename = path;
const index = std.mem.lastIndexOfScalar(u8, path, '.') orelse return path;
if (index > 0) filename = path[0..index];
return try concat(alloc, &.{ filename, ".patch" });
}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
try heal(alloc);
try check_healed(alloc);
}