263 Commits

Author SHA1 Message Date
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
Chris Boesch
c6f18ba448 Merge pull request '102: Fix formatting reference link' (#402) from tjk/ziglings-exercises:102-link into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/402
2026-04-15 11:44:57 +02:00
Tom
cb0489904e Fix formatting reference link 2026-04-14 21:36:06 -07:00
Chris Boesch
d4a3bad19c fixed merge conflict 2026-04-14 22:23:33 +02:00
Chris Boesch
e412619d88 added cancelation if nothing found 2026-04-14 22:07:57 +02:00
Chris Boesch
3f11ad1253 Merge pull request '093_async9: small fixes' (#400) from tjk/ziglings-exercises:93-async9 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/400
2026-04-14 17:56:35 +02:00
Tom
2afd0f9709 093_async9: small fixes 2026-04-14 08:32:50 -07:00
Chris Boesch
a4efd69e11 Merge pull request '095_quiz_async: Add missing Bug 4' (#396) from tjk/ziglings-exercises:95-bug4 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/396
2026-04-14 12:53:03 +02:00
Chris Boesch
f10c3d4e7a Merge branch 'main' into 95-bug4 2026-04-14 12:43:29 +02:00
Chris Boesch
152f3a6113 Merge pull request 'new example for concurrency' (#398) from improving_ansync9 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/398
2026-04-14 11:41:15 +02:00
Chris Boesch
ab3c498226 new example for concurrency 2026-04-14 11:07:13 +02:00
Chris Boesch
8c08482452 Merge pull request 'added result for 12 digits' (#397) from pi_precision into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/397
2026-04-14 08:22:30 +02:00
Chris Boesch
5eadcdc9f9 added result for 12 digits 2026-04-14 08:17:16 +02:00
Tom
b8a639e798 095_quiz_async: Add missing Bug 4 2026-04-13 19:11:39 -07:00
Chris Boesch
b9cbe199ab Merge pull request 'typo' (#394) from typo_async9 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/394
2026-04-13 18:36:22 +02:00
Chris Boesch
e71175e4f8 typo 2026-04-13 18:06:01 +02:00
Chris Boesch
7fcfb60cc4 typo 2026-04-13 17:56:05 +02:00
Chris Boesch
34c3125411 Merge pull request 'AI hint added' (#392) from ai-slop into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/392
2026-04-12 19:13:44 +02:00
Chris Boesch
8fc1bfac38 AI hint added 2026-04-12 19:06:03 +02:00
Chris Boesch
8e055fcd18 Merge pull request 'Replace exercise 074_comptime9' (#391) from tjk/ziglings-exercises:comptime9 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/391
2026-04-12 17:28:37 +02:00
Tom
2472caa183 Replace exercise 074_comptime9
The exercise no longer needed any modifications to pass due to
advancements in Zig.  This new exercise attempts to teach about
@compileError, @compileLog, and some comptime debugging.  It tries to
help prepare users for the "super bonus challenge" in 075_quiz8.
2026-04-12 07:16:33 -07:00
Chris Boesch
b225538aed Merge pull request 'Changed exercise 60 to require change of format' (#384) from markuxcu/exercises:fix-060 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/384
2026-04-07 09:25:59 +02:00
Chris Boesch
7cb7a9948a Merge branch 'main' into fix-060 2026-04-07 09:18:37 +02:00
Chris Boesch
3574fd3ae0 Merge pull request 'Improvements for async-io' (#388) from async-improvements into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/388
2026-04-06 20:56:29 +02:00
Chris Boesch
882c6aa0ab improvements for async-io 2026-04-06 19:57:32 +02:00
Chris Boesch
55a4841b07 improvements for async-io 2026-04-06 19:38:19 +02:00
Chris Boesch
09bae6a70e improvements for async-io 2026-04-06 19:30:56 +02:00
Chris Boesch
aeeb18931d improvements for async-io 2026-04-06 18:50:57 +02:00
Chris Boesch
63e506586f improvements for async-io 2026-04-06 16:57:48 +02:00
Chris Boesch
446da3ce5a improvements for async-io 2026-04-06 12:22:41 +02:00
Chris Boesch
5e474ea5d1 improvements for async-io 2026-04-05 16:13:42 +02:00
Chris Boesch
2acf192775 improvements for async-io 2026-04-05 12:42:42 +02:00
Chris Boesch
58f8df66d5 improvements for async-io 2026-04-04 16:05:35 +02:00
Chris Boesch
966c1f83af Merge pull request 'fixed missing uppercase letter' (#387) from async2-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/387
2026-04-03 21:36:33 +02:00
Chris Boesch
261c12d6a2 fixed missing uppercase letter 2026-04-03 21:30:49 +02:00
Chris Boesch
0206db8129 fixed missing uppercase letter 2026-04-03 21:27:55 +02:00
Chris Boesch
34bbe1347d Merge pull request 'removed unnecessary patches' (#385) from patches into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/385
2026-04-03 19:55:14 +02:00
Chris Boesch
366b597519 removed unnecessary patches 2026-04-03 19:47:02 +02:00
Chris Boesch
5307b2a338 Merge pull request 'revival of the async-io functions' (#383) from asyncIo into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/383
2026-04-03 19:32:53 +02:00
Chris Boesch
f6a6798c8b improved report design 2026-04-03 19:28:12 +02:00
Chris Boesch
7fae6e0607 improved timestamp comparison 2026-04-03 19:07:10 +02:00
Chris Boesch
1c6487c1e7 added async-io quiz 2026-04-03 18:11:00 +02:00
markuxcu
1166f3cfb6 exercise 60: changed patch file 2026-04-03 14:52:22 +02:00
markuxcu
52ba66c5e9 exercise 60: added new expected output 2026-04-03 14:48:34 +02:00
Chris Boesch
2500936153 new async exercise 2026-04-03 14:28:19 +02:00
markuxcu
9f314ce8e6 exercise 60: added hint on correct format 2026-04-03 14:25:44 +02:00
Chris Boesch
903c33cd0a new async exercise 2026-04-03 13:46:35 +02:00
Chris Boesch
e0259f43a7 Insert space for additional async exercises 2026-04-03 13:35:56 +02:00
Chris Boesch
ffde357f30 revival of the async-io functions, #90 2026-04-02 10:38:45 +02:00
Chris Boesch
3b22bfd898 revival of the async-io functions 2026-04-02 10:28:40 +02:00
Chris Boesch
e22748d488 revival of the async-io functions 2026-04-01 23:44:24 +02:00
Chris Boesch
fcfb0e80a0 revival of the async-io functions 2026-04-01 23:34:16 +02:00
Chris Boesch
db1fef8b86 revival of the async-io functions 2026-04-01 22:52:04 +02:00
Chris Boesch
6d89dcd2de revival of the async-io functions 2026-04-01 22:31:48 +02:00
Chris Boesch
77d3b684cb revival of the async-io functions 2026-04-01 22:28:37 +02:00
Chris Boesch
3056a2b544 Merge pull request 'fixed community link and improved the overview' (#382) from cummunity into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/382
2026-03-28 13:11:50 +01:00
Chris Boesch
149a2bea50 fixed community link and improved the overview 2026-03-28 12:56:49 +01:00
Chris Boesch
c5ad9ff6cf Merge pull request 'Switching the C exercises to LLVM' (#379) from sframe into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/379
2026-03-21 20:30:33 +01:00
Chris Boesch
8f7a629ffb switched to llvm for the c-exercixes 2026-03-21 20:14:31 +01:00
Chris Boesch
d3ec872dec Merge pull request 'readme updated' (#377) from readme into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/377
2026-03-20 19:30:58 +01:00
Chris Boesch
2cddd566bb readme updated 2026-03-20 19:29:50 +01:00
Chris Boesch
3409760a62 Merge pull request 'bump required Zig version' (#376) from MatthijsBlom/ziglings:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/376
2026-03-20 19:19:56 +01:00
Chris Boesch
9f6092aea6 Merge branch 'main' into main 2026-03-20 19:13:22 +01:00
Chris Boesch
95ad73013a Merge pull request 'Added field .skip_hint to show information on why a file has been' (#375) from cor-draconia/exercises:skip_hints into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/375
2026-03-20 19:08:46 +01:00
MatthijsBlom
04f4c1e32c bump required Zig version 2026-03-20 17:23:11 +01:00
inke
2b5a603c43 Added field .skip_hint to show information on why a file has been
skipped.
2026-03-18 13:55:35 +01:00
Chris Boesch
e6d93d731a Merge pull request 'Add exercises for packed structs/unions' (#374) from justusk/ziglings:packed into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/374
2026-03-13 18:18:50 +01:00
Justus Klausecker
16a794fbee 111/112: Add exercises for packed structs/unions
The first exercise introduces the `packed` keyword as an alternative for
bitwise operations. Its main goals are establishing a solid understanding
of field order and conveying the fact that packed containers are basically
integers.
It introduces the concept of container layouts and briefly explains the
default `auto` layout before introducing the `packed` layout (but doesn't
touch `extern` at all).
The exercise also presents a real-world use case for packed containers,
namely LZ4 frame descriptors.
Furthermore it covers equality comparisons between packed containers.

The second exercise talks about switch statements with packed containers
and goes into some more detail on packed unions.
2026-03-13 11:21:03 +01:00
Justus Klausecker
973ec41097 build.zig: replace deprecated GeneralPurposeAllocator alias with DebugAllocator 2026-03-13 09:50:58 +01:00
Chris Boesch
1de4e14096 Merge pull request 'update description of 050_no_value for clarity' (#373) from robertefry/ziglings_exercises:fix/050-clarity into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/373
2026-03-11 00:03:12 +01:00
Robert Fry
0385f5d017 fix: oops 2026-03-09 23:24:59 +00:00
Robert Fry
de3c99dddd update description of 050_no_value for clarity 2026-03-09 23:16:30 +00:00
Chris Boesch
1be6fcd7db Merge pull request 'improve grammar in 103_tokenization' (#365) from pebose/exercises:improve-grammar into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/365
2026-02-27 19:54:41 +01:00
Chris Boesch
d5fdfe708c Merge pull request 'emphasize that the end number of a for-loop range is exclusive' (#366) from pebose/exercises:emphasize-for-loop-range into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/366
2026-02-27 19:44:35 +01:00
Chris Boesch
88510735e1 Merge branch 'main' into emphasize-for-loop-range 2026-02-27 19:43:31 +01:00
Chris Boesch
07031c5daa Merge pull request 'add commas' (#364) from pebose/exercises:add-punctuations into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/364
2026-02-27 19:38:15 +01:00
Chris Boesch
1813c0ded8 Merge pull request 'replace deprecated mem.indexOf with mem.find' (#363) from pebose/exercises:indexof-to-find into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/363
2026-02-27 19:36:00 +01:00
Paul Ebose
8c119bebdc update output for exercise 095_for3 to include full range of numbers 2026-02-27 19:35:10 +01:00
Paul Ebose
dc71c2cd06 update patch file for 095_for3 2026-02-27 19:34:53 +01:00
Paul Ebose
7d03b8464d update patch file for 103_tokenization 2026-02-27 19:27:04 +01:00
Chris Boesch
908109df2d Merge pull request 'add hint that @field() works differently on types and values' (#367) from pebose/exercises:add-hint-to-exercise-82 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/367
2026-02-27 19:17:59 +01:00
Chris Boesch
c032550633 Merge pull request 'improve comment stating 'Zig 0.10.0' @typeName change' (#370) from pebose/exercises:improve-builtins2-comment-zig-0-10 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/370
2026-02-27 19:09:51 +01:00
Chris Boesch
a83a42528e Merge pull request 'fix zig build error when '.progress.txt' contains newline' (#368) from pebose/exercises:fix-zig-build-error into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/368
2026-02-27 19:05:06 +01:00
Chris Boesch
4500fba8b3 Merge branch 'main' into fix-zig-build-error 2026-02-27 18:56:08 +01:00
Paul Ebose
9db32388e3 update patch file 2026-02-27 13:11:03 +01:00
Paul Ebose
53f7e015cb update patch file 2026-02-27 13:09:54 +01:00
Chris Boesch
2e4c7a2310 Merge pull request 'improve comment on continue expression behavior' (#369) from pebose/exercises:improve-013-while3-comment into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/369
2026-02-27 12:49:58 +01:00
Chris Boesch
0785c71532 Merge pull request 'fix 068_comptime3 comment to 'std.Io.Writer.print'' (#371) from pebose/exercises:correct-comptime3-comment into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/371
2026-02-27 12:25:13 +01:00
Paul Ebose
178fd9cef3 fix zig build error when '.progress.txt' contains newline 2026-02-27 04:12:46 +01:00
Paul Ebose
93aa733d33 improve comment on continue expression behavior 2026-02-27 04:07:06 +01:00
Paul Ebose
686342375e improve comment stating 'Zig 0.10.0' @typeName change 2026-02-27 03:55:37 +01:00
Paul Ebose
46cf5e802c fix 068_comptime3 comment to 'std.Io.Writer.print' 2026-02-27 03:51:12 +01:00
Paul Ebose
2e981d408f add hint that @field() works differently on types and values 2026-02-27 03:48:11 +01:00
Paul Ebose
9fb6d21ce6 emphasize that the end number of a for-loop range is exclusive 2026-02-27 03:45:09 +01:00
Paul Ebose
9798e80deb improve grammar in 103_tokenization 2026-02-27 03:41:27 +01:00
Paul Ebose
4aeb7b83b9 add commas 2026-02-27 03:38:59 +01:00
Paul Ebose
8a2e40040b replace deprecated mem.indexOf with mem.find 2026-02-27 03:38:26 +01:00
Chris Boesch
3ecaa34271 Merge pull request 'use Io interface for enabling ansi escape codes' (#361) from sno2/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/361
2026-02-08 21:41:30 +01:00
Carter Snook
df3f2df50b use Io interface for enabling ansi escape codes 2026-02-08 18:53:27 -06:00
Chris Boesch
7d4162e388 Merge pull request 'changed to zig-dev version 2471' (#359) from i357 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/359
2026-02-04 11:44:04 +01:00
Chris Boesch
8840f201d5 changed to zig-dev version 2471 2026-02-04 10:37:03 +01:00
Chris Boesch
bfc57ca6bb Merge pull request 'fix: std.process.RunOptions fields changed (used in build.zig)' (#358) from tadakuso/ziglings:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/358
2026-02-04 10:28:34 +01:00
tadakuso
6fe9625899 fix: std.process.RunOptions fields changed (used in build.zig) 2026-02-04 08:16:11 +08:00
Chris Boesch
0ceb0df875 Merge pull request 'fix: 071 update TypeInfo field case to .int' (#356) from arthursolomiac/exercises:fix-071-typeinfo-case into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/356
2026-01-10 13:53:39 +01:00
Arthur Solomiac
3f9e390bcc fix: 071 update TypeInfo field case to .int 2026-01-10 10:50:37 +01:00
Chris Boesch
07583db582 Merge pull request 'I/O improvements' (#355) from io_improvements into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/355
2026-01-09 23:07:31 +01:00
Chris Boesch
1e552a1dd6 I/O improvements 2026-01-09 22:56:23 +01:00
Chris Boesch
6972af2178 Merge pull request 'fix: use new randomness api' (#354) from itsnoctural/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/354
2026-01-09 10:26:28 +01:00
itsnoctural
91f484ed47 bump required zig version 2026-01-09 00:37:33 +02:00
itsnoctural
551008ac19 fix: use new randomness api 2026-01-09 00:19:24 +02:00
Chris Boesch
335aaafcff Merge pull request 'Change link to documentation' (#351) from BartShoot/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/351
2026-01-08 13:34:03 +01:00
BartShoot
b98fb4d9fb Change link to documentation
It's pointing to pre-writergate documentation
2026-01-08 01:38:18 +01:00
Chris Boesch
9b18647fd2 Merge pull request 'adjust temp files' (#350) from i348 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/350
2026-01-07 20:05:10 +01:00
Chris Boesch
639a763044 adjust temp files 2026-01-07 19:57:43 +01:00
Chris Boesch
878bebb1d1 Merge pull request 'docs: add zig 0.16.0-dev.1976 version change' (#347) from doprz/exercises:docs/zig-version-change into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/347
2026-01-07 16:21:30 +01:00
Chris Boesch
d10c31672a Merge branch 'main' into docs/zig-version-change 2026-01-07 16:20:42 +01:00
Chris Boesch
bf4bea929f Merge pull request 'entered current version number' (#349) from i348 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/349
2026-01-07 16:20:02 +01:00
Chris Boesch
05b324d697 entered current version number 2026-01-07 15:39:08 +01:00
doprz
8f0771d7bf docs: add zig 0.16.0-dev.1976 version change 2026-01-06 11:13:19 -06:00
Chris Boesch
d776e07af2 Merge pull request 'fix build errors in new Zig compiler' (#344) from prasefia/ziglings:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/344
2026-01-06 14:26:14 +01:00
prasefia
a04f945d36 fix build errors in new Zig compiler 2026-01-06 02:55:10 +03:00
Chris Boesch
6212297335 Merge pull request 'minor typos' (#343) from typos into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/343
2026-01-04 14:35:07 +01:00
Chris Boesch
f87097ae54 minor typos 2026-01-04 14:17:27 +01:00
Chris Boesch
8791b9440a Merge pull request 'change io explanation' (#341) from std.Io into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/341
2025-12-30 13:52:40 +01:00
Chris Boesch
af2a30e6da change io explanation 2025-12-30 13:40:24 +01:00
Chris Boesch
4927ef6a26 Merge pull request 'Integrate file system I/O with the std.Io interface' (#339) from std_Io into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/339
2025-12-29 12:46:28 +01:00
Chris Boesch
e8f81ddb96 finish new i/o 2025-12-29 12:42:35 +01:00
Chris Boesch
b332dc879e build revised 2025-12-28 21:39:32 +01:00
Chris Boesch
5685c94194 tests revised 2025-12-28 21:28:26 +01:00
Chris Boesch
3a645ac9db exc. 107 migrated 2025-12-28 21:19:31 +01:00
Chris Boesch
4340642f3c exc. 106 migrated 2025-12-28 20:40:31 +01:00
Chris Boesch
8f9daa12b2 exc. 34 migrated 2025-12-28 14:46:23 +01:00
Chris Boesch
8e30debc6a improved i/o explanation for exc. 26 2025-12-28 02:01:19 +01:00
Chris Boesch
b33fd5a744 exc. 26 migrated 2025-12-28 01:55:06 +01:00
Chris Boesch
21f86f07ad migrated build and test 2025-12-27 23:44:12 +01:00
Chris Boesch
a5febf58c9 Merge pull request 'added dark ziglings image' (#338) from new_image into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/338
2025-12-24 14:00:12 +01:00
Chris Boesch
209ef19596 added dark ziglings image 2025-12-24 13:59:19 +01:00
Chris Boesch
80eb7054eb Merge pull request '099: Update URL to, and function name of, fmt string documentation/comment' (#337) from gregorh/fmtstring-documentation:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/337
2025-12-24 12:03:04 +01:00
gregorh
1c552813d9 Formatting 2025-12-24 01:10:39 +01:00
Gregor Hartmann
acf8c483ef change back to original 'format()' sentence 2025-12-24 01:08:19 +01:00
gregorh
8cbce258a6 Update to correct line number
Line 537 is the actual start of the documenting comment.
2025-12-19 17:56:02 +01:00
Gregor Hartmann
0010cb2a68 Update URL to, and function name of, fmt string documentation/comment 2025-12-19 15:46:02 +01:00
Chris Boesch
bb4e984a8e Merge pull request 'Update zig homepage example in 103' (#336) from nkhl/exercises:103-update-homepage-example into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/336
2025-12-15 15:04:38 +01:00
Nikhil
82d5dda273 Update zig homepage example in 103 2025-12-14 18:12:51 -08:00
Chris Boesch
2ffc3ee1d8 Merge pull request 'Update to new Zig mem trim API' (#335) from dpflug/ziglings_exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/335
2025-12-14 13:52:52 +01:00
David Pflug
7d1184a140 Update to new Zig mem trim API
trimRight is now trimEnd
2025-12-13 12:01:10 -05:00
Chris Boesch
e767de2337 Merge pull request 'restrict parameter type in 'maximumNarcissism' in exercise 65' (#332) from Castanearie/ziglings:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/332
2025-12-08 20:15:55 +01:00
Castanearie
0c9d5abccd restrict parameter type in 'maximumNarcissism' 2025-12-07 23:15:42 +01:00
Chris Boesch
739d5ccce8 Merge pull request 'Added art description' (#331) from i330 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/331
2025-12-07 23:01:49 +01:00
Chris Boesch
274a49aa53 Added art description 2025-12-07 22:53:34 +01:00
Chris Boesch
9302d10282 Merge pull request 'changed 1 to 42 for better understanding' (#329) from i326 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/329
2025-11-28 15:15:49 +01:00
Chris Boesch
757826d45a changed 1 to 42 for better understanding 2025-11-28 14:42:58 +01:00
Chris Boesch
ed9694e557 Merge pull request 'changed 'sleep' to async I/O' (#328) from i323 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/328
2025-11-28 14:31:30 +01:00
Chris Boesch
dc416b6c5a changed 'sleep' to async I/O 2025-11-28 14:22:51 +01:00
Chris Boesch
39e346303c Merge branch 'psd-fix' 2025-11-15 19:23:27 +01:00
Chris Boesch
11d8172136 forgotten thread-sleep fix added 2025-11-15 19:14:36 +01:00
Chris Boesch
189376944c fixed path for freeBSD 2025-11-15 17:58:27 +01:00
Chris Boesch
56b220e98d Merge pull request 'pause slightly extended for faster computers' (#317) from new_io_2 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/317
2025-11-01 16:02:14 +01:00
Chris Boesch
850700e68e pause slightly extended for faster computers 2025-11-01 15:59:07 +01:00
Chris Boesch
6c8f4ef507 pause slightly extended for faster computers 2025-11-01 15:57:29 +01:00
Chris Boesch
4c291f35d5 Merge pull request 'fixed more changes due to new I/O API' (#316) from new_io_2 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/316
2025-11-01 15:52:40 +01:00
Chris Boesch
147ff302ec fixed more changes due to new I/O API 2025-11-01 15:46:31 +01:00
Chris Boesch
c45b9cd383 Merge pull request 'Update for new zig IO' (#315) from esensar/exercises:fix/zig-0.16-new-io into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/315
2025-11-01 15:33:46 +01:00
Ensar Sarajčić
26fc4fdaaa Update for new zig IO
`test/tests.zig` fails after https://github.com/ziglang/zig/pull/25592 was
merged in. This just ensures that Io is passed, it might not be the
ideal solution.
`build.zig` was also failing due to new color parameter.
2025-11-01 10:34:18 +01:00
Chris Boesch
7ad02b084f Merge pull request 'Wrap comment at 80 chars in 102' (#314) from whlr/ziglings-exercises:rewrap-102 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/314
2025-10-25 00:02:26 +02:00
Adam Wheeler
a5622fd5a8 update patch for 102 to match 2025-10-24 12:58:11 -04:00
Adam Wheeler
87358c610b Wrap comment at 80 chars in 102.
Some of the inline comments in 102 are wrapped into very short lines. This
rewraps the shortest ones for readability.
2025-10-24 11:54:25 -04:00
Chris Boesch
b72bcd7e47 Merge pull request 'Fixed description to decimal and scientific notation' (#312) from i311 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/312
2025-10-17 18:08:59 +02:00
Chris Boesch
837e6aba3b Fixed description to decimal and scientific notation 2025-10-17 17:59:57 +02:00
Chris Boesch
b25f9bb66e Merge pull request 'Fixed conversion from kg to tons' (#310) from i309 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/310
2025-10-03 13:37:39 +02:00
Chris Boesch
c7e59864ca Fixed conversion from kg to tons 2025-10-03 13:30:14 +02:00
chrboesch
c9218fbb22 Delete .woodpecker/healthcheck.yaml
needs a token :-(
2025-09-25 14:04:38 +02:00
chrboesch
0e27b4de2f Add .woodpecker/healthcheck.yaml 2025-09-25 09:41:04 +02:00
Chris Boesch
9a848f46b7 Merge pull request 'Fixed error in captureStdErr()' (#308) from i306 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/308
2025-09-24 23:25:23 +02:00
chrboesch
f1921e64d8 Update .woodpecker/eowyn.yaml
try with branch 'main'
2025-09-24 23:21:59 +02:00
chrboesch
b27a6a4d1f Update .woodpecker/eowyn.yaml
separate steps
2025-09-24 23:18:49 +02:00
chrboesch
e4a7db17b3 Update .woodpecker/eowyn.yaml
fix refs
2025-09-24 23:16:55 +02:00
chrboesch
f281c36f79 Update .woodpecker/eowyn.yaml
push only on main
2025-09-24 23:14:04 +02:00
Chris Boesch
4346d5f1d8 Fixed error in captureStdErr() 2025-09-24 20:30:54 +02:00
Chris Boesch
62ad50b71e Merge pull request 'Switched to new reader' (#305) from i304 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/305
2025-09-03 10:57:17 +02:00
Chris Boesch
ed93882b19 Switched to new reader 2025-09-03 10:45:19 +02:00
Chris Boesch
a4c1774847 Merge pull request 'formating contributing' (#301) from contributing into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/301
2025-08-25 21:50:29 +02:00
Chris Boesch
e12ee5cb9e formating contributing 2025-08-25 21:46:25 +02:00
Chris Boesch
0dbe83b20e Merge pull request 'fixes some minor typos' (#300) from i122 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/300
2025-08-24 19:30:51 +02:00
Chris Boesch
e422e24215 fixes some minor typos 2025-08-24 19:26:30 +02:00
Chris Boesch
4134d4fa6f Merge pull request 'Formatting' (#299) from gollum into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/299
2025-08-24 17:55:46 +02:00
Chris Boesch
ad361c4b18 Formatting 2025-08-24 17:55:03 +02:00
Chris Boesch
d4215ce5c6 Merge pull request 'Added instructions for patch files' (#298) from gollum into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/298
2025-08-24 16:31:54 +02:00
Chris Boesch
fd9e9aa36e Added instructions for patch files 2025-08-24 16:31:22 +02:00
Chris Boesch
b3178e81a8 Merge pull request 'Added instructions for patch files' (#297) from gollum into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/297
2025-08-24 16:27:03 +02:00
Chris Boesch
c36fe8007d Added instructions for patch files 2025-08-24 16:23:51 +02:00
Chris Boesch
7488727625 Merge pull request 'testing exercise, fix order of parameters to expectEqual' (#281) from cstyan/exercises:test-expect into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/281
2025-08-24 15:08:32 +02:00
Chris Boesch
a46db7e0e8 Merge branch 'main' into test-expect 2025-08-24 14:59:26 +02:00
Chris Boesch
564ea3405d Merge pull request 'fixed typo and broken link' (#296) from readme into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/296
2025-08-24 14:54:05 +02:00
Chris Boesch
16ec207471 fixed typo and broken link 2025-08-24 14:50:42 +02:00
Chris Boesch
04857aa75e Merge pull request 'fixed version numbers' (#295) from readme into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/295
2025-08-24 14:44:25 +02:00
Chris Boesch
6364087569 fixed version numbers 2025-08-24 14:42:03 +02:00
Chris Boesch
ad32e402a5 Merge pull request 'pass -freference-trace to executed compile command' (#294) from SimonLSchlee/exercises:pass-reference-trace into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/294
2025-08-24 14:37:57 +02:00
Chris Boesch
470f695968 Merge branch 'main' into pass-reference-trace 2025-08-24 13:48:18 +02:00
Chris Boesch
5b3b0eb26b Merge pull request '[Exercise 019] fix: typo in example function, missing return type' (#290) from imomaliev/ziglings:chore/fix-019-function-description into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/290
2025-08-24 13:40:04 +02:00
Chris Boesch
0661d4fd48 Merge branch 'main' into chore/fix-019-function-description 2025-08-24 13:19:25 +02:00
Simon 'Sze' L. Schlee
02db832677 pass -freference-trace to executed compile command
Currently users executing `zig build` to run the exercises,
may encounter compiler output that tells them to use
`-freference-trace=[num]` to see more of the hidden
reference traces, so we should pass this parameter to
the zigling exercise being compiled, so that the learner
can provide this parameter like they would normally, when
using `zig build` directly in normal Zig programs.
2025-08-24 03:08:17 +02:00
Sardorbek Imomaliev
06e56be9c5 fix: 019 typo in example function, missing return type 2025-08-11 21:01:47 +01:00
Callum Styan
f5d2c5124c for expectEqual the first param is expected
Signed-off-by: Callum Styan <callumstyan@gmail.com>
2025-07-13 17:02:50 -07:00
125 changed files with 3382 additions and 2733 deletions

View File

@@ -6,4 +6,5 @@ steps:
- sh ./patches/eowyn.sh
when:
event: [pull_request, push, cron]
branch: main
cron: daily*

View File

@@ -12,10 +12,10 @@ the current Zig snapshot, setup a copy of Ziglings, and knows
common language building blocks (if/then/else, loops, and
functions) is ready for Ziglings.
Ziglings is intended to be completely self-contained. If you
can't solve an exercise from the information you've gleaned so
far from Ziglings, then the exercise probably needs some
additional work. Please file an issue!
Zigling's excercises are self-contained. If you can't solve
an exercise from the information you've gleaned so far from
Ziglings, then the exercise probably needs some additional work.
Please file an issue!
If an example doesn't match a description or if something is
unclear, please file an issue!
@@ -38,6 +38,23 @@ Feel free to submit new exercises but please understand that they
may be heavily edited or rejected entirely if we feel they don't
fit for one reason or another.
## AI-Generated Content
We welcome contributions from non-native English speakers and
appreciate the effort it takes to write in a foreign language.
Using AI tools to polish your English is fine.
What we don't accept is AI-generated exercises, AI-generated
comments, or pull requests produced by coding agents. Ziglings
exercises require understanding the Zig build system, the
didactic progression, and the specific conventions of this
project. That understanding doesn't come from a prompt.
If we suspect a contribution is primarily AI-generated, we will
close it.
## Platforms and Zig Versions
@@ -52,6 +69,8 @@ If you run into an error in Ziglings caused by breaking changes
in the latest development build of Zig, that's a new bug in
Ziglings. Please file an issue...or make a pull request!
For the latter, also read "The Secrets” section.
## Formatting
@@ -89,3 +108,23 @@ contribution for any other purpose.
If you want to peek at the secrets, take a look at the `patches/`
directory.
Every Ziglings exercise contains mistakes on purpose.
To keep our automated tests happy, each exercise also
has a patch in `patches/patches` that “heals” it.
When you change an exercise, you will usually need to update
its patch too. Thats where our little helper Gollum comes in:
1. In the project root, create a folder called `answers/`
2. Put your solved version of the exercise file in there
3. Back in the root, run:
`./patches/gollum.sh <exercise-number>`<br>
For example: `./patches/gollum.sh 106`
This will generate a shiny new patch.
Double-check everything by asking the magical Eowyn:
`./patches/eowyn.sh`<br>
If all tests pass: You are done!
Dont forget to commit the patch file.

132
README.md
View File

@@ -1,10 +1,10 @@
# Ziglings
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.
![Ziglings](images/ziglings.jpg "Ziglings")
![Ziglings](images/ziglings_dark.jpg "Ziglings")
Those broken programs need your help! (You'll also save the
planet from evil aliens and help some friendly elephants stick
@@ -25,27 +25,27 @@ language such as C.
Each exercise is self-contained and self-explained. However,
you're encouraged to also check out these Zig language resources
for more detail:
for more details:
* https://ziglang.org/learn/
* https://ziglearn.org/
* https://ziglang.org/documentation/master/
* [Zig in Depth! (video series)](https://www.youtube.com/watch?v=MMtvGA1YhW4&list=PLtB7CL7EG7pCw7Xy1SQC53Gl8pI7aDg9t&pp=iAQB)
Also, the [Zig community](https://github.com/ziglang/zig/wiki/Community)
Also, the [Zig community](https://ziglang.org/community/)
is incredibly friendly and helpful!
## Getting Started
Install a [development build](https://ziglang.org/download/) of
the Zig compiler. (See the "master" section of the downloads
page.)
page.) Sometimes the latest build is not available there;
in that case, you can download it directly from the [build directory](https://ziglang.org/download/index.json).
Verify the installation and build number of `zig` like so:
```
$ zig version
0.15.0-dev.xxxx+xxxxxxxxx
0.17.0-dev.xxxx+xxxxxxxxx
```
Clone this repository with Git:
@@ -73,10 +73,12 @@ 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.16`, but Ziglings needs a dev build with
pre-release version "0.17.0-dev" and a build number at least as high
as that shown in the example version check above.
**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
the minimum.
@@ -84,46 +86,6 @@ Once you have a build of the Zig compiler that works with
Ziglings, they'll continue to work together. But keep in mind
that if you update one, you may need to also update the other.
### Version Changes
* *2025-08-15* zig 0.15.0-dev.1519 - changes in array list, see [#24801](https://github.com/ziglang/zig/pull/24801)
* *2025-08-08* zig 0.15.0-dev.1380 - changes in build system, see [#24588](https://github.com/ziglang/zig/pull/24588)
* *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)
* *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)
* *2024-02-05* zig 0.12.0-dev.2618 - changes in `build system` - from `Step.zig_exe` to `Step.graph.zig_exe` - see [#18778](https://github.com/ziglang/zig/issues/18778)
* *2024-01-05* zig 0.12.0-dev.2043 - rename of `std.Build.FileSource` to `std.Build.LazyPath` - see [#16353](https://github.com/ziglang/zig/issues/16353)
* *2023-10-24* zig 0.12.0-dev.1243 - changes in `std.ChildProcess`: renamed exec to run - see [#5853](https://github.com/ziglang/zig/issues/5853)
* *2023-06-26* zig 0.11.0-dev.4246 - changes in compile step (now it can be null)
* *2023-06-26* zig 0.11.0-dev.3853 - removal of destination type from all cast builtins
* *2023-06-20* zig 0.11.0-dev.3747 - `@enumToInt` is now `@intFromEnum` and `@intToFloat` is now `@floatFromInt`
* *2023-05-25* zig 0.11.0-dev.3295 - `std.debug.TTY` is now `std.io.tty`
* *2023-04-30* zig 0.11.0-dev.2704 - use of the new `std.Build.ExecutableOptions.link_libc` field
* *2023-04-12* zig 0.11.0-dev.2560 - changes in `std.Build` - remove run() and install()
* *2023-04-07* zig 0.11.0-dev.2401 - fixes of the new build system - see [#212](https://github.com/ratfactor/ziglings/pull/212)
* *2023-02-21* zig 0.11.0-dev.2157 - changes in `build system` - new: parallel processing of the build steps
* *2023-02-21* zig 0.11.0-dev.1711 - changes in `for loops` - new: Multi-Object For-Loops + Struct-of-Arrays
* *2023-02-12* zig 0.11.0-dev.1638 - changes in `std.Build` cache_root now returns a directory struct
* *2023-02-04* zig 0.11.0-dev.1568 - changes in `std.Build` (combine `std.build` and `std.build.Builder` into `std.Build`)
* *2023-01-14* zig 0.11.0-dev.1302 - changes in `@addWithOverflow` (now returns a tuple) and `@typeInfo`; temporary disabled async functionality
* *2022-09-09* zig 0.10.0-dev.3978 - change in `NativeTargetInfo.detect` in build
* *2022-09-06* zig 0.10.0-dev.3880 - Ex 074 correctly fails again: comptime array len
* *2022-08-29* zig 0.10.0-dev.3685 - `@typeName()` output change, stage1 req. for async
* *2022-07-31* zig 0.10.0-dev.3385 - std lib string `fmt()` option changes
* *2022-03-19* zig 0.10.0-dev.1427 - method for getting sentinel of type changed
* *2021-12-20* zig 0.9.0-dev.2025 - `c_void` is now `anyopaque`
* *2021-06-14* zig 0.9.0-dev.137 - std.build.Id `.Custom` is now `.custom`
* *2021-04-21* zig 0.8.0-dev.1983 - std.fmt.format() `any` format string required
* *2021-02-12* zig 0.8.0-dev.1065 - std.fmt.format() `s` (string) format string required
## Advanced Usage
It can be handy to check just a single exercise:
@@ -144,12 +106,6 @@ Or let Ziglings pick an exercise for you:
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
compiler if you're into that sort of thing:
@@ -157,22 +113,10 @@ compiler if you're into that sort of thing:
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:
```
zig build -Dn=19 -l
install Install 019_functions2.zig to prefix path
uninstall Uninstall 019_functions2.zig from prefix path
test Run 019_functions2.zig without checking output
...
zig build -h
```
To reset the progress (have it run all the exercises that have already been completed):
@@ -231,7 +175,7 @@ Zig Core Language
* [x] Sentinel termination
* [x] Quoted identifiers @""
* [x] Anonymous structs/tuples/lists
* [ ] Async <--- ironically awaiting upstream Zig updates
* [x] Async I/O
* [X] Interfaces
* [X] Bit manipulation
* [X] Working with C
@@ -247,6 +191,55 @@ Zig Standard Library
* [X] Tokenization
* [X] File handling
### 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-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-07 zig 0.16.0-dev.2040 - adjust temp files, see [#30683](https://codeberg.org/ziglang/zig/pulls/30683)
* 2026-01-06 zig 0.16.0-dev.1976 - move process API to `std.Io` and changes to main/environ/argv, see [#30644](https://codeberg.org/ziglang/zig/pulls/30644)
* *2025-12-28* zig 0.16.0-dev.1859 - file system I/O integrated with the std.Io interface, see [#30232](https://codeberg.org/ziglang/zig/pulls/30232)
* *2025-11-01* zig 0.16.0-dev.1204 - more changes due to new I/O API, see [#25592](https://github.com/ziglang/zig/pull/25592)
* *2025-09-24* zig 0.16.0-dev.377 - Enable passing file content as args, see [#25228](https://github.com/ziglang/zig/pull/25228)
* *2025-09-03* zig 0.16.0-dev.164 - changes in reader, see [#25077](https://github.com/ziglang/zig/pull/25077)
* *2025-08-15* zig 0.15.0-dev.1519 - changes in array list, see [#24801](https://github.com/ziglang/zig/pull/24801)
* *2025-08-08* zig 0.15.0-dev.1380 - changes in build system, see [#24588](https://github.com/ziglang/zig/pull/24588)
* *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)
* *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)
* *2024-02-05* zig 0.12.0-dev.2618 - changes in `build system` - from `Step.zig_exe` to `Step.graph.zig_exe` - see [#18778](https://github.com/ziglang/zig/issues/18778)
* *2024-01-05* zig 0.12.0-dev.2043 - rename of `std.Build.FileSource` to `std.Build.LazyPath` - see [#16353](https://github.com/ziglang/zig/issues/16353)
* *2023-10-24* zig 0.12.0-dev.1243 - changes in `std.ChildProcess`: renamed exec to run - see [#5853](https://github.com/ziglang/zig/issues/5853)
* *2023-06-26* zig 0.11.0-dev.4246 - changes in compile step (now it can be null)
* *2023-06-26* zig 0.11.0-dev.3853 - removal of destination type from all cast builtins
* *2023-06-20* zig 0.11.0-dev.3747 - `@enumToInt` is now `@intFromEnum` and `@intToFloat` is now `@floatFromInt`
* *2023-05-25* zig 0.11.0-dev.3295 - `std.debug.TTY` is now `std.io.tty`
* *2023-04-30* zig 0.11.0-dev.2704 - use of the new `std.Build.ExecutableOptions.link_libc` field
* *2023-04-12* zig 0.11.0-dev.2560 - changes in `std.Build` - remove run() and install()
* *2023-04-07* zig 0.11.0-dev.2401 - fixes of the new build system - see [#212](https://github.com/ratfactor/ziglings/pull/212)
* *2023-02-21* zig 0.11.0-dev.2157 - changes in `build system` - new: parallel processing of the build steps
* *2023-02-21* zig 0.11.0-dev.1711 - changes in `for loops` - new: Multi-Object For-Loops + Struct-of-Arrays
* *2023-02-12* zig 0.11.0-dev.1638 - changes in `std.Build` cache_root now returns a directory struct
* *2023-02-04* zig 0.11.0-dev.1568 - changes in `std.Build` (combine `std.build` and `std.build.Builder` into `std.Build`)
* *2023-01-14* zig 0.11.0-dev.1302 - changes in `@addWithOverflow` (now returns a tuple) and `@typeInfo`; temporary disabled async functionality
* *2022-09-09* zig 0.10.0-dev.3978 - change in `NativeTargetInfo.detect` in build
* *2022-09-06* zig 0.10.0-dev.3880 - Ex 074 correctly fails again: comptime array len
* *2022-08-29* zig 0.10.0-dev.3685 - `@typeName()` output change, stage1 req. for async
* *2022-07-31* zig 0.10.0-dev.3385 - std lib string `fmt()` option changes
* *2022-03-19* zig 0.10.0-dev.1427 - method for getting sentinel of type changed
* *2021-12-20* zig 0.9.0-dev.2025 - `c_void` is now `anyopaque`
* *2021-06-14* zig 0.9.0-dev.137 - std.build.Id `.Custom` is now `.custom`
* *2021-04-21* zig 0.8.0-dev.1983 - std.fmt.format() `any` format string required
* *2021-02-12* zig 0.8.0-dev.1065 - std.fmt.format() `s` (string) format string required
## Contributing
Contributions are very welcome! I'm writing this to teach myself
@@ -259,3 +252,4 @@ tons of room for improvement:
Please see [CONTRIBUTING](https://codeberg.org/ziglings/exercises/src/branch/main/CONTRIBUTING.md)
in this repo for the full details.

1299
build.zig

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,8 @@
// Oh no, this is supposed to print "Hello world!" but it needs
// your help.
//
// Zig functions are private by default but the main() function
// should be public.
// Zig functions are private by default, but the main() function
// must be public.
//
// A function is made public with the "pub" statement like so:
//

View File

@@ -1,5 +1,5 @@
//
// Zig has some fun array operators.
// Zig has one array operator.
//
// You can use '++' to concatenate two arrays:
//
@@ -7,12 +7,8 @@
// const b = [_]u8{ 3,4 };
// const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
//
// You can use '**' to repeat an array:
//
// 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
// Note that '++' only operates 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
// later.
//
@@ -30,7 +26,8 @@ pub fn main() void {
// (Problem 2)
// Please set this array using repetition.
// 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.
//

View File

@@ -27,10 +27,6 @@ pub fn main() void {
const d: u8 = ziggy[???];
// (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".
// (You'll need to add a space as well!)
const major = "Major";
@@ -38,7 +34,7 @@ pub fn main() void {
const major_tom = major ??? tom;
// 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 '{}'
// placeholders in the format string above. This tells the
// print() function to format the values as a UTF-8 character and

View File

@@ -21,13 +21,13 @@
const std = @import("std");
pub fn main() void {
const foo = 1;
const foo = 42;
// Please fix this condition:
if (foo) {
// We want our program to print this message!
std.debug.print("Foo is 1!\n", .{});
std.debug.print("Foo is 42!\n", .{});
} else {
std.debug.print("Foo is not 1!\n", .{});
std.debug.print("Foo is not 42!\n", .{});
}
}

View File

@@ -11,8 +11,8 @@
//
// }
//
// The "continue expression" executes every time the loop restarts
// whether the "continue" statement happens or not.
// The "continue expression" executes every single time the loop restarts,
// even when a `continue` statement skips the rest of the loop body.
//
const std = @import("std");

View File

@@ -3,7 +3,7 @@
// example that takes two parameters. As you can see, parameters
// are declared just like any other types ("name": "type"):
//
// fn myFunction(number: u8, is_lucky: bool) {
// fn myFunction(number: u8, is_lucky: bool) void {
// ...
// }
//

View File

@@ -14,14 +14,19 @@ const std = @import("std");
// You can find more information at:
// https://ziglang.org/documentation/master/#Inferred-Error-Sets
//
pub fn main() !void {
// We get a Writer for Standard Out so we can print() to it.
var stdout = std.fs.File.stdout().writer(&.{});
pub fn main(init: std.process.Init) !void {
// Instance for input/output operations; we will learn more about this later.
const io = init.io;
// We get a Writer for Standard Out...
var stdout_writer = std.Io.File.stdout().writer(io, &.{});
// ...and extract its interface so we can print() to it.
const stdout = &stdout_writer.interface;
// 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

@@ -10,6 +10,8 @@ pub fn main() void {
for (animals) |a| printAnimal(a);
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
@@ -35,3 +37,24 @@ fn printAnimal(animal: u8) void {
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

@@ -9,12 +9,14 @@ const std = @import("std");
const NumError = error{IllegalNumber};
pub fn main() void {
var stdout = std.fs.File.stdout().writer(&.{});
pub fn main(init: std.process.Init) !void {
const io = init.io;
var stdout_writer = std.Io.File.stdout().writer(io, &.{});
const stdout = &stdout_writer.interface;
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

@@ -4,7 +4,7 @@
// var foo: u8 = 5; // foo is 5
// var bar: *u8 = &foo; // bar is a pointer
//
// What is a pointer? It's a reference to a value. In this example
// What is a pointer? It's a reference to a value. In this example,
// bar is a reference to the memory space that currently contains the
// value 5.
//

View File

@@ -27,3 +27,19 @@ pub fn main() void {
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

@@ -14,8 +14,8 @@
//
// "undefined" should not be thought of as a value, but as a way
// of telling the compiler that you are not assigning a value
// _yet_. Any type may be set to undefined, but attempting
// to read or use that value is _always_ a mistake.
// _yet_. Any variable may be set to undefined, but attempting to
// read its value before assigning one is _always_ a mistake.
//
// * null
//
@@ -24,7 +24,7 @@
// The "null" primitive value _is_ a value that means "no value".
// This is typically used with optional types as with the ?u8
// shown above. When foo equals null, that's not a value of type
// u8. It means there is _no value_ of type u8 in foo at all!
// u8. It means you have assigned foo to have _no value_!
//
// * error
//
@@ -32,10 +32,9 @@
//
// Errors are _very_ similar to nulls. They _are_ a value, but
// they usually indicate that the "real value" you were looking
// for does not exist. Instead, you have an error. The example
// error union type of MyError!u8 means that foo either holds
// a u8 value OR an error. There is _no value_ of type u8 in foo
// when it's set to an error!
// for does not exist. Instead of "no value", you have an error.
// The example error union type of MyError!u8 means that foo
// either holds a u8 value OR a MyError error.
//
// * void
//
@@ -55,7 +54,7 @@
// * undefined - there is no value YET, this cannot be read YET
// * null - there is an explicit value of "no value"
// * errors - there is no value because something went wrong
// * void - there will NEVER be a value stored here
// * void - there will NEVER be a value here
//
// Please use the correct "no value" for each ??? to make this program
// print out a cursed quote from the Necronomicon. ...If you dare.

View File

@@ -54,7 +54,7 @@ var global_wizard = Character{};
// an extremely efficient place for memory storage.
//
// 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
// a stack entry (called a "frame").

View File

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

View File

@@ -27,10 +27,17 @@
// the types match. Zig does not perform unsafe type coercions
// 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
// display the result as a whole number.
@@ -45,10 +52,13 @@ pub fn main() void {
// conversion of 0.453592 kg to the pound.
const shuttle_weight: f16 = 0.453592 * 4480e3;
// 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});
// By default, float values are formatted in standard decimal
// notation. Experiment with '{d}' and '{d:.3}' to see how
// decimal formatting works, or try '{e}' and '{e:.3}' for
// scientific notation.
// NOTE: The weight of the shuttle is a huge number, a scientific notation
// may be more appropriate.
print("Shuttle liftoff weight: {d:.0} metric tons\n", .{shuttle_weight / 1e3});
}
// Floating further:

View File

@@ -93,36 +93,25 @@ pub fn main() void {
print("He has room in his heart for:", .{});
// A StructFields array
const fields = @typeInfo(Narcissus).@"struct".fields;
// `field_names` is a slice of strings and it holds the names of the struct's 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
// 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});
if (field_???[???] != void) {
print(" {s}", .{field_???[???]});
}
if (fields[1].??? != void) {
print(" {s}", .{fields[1].name});
if (field_???[???] != void) {
print(" {s}", .{field_???[???]});
}
if (fields[2].??? != void) {
print(" {s}", .{fields[2].name});
if (field_???[???] != void) {
print(" {s}", .{field_???[???]});
}
// Yuck, look at all that repeated code above! I don't know
@@ -137,19 +126,20 @@ pub fn main() void {
}
// NOTE: This exercise did not originally include the function below.
// But a change after Zig 0.10.0 added the source file name to the
// type. "Narcissus" became "065_builtins2.Narcissus".
// After Zig 0.10.0, `@typeName` began prefixing the returned type name
// with the source file name. For example, "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 ".")
// starting just after the ".")
//
// 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;
fn maximumNarcissism(myType: type) []const u8 {
const find = @import("std").mem.find;
// Turn "065_builtins2.Narcissus" into "Narcissus"
const name = @typeName(myType);
return name[indexOf(u8, name, ".").? + 1 ..];
return name[find(u8, name, ".").? + 1 ..];
}

View File

@@ -8,6 +8,7 @@
// --o-- comptime * | .. .
// * | * . . . . --*-- . * .
// . . . . . . . . . | . . .
// (ASCII art depicting a starry sky with "comptime" as rising star)
//
// When placed before a variable declaration, 'comptime'
// guarantees that every usage of that variable will be performed
@@ -38,16 +39,16 @@ pub fn main() void {
var count = 0;
count += 1;
const a1: [count]u8 = .{'A'} ** count;
const a1: [count]u8 = @splat('A');
count += 1;
const a2: [count]u8 = .{'B'} ** count;
const a2: [count]u8 = @splat('B');
count += 1;
const a3: [count]u8 = .{'C'} ** count;
const a3: [count]u8 = @splat('C');
count += 1;
const a4: [count]u8 = .{'D'} ** count;
const a4: [count]u8 = @splat('D');
print("{s} {s} {s} {s}\n", .{ a1, a2, a3, a4 });

View File

@@ -11,7 +11,7 @@
// format string can be checked for errors at compile time rather
// than crashing at runtime.
//
// (The actual formatting is done by std.fmt.format() and it
// (The actual formatting is done by std.Io.Writer.print() and it
// contains a complete format string parser that runs entirely at
// compile time!)
//

View File

@@ -12,7 +12,7 @@
// wouldn't be allowed:
//
// inline for (.{ u8, u16, u32, u64 }) |T| {
// print("{} ", .{@typeInfo(T).Int.bits});
// print("{} ", .{@typeInfo(T).int.bits});
// }
//
// In the above example, we're looping over a list of types,
@@ -36,13 +36,14 @@ pub fn main() void {
// statement was repeated three times almost verbatim. Yuck!
//
// 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) {
print(" {s}", .{field.name});
if (field_type != void) {
print(" {s}", .{field_name});
}
}

View File

@@ -1,62 +1,112 @@
//
// In addition to knowing when to use the 'comptime' keyword,
// it's also good to know when you DON'T need it.
//
// The following contexts are already IMPLICITLY evaluated at
// compile time, and adding the 'comptime' keyword would be
// superfluous, redundant, and smelly:
//
// * The container-level scope (outside of any function in a source file)
// * Type declarations of:
// * Variables
// * Functions (types of parameters and return values)
// * Structs
// * Unions
// * Enums
// * The test expressions in inline for and while loops
// * An expression passed to the @cImport() builtin
//
// Work with Zig for a while, and you'll start to develop an
// intuition for these contexts. Let's work on that now.
//
// You have been given just one 'comptime' statement to use in
// the program below. Here it is:
//
// comptime
//
// Just one is all it takes. Use it wisely!
//
const print = @import("std").debug.print;
const std = @import("std");
const print = std.debug.print;
// Being in the container-level scope, everything about this value is
// implicitly required to be known compile time.
const llama_count = 5;
// 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.
//
// Let's try a MouseLlama instead.
//
// We'll make a function that runs at comptime and takes a short code describing
// the desired creature. A Mouse is represented by "m" and a Llama is "lm".
// A MouseLlama hybrid, then, would be represented by "mlm".
// Again, this value's type and size must be known at compile
// time, but we're letting the compiler infer both from the
// return type of a function.
const llamas = makeLlamas(llama_count);
const Animal = enum {
Mouse,
Llama,
Gator,
};
// And here's the function. Note that the return value type
// depends on one of the input arguments!
fn makeLlamas(count: usize) [count]u8 {
var temp: [count]u8 = undefined;
var i = 0;
// makeCreature takes the count of animals making up the hybrid creature (so we
// know how big a pen we'll need) and a format string, like the "mlm" for
// MouseLlama.
fn makeCreature(comptime count: usize, comptime fmt: []const u8) [count]Animal {
// Note that this does NOT need to be an inline 'while'.
while (i < count) : (i += 1) {
temp[i] = i;
// Since not every animal is represented by a single character, we need to
// track the state of things as we move along. For example, if we see an
// "m", is that a new Mouse or the end of a Llama?
const State = enum {
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.
};
var state = State.start;
// We return an array of animals representing the creature. (This is why we
// really needed the 'count' parameter. Arrays need a size.)
var animals: [count]Animal = undefined;
var next_animal: usize = 0;
inline for (fmt) |char| {
// This is a good spot to add a @compileLog() call if you need to debug
// any variables... (Come back here after you see main().)
switch (state) {
.start => switch (char) {
// We've seen the start of a Llama.
'l' => state = .l,
// Mice are smaller. An "m" is a full Mouse.
'm' => {
animals[next_animal] = .Mouse;
next_animal += 1;
},
// @compileError lets us stop the build immediately if something
// is wrong. It's like @compileLog but it prints a message
// instead of inspecting values.
//
// What do you think happens with Gators? Do they join with
// other animals or is this an error?
'g' => ???,
else => @compileError(std.fmt.comptimePrint("No animal starts with '{c}'!", .{char})),
},
.l => switch (char) {
// We've seen the end of a Llama.
'm' => {
animals[next_animal] = .Llama;
next_animal += 1;
// Something is missing here. After we finish a Llama, we
// need to be ready to _start_ over with a new animal...
???
},
else => @compileError("Only llamas start with 'l'!"),
},
}
}
return temp;
if (state != .start) {
@compileError("Oh no, an incomplete llama!");
}
if (next_animal != count) {
@compileError("Creature is missing an animal (format string too short).");
}
return animals;
}
pub fn main() void {
print("My llama value is {}.\n", .{llamas[2]});
// Once you've fixed the ??? marks above, this makeCreature call will still
// only succeed if you move it outside of main, so it will run at comptime.
//
// With the call here, Zig will try to make the creature at runtime, and
// you'll get an interesting error.
//
// You may think the state got mixed up, but if you use @compileLog to check
// some variables in makeCreature, you'll see that Zig is trying to compare
// comptime values with "[runtime value]", which will never match.
//
// You can solve this by adding "comptime" to two of the variables in
// makeCreature...
const creature = makeCreature(2, "mlm");
for (creature) |animal| {
// @tagName gives us a string representing which variant of an enum we
// have. This lets us print the names of animals without repeating them
// here.
print("{s}", .{@tagName(animal)});
}
print(" joins the crew!", .{});
}
//
// The lesson here is to not pepper your program with 'comptime'
// keywords unless you need them. Between the implicit compile
// time contexts and Zig's aggressive evaluation of any
// expression it can figure out at compile time, it's sometimes
// surprising how few places actually need the keyword.

View File

@@ -48,9 +48,7 @@ const Path = struct {
// instead.
//
// 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
// space in our program now!
@@ -97,7 +95,7 @@ const NotebookEntry = struct {
};
const HermitsNotebook = struct {
entries: [place_count]?NotebookEntry = .{null} ** place_count,
entries: [place_count]?NotebookEntry = @splat(null),
next_entry: 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| {
print("Oh no! {}\n", .{err});

View File

@@ -74,36 +74,27 @@ fn printTuple(tuple: anytype) void {
// @typeInfo() - takes a type, returns a TypeInfo union
// with fields specific to that type.
//
// The list of a struct type's fields can be found in
// TypeInfo's @"struct".fields.
// The list of a struct type's field types can be found in
// TypeInfo's @"struct".field_types.
//
// Example:
//
// @typeInfo(Circle).@"struct".fields
// @typeInfo(Circle).@"struct".field_types
//
// This will be an array of StructFields.
const fields = ???;
// This will be an array of field types.
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
// time.
//
// Hint: remember 'inline' loops?
//
for (fields) |field| {
for (???, ???) |???, ???| {
// 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:
//
// @field(lhs: anytype, comptime field_name: []const u8)
@@ -118,9 +109,13 @@ fn printTuple(tuple: anytype) void {
// @field(foo, "x"); // returns the value at foo.x
//
// The first field should print as: "0"(bool):true
//
// Hint: Be careful! If your 'lhs' is a type, @field() looks
// for declarations. If it's a value, it looks for data.
//
print("\"{s}\"({any}):{any} ", .{
field.???,
field.???,
field_name,
field_type,
???,
});
}

View File

@@ -1,58 +0,0 @@
//
// Six Facts:
//
// 1. The memory space allocated to your program for the
// invocation of a function and all of its data is called a
// "stack frame".
//
// 2. The 'return' keyword "pops" the current function
// invocation's frame off of the stack (it is no longer needed)
// and returns control to the place where the function was
// called.
//
// fn foo() void {
// return; // Pop the frame and return control
// }
//
// 3. Like 'return', the 'suspend' keyword returns control to the
// place where the function was called BUT the function
// invocation's frame remains so that it can regain control again
// at a later time. Functions which do this are "async"
// functions.
//
// fn fooThatSuspends() void {
// suspend {} // return control, but leave the frame alone
// }
//
// 4. To call any function in async context and get a reference
// to its frame for later use, use the 'async' keyword:
//
// var foo_frame = async fooThatSuspends();
//
// 5. If you call an async function without the 'async' keyword,
// the function FROM WHICH you called the async function itself
// becomes async! In this example, the bar() function is now
// async because it calls fooThatSuspends(), which is async.
//
// fn bar() void {
// fooThatSuspends();
// }
//
// 6. The main() function cannot be async!
//
// Given facts 3 and 4, how do we fix this program (broken by facts
// 5 and 6)?
//
const print = @import("std").debug.print;
pub fn main() void {
// Additional Hint: you can assign things to '_' when you
// don't intend to do anything with them.
foo();
}
fn foo() void {
print("foo() A\n", .{});
suspend {}
print("foo() B\n", .{});
}

View File

@@ -102,7 +102,7 @@ pub fn main() !void {
Insect{ .grasshopper = Grasshopper{ .distance_hopped = 32 } },
};
std.debug.print("Daily Insect Report:\n", .{});
std.debug.print("=== Doctor Zoraptera's Insect Report ===\n", .{});
for (my_insects) |insect| {
// Almost done! We want to print() each insect with a
// single method call here.

49
exercises/085_async.zig Normal file
View File

@@ -0,0 +1,49 @@
//
// In previous versions of Zig, async/await used special keywords
// like 'suspend', 'resume', and 'async' that operated on stackframes
// directly. Those keywords no longer exist!
//
// Zig 0.16 replaced them with a unified I/O interface: std.Io.
// This interface uses a VTable pattern - a struct of function pointers -
// to abstract over different concurrency backends:
//
// * Threaded - thread-pool based I/O
// * Evented - chooses the best event-loop backend for your OS:
// * Uring on Linux (io_uring)
// * Kqueue on BSD/macOS
// * Dispatch on macOS (Grand Central Dispatch)
//
// The Io struct itself is tiny:
//
// const Io = struct {
// userdata: ?*anyopaque, // opaque state of the backend
// vtable: *const VTable, // table of function pointers
// };
//
// Your code receives an Io value and calls methods on it.
// The backend is chosen at initialization time - your code doesn't
// need to know which one it is!
//
// In Zig 0.16, main() receives a std.process.Init struct to opt
// into I/O and concurrency support:
//
// pub fn main(init: std.process.Init) !void {
// const io = init.io;
// // ... use io ...
// }
//
// Let's start simple. Fix the main function to extract the Io
// interface from init, then use it to get the current time.
//
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const io = init.???;
// Get the current wall-clock time using the Io interface.
// Hint: Timestamp.now() takes an Io and a Clock type (.real = wall clock).
const timestamp = std.Io.Timestamp.now(io, .real);
// Print the timestamp in seconds since the Unix epoch.
std.debug.print("Current time: {}s since epoch\n", .{timestamp.toSeconds()});
}

View File

@@ -1,28 +0,0 @@
//
// So, 'suspend' returns control to the place from which it was
// called (the "call site"). How do we give control back to the
// suspended function?
//
// For that, we have a new keyword called 'resume' which takes an
// async function invocation's frame and returns control to it.
//
// fn fooThatSuspends() void {
// suspend {}
// }
//
// var foo_frame = async fooThatSuspends();
// resume foo_frame;
//
// See if you can make this program print "Hello async!".
//
const print = @import("std").debug.print;
pub fn main() void {
var foo_frame = async foo();
}
fn foo() void {
print("Hello ", .{});
suspend {}
print("async!\n", .{});
}

54
exercises/086_async2.zig Normal file
View File

@@ -0,0 +1,54 @@
//
// Now that we know how to get an Io value, let's use it for
// asynchronous execution!
//
// io.async() launches a function and returns a Future. The result
// won't necessarily be available until you call .await() on it:
//
// var future = io.async(someFunction, .{ arg1, arg2 });
// const result = future.await(io);
//
// The function *may* run immediately or on another thread -
// your code doesn't need to care! That's the beauty of the
// Io abstraction.
//
// IMPORTANT: Every Future MUST be either .await()ed or .cancel()ed.
// Failing to do so leaks resources! A safe pattern is:
//
// var future = io.async(myFn, .{});
// defer _ = future.cancel(io); // safety net
// // ... later, if we want the result:
// const result = future.await(io);
// // (await after cancel is fine — it just returns the result)
//
// Both .await() and .cancel() block until the task finishes and
// return the result. The difference is that .cancel() also
// requests the task to stop at its next cancellation point.
// Calling either one more than once is safe — subsequent calls
// just return a copy of the result.
//
// Fix this program so that computeAnswer runs asynchronously
// and its result is properly awaited.
//
const std = @import("std");
const print = std.debug.print;
pub fn main(init: std.process.Init) !void {
const io = init.io;
// Launch computeAnswer asynchronously.
var future = io.async(computeAnswer, .{ 6, 7 });
defer _ = future.cancel(io); // always clean up!
print("Computing... ", .{});
// Now collect the result. What method on Future gives us
// the value, blocking until it's ready?
const answer = future.???(io);
print("The answer is: {}\n", .{answer});
}
fn computeAnswer(a: u32, b: u32) u32 {
return a * b;
}

View File

@@ -1,29 +0,0 @@
//
// Because they can suspend and resume, async Zig functions are
// an example of a more general programming concept called
// "coroutines". One of the neat things about Zig async functions
// is that they retain their state as they are suspended and
// resumed.
//
// See if you can make this program print "5 4 3 2 1".
//
const print = @import("std").debug.print;
pub fn main() void {
const n = 5;
var foo_frame = async foo(n);
???
print("\n", .{});
}
fn foo(countdown: u32) void {
var current = countdown;
while (current > 0) {
print("{} ", .{current});
current -= 1;
suspend {}
}
}

49
exercises/087_async3.zig Normal file
View File

@@ -0,0 +1,49 @@
//
// The real power of async shows when you launch MULTIPLE tasks!
//
// With io.async(), you can start several operations, then await
// them all. The Io backend may run them concurrently:
//
// var f1 = io.async(taskA, .{});
// defer _ = f1.cancel(io);
// var f2 = io.async(taskB, .{});
// defer _ = f2.cancel(io);
// const a = f1.await(io);
// const b = f2.await(io);
//
// Notice the defer pattern: each async call is immediately
// followed by a defer cancel. This ensures cleanup even if
// we return early or hit an error before reaching await.
// Since await/cancel are idempotent, the defer is harmless
// if we've already awaited.
//
// Fix this program to launch both tasks and collect their results.
//
const std = @import("std");
const print = std.debug.print;
pub fn main(init: std.process.Init) !void {
const io = init.io;
// Launch both tasks asynchronously.
var future_a = io.async(slowAdd, .{ 1, 2 });
defer _ = future_a.cancel(io);
var future_b = ???(slowMul, .{ 6, 7 });
defer _ = future_b.cancel(io);
// Await both results.
const sum = future_a.await(io);
const product = future_b.await(io);
print("{} + {} = {}\n", .{ 1, 2, sum });
print("{} * {} = {}\n", .{ 6, 7, product });
print("Total: {}\n", .{sum + product});
}
fn slowAdd(a: u32, b: u32) u32 {
return a + b;
}
fn slowMul(a: u32, b: u32) u32 {
return a * b;
}

View File

@@ -1,30 +0,0 @@
//
// It has probably not escaped your attention that we are no
// longer capturing a return value from foo() because the 'async'
// keyword returns the frame instead.
//
// One way to solve this is to use a global variable.
//
// See if you can make this program print "1 2 3 4 5".
//
const print = @import("std").debug.print;
var global_counter: i32 = 0;
pub fn main() void {
var foo_frame = async foo();
while (global_counter <= 5) {
print("{} ", .{global_counter});
???
}
print("\n", .{});
}
fn foo() void {
while (true) {
???
???
}
}

50
exercises/088_async4.zig Normal file
View File

@@ -0,0 +1,50 @@
//
// When you have many tasks that don't return individual values,
// use a Group! A Group is an unordered set of tasks that can
// only be awaited or canceled as a whole:
//
// var group: std.Io.Group = .init;
// group.async(io, myTask, .{arg1});
// group.async(io, myTask, .{arg2});
// try group.await(io); // blocks until ALL tasks finish
//
// Important rules:
// * The return type of functions spawned in a group must be
// coercible to Cancelable!void (i.e. void, or error{Canceled}!void).
// * Once you call group.async(), you MUST eventually call
// group.await() or group.cancel() to release resources.
// * group.cancel() requests cancellation on ALL members,
// then blocks until they all finish.
//
// Unlike Future, Group tasks don't return values to the caller.
// They're ideal for parallel work that communicates through
// shared state or side effects (like printing).
//
// Fix this program to await all tasks in the group.
//
const std = @import("std");
const print = std.debug.print;
pub fn main(init: std.process.Init) !void {
const io = init.io;
var group: std.Io.Group = .init;
// Spawn 3 tasks in any order. Each sleeps for (id * 1) seconds
// before printing, so the output order is deterministic.
group.async(io, doWork, .{ io, 1 });
group.async(io, doWork, .{ io, 3 });
group.async(io, doWork, .{ io, 2 });
// Wait for all tasks to finish.
// What Group method blocks until all tasks complete?
try group.???(io);
print("All tasks finished!\n", .{});
}
fn doWork(io: std.Io, id: u32) void {
// Sleep ensures deterministic output order.
io.sleep(std.Io.Duration.fromSeconds(id), .awake) catch return;
print("Task {} done.\n", .{id});
}

View File

@@ -1,48 +0,0 @@
//
// Sure, we can solve our async value problem with a global
// variable. But this hardly seems like an ideal solution.
//
// So how do we REALLY get return values from async functions?
//
// The 'await' keyword waits for an async function to complete
// and then captures its return value.
//
// fn foo() u32 {
// return 5;
// }
//
// var foo_frame = async foo(); // invoke and get frame
// var value = await foo_frame; // await result using frame
//
// The above example is just a silly way to call foo() and get 5
// back. But if foo() did something more interesting such as wait
// for a network response to get that 5, our code would pause
// until the value was ready.
//
// As you can see, async/await basically splits a function call
// into two parts:
//
// 1. Invoke the function ('async')
// 2. Getting the return value ('await')
//
// Also notice that a 'suspend' keyword does NOT need to exist in
// a function to be called in an async context.
//
// Please use 'await' to get the string returned by
// getPageTitle().
//
const print = @import("std").debug.print;
pub fn main() void {
var myframe = async getPageTitle("http://example.com");
var value = ???
print("{s}\n", .{value});
}
fn getPageTitle(url: []const u8) []const u8 {
// Please PRETEND this is actually making a network request.
_ = url;
return "Example Title.";
}

67
exercises/089_async5.zig Normal file
View File

@@ -0,0 +1,67 @@
//
// One of the most important features of the new Io system is
// structured cancellation!
//
// Every Future has a .cancel() method that:
// 1. Requests the task to stop (via error.Canceled at the
// next "cancellation point")
// 2. BLOCKS until the task actually finishes
// 3. Returns whatever result the task produced
//
// A "cancellation point" is any Io function that can return
// error.Canceled - most commonly io.sleep():
//
// fn myTask(io: std.Io) u32 {
// io.sleep(...) catch |err| switch (err) {
// error.Canceled => return 0, // error handle
// };
// return 42;
// }
//
// This is fundamentally different from killing a thread -
// the task gets a chance to clean up and return a value!
//
// Remember: both .await() and .cancel() block and return the
// result. The only difference is that .cancel() also sends
// the cancellation request. And both are idempotent — calling
// either one again just returns the same result.
//
// Fix this program: the slow task would take 10 seconds,
// but we cancel it after 1 second. The task should detect
// the cancellation and return early.
//
const std = @import("std");
const print = std.debug.print;
pub fn main(init: std.process.Init) !void {
const io = init.io;
var future = io.async(slowTask, .{io});
defer _ = future.cancel(io); // safety net
// Wait 1 second, then cancel instead of waiting the full 10.
io.sleep(std.Io.Duration.fromSeconds(1), .awake) catch {};
print("Canceling slow task...\n", .{});
// We don't want to wait 10 seconds!
// Which Future method requests cancellation AND returns the result?
const result = future.???(io);
print("Task returned: {}\n", .{result});
}
fn slowTask(io: std.Io) u32 {
print("Starting long computation...\n", .{});
// Try to sleep for 10 seconds - but we might get canceled!
io.sleep(std.Io.Duration.fromSeconds(10), .awake) catch |err| switch (err) {
error.Canceled => {
print("Task was canceled, cleaning up.\n", .{});
return 0;
},
};
print("Task completed normally.\n", .{});
return 42;
}

View File

@@ -1,54 +0,0 @@
//
// The power and purpose of async/await becomes more apparent
// when we do multiple things concurrently. Foo and Bar do not
// depend on each other and can happen at the same time, but End
// requires that they both be finished.
//
// +---------+
// | Start |
// +---------+
// / \
// / \
// +---------+ +---------+
// | Foo | | Bar |
// +---------+ +---------+
// \ /
// \ /
// +---------+
// | End |
// +---------+
//
// We can express this in Zig like so:
//
// fn foo() u32 { ... }
// fn bar() u32 { ... }
//
// // Start
//
// var foo_frame = async foo();
// var bar_frame = async bar();
//
// var foo_value = await foo_frame;
// var bar_value = await bar_frame;
//
// // End
//
// Please await TWO page titles!
//
const print = @import("std").debug.print;
pub fn main() void {
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;
print(".com: {s}, .org: {s}.\n", .{ com_title, org_title });
}
fn getPageTitle(url: []const u8) []const u8 {
// Please PRETEND this is actually making a network request.
_ = url;
return "Example Title";
}

76
exercises/090_async6.zig Normal file
View File

@@ -0,0 +1,76 @@
//
// Sometimes you want to race multiple tasks and act on whichever
// finishes first. That's what Select is for!
//
// Select is like a Group, but lets you receive individual results
// as tasks complete — one at a time:
//
// const Race = std.Io.Select(union(enum) {
// fast: u32,
// slow: u32,
// });
//
// var buffer: [2]Race.Union = undefined;
// var sel = Race.init(io, &buffer);
//
// sel.async(.fast, fastFn, .{io});
// sel.async(.slow, slowFn, .{io});
//
// const winner = try sel.await(); // returns first completed
// switch (winner) {
// .fast => |val| ...,
// .slow => |val| ...,
// }
// sel.cancelDiscard(); // cancel remaining, discard results
//
// As with all async primitives: tasks spawned in a Select MUST
// be cleaned up. Use sel.cancel() to get remaining results one
// by one (for resource cleanup), or sel.cancelDiscard() if you
// don't need them.
//
// The buffer must be large enough for all tasks that might
// complete before you call cancelDiscard().
//
// Fix this program to receive the winner of the race.
//
const std = @import("std");
const print = std.debug.print;
const RaceResult = std.Io.Select(union(enum) {
hare: []const u8,
tortoise: []const u8,
});
pub fn main(init: std.process.Init) !void {
const io = init.io;
var buffer: [2]RaceResult.Union = undefined;
var sel = RaceResult.init(io, &buffer);
sel.async(.hare, runHare, .{io});
sel.async(.tortoise, runTortoise, .{io});
// Wait for the first finisher.
// What Select method returns the first completed result?
const winner = try sel.???();
switch (winner) {
.hare => |msg| print("Hare: {s}\n", .{msg}),
.tortoise => |msg| print("Tortoise: {s}\n", .{msg}),
}
// Clean up the loser — we don't need their result.
sel.cancelDiscard();
}
fn runHare(io: std.Io) []const u8 {
// The hare is fast — only 1 second!
io.sleep(std.Io.Duration.fromSeconds(1), .awake) catch return "I got canceled!";
return "I'm fast!";
}
fn runTortoise(io: std.Io) []const u8 {
// The tortoise is slow — 10 seconds.
io.sleep(std.Io.Duration.fromSeconds(10), .awake) catch return "I got canceled!";
return "Slow and steady...";
}

View File

@@ -1,87 +0,0 @@
//
// Remember how a function with 'suspend' is async and calling an
// async function without the 'async' keyword makes the CALLING
// function async?
//
// fn fooThatMightSuspend(maybe: bool) void {
// if (maybe) suspend {}
// }
//
// fn bar() void {
// fooThatMightSuspend(true); // Now bar() is async!
// }
//
// But if you KNOW the function won't suspend, you can make a
// promise to the compiler with the 'nosuspend' keyword:
//
// fn bar() void {
// nosuspend fooThatMightSuspend(false);
// }
//
// If the function does suspend and YOUR PROMISE TO THE COMPILER
// IS BROKEN, the program will panic at runtime, which is
// probably better than you deserve, you oathbreaker! >:-(
//
const print = @import("std").debug.print;
pub fn main() void {
// 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);
print("beef? {X}!\n", .{my_beef});
}
fn getBeef(input: u32) u32 {
if (input == 0xDEAD) {
suspend {}
}
return 0xBEEF;
}
//
// Going Deeper Into...
// ...uNdeFiNEd beHAVi0r!
//
// We haven't discussed it yet, but runtime "safety" features
// require some extra instructions in your compiled program.
// Most of the time, you're going to want to keep these in.
//
// But in some programs, when data integrity is less important
// than raw speed (some games, for example), you can compile
// without these safety features.
//
// Instead of a safe panic when something goes wrong, your
// program will now exhibit Undefined Behavior (UB), which simply
// means that the Zig language does not (cannot) define what will
// happen. The best case is that it will crash, but in the worst
// case, it will continue to run with the wrong results and
// corrupt your data or expose you to security risks.
//
// This program is a great way to explore UB. Once you get it
// working, try calling the getBeef() function with the value
// 0xDEAD so that it will invoke the 'suspend' keyword:
//
// getBeef(0xDEAD)
//
// Now when you run the program, it will panic and give you a
// nice stack trace to help debug the problem.
//
// zig run exercises/090_async7.zig
// thread 328 panic: async function called...
// ...
//
// But see what happens when you turn off safety checks by using
// ReleaseFast mode:
//
// zig run -O ReleaseFast exercises/090_async7.zig
// beef? 0!
//
// This is the wrong result. On your computer, you may get a
// different answer or it might crash! What exactly will happen
// is UNDEFINED. Your computer is now like a wild animal,
// reacting to bits and bytes of raw memory with the base
// instincts of the CPU. It is both terrifying and exhilarating.
//

63
exercises/091_async7.zig Normal file
View File

@@ -0,0 +1,63 @@
//
// When multiple async tasks access shared data, you need
// synchronization! Io provides a Mutex for this:
//
// var mutex: std.Io.Mutex = .init;
//
// // In a task:
// try mutex.lock(io); // blocks until lock is acquired
// defer mutex.unlock(io);
// // ... critical section: safe to modify shared data ...
//
// Without the mutex, concurrent tasks could read and write the
// same memory simultaneously, causing a data race — the result
// would be unpredictable.
//
// mutex.lock() is a cancellation point — it can return
// error.Canceled. There's also tryLock() which returns
// immediately (true if acquired, false if not).
//
// Fix this program so the counter is correctly synchronized.
// Without the fix, the final count would be unpredictable.
// With it, four tasks incrementing 100 times each = 400.
//
const std = @import("std");
const print = std.debug.print;
const SharedState = struct {
counter: u32 = 0,
mutex: std.Io.Mutex = .init,
};
pub fn main(init: std.process.Init) !void {
const io = init.io;
var state = SharedState{};
var group: std.Io.Group = .init;
group.async(io, increment, .{ io, &state, 100 });
group.async(io, increment, .{ io, &state, 100 });
group.async(io, increment, .{ io, &state, 100 });
group.async(io, increment, .{ io, &state, 100 });
try group.await(io);
print("Counter: {}\n", .{state.counter});
}
fn increment(io: std.Io, state: *SharedState, times: u32) void {
for (0..times) |_| {
// Acquire the lock before modifying shared state.
// What Mutex method blocks until the lock is acquired?
state.mutex.??? catch return;
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;
}
}

View File

@@ -1,35 +0,0 @@
//
// You have doubtless noticed that 'suspend' requires a block
// expression like so:
//
// suspend {}
//
// The suspend block executes when a function suspends. To get
// sense for when this happens, please make the following
// program print the string
//
// "ABCDEF"
//
const print = @import("std").debug.print;
pub fn main() void {
print("A", .{});
var frame = async suspendable();
print("X", .{});
resume frame;
print("F", .{});
}
fn suspendable() void {
print("X", .{});
suspend {
print("X", .{});
}
print("X", .{});
}

62
exercises/092_async8.zig Normal file
View File

@@ -0,0 +1,62 @@
//
// Tasks often need to communicate! Io provides Queue for this —
// a bounded, thread-safe channel for passing data between tasks:
//
// var backing: [16]u32 = undefined;
// var queue: std.Io.Queue(u32) = .init(&backing);
//
// // Producer task:
// try queue.putOne(io, value); // blocks if queue is full
//
// // Consumer task:
// const val = try queue.getOne(io); // blocks if queue is empty
//
// When the producer is done, it calls queue.close(io) to signal
// that no more data is coming. After that, getOne() will return
// error.Closed once the queue is drained.
//
// This is the classic producer/consumer pattern — one task
// generates work, another processes it, and the queue handles
// all the synchronization automatically.
//
// Fix this program: the producer sends numbers 1..10, the
// consumer sums them up. The expected sum is 55.
//
const std = @import("std");
const print = std.debug.print;
pub fn main(init: std.process.Init) !void {
const io = init.io;
var backing: [4]u32 = undefined;
var queue: std.Io.Queue(u32) = .init(&backing);
var group: std.Io.Group = .init;
group.async(io, producer, .{ io, &queue });
group.async(io, consumer, .{ io, &queue });
try group.await(io);
}
fn producer(io: std.Io, queue: *std.Io.Queue(u32)) void {
// Send numbers 1 through 10 into the queue.
for (1..11) |i| {
// What Queue method sends a single element, blocking if full?
queue.???(io, @intCast(i)) catch return;
}
// Signal that we're done sending.
queue.close(io);
}
fn consumer(io: std.Io, queue: *std.Io.Queue(u32)) void {
var sum: u32 = 0;
while (true) {
const value = queue.getOne(io) catch |err| switch (err) {
error.Closed => break,
error.Canceled => return,
};
sum += value;
}
print("Sum of 1..10 = {}\n", .{sum});
}

109
exercises/093_async9.zig Normal file
View File

@@ -0,0 +1,109 @@
//
// We've been using io.async() to launch tasks. But there's a
// stronger variant: io.concurrent().
//
// The difference:
//
// io.async():
// * The function MAY run on a separate unit of concurrency,
// or it may run immediately on the caller (synchronously).
// * Never fails — if no concurrency is available, it just
// runs the function right away.
// * More portable, works with all Io backends.
//
// io.concurrent():
// * GUARANTEES a separate unit of concurrency.
// * Can fail with error.ConcurrencyUnavailable if resources
// are exhausted or the backend doesn't support it.
// * Use when you NEED the task to run independently of the
// caller.
//
// What is a "unit of concurrency"? That depends on the backend!
// The Threaded backend uses OS threads. But the Evented backends
// (Uring, Kqueue, Dispatch) use M:N green threads / fibers,
// which can provide concurrency even on a SINGLE OS thread.
// Your code doesn't need to know the difference.
//
// Because concurrent() can fail, you must handle the error:
//
// var future = try io.concurrent(myFn, .{args});
// defer _ = future.cancel(io);
// const result = future.await(io);
//
// Let's try a slightly simplified example from signal processing:
// Suppose we're looking for the beginning of a signal above the noise
// level. To do this, we compare each entry from beginning to end with
// the threshold. To speed things up a bit, we split the signal into
// two halves and have two parallel workers search for them.
// Who finds the beginning first "wins" and thus ends the other one.
//
// As I said, this is a simplified explanation,
// but in practice it's done more or less like this.
//
const std = @import("std");
const Io = std.Io;
const print = std.debug.print;
const SearchResult = struct {
found: bool,
worker_id: u8 = 0,
index: usize = 0,
};
pub fn main(init: std.process.Init) !void {
const io = init.io;
const data = [_]u32{ 10, 23, 45, 67, 12, 69, 3, 54, 69, 42, 68, 56, 71, 79, 79, 75, 70, 77 };
const threshold = 70;
const mid = data.len / 2;
// A queue with space for one result.
var buf: [1]SearchResult = undefined;
var queue = Io.Queue(SearchResult).init(&buf);
// Launch two workers, each searching half the array.
// Remember, we want them to be guaranteed separate units of concurrency.
var f1 = ???(searchThreshold, .{ io, data[0..mid], threshold, 0, 0, &queue });
defer _ = f1.cancel(io);
var f2 = ???(searchThreshold, .{ io, data[mid..], threshold, mid, 1, &queue });
defer _ = f2.cancel(io);
// Wait for the first result.
const result = try queue.getOne(io);
if (result.found)
print("Worker {} found signal start over threshold at index {}!\n", .{ result.worker_id, result.index });
}
fn searchThreshold(
io: Io,
slice: []const u32,
threshold: u32,
base_offset: usize,
worker_id: u8,
queue: *Io.Queue(SearchResult),
) void {
for (slice, 0..) |val, i| {
// This pause is necessary so that the process can be canceled
// if another one has already finished. Without this pause,
// all workers would continue until the end.
io.sleep(Io.Duration.fromMilliseconds(1), .awake) catch return;
// To test this, you can uncomment this to view the work of the workers
// and then comment out the pause.
// print("id: {} - val: {}\n", .{ worker_id, val });
if (val >= threshold) {
queue.putOne(io, .{
.found = true,
.worker_id = worker_id,
.index = base_offset + i,
}) catch return;
return;
}
}
// Nothing found
queue.putOneUncancelable(io, .{ .found = false }) catch return;
}

68
exercises/094_async10.zig Normal file
View File

@@ -0,0 +1,68 @@
//
// In exercise 089, we learned that cancellation happens at
// "cancellation points" — any Io function that can return
// error.Canceled.
//
// But sometimes a task has a critical section that MUST NOT
// be interrupted — for example, writing a consistent state
// to disk, or completing a transaction.
//
// Io provides CancelProtection for this:
//
// const old = io.swapCancelProtection(.blocked);
// defer _ = io.swapCancelProtection(old);
// // In this block, NO Io function will return error.Canceled.
// // The cancel request is held until protection is restored.
//
// There are two states:
// .unblocked — normal: cancellation points can fire (default)
// .blocked — protected: error.Canceled is never returned
//
// There's also io.checkCancel() — a pure cancellation point
// that does nothing except return error.Canceled if a cancel
// request is pending. Useful in long CPU-bound loops.
//
// And io.recancel() — re-arms a consumed cancel request so
// the NEXT cancellation point will fire again.
//
// Fix this program so the critical section completes even
// when the task is canceled.
//
const std = @import("std");
const print = std.debug.print;
pub fn main(init: std.process.Init) !void {
const io = init.io;
var future = io.async(importantTask, .{io});
defer _ = future.cancel(io);
// Give the task time to start and enter its critical section.
io.sleep(std.Io.Duration.fromMilliseconds(200), .awake) catch {};
// Cancel while the task is in its protected section.
const result = future.cancel(io);
print("Task result: {s}\n", .{result});
}
fn importantTask(io: std.Io) []const u8 {
print("Starting critical section...\n", .{});
// Protect this section from cancellation.
// What method swaps the cancel protection state?
const old = io.???(.blocked);
defer _ = io.???(old);
// This sleep will NOT return error.Canceled even though
// we get canceled during it — protection is active!
io.sleep(std.Io.Duration.fromMilliseconds(300), .awake) catch |err| switch (err) {
error.Canceled => {
// This should never happen while protected!
return "ERROR: canceled during critical section!";
},
};
print("Critical section completed safely.\n", .{});
return "All data saved.";
}

View File

@@ -0,0 +1,187 @@
//
// Quiz Time — Async I/O!
//
// Doctor Zoraptera's insect simulation is going well, but she
// realized that her virtual garden needs weather data! Insects
// behave differently depending on temperature, humidity, and
// wind conditions.
//
// She has set up three weather sensors around the garden that
// measure conditions in parallel and report their readings
// through a shared data channel. A collector task gathers the
// readings, and after all sensors have reported, a garden
// report is printed.
//
// But Doctor Z rushed through the code (she was being chased
// by a grasshopper) and left several bugs. Can you fix them?
//
// Here's what the program should do:
// 1. Three sensor tasks send exactly 3 readings each through
// a Queue
// 2. A collector task receives readings concurrently,
// protected by a Mutex
// 3. After all sensors finish, the queue is closed
// 4. The final report is written in a cancel-protected section
//
// *************************************************************
// * A NOTE ABOUT THIS EXERCISE *
// * *
// * This quiz uses concepts from exercises 085-094. *
// * There are 6 bugs to fix — look for the ???s! *
// * *
// *************************************************************
//
const std = @import("std");
const print = std.debug.print;
const SensorType = enum { thermometer, hygrometer, anemometer };
const Reading = struct {
sensor_type: SensorType,
value: i32,
};
const GardenWeather = struct {
temperature: i32 = 0,
humidity: i32 = 0,
wind: i32 = 0,
readings_count: u32 = 0,
mutex: std.Io.Mutex = .init,
fn addReading(self: *GardenWeather, io: std.Io, reading: Reading) void {
// Bug 1: The collector needs to lock before modifying
// shared state. What Mutex method acquires the lock?
self.mutex.???(io) catch return;
defer self.mutex.unlock(io);
switch (reading.sensor_type) {
.thermometer => self.temperature = reading.value,
.hygrometer => self.humidity = reading.value,
.anemometer => self.wind = reading.value,
}
self.readings_count += 1;
}
};
pub fn main(init: std.process.Init) !void {
const io = init.io;
var weather = GardenWeather{};
var reading_buf: [8]Reading = undefined;
var queue: std.Io.Queue(Reading) = .init(&reading_buf);
// The collector must run concurrently so it can process
// readings while the sensors are still sending.
// Start it FIRST to ensure its concurrency unit is reserved.
//
// Bug 2: The collector needs guaranteed concurrency.
// What method ensures a separate unit of concurrency?
// (Don't forget: it can fail!)
var collector_future = try io.???(collector, .{ io, &queue, &weather });
defer _ = collector_future.cancel(io);
// Sensor group: the sensors can use async — they just need
// to run, and async is more portable.
var sensors: std.Io.Group = .init;
sensors.async(io, sensor, .{ io, &queue, .thermometer, 20 });
sensors.async(io, sensor, .{ io, &queue, .hygrometer, 60 });
sensors.async(io, sensor, .{ io, &queue, .anemometer, 10 });
// Bug 3: Wait for ALL sensors to finish sending their readings.
// What Group method blocks until all tasks complete?
try sensors.???(io);
// All sensors done — close the queue so the collector knows
// there's no more data coming.
queue.close(io);
// Bug 4: How do we wait for the collector to drain the remaining queue?
_ = collector_future.???(io);
// Now write the garden report. This is critical — it must
// NOT be interrupted, even if something tries to cancel us!
//
// Bug 5: Protect this section from cancellation.
// What Io method swaps the cancel protection state?
const old_protection = io.???(.blocked);
defer _ = io.???(old_protection);
printGardenReport(&weather);
}
fn sensor(
io: std.Io,
queue: *std.Io.Queue(Reading),
sensor_type: SensorType,
base_value: i32,
) void {
// Each sensor takes exactly 3 measurements.
for (1..4) |i| {
io.sleep(std.Io.Duration.fromMilliseconds(100), .awake) catch return;
const reading = Reading{
.sensor_type = sensor_type,
.value = base_value + @as(i32, @intCast(i)),
};
// Bug 6: Send the reading into the queue.
// What Queue method sends a single element?
queue.???(io, reading) catch return;
}
}
fn collector(
io: std.Io,
queue: *std.Io.Queue(Reading),
weather: *GardenWeather,
) void {
while (true) {
const reading = queue.getOne(io) catch |err| switch (err) {
error.Closed => break,
error.Canceled => return,
};
weather.addReading(io, reading);
}
}
fn printGardenReport(weather: *GardenWeather) void {
print("=== Doctor Zoraptera's Garden Report ===\n", .{});
print("Temperature : {}C\n", .{weather.temperature});
print("Humidity : {}%\n", .{weather.humidity});
print("Wind : {} km/h\n", .{weather.wind});
print("Readings : {}\n", .{weather.readings_count});
if (weather.temperature > 20 and weather.wind < 15) {
print("Bee-friendly conditions! Expect high pollination.\n", .{});
} else {
print("Grasshoppers will be grumpy today.\n", .{});
}
}
// Further reading for the curious:
//
// This quiz covered the main async I/O primitives:
// io.async() - launch a task (may run inline)
// io.concurrent() - guaranteed unit of concurrency
// Future.await/cancel - collect or cancel a single task
// Group.async/await/cancel - manage fire-and-forget tasks
// Select.async/await - race tasks, act on first completion
// Queue - bounded channel between tasks
// Mutex - protect shared state
// CancelProtection - shield critical sections
//
// There are more synchronization primitives we didn't cover:
// Condition - wait for a condition to become true
// RwLock - multiple readers OR one writer
// Semaphore - limit concurrent access to a resource
// Futex - low-level wait/wake on a memory address
// Batch - submit multiple I/O operations at once
//
// The key insight: all of these work through the Io VTable,
// so your code is portable across backends — whether Threaded
// (OS thread pool), or Evented (M:N green threads / fibers
// that can provide concurrency even on a single OS thread).
//
// Doctor Zoraptera approves.

View File

@@ -28,6 +28,8 @@
// 0..10 is a range from 0 to 9
// 1..4 is a range from 1 to 3
//
// Crucially, the end value is EXCLUSIVE.
//
// At the moment, ranges in loops are only supported in 'for' loops.
//
// Perhaps you recall Exercise 13? We were printing a numeric
@@ -64,6 +66,12 @@ pub fn main() void {
}
std.debug.print("\n", .{});
// Let's also print every number from 1 through 15
for (???) |n| {
std.debug.print("{} ", .{n});
}
std.debug.print("\n", .{});
}
//
// That's a bit nicer, right?

View File

@@ -93,3 +93,12 @@ pub fn main() !void {
//
// For Crypto it is better not to use this, but in sorting algorithms like
// 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

@@ -13,10 +13,10 @@
// no official documentation for standard library features such
// as string formatting.
//
// Therefore, the comments for the format() function are the only
// Therefore, the comments for the print() 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://ziglang.org/documentation/master/std/#std.Io.Writer.print
//
// Zig already has a very nice selection of formatting options.
// These can be used in different ways, but generally to convert
@@ -56,7 +56,7 @@
// the placeholder will determine how the corresponding value,
// e.g. foo, is displayed.
//
// And this is where it gets exciting, because format() accepts a
// And this is where it gets exciting, because print() accepts a
// variety of formatting instructions. It's basically a tiny
// language of its own. Here's a numeric example:
//

View File

@@ -51,12 +51,3 @@ pub fn main() void {
//
// You are perhaps wondering what happens if one of the two lists
// 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

@@ -37,63 +37,48 @@
const std = @import("std");
const testing = std.testing;
// This is a simple function
// that builds a sum from the
// passed parameters and returns.
// This is a simple function that builds a sum from the passed parameters and
// returns.
fn add(a: f16, b: f16) f16 {
return a + b;
}
// The associated test.
// It always starts with the keyword "test",
// followed by a description of the tasks
// of the test. This is followed by the
// test cases in curly brackets.
// The associated test. It always starts with the keyword "test", followed by a
// description of the tasks of the test. This is followed by the test cases in
// curly brackets.
test "add" {
// The first test checks if the sum
// of '41' and '1' gives '42', which
// is correct.
// The first test checks if the sum of '41' and '1' gives '42', which is
// correct.
try testing.expect(add(41, 1) == 42);
// Another way to perform this test
// is as follows:
try testing.expectEqual(add(41, 1), 42);
// Another way to perform this test is as follows:
try testing.expectEqual(42, add(41, 1));
// This time a test with the addition
// of a negative number:
// This time a test with the addition of a negative number:
try testing.expect(add(5, -4) == 1);
// And a floating point operation:
try testing.expect(add(1.5, 1.5) == 3);
}
// Another simple function
// that returns the result
// of subtracting the two
// Another simple function that returns the result of subtracting the two
// parameters.
fn sub(a: f16, b: f16) f16 {
return a - b;
}
// The corresponding test
// is not much different
// from the previous one.
// Except that it contains
// an error that you need
// to correct.
// The corresponding test is not much different from the previous one. Except
// that it contains an error that you need to correct.
test "sub" {
try testing.expect(sub(10, 5) == 6);
try testing.expect(sub(3, 1.5) == 1.5);
}
// This function divides the
// numerator by the denominator.
// Here it is important that the
// denominator must not be zero.
// This is checked and if it
// occurs an error is returned.
// This function divides the numerator by the denominator. Here it is important
// that the denominator must not be zero. This is checked and if it occurs an
// error is returned.
fn divide(a: f16, b: f16) !f16 {
if (b == 0) return error.DivisionByZero;
return a / b;
@@ -105,8 +90,7 @@ test "divide" {
try testing.expect(divide(10, 2) catch unreachable == 5);
try testing.expect(divide(1, 3) catch unreachable == 0.3333333333333333);
// Now we test if the function returns an error
// if we pass a zero as denominator.
// But which error needs to be tested?
// Now we test if the function returns an error if we pass a zero as
// denominator. But which error needs to be tested?
try testing.expectError(error.???, divide(15, 0));
}

View File

@@ -2,7 +2,7 @@
// The functionality of the standard library is becoming increasingly
// important in Zig. First of all, it is helpful to take a look at how
// the individual functions are implemented. Because this is wonderfully
// suitable as a template for your own functions. In addition these
// suitable as a template for your own functions. In addition, these
// standard functions are part of the basic configuration of Zig.
//
// This means that they are always available on every system.
@@ -14,7 +14,7 @@
// exercises.
//
// 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
// to the elegance of Zig and that's a pity, if someone takes a short,
@@ -24,8 +24,7 @@
// suited to understand the basic principles.
//
// In the following exercises we will also read and process data from
// large files and at the latest then it will be clear to everyone how
// useful all this is.
// large files, it will then be clearer to you how useful all this is.
//
// Let's start with the analysis of the example from the Zig homepage
// and explain the most important things.
@@ -48,15 +47,14 @@
// // In order to be able to process the input values,
// // memory is required. An allocator is defined here for
// // this purpose.
// const ally = std.testing.allocator;
// const gpa = std.testing.allocator;
//
// // The allocator is used to initialize an array into which
// // the numbers are stored.
// var list = std.ArrayList(u32).init(ally);
// // An array into which the numbers are stored is initialized.
// var list: std.ArrayList(u32) = .empty;
//
// // This way you can never forget what is urgently needed
// // and the compiler doesn't grumble either.
// defer list.deinit();
// defer list.deinit(gpa);
//
// // Now it gets exciting:
// // A standard tokenizer is called (Zig has several) and
@@ -73,7 +71,7 @@
// const n = try parseInt(u32, num, 10);
//
// // Finally the individual values are stored in the array.
// try list.append(n);
// try list.append(gpa, n);
// }
//
// // For the subsequent test, a second static array is created,

View File

@@ -1,31 +1,22 @@
//
// Whenever there is a lot to calculate, the question arises as to how
// tasks can be carried out simultaneously. We have already learned about
// one possibility, namely asynchronous processes, in Exercises 84-91.
// In Exercises 84-91, we learned about Zig's Io interface for
// concurrent execution: io.async(), Group, Select, and Futures.
// Under the hood, the Threaded backend manages a pool of real
// OS threads for you - including scheduling, cancellation, and
// resource cleanup.
//
// 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.
// But sometimes you need direct control over threads:
// * Long-lived dedicated workers
// * Specific stack sizes or thread counts
// * Code that doesn't have an Io interface available
// * Fine-grained synchronization patterns
//
// 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
// in the hash of the character string and the nonce are zeros.
// As the miner who can solve the task first receives the reward, everyone
// tries to complete the calculations as quickly as possible.
// That's where std.Thread comes in. It gives you a raw OS thread
// that you spawn, manage, and join yourself. No pool, no Futures,
// no automatic cancellation - but full control.
//
// This is where multithreading comes into play, where tasks are actually
// distributed across several cores of the CPU or GPU, which then really
// means a multiplication of performance.
//
// The following diagram roughly illustrates the difference between the
// various types of process execution.
// The 'Overall Time' column is intended to illustrate how the time is
// affected if, instead of one core as in synchronous and asynchronous
// processing, a second core now helps to complete the work in multithreading.
//
// In the ideal case shown, execution takes only half the time compared
// to the synchronous single thread. And even asynchronous processing
// is only slightly faster in comparison.
// The following diagram roughly illustrates the difference between
// the various types of process execution:
//
//
// Synchronous Asynchronous
@@ -106,7 +97,9 @@ 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);
var io_instance: std.Io.Threaded = .init_single_threaded;
const io = io_instance.io();
try io.sleep(std.Io.Duration.fromMilliseconds(400), .awake);
std.debug.print("Some weird stuff, after starting the threads.\n", .{});
}
// After we have left the closed area, we wait until
@@ -116,15 +109,17 @@ 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.debug.print("thread {d}: {s}\n", .{ num, "started." });
fn thread_function(id: usize) !void {
var io_instance: std.Io.Threaded = .init_single_threaded;
const io = io_instance.io();
try io.sleep(std.Io.Duration.fromMilliseconds(100 * @as(isize, @intCast(id))), .awake);
std.debug.print("thread {d}: {s}\n", .{ id, "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);
const work_time = 300 * ((5 - id % 3) - 2);
try io.sleep(std.Io.Duration.fromMilliseconds(@intCast(work_time)), .awake);
std.debug.print("thread {d}: {s}\n", .{ num, "finished." });
std.debug.print("thread {d}: {s}\n", .{ id, "finished." });
}
// This is the easiest way to run threads in parallel.
// In general, however, more management effort is required,

View File

@@ -105,3 +105,6 @@ fn thread_pi(pi: *f64, begin: u64, end: u64) !void {
//
// And you should remove the formatting restriction in "print",
// otherwise you will not be able to see the additional digits.
//
// If count = 10_000_000_000_000 you should see the following:
// 3.141592653589

View File

@@ -12,23 +12,29 @@
// Fortunately, the Zig Standard Library provides a simple API for interacting
// with the file system, see the detail documentation here:
//
// https://ziglang.org/documentation/master/std/#std.fs
// https://ziglang.org/documentation/master/std/#std.Io
//
// 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
// Note: For simplicity, we write byte-by-byte without buffering.
// In real applications, you'd typically use a buffer for better
// performance. We'll learn about buffered I/O in a later exercise.
//
const std = @import("std");
pub fn main() !void {
pub fn main(init: std.process.Init) !void {
// default I/O implementation
const io = init.io;
// first we get the current working directory
const cwd: std.fs.Dir = std.fs.cwd();
const cwd: std.Io.Dir = std.Io.Dir.cwd();
// then we'll try to make a new directory /output/
// to store our output files.
cwd.makeDir("output") catch |e| switch (e) {
cwd.createDir(io, "output", .default_dir) catch |e| switch (e) {
// there is a 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
@@ -44,34 +50,38 @@ pub fn main() !void {
// 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();
var output_dir: std.Io.Dir = try cwd.openDir(io, "output", .{});
defer output_dir.close(io);
// we try to open the file `zigling.txt`,
// and propagate any error up
const file: std.fs.File = try output_dir.createFile("zigling.txt", .{});
const file: std.Io.File = try output_dir.createFile(io, "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
// 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...
file.close();
file.close(io);
// you are not allowed to move these two lines above the file closing line!
const byte_written = try file.write("It's zigling time!");
// you are not allowed to move these lines above the file closing line!
var file_writer = file.writer(io, &.{});
const writer = &file_writer.interface;
const byte_written = try writer.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
// >> cat ./output/zigling.txt
// 2. print the content of the file in the console with one of these commands
// Linux/macOS: >> cat ./output/zigling.txt
// Windows (CMD): >> type .\output\zigling.txt
//
//
// More on Creating files
//
// notice in:
// ... try output_dir.createFile("zigling.txt", .{});
// ^^^
// ... try output_dir.createFile(io, "zigling.txt", .{});
// ^^^
// we passed this anonymous struct to the function call
//
// this is the struct `CreateFlag` with default fields
@@ -86,7 +96,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:
// https://ziglang.org/documentation/master/std/#std.fs.Dir
// - go to the documentation of the struct `std.Io.Dir` here:
// https://ziglang.org/documentation/master/std/#std.Io.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?

View File

@@ -1,8 +1,8 @@
//
// Prerequisite :
// - exercise/106_files.zig, or
// - exercise/109_files.zig, or
// - create a file {project_root}/output/zigling.txt
// with content `It's zigling time!`(18 byte total)
// with content `It's zigling time!`(18 bytes 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.
@@ -15,32 +15,41 @@
// - 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
//
// Note: For simplicity, we read byte-by-byte without buffering.
// In real applications, you'd typically use a buffer for better
// performance. We'll learn about buffered I/O in a later exercise.
const std = @import("std");
pub fn main() !void {
// Get the current working directory
const cwd = std.fs.cwd();
pub fn main(init: std.process.Init) !void {
const io = init.io;
// try to open ./output assuming you did your 106_files exercise
var output_dir = try cwd.openDir("output", .{});
defer output_dir.close();
// Get the current working directory
const cwd = std.Io.Dir.cwd();
// try to open ./output assuming you did your 109_files exercise
var output_dir = try cwd.openDir(io, "output", .{});
defer output_dir.close(io);
// try to open the file
const file = try output_dir.openFile("zigling.txt", .{});
defer file.close();
const file = try output_dir.openFile(io, "zigling.txt", .{});
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
// fix the initialization below
var content = ['A']*64;
// do you remember the array repetition function?
var content: ??? = ???('A');
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content});
var file_reader = file.reader(io, &.{});
const reader = &file_reader.interface;
// 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?
// https://ziglang.org/documentation/master/std/#std.fs.File
// hint: you might find two answers that are both valid in this case
// https://ziglang.org/documentation/master/std/#std.Io.Reader
// hint: look for a method that reads into a slice
const bytes_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.

View File

@@ -73,7 +73,7 @@
// 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.
// Defining vectors in Zig is straightforward. No library import is needed.
const v1 = @Vector(3, i32){ 1, 10, 100 };
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };

175
exercises/114_packed.zig Normal file
View File

@@ -0,0 +1,175 @@
//
// We've already learned plenty about bit manipulation using bitwise operations
// 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
// quite tedious and unwieldy pretty quickly.
// What if there was a better, a more convenient way to control individual bits?
//
// Luckily, Zig has a keyword for exactly this purpose:
//
// packed
//
// It doesn't do anything on its own, to unlock its potential (and to get our
// program to compile) we have to attach it either to a struct or to a union
// declaration:
//
// const Foo = packed struct { ... };
// const Bar = packed union { ... };
//
// Now, what does this keyword even do?
// To answer this question we first have to talk about *container layouts*.
//
// Plain structs and unions use the `auto` layout; it gives no guarantees about
// their size or the order of the fields they contain, both are fully up to the
// compiler (though both size and field order *are* guaranteed to be the same
// across any single compilation unit).
//
// Attaching the `packed` keyword to a container makes it use `packed` layout:
// Suddenly, all of its fields are *packed* together tightly without any padding
// in between and their order is guaranteed to be the same as the one specified
// in our source code. For structs, the size of the container is guaranteed to
// be the sum of the (bit-)sizes of all of its fields. For unions, all fields
// have to have the exact same (bit-)size (no padding allowed!); the union itself
// is also guaranteed to be exactly of this size.
//
// If you're familiar with C, you might have already heard of structure packing
// in a different context: arranging fields in a way that minimizes the amount
// of alignment padding between them (or having the compiler do it for you).
// This is *not* what Zig's `packed` keyword is for!
//
// Try to make the comptime assertions below pass:
const PackedStruct = packed struct {
a: u2,
b: u?,
};
comptime {
assert(@bitSizeOf(PackedStruct) == 6);
}
const PackedUnion = packed union {
a: bool,
b: u?,
};
comptime {
assert(@bitSizeOf(PackedUnion) == 1);
}
// Now, how can we use this new knowledge to manipulate some bits?
//
// As you might have already guessed, `packed` containers are very useful for
// representing bitflags or other tightly packed collections of bit-sized values
// often found in file headers and network protocols.
//
// Let's take a look at a real-life example:
// The LZ4 compression format (†) specifies a frame format to describe compressed
// data. Each LZ4 frame has a descriptor, and each descriptor contains a 'FLG'
// byte that specifies the contents of its frame:
/// | BitNb | 7-6 | 5 | 4 | 3 | 2 | 1 | 0 |
/// | ------- |-------|-------|----------|------|----------|--------|------|
/// |FieldName|Version|B.Indep|B.Checksum|C.Size|C.Checksum|Reserved|DictID|
///
const FLG = packed struct(u8) {
dict_id: bool,
reserved: u1 = 0,
content_checksum: bool,
content_size: bool,
block_checksum: bool,
block_indepencence: bool,
version: u2,
};
// Wait, what's with the `(u8)` after the `struct` keyword? What do integers have
// to do with all of this?
// Well, this is a good opportunity to come clear about something:
// packed structs and packed unions aren't actually structs or unions at all...
// They are merely integers in disguise! For all intents and purposes, their
// fields are just convenient names for ranges of their underlying bits. To make
// it easier to enforce size requirements for packed containers, Zig allows us
// to specify a *backing integer* for them, just like for enums.
//
// In the case of `FLG`, we want our struct to occupy exactly a single byte, so
// we specify `u8` as the backing integer. It's safe to convert between a packed
// container and its backing integer using the builtin `@bitCast`.
// The LZ4 spec also mandates that reserved bits must always be zero, so it's
// good practice to set `0` as a default value for `reserved`.
//
// The fields of a packed struct start at the least significant bit of its backing
// integer and end at its most significant bit. This is the case no matter what
// endianness our target has.
//
// Try to silence the complaints below:
const Bits = packed struct(u4) {
a: u1 = 0,
b: u1 = 0,
c: u1 = 0,
d: u1 = 0,
};
pub fn main() void {
{
const expected: Bits = @bitCast(@as(u4, 0b1000));
const my_bits: Bits = .{};
if (my_bits != expected) complain(my_bits, expected, @src());
}
{
const expected: Bits = @bitCast(@as(u4, 0b0001));
const my_bits: Bits = .{};
if (my_bits != expected) complain(my_bits, expected, @src());
}
{
const expected: Bits = @bitCast(@as(u4, 0b0010));
const my_bits: Bits = .{};
if (my_bits != expected) complain(my_bits, expected, @src());
}
{
const expected: Bits = @bitCast(@as(u4, 0b0011));
const my_bits: Bits = .{};
if (my_bits != expected) complain(my_bits, expected, @src());
}
{
const expected: Bits = @bitCast(@as(u4, 0b1101));
const my_bits: Bits = .{};
if (my_bits != expected) complain(my_bits, expected, @src());
}
}
// As we can see, equality comparisons (`==` and `!=`) work for packed structs.
// They also work for packed unions. However, since packed containers are not
// naturally ordered, we can't use any other comparison operators on them.
//
// It's also possible to use packed containers in `switch` statements, which we
// will cover in the next exercise!
//
// Since packed containers make very strong guarantees about their memory layout,
// only a handful of types are eligible to be part of them.
// The following types are allowed as field types:
//
// - integers
// - floats
// - bool
// - void
// - enums with explicit backing integers
// - packed unions
// - packed structs
//
const std = @import("std");
const assert = std.debug.assert;
fn complain(my_bits: Bits, expected: Bits, src_loc: std.builtin.SourceLocation) void {
std.debug.print(
"That's not quite right! You've got 0b{b:0>4}, but we want 0b{b:0>4} in line {d}.\n",
.{ @as(u4, @bitCast(my_bits)), @as(u4, @bitCast(expected)), src_loc.line },
);
}
// (†) https://github.com/lz4/lz4/blob/5c4c1fb2354133e1f3b087a341576985f8114bd5/doc/lz4_Frame_format.md#frame-descriptor

78
exercises/115_packed2.zig Normal file
View File

@@ -0,0 +1,78 @@
//
// We've already learned about switch statements in exercises 030, 031 and 108.
// They also work with packed containers:
const S = packed struct(u2) {
a: bool,
b: i1,
};
// Try to make it compile without adding an `else` prong!
comptime {
const s: S = .{ .a = true, .b = -1 };
switch (s) {
.{ .a = true, .b = -1 } => {}, // ok!
.{ .a = true, .b = ??? },
.{ .a = ???, .b = 0 },
.{ .a = ???, .b = ??? },
=> @compileError("We don't want to end up here!"),
}
}
// As we can see, switching on packed structs is pretty straightforward.
// When switching on packed unions however, we'll realize that a packed
// union never keeps track of its active tag, not even in debug mode! This
// means that packed unions compare solely by their bit pattern (again, just
// like integers).
const U = packed union(u2) {
a: u2,
b: i2,
};
// Find and remove the duplicate case!
comptime {
const u: U = .{ .a = 3 };
switch (u) {
.{ .a = 3 } => {}, // ok!
.{ .a = 2 },
.{ .b = 1 },
.{ .b = -1 },
.{ .a = 0 },
=> @compileError("We don't want to end up here!"),
}
}
// Since packed unions don't have the concept of an active tag, it's always legal
// to access any of their fields. This can be useful to view the same data from
// different perspectives seamlessly.
//
// Try to make the float below negative:
/// IEEE 754 half precision float
const Float = packed union(u16) {
value: f16,
bits: packed struct(u16) {
mantissa: u10,
exponent: u5,
sign: u1,
},
};
pub fn main() void {
// Reminder: if the sign bit of a float is set, the number is negative!
var number: Float = .{ .value = 2.34 };
number.bits.??? = ???;
if (number.value != -2.34) {
std.debug.print("Make it negative!\n", .{});
}
}
// This concludes our introduction to packed containers. The next time you need
// control over individual bits, keep them in mind as a potent alternative!
//
const std = @import("std");

BIN
images/ziglings_dark.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
#
# "I will be a shieldmaiden no longer,
# nor vie with the great Riders, nor

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
#
# "How do you pick up the threads of an old life?
# How do you go on, when in your heart you begin

View File

@@ -1,5 +1,5 @@
--- exercises/001_hello.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/001_hello.zig 2023-10-05 20:04:06.846096282 +0200
--- exercises/001_hello.zig 2026-01-04 14:04:52.752848018 +0100
+++ answers/001_hello.zig 2026-01-04 14:04:54.209877278 +0100
@@ -16,6 +16,6 @@
//
const std = @import("std");

View File

@@ -1,6 +1,6 @@
--- exercises/005_arrays2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/005_arrays2.zig 2023-10-05 20:04:06.862763262 +0200
@@ -25,12 +25,12 @@
--- exercises/005_arrays2.zig 2026-05-04 16:26:32.778330847 +0200
+++ answers/005_arrays2.zig 2026-05-04 16:26:13.082917974 +0200
@@ -21,12 +21,12 @@
// (Problem 1)
// Please set this array concatenating the two arrays above.
// It should result in: 1 3 3 7
@@ -10,8 +10,8 @@
// (Problem 2)
// Please set this array using repetition.
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
- const bit_pattern = [_]u8{ ??? } ** 3;
+ const bit_pattern = [_]u8{ 1, 0, 0, 1 } ** 3;
- const bit_pattern_unit = [_]u8{ ??? };
+ 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.
//

View File

@@ -1,6 +1,6 @@
--- exercises/006_strings.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/006_strings.zig 2023-10-05 20:04:06.869430053 +0200
@@ -24,18 +24,18 @@
--- exercises/006_strings.zig 2026-05-04 17:04:31.763821070 +0200
+++ answers/006_strings.zig 2026-05-04 17:02:11.672866263 +0200
@@ -24,14 +24,14 @@
// (Problem 1)
// Use array square bracket syntax to get the letter 'd' from
// the string "stardust" above.
@@ -8,11 +8,6 @@
+ const d: u8 = ziggy[4];
// (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".
// (You'll need to add a space as well!)
const major = "Major";
@@ -21,4 +16,4 @@
+ const major_tom = major ++ " " ++ tom;
// 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,11 +1,11 @@
--- exercises/009_if.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/009_if.zig 2023-10-05 20:04:06.882763636 +0200
--- exercises/009_if.zig 2025-11-28 14:40:19.301738185 +0100
+++ answers/009_if.zig 2025-11-28 14:39:07.756077340 +0100
@@ -24,7 +24,7 @@
const foo = 1;
const foo = 42;
// Please fix this condition:
- if (foo) {
+ if (foo == 1) {
+ if (foo == 42) {
// We want our program to print this message!
std.debug.print("Foo is 1!\n", .{});
std.debug.print("Foo is 42!\n", .{});
} else {

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
@@ -23,5 +23,5 @@
--- exercises/026_hello2.zig 2026-01-09 22:51:45.803358789 +0100
+++ answers/026_hello2.zig 2026-01-09 22:50:46.016166527 +0100
@@ -28,5 +28,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,6 +1,6 @@
--- exercises/028_defer2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/028_defer2.zig 2023-10-05 20:04:06.966098530 +0200
@@ -18,7 +18,7 @@
--- exercises/028_defer2.zig 2026-06-02 06:08:12.713672612 +0200
+++ answers/028_defer2.zig 2026-06-02 06:08:43.262234023 +0200
@@ -20,7 +20,7 @@
fn printAnimal(animal: u8) void {
std.debug.print("(", .{});
@@ -9,3 +9,15 @@
if (animal == 'g') {
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,15 +1,11 @@
--- exercises/034_quiz4.zig 2025-07-22 09:55:51.337832401 +0200
+++ answers/034_quiz4.zig 2025-07-22 10:05:08.320323184 +0200
@@ -9,10 +9,10 @@
const NumError = error{IllegalNumber};
-pub fn main() void {
+pub fn main() !void {
var stdout = std.fs.File.stdout().writer(&.{});
--- exercises/034_quiz4.zig 2026-01-09 22:45:53.115325559 +0100
+++ answers/034_quiz4.zig 2026-01-09 22:45:15.658578603 +0100
@@ -14,7 +14,7 @@
var stdout_writer = std.Io.File.stdout().writer(io, &.{});
const stdout = &stdout_writer.interface;
- 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,5 +1,5 @@
--- exercises/040_pointers2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/040_pointers2.zig 2023-10-05 20:04:07.022766257 +0200
--- exercises/040_pointers2.zig 2026-05-22 21:57:28.601255748 +0200
+++ answers/040_pointers2.zig 2026-05-22 21:57:27.672235943 +0200
@@ -23,7 +23,7 @@
pub fn main() void {

View File

@@ -1,5 +1,5 @@
--- 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 2026-05-04 16:34:31.692458399 +0200
+++ answers/058_quiz7.zig 2026-05-04 16:34:29.239406323 +0200
@@ -192,8 +192,8 @@
// Oops! The hermit forgot how to capture the union values
// 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
// together. Remember that you return the address with the
// "&" operator.
@@ -20,7 +20,7 @@
// Try to make your answer this long:__________;
}
return null;
@@ -309,7 +309,7 @@
@@ -308,7 +308,7 @@
//
// Looks like the hermit forgot something in the return value of
// this function. What could that be?

View File

@@ -1,11 +1,20 @@
--- exercises/060_floats.zig 2025-03-03 20:23:40.255443963 +0400
+++ answers/060_floats.zig 2025-03-03 20:29:58.554854977 +0400
@@ -43,7 +43,7 @@
--- exercises/060_floats.zig 2026-05-02 19:22:46.225370223 +0200
+++ answers/060_floats.zig 2026-05-02 19:22:47.523142218 +0200
@@ -50,7 +50,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;
+ const shuttle_weight: f32 = 0.453592 * 4480e3;
// By default, float values are formatted in scientific
// notation. Try experimenting with '{d}' and '{d:.3}' to see
// By default, float values are formatted in standard decimal
// notation. Experiment with '{d}' and '{d:.3}' to see how
@@ -58,7 +58,7 @@
// scientific notation.
// NOTE: The weight of the shuttle is a huge number, a scientific notation
// may be more appropriate.
- print("Shuttle liftoff weight: {d:.0} metric tons\n", .{shuttle_weight / 1e3});
+ print("Shuttle liftoff weight: {e:.3} metric tons\n", .{shuttle_weight / 1e3});
}
// Floating further:

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 2026-06-01 15:33:16.617432671 +0200
+++ answers/065_builtins2.zig 2026-06-01 15:33:31.104018108 +0200
@@ -58,7 +58,7 @@
// Oops! We cannot leave the 'me' and 'myself' fields
// undefined. Please set them here:
@@ -18,22 +18,26 @@
// Now we print a pithy statement about Narcissus.
print("A {s} loves all {s}es. ", .{
@@ -113,15 +113,15 @@
@@ -102,16 +102,16 @@
// 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});
- if (field_???[???] != void) {
- print(" {s}", .{field_???[???]});
+ if (field_types[0] != void) {
+ print(" {s}", .{field_names[0]});
}
- if (fields[1].??? != void) {
+ if (fields[1].type != void) {
print(" {s}", .{fields[1].name});
- if (field_???[???] != void) {
- print(" {s}", .{field_???[???]});
+ if (field_types[1] != void) {
+ print(" {s}", .{field_names[1]});
}
- if (fields[2].??? != void) {
+ if (fields[2].type != void) {
print(" {s}", .{fields[2].name});
- if (field_???[???] != void) {
- print(" {s}", .{field_???[???]});
+ 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,6 +1,6 @@
--- exercises/067_comptime2.zig 2023-11-21 14:36:12.080295365 +0100
+++ answers/067_comptime2.zig 2023-11-21 15:11:50.814098876 +0100
@@ -35,7 +35,7 @@
--- exercises/067_comptime2.zig 2026-05-04 15:38:52.565144012 +0200
+++ answers/067_comptime2.zig 2026-05-04 15:37:20.257213463 +0200
@@ -36,7 +36,7 @@
// In this contrived example, we've decided to allocate some
// arrays using a variable count! But something's missing...
//
@@ -8,4 +8,4 @@
+ comptime var count = 0;
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
+++ answers/071_comptime6.zig 2024-09-02 19:21:23.553250563 +0200
@@ -40,7 +40,7 @@
const fields = @typeInfo(Narcissus).@"struct".fields;
--- exercises/071_comptime6.zig 2026-06-01 15:35:27.223400223 +0200
+++ answers/071_comptime6.zig 2026-06-01 15:36:35.349728561 +0200
@@ -41,7 +41,7 @@
const field_names = @typeInfo(Narcissus).@"struct".field_names;
const field_types = @typeInfo(Narcissus).@"struct".field_types;
- ??? {
+ inline for (fields) |field| {
if (field.type != void) {
print(" {s}", .{field.name});
+ inline for (field_names, field_types) |field_name, field_type| {
if (field_type != void) {
print(" {s}", .{field_name});
}

View File

@@ -1,11 +1,35 @@
--- exercises/074_comptime9.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/074_comptime9.zig 2023-10-05 20:04:07.176102462 +0200
@@ -39,7 +39,7 @@
--- exercises/074_comptime9.zig 2026-05-04 17:11:05.144118157 +0200
+++ answers/074_comptime9.zig 2026-05-04 17:10:36.778519877 +0200
@@ -28,12 +28,12 @@
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.
};
- var state = State.start;
+ comptime var state = State.start;
// And here's the function. Note that the return value type
// depends on one of the input arguments!
-fn makeLlamas(count: usize) [count]u8 {
+fn makeLlamas(comptime count: usize) [count]u8 {
var temp: [count]u8 = undefined;
var i = 0;
// We return an array of animals representing the creature. (This is why we
// really needed the 'count' parameter. Arrays need a size.)
var animals: [count]Animal = undefined;
- var next_animal: usize = 0;
+ comptime var next_animal: usize = 0;
inline for (fmt) |char| {
@@ -57,7 +57,7 @@
//
// What do you think happens with Gators? Do they join with
// other animals or is this an error?
- 'g' => ???,
+ 'g' => @compileError("Gators refuse to join with other animals."),
else => @compileError(std.fmt.comptimePrint("No animal starts with '{c}'!", .{char})),
},
@@ -69,7 +69,7 @@
next_animal += 1;
// Something is missing here. After we finish a Llama, we
// need to be ready to _start_ over with a new animal...
- ???
+ state = .start;
},
else => @compileError("Only llamas start with 'l'!"),

View File

@@ -1,15 +1,17 @@
--- exercises/075_quiz8.zig 2023-11-21 14:48:15.440702720 +0100
+++ answers/075_quiz8.zig 2023-11-21 14:50:23.453311616 +0100
@@ -49,7 +49,11 @@
--- exercises/075_quiz8.zig 2026-05-04 15:51:48.254371574 +0200
+++ answers/075_quiz8.zig 2026-05-04 15:49:28.426445382 +0200
@@ -48,7 +48,13 @@
// instead.
//
// 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{
+ .from = from,
+ .to = to,
+ .dist = dist,
+ };
}
+}
// 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 2025-03-14 16:41:17.892873287 +0200
+++ answers/082_anonymous_structs3.zig 2025-03-14 16:40:56.043829543 +0200
@@ -82,14 +82,14 @@
// @typeInfo(Circle).@"struct".fields
--- exercises/082_anonymous_structs3.zig 2026-06-01 15:59:11.872467805 +0200
+++ answers/082_anonymous_structs3.zig 2026-06-01 15:58:38.004730144 +0200
@@ -82,17 +82,17 @@
// @typeInfo(Circle).@"struct".field_types
//
// This will be an array of StructFields.
- const fields = ???;
+ const fields = @typeInfo(@TypeOf(tuple)).@"struct".fields;
// This will be an array of field types.
- const field_types = ???;
+ 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
// time.
//
// Hint: remember 'inline' loops?
//
- for (fields) |field| {
+ inline for (fields) |field| {
- for (???, ???) |???, ???| {
+ inline for (field_types, field_names) |field_type, field_name| {
// 3. Print the field's name, type, and value.
//
// Each 'field' in this loop is one of these:
@@ -119,9 +119,9 @@
//
// The first field should print as: "0"(bool):true
// You'll need this builtin:
@@ -116,7 +116,7 @@
print("\"{s}\"({any}):{any} ", .{
- field.???,
- field.???,
field_name,
field_type,
- ???,
+ field.name,
+ field.type,
+ @field(tuple, field.name),
+ @field(tuple, field_name),
});
}
}

View File

@@ -0,0 +1,11 @@
--- exercises/084_async.zig 2026-04-01 20:40:08.904999609 +0200
+++ answers/084_async.zig 2026-04-01 20:40:05.641933231 +0200
@@ -37,7 +37,7 @@
const std = @import("std");
pub fn main(init: std.process.Init) !void {
- const io = init.???;
+ const io = init.io;
// Get the current wall-clock time using the Io interface.
// Hint: Timestamp.now() takes an Io and a Clock type (.real = wall clock).

View File

@@ -1,5 +1,5 @@
--- exercises/092_interfaces.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/092_interfaces.zig 2023-10-05 20:04:07.259437354 +0200
--- exercises/084_interfaces.zig 2026-04-03 19:24:51.764327692 +0200
+++ answers/084_interfaces.zig 2026-04-03 19:27:31.552579474 +0200
@@ -106,7 +106,7 @@
for (my_insects) |insect| {
// Almost done! We want to print() each insect with a

View File

@@ -0,0 +1,11 @@
--- exercises/085_async.zig 2026-04-04 16:01:01.509555724 +0200
+++ answers/085_async.zig 2026-04-04 16:00:58.541495688 +0200
@@ -38,7 +38,7 @@
const std = @import("std");
pub fn main(init: std.process.Init) !void {
- const io = init.???;
+ const io = init.io;
// Get the current wall-clock time using the Io interface.
// Hint: Timestamp.now() takes an Io and a Clock type (.real = wall clock).

View File

@@ -0,0 +1,11 @@
--- exercises/086_async2.zig 2026-04-05 12:41:11.350626443 +0200
+++ answers/086_async2.zig 2026-04-05 12:42:00.879791167 +0200
@@ -44,7 +44,7 @@
// Now collect the result. What method on Future gives us
// the value, blocking until it's ready?
- const answer = future.???(io);
+ const answer = future.await(io);
print("The answer is: {}\n", .{answer});
}

View File

@@ -0,0 +1,11 @@
--- exercises/087_async3.zig 2026-04-05 16:12:48.317265515 +0200
+++ answers/087_async3.zig 2026-04-05 16:12:52.269343030 +0200
@@ -28,7 +28,7 @@
// Launch both tasks asynchronously.
var future_a = io.async(slowAdd, .{ 1, 2 });
defer _ = future_a.cancel(io);
- var future_b = ???(slowMul, .{ 6, 7 });
+ var future_b = io.async(slowMul, .{ 6, 7 });
defer _ = future_b.cancel(io);
// Await both results.

View File

@@ -0,0 +1,11 @@
--- exercises/088_async4.zig 2026-04-06 12:22:06.643385622 +0200
+++ answers/088_async4.zig 2026-04-06 12:22:11.820491035 +0200
@@ -38,7 +38,7 @@
// Wait for all tasks to finish.
// What Group method blocks until all tasks complete?
- try group.???(io);
+ try group.await(io);
print("All tasks finished!\n", .{});
}

View File

@@ -0,0 +1,11 @@
--- exercises/089_async5.zig 2026-04-06 14:38:54.443726849 +0200
+++ answers/089_async5.zig 2026-04-06 14:38:39.945438309 +0200
@@ -46,7 +46,7 @@
// We don't want to wait 10 seconds!
// Which Future method requests cancellation AND returns the result?
- const result = future.???(io);
+ const result = future.cancel(io);
print("Task returned: {}\n", .{result});
}

View File

@@ -0,0 +1,11 @@
--- exercises/090_async6.zig 2026-04-06 18:49:37.232023422 +0200
+++ answers/090_async6.zig 2026-04-06 18:49:22.189720687 +0200
@@ -52,7 +52,7 @@
// Wait for the first finisher.
// What Select method returns the first completed result?
- const winner = try sel.???();
+ const winner = try sel.await();
switch (winner) {
.hare => |msg| print("Hare: {s}\n", .{msg}),

View File

@@ -0,0 +1,13 @@
--- exercises/091_async7.zig 2026-04-18 23:30:40.963951835 +0200
+++ answers/091_async7.zig 2026-04-18 23:33:47.313340585 +0200
@@ -49,8 +49,8 @@
for (0..times) |_| {
// Acquire the lock before modifying shared state.
// What Mutex method blocks until the lock is acquired?
- state.mutex.??? catch return;
- defer state.mutex.unlock(); // <-- what's missing here?
+ state.mutex.lock(io) catch return;
+ defer state.mutex.unlock(io);
// 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

@@ -0,0 +1,11 @@
--- exercises/092_async8.zig 2026-04-02 10:49:27.925721496 +0200
+++ answers/092_async8.zig 2026-04-02 10:49:31.694795212 +0200
@@ -43,7 +43,7 @@
// Send numbers 1 through 10 into the queue.
for (1..11) |i| {
// What Queue method sends a single element, blocking if full?
- queue.???(io, @intCast(i)) catch return;
+ queue.putOne(io, @intCast(i)) catch return;
}
// Signal that we're done sending.
queue.close(io);

View File

@@ -0,0 +1,15 @@
--- exercises/093_async9.zig 2026-04-14 22:20:23.519107582 +0200
+++ answers/093_async9.zig 2026-04-14 22:06:30.606912044 +0200
@@ -63,10 +63,10 @@
// Launch two workers, each searching half the array.
// Remember, we want them to be guaranteed separate units of concurrency.
- var f1 = ???(searchThreshold, .{ io, data[0..mid], threshold, 0, 0, &queue });
+ var f1 = try io.concurrent(searchThreshold, .{ io, data[0..mid], threshold, 0, 0, &queue });
defer _ = f1.cancel(io);
- var f2 = ???(searchThreshold, .{ io, data[mid..], threshold, mid, 1, &queue });
+ var f2 = try io.concurrent(searchThreshold, .{ io, data[mid..], threshold, mid, 1, &queue });
defer _ = f2.cancel(io);
// Wait for the first result.

View File

@@ -0,0 +1,13 @@
--- exercises/094_async10.zig 2026-04-06 19:36:59.873966580 +0200
+++ answers/094_async10.zig 2026-04-06 19:37:12.416216872 +0200
@@ -51,8 +51,8 @@
// Protect this section from cancellation.
// What method swaps the cancel protection state?
- const old = io.???(.blocked);
- defer _ = io.???(old);
+ const old = io.swapCancelProtection(.blocked);
+ defer _ = io.swapCancelProtection(old);
// This sleep will NOT return error.Canceled even though
// we get canceled during it — protection is active!

View File

@@ -1,11 +0,0 @@
--- exercises/095_for3.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/095_for3.zig 2023-10-05 20:04:07.272770937 +0200
@@ -54,7 +54,7 @@
// I want to print every number between 1 and 20 that is NOT
// divisible by 3 or 5.
- for (???) |n| {
+ for (1..21) |n| {
// The '%' symbol is the "modulo" operator and it
// returns the remainder after division.

View File

@@ -0,0 +1,56 @@
--- exercises/095_quiz_async.zig 2026-04-13 19:11:04.173440326 -0700
+++ answers/095_quiz_async.zig 2026-04-13 19:10:31.618592222 -0700
@@ -51,7 +51,7 @@
fn addReading(self: *GardenWeather, io: std.Io, reading: Reading) void {
// Bug 1: The collector needs to lock before modifying
// shared state. What Mutex method acquires the lock?
- self.mutex.???(io) catch return;
+ self.mutex.lock(io) catch return;
defer self.mutex.unlock(io);
switch (reading.sensor_type) {
@@ -78,7 +78,7 @@
// Bug 2: The collector needs guaranteed concurrency.
// What method ensures a separate unit of concurrency?
// (Don't forget: it can fail!)
- var collector_future = try io.???(collector, .{ io, &queue, &weather });
+ var collector_future = try io.concurrent(collector, .{ io, &queue, &weather });
defer _ = collector_future.cancel(io);
// Sensor group: the sensors can use async — they just need
@@ -91,22 +91,22 @@
// Bug 3: Wait for ALL sensors to finish sending their readings.
// What Group method blocks until all tasks complete?
- try sensors.???(io);
+ try sensors.await(io);
// All sensors done — close the queue so the collector knows
// there's no more data coming.
queue.close(io);
// Bug 4: How do we wait for the collector to drain the remaining queue?
- _ = collector_future.???(io);
+ _ = collector_future.await(io);
// Now write the garden report. This is critical — it must
// NOT be interrupted, even if something tries to cancel us!
//
// Bug 5: Protect this section from cancellation.
// What Io method swaps the cancel protection state?
- const old_protection = io.???(.blocked);
- defer _ = io.???(old_protection);
+ const old_protection = io.swapCancelProtection(.blocked);
+ defer _ = io.swapCancelProtection(old_protection);
printGardenReport(&weather);
}
@@ -128,7 +128,7 @@
// Bug 6: Send the reading into the queue.
// What Queue method sends a single element?
- queue.???(io, reading) catch return;
+ queue.putOne(io, reading) catch return;
}
}

View File

@@ -1,5 +1,5 @@
--- exercises/093_hello_c.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/093_hello_c.zig 2023-10-05 20:04:07.262770750 +0200
--- exercises/096_hello_c.zig 2025-08-15 15:17:57.839348063 +0200
+++ answers/096_hello_c.zig 2026-04-03 13:09:26.195163128 +0200
@@ -54,7 +54,7 @@
//
// In this exercise we use 'write' to output 17 chars,

Some files were not shown because too many files have changed in this diff Show More