Skip to content

Exercises

This chapter introduced the basic forms of values and declarations: names, constants, variables, integer types, floating-point types, booleans, bytes, inferred types, and...

This chapter introduced the basic forms of values and declarations: names, constants, variables, integer types, floating-point types, booleans, bytes, inferred types, and explicit casts.

The exercises below are small. Type them by hand. Compile them. Change them. Read the error messages.

  1. Write a program that declares a constant named answer with value 42 and prints it.
const std = @import("std");

pub fn main() void {
    const answer = 42;
    std.debug.print("{d}\n", .{answer});
}
  1. Write a program that declares a mutable variable named count, increments it three times, and prints the result.
const std = @import("std");

pub fn main() void {
    var count: i32 = 0;

    count += 1;
    count += 1;
    count += 1;

    std.debug.print("{d}\n", .{count});
}
  1. Change the previous program so that count is declared with const. Compile it and read the error.
const std = @import("std");

pub fn main() void {
    const count: i32 = 0;

    count += 1;

    std.debug.print("{d}\n", .{count});
}

The program is wrong because count cannot be assigned after initialization.

  1. Declare three integer values: one i32, one u32, and one usize. Print all three.
const std = @import("std");

pub fn main() void {
    const a: i32 = -10;
    const b: u32 = 20;
    const c: usize = 30;

    std.debug.print("{d} {d} {d}\n", .{ a, b, c });
}
  1. Try to assign 256 to a u8.
pub fn main() void {
    const x: u8 = 256;
    _ = x;
}

This is an error because u8 can hold only values from 0 to 255.

  1. Write the same integer in decimal, hexadecimal, binary, and octal.
const std = @import("std");

pub fn main() void {
    const a = 255;
    const b = 0xff;
    const c = 0b1111_1111;
    const d = 0o377;

    std.debug.print("{d} {d} {d} {d}\n", .{ a, b, c, d });
}
  1. Write a program that demonstrates wrapping addition on u8.
const std = @import("std");

pub fn main() void {
    var x: u8 = 255;
    x +%= 1;

    std.debug.print("{d}\n", .{x});
}

The output is:

0
  1. Write a program that demonstrates saturating addition on u8.
const std = @import("std");

pub fn main() void {
    var x: u8 = 255;
    x +|= 1;

    std.debug.print("{d}\n", .{x});
}

The output is:

255
  1. Declare two f64 values and print their average.
const std = @import("std");

pub fn main() void {
    const a: f64 = 10.0;
    const b: f64 = 20.0;

    const avg = (a + b) / 2.0;

    std.debug.print("{d}\n", .{avg});
}
  1. Write a function that returns the average of two f64 values.
const std = @import("std");

fn average(a: f64, b: f64) f64 {
    return (a + b) / 2.0;
}

pub fn main() void {
    std.debug.print("{d}\n", .{average(10.0, 20.0)});
}
  1. Write a function that tests whether an integer is positive.
const std = @import("std");

fn isPositive(n: i32) bool {
    return n > 0;
}

pub fn main() void {
    std.debug.print("{}\n", .{isPositive(3)});
    std.debug.print("{}\n", .{isPositive(-3)});
}
  1. Write a condition that checks whether a number is between 1 and 10, inclusive.
const std = @import("std");

pub fn main() void {
    const n = 7;

    if (n >= 1 and n <= 10) {
        std.debug.print("in range\n", .{});
    }
}
  1. Declare a byte with value 'A' and print it as both a character and a number.
const std = @import("std");

pub fn main() void {
    const c: u8 = 'A';

    std.debug.print("{c}\n", .{c});
    std.debug.print("{d}\n", .{c});
}
  1. Loop over the bytes of a string.
const std = @import("std");

pub fn main() void {
    const s = "zig";

    for (s) |b| {
        std.debug.print("{c} {d}\n", .{ b, b });
    }
}
  1. Check the byte length of a UTF-8 string.
const std = @import("std");

pub fn main() void {
    const s = "é";

    std.debug.print("{d}\n", .{s.len});
}

The result is 2, because the string contains two UTF-8 bytes.

  1. Create an array whose length is inferred.
const std = @import("std");

pub fn main() void {
    const data = [_]u8{ 1, 2, 3, 4 };

    std.debug.print("{d}\n", .{data.len});
}

The type of data is [4]u8.

  1. Create a slice of constant bytes.
const std = @import("std");

pub fn main() void {
    const name: []const u8 = "zig";

    std.debug.print("{s}\n", .{name});
}
  1. Convert an i32 to f64.
const std = @import("std");

pub fn main() void {
    const n: i32 = 10;
    const x: f64 = @floatFromInt(n);

    std.debug.print("{d}\n", .{x});
}
  1. Convert a u32 to u8.
const std = @import("std");

pub fn main() void {
    const big: u32 = 100;
    const small: u8 = @intCast(big);

    std.debug.print("{d}\n", .{small});
}
  1. Write a function square that takes an i32 and returns an i32.
const std = @import("std");

fn square(n: i32) i32 {
    return n * n;
}

pub fn main() void {
    std.debug.print("{d}\n", .{square(12)});
}

These exercises are not puzzles. They are meant to build hand memory. Zig rewards precision, and precision begins with small declarations that say exactly what they mean.