Check out directly our handy new URL: https://ziglings.org
Or visit the repo at: https://codeberg.org/ziglings/exercises
Learn the Zig programming language by fixing tiny broken programs.
License: MIT License
Check out directly our handy new URL: https://ziglings.org
Or visit the repo at: https://codeberg.org/ziglings/exercises
// When we use these identifiers in our program, the VALUES
// are inserted at compile time into the executable code. The
// identifiers "my_int" and "my_float" don't really exist in
// our compiled application and do not refer to any
// particular areas of memory!
const const_int = 12345;
const const_float = 987.654;
The comment should reference const_int and const_float instead of my_int and my_float.
_Compiling 050_no_value.zig...
/snap/zig/5567/lib/std/fmt.zig:508:17: error: cannot format error union without a specifier (i.e. {!} or {any})
@CompileError("cannot format error union without a specifier (i.e. {!} or {any})");
^
/snap/zig/5567/lib/std/fmt.zig:183:23: note: called from here
try formatType(
^
/snap/zig/5567/lib/std/io/writer.zig:28:34: note: called from here
return std.fmt.format(self, format, args);
^
/snap/zig/5567/lib/std/debug.zig:93:27: note: called from here
nosuspend stderr.print(fmt, args) catch return;
^
./exercises/050_no_value.zig:74:20: note: called from here
std.debug.print("{s} {Err!s} / ", .{ first_line1, first_line2 });
^
./exercises/050_no_value.zig:67:20: note: called from here
pub fn main() void {
^
050_no_value.zig: The following command exited with error code 1:
/snap/zig/5567/zig build-exe /home/timos/Projects/zg/ziglings/exercises/050_no_value.zig --cache-dir /home/timos/Projects/zg/ziglings/zig-cache --enable-cache
Edit exercises/050_no_value.zig and run this again.
To continue from this zigling, use this command:
zig build 50_
Line 74
std.debug.print("{s} {!s} / ", .{ first_line1, first_line2 });
36_enums2.zig has a typo at line 36:
Remeber
should be Remember
.
37_structs.zig has two typos at line 13 and 14:
// point1 = Point{ .x=3, .y=16, .y=27 };
// point2 = Point{ .x=7, .y=13, .y=34 };
Should be:
// point1 = Point{ .x=3, .y=16, .z =27 };
// point2 = Point{ .x=7, .y=13, .z =34 };
Hello, first of all thanks for creating this Repo, it's been a great ressource to get used to zig so far.
However, now that i have essentially finished all the exercises, and i wanted to write a simple Brainfuck interpreter in Zig, i came across my first use of allocators, which (coming from C / C++) is a very major thing, which i believe has a great benefit to be also teached in Ziglings.
I am sort of in the middle of learning about allocators in zig and its usage so far, if i would have a better understanding of them yet, i would (or maybe will?) open a PR once i have learned about them in a more clear and detailed manner.
What is your opinion on that, as i have read that you do not want to teach about the standard library, yet i believe this is essentially a core part of Zig.
Cheers.
The idiomatic way to pass a struct that isn't modified is now to pass it by value: https://ziglang.org/documentation/master/#toc-Pass-by-value-Parameters
The text in 043_pointers5.zig
directly contradicts this, and there are a lot of other places where pointer parameters probably ought to be changed to value parameters.
Seems like a very minor issue but maybe worth fixing.
In exercise 19 the last comment states that:
std.math.pow(type, a, b) takes a numeric type and two numbers of that type
but that doesn't seem to be true, setting my_number
to a u8
works fine.
Exercise No. 005 (arrays2) contains the following description of the ++
operator:
You can use
++
to concatenate two arrays:const a = [_]u8{ 1, 2 }; const b = [_]u8{ 3, 4 }; const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
This is slightly misleading, as it neglects to mention that (for now?) the ++
operator can only be used on arrays whose values are known at compiletime.
I would submit a PR to fix this but I'm honestly not quite sure how it should be amended, since the concept of comptime seems to be introduced somewhere in the 70's.
The lack was noted by @samwho and the wrong must be righted!
patch 48 shows
57c57
< e = e.???; // Which method do we want here?
---
> e = e.getTail(); // Which method do we want here?
trying to run this with zig build
will give an error
modifying the while on line 52 seems to be the correct answer.
So that the while look is
while (!e.visited) {
e.print();
e.visit();
// Get the next elephant or stop.
if (e.hasTail()) {
e = e.getTail(); // Which method do we want here?
} else {
break;
}
}
I'm only mentioning this because path 49 appears to be wrong too. You need to modify the while loops and add another statement to make it work.
I'm writing an issue since perhaps, I'm wrong of course.
$ zig version
0.9.0-dev.2025+ecf0050a9
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 21.10
Release: 21.10
Codename: impish
ziglings$ zig build
/home/andrea/projects/ziglings/build.zig:504:85: error: use of undeclared identifier 'c_void'
extern "kernel32" fn GetStdHandle(id: DWORD) callconv(WINAPI) ?*c_void;
^
/home/andrea/projects/ziglings/build.zig:505:68: error: use of undeclared identifier 'c_void'
extern "kernel32" fn GetConsoleMode(console: ?*c_void, out_mode: *DWORD) callconv(WINAPI) u32;
^
/home/andrea/projects/ziglings/build.zig:506:68: error: use of undeclared identifier 'c_void'
extern "kernel32" fn SetConsoleMode(console: ?*c_void, mode: DWORD) callconv(WINAPI) u32;
This is mainly a note to self and I plan to implement this ASAP, but certainly anyone is welcome to suggest additional/other ideas.
Testing intentionally broken programs presents an interesting problem. My initial thought was to write a shell script with sed
one-liners (or maybe even awk
, but I don't think any of the changes would require its mighty power) that would alter each exercise and then test it in turn.
But after sleeping on the problem for a week, I think I've had my eureka moment this morning (which was, "Wait, UNIX already has much better tooling for this!") and my half-formed idea of a solution is:
answers/
dir that is not committed to the repoanswers/
diff
on the two to create a patch, put it in a new patches/
dir that is committedpatches/
that
test/
(if needed), which is not committedpatch
and puts the output in test/
test/
directory as the target?patches/
explaining what it's for and admonishing folks to try not to peekI'm not concerned about the answers being available. I would rather somebody peeks at an answer now and then rather than give up completely. I think it will also help experts help me by verifying that I truly have a mistake or I'm behind on Zig versions or something.
On the other hand, struggling at least a little bit with these exercises is probably a good thing for learning, so I don't want to call attention to the fact that the answers are in the repo. It helps that diff
output is slightly unpleasant to read if you're not used to it. And the directory will be called "patches" rather than "answers".
//
// It seems we got a little carried away making everything "const u8"!
//
// "const" values cannot change.
// "u" types are "unsigned" and cannot store negative values.
// "8" means the type is 8 bits in size.
we could add
//
// It seems we got a little carried away making everything "const u8"!
//
// "const" values cannot change.
// "u" types are "unsigned" and cannot store negative values.
//=>=> "i" types are "signed" and can store negative values.
// "8" means the type is 8 bits in size.
I've done all your exercises. Those helped me so thank you.
I've read the patches to better grasp the Zigly way of coding. It's, imo, the benefit of having access to them.
Some are not written in a Zigly way like 086_async3.patch.
Others have comments changing like 058_quiz7.patch.
Those little things could help beginners to write better code and not getting confused while reading patches.
I just wanted to say thank you. This learning material is great! I'm an old-time C-programmer and after a couple of hours with your material I'm having a great time in Zig!
Thanks for all the hard work coming up with thorough excercises!
I'm using zig version 0.8.0-dev.1059+79730e6f5
for Windows, but I'm running it from a Cygwin bash shell. Attempting to build the ziglings repo and getting the following error:
$ zig build
Error: Your version of zig is too old. Please download a master build from
https://ziglang.org/download/
The comments from zig.build
indicate this should only apply for versions older than 0.5.0 or so. Is this a false positive I can fix easily?
Running zig build 0X
can produce errors (or not) for the current exercise. When the exercise succeeds however it progresses immediately to the next one and so the screen fills with errors again. I find two issues here:
zig build $TARGET
is equivalent to make $TARGET
which generally only indicates success or not and then stops.Perhaps something like (just guessing here) zig build
could use auto progression and zig build $TARGET
could stop after success? Or at least zig build $TARGET
should only run the next compilation after a "SUCCESS! Continue to exercise $TARGET+1? [Y/n]" or similar prompt.
p.s. Thanks for your hard work so far!
Hi,
I just wanted to say that the o
editor is a great fit for solving the ziglings exercises.
When editing ie. 03_assignment.zig
, it's possible to just press ctrl-space
and the cursor will jump to the problematic location, while the error message is displayed at the bottom of the screen. It is intended to help lowering the threshold of getting started with Zig.
The o
editor is configuration-free.
I guess this is a feature request for mentioning o
in the README, if you should happen to like it.
o
editor.Thanks for making the ziglings exercises!
Best regards,
Alexander F. Rødseth
Problem
In zigling 058 few print()'s defined so new ZIG won't compile.
Possible solution is to change first definition to const print = @import("std")
and call standard print() using std.debug.print()
.
Or just change method name in TripItem
union to something else, like printTrip()
,
so const print = @import("std").debug.print;
still can be demonstrated.
Code
First print() was defined at line 31
const print = @import("std").debug.print;
Another print() was defined at line 183, so next references of print() becoming ambiguous?
fn print(self: TripItem) void {
switch (self) {
// Oops! The hermit forgot how to capture the union values
// in a switch statement. Please capture both values as
// 'p' so the print statements work!
.place => |p| print("{s}", .{p.name}),
.path => |p| print("--{}->", .{p.dist}),
}
}
Compilation
Compiling 051_values.zig...
051_values.zig:77:11: error: unused local constant
const skull_farmer = Character{};
^
This seems like a better place than the README to let everyone know I'm still alive and well and very much interested in the future of Ziglings. I see the PRs and issues as they come in. I've also had some excellent feedback via email. I know better than to promise any sort of timeline, but I'm ready to dive into this Real Soon. Thank you all for the activity and your patience!
https://github.com/ratfactor/ziglings/blob/main/exercises/10_if2.zig#L12
Here you say the reason that it fails is that the compiler is not smart enough, but this is not true.
The error is
./main.zig:4:1: error: variable of type 'comptime_int' must be const or comptime
var i = if (true) 1 else 2;
^
./main.zig:2:29: note: referenced here
pub fn main() anyerror!void {
^
The reason is that you cannot have a comptime_int variable in a non comptime scope. The compiler is smart enough to do peer type resolution.
For example, this works:
var b: bool = true;
var x = if (b) null else @as(u32, 5);
if (@TypeOf(x) != ?u32) @panic("OOF");
This example should be fixed and an explanation of peer type resolution should probably be made.
Ex. 10 is focused on if
expressions. The line we are supposed to be fixing is:
Line 13 in f33d698
My first attempt at fixing this was:
var price = if (discount) 17 else 20;
which resulted in error:
./exercises/10_if2.zig:13:31: error: cannot store runtime value in type 'comptime_int'
var price = if (discount) 17 else 20;
^
It took me awhile to figure out that I needed to add the type:
var price: u8 = if (discount) 17 else 20;
I'm not sure if that additional challenge was intended. The example in that file gave a good hint that type would be needed, but up to this point in the exercises, the "challenge" was always explicit. The missing type deceleration for price was an implicit challenge and broke the pattern I was expecting up to this point.
IMO, remove the implicit nature of the challenge by adding the type to the line, adding a hint in the comments that some extra troubleshooting will be involved, or another "???" where the type should go.
Thanks.
First of all, thanks for this project, it makes learning zig so much fun!
Problem 46 asks to make tail
optional, when I did it, I also changed the default from undefined to null (tail: ?*Elephant = null,
) thinking it needs to be null so the check on line 42 (if (e.tail == null)
) works. However I changed it back to undefined later to see if it would break and it didn't!
So far it seems like undefined == null
. I also checked with swapping the values around and what I got is:
undefined != undefined
null != undefined
undefined == null
So undefined == null
when a value is optional but not the other way around, and no two undefined are the same.
Maybe there could be a note about this in the exercise, or make a new exercise that explains this?
New compiler gives error on zigling 51, when demonstrating data segment storage.
Code
// However, this "skull_farmer" character will be put in the
// global immutable data even though it's defined in a function.
// Since it's immutable, all invocations of the function can share
// this one value.
const skull_farmer = Character{};
Result
Compiling 051_values.zig...
051_values.zig:77:11: error: unused local constant
const skull_farmer = Character{};
^
'by directly by' should be 'directly by'.
Rather than hem and haw about it in the beginning, I went ahead and started these with two digit padding. But at this rate, I might run out of two-digit numbers before I'm even done with the language itself, let alone the standard library!
01_hello.zig
02_std.zig
...
98_unfragulating.zig
99_unfragulating2.zig
The other problem is inserting new items. Changing the order of items is even worse, but obviously won't ever be needed because this order is objectively perfect as is. 😆
I thought about leaving the exercises un-numbered, which would allow me to change the order or insert new topics easily. But I really like the fact that you can go into the exercises directory and see the order plain as day.
100_foo.zig
and it'll alpha-sort between 09 and 11 orIf the filenames are to be triple-digit, I'd rather bite the bullet sooner rather than later.
12.1_while2.1.zig
orI have a strong aversion to renumbering anything at any time because it will screw up existing written references to specific exercises (in commits and mentions internal or external to the project). In general, I really like the ability to refer to the exercises by absolute number and being able to count on that not changing.
I've decide to limit Ziglings to the core language and not attempt coverage of the Standard Library. Perhaps you can change my mind?
I don't necessarily want to change your mind, as it's probably a good idea to keep this repo focused. But what about starting a new repo for other people to contribute similar ziglings aimed at the standard library. If everyone who went through your repo contributed one zigling for the standard library, you'd have an extensive collection in short order!
Several of the exercises have minor formatting inconsistencies:
// 11_while.zig (line 24)
while ( ??? ){ // missing space before opening brace
// 13_while3.zig (line 24)
while (n <= 20) : (n+=1) { // condition has spaces around operator, continue expression does not
// etc.
Rather than depending on manual scanning to catch/fix these small issues, maybe it would be preferable to run zig fmt
on the exercises en masse? It's a slightly more annoying job than it seems at first because most of the exercises have syntax errors as is (so the task requires temporarily fixing, reformatting, then un-fixing the exercises). If this approach seems sensible, I'm happy to undertake the work.
Creating an issue here, so it does not get lost in chat history.
Firstly, thank you for this amazing learning resource! 🎉
While I completely understand you'd like to keep the exercises compatible with the current dev version of Zig, it'd be awesome to not force people to remain in lockstep and allow them to also use older versions of the lang (e.g. to avoid having to build from source because there's no prebuilt v0.9+ for 32bit ARM [RPI]). One easy solution to this could be adding tags to your repo before updating any examples to a future dev version... This would allow people to check out a version of the exercises compatible with an older Zig version without having to manually pinpoint (trial & error) a suitable commit from the history. What do you think? Too much effort?
It fails with this error after the other problems have been solved.
Compiling 065_builtins2.zig...
.\exercises\065_builtins2.zig:56:5: error: unable to evaluate constant expression
var narcissus: Narcissus = Narcissus{};
Hi,
In this file https://github.com/ratfactor/ziglings/blob/main/exercises/19_functions2.zig, the sentence is
Here's an
// example that takes two parameters. As you can see, parameters
// are declared just like an other types ("name": "type"):
I am not sure if "like an other types" is what was intended. Should it be "like any other types" ?
Introducing a slight typo into exercise 38 produces this output:
----------- Expected this output -----------
Character 2 - G:10 H:100 XP:20
----------- but found -----------
Character 1 - G:20 H:100 XP:10
Character 2 - G:10 H:100 XP:2
-----------
When, really, the printout should be this based on the expected answer:
----------- Expected this output -----------
Character 1 - G:20 H:100 XP:10
Character 2 - G:10 H:100 XP:20
----------- but found -----------
Character 1 - G:20 H:100 XP:10
Character 2 - G:10 H:100 XP:2
-----------
In cases such as Exercise 058, the current Zig compiler errors are downright misleading for newbies, which makes it way too frustrating to solve the expected issue. (Thanks to @alexgenaud for pointing this out specifically.)
I think this is also the source of the trouble with #120 (Exercise 065).
Instead of relying entirely on the compiler to lead Ziglings victims students to the right place to make a fix, these should be consistently pointed out with something like a comment with "FIX ME".
Additionally, consider expanding ???
sections to ??????...
to match the exact length of the expected answer to help out.
Maybe we can remove these extra hints when the Zig Stage 2 compiler comes out (since part of the idea of these exercises is to get you comfortable with reading the compiler's error messages)!
Hey, thanks a lot for the ziglings! They really are helping me get started.
In zigling number 38 you mention
// Feel free to run this program without adding Zump. What does
// it do and why?
For me it was unclear why exactly the garbage values are what they are. (2863311530, 170 and 2863311530).
So I went out and looked a bit to discover that they are basically just filled with 0xAAAAAA...
Then someone in the zig matrix room sent me the link to the zig documentation, clarifying why exactly this was the case
According to your contribution guidelines ziglings should be self contained, which this broke for me.
That is unless I missed something in an earlier zigling that explained this.
"Error: Sorry it looks like your version of zig is too old" - while i'm on the latest version...
ziglings/exercises/011_while.zig
Line 3 in 1a89e4e
// condition is true. This runs once (at least):
In the repository settings, you can mark the repo as a template. This allows users to create a copy of the repo without making a fork. https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-template-repository
sorry to bother you with a question.
this issue comes from the 75th exercise.
i know pyparsing or regex can be used to do this in python world, but i can not find any equivalent in zig. i must missed some thing in the previous exercises. (i tried to ask in zig discord, but had not get any replies)
.\exercises\072_comptime7.zig:43:54: error: operation caused overflow
comptime var digit: u8 = instructions[i + 1] - '0';
In exercise 047, we have this line:
// Loop through every alien...
for (aliens) |*alien| {
// *** Zap the Alien Here! ***
???.zap(heat_ray_strength);
And I don't know what *alien
represents. I tried using alien
and the compiler complains that error: expected type '*Alien', found '*const Alien'
. I tried using &alien
and the compiler complains that error: expected 'an identifier', found '&'
.
But in Ziglings we haven't covered this syntax? Why wouldn't alien
or &alien
(like Rust's pattern match) work? Thanks :)
It should be noted somewhere in the readme that using an editor that produces CRLF at end of line will cause at least one of the lessons to fail, even though it generates the correct output.
I know what I meant by
// Just don't modify this function. It's "perfect" the way it is. :-)
but I see how it's really misleading and confusing for people trying to solve this quiz! Need to fix to make it clear that what I meant was something like
// This function is obviously weird and non-functional. But you will not be changing it for this quiz.
All I need is probably just "Leave this function alone." I'm already being silly enough in Ziglings.
Code
var n = numberMaybeFail(num);
if (n) |value| {
std.debug.print("=4. ", .{});
} else |err| switch (err) {
MyNumberError.TooBig => std.debug.print(">4. ", .{}),
}
Result
033_iferror.zig:32:17: error: unused capture
if (n) |value| {
Easy avoidable by using that value in print(), but I think #33
may need some changes to encourage proper use of that technique.
20: // intuition for these contexts. Let's work on that/ now.
I think that the / should not be there.
I came to check out ziglings, got the nightly build from 2022-04-20, and saw the following error.
$ zig version
0.10.0-dev.1888+e3cbea934
$ zig build
/home/user/.local/lib/std/compress/deflate.zig:25:56: error: unable to override alignment of packed struct fields
const LUTEntry = packed struct { symbol: u16 align(4), len: u16 };
^
/home/user/.local/lib/std/builtin.zig:252:19: error: expected type expression, found 'anytype'
sentinel: anytype,
^
The test runner caught this:
patching file patches/healed/091_async8.zig (read from exercises/091_async8.zig)
./build.zig:627:44: error: expected error union type, found 'std.child_process.ChildProcess'
const child = std.ChildProcess.init(&argv, self.builder.allocator) catch unreachable;
^
Error: Process completed with exit code 1.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.