diff options
Diffstat (limited to 'zig.html.markdown')
| -rw-r--r-- | zig.html.markdown | 980 | 
1 files changed, 980 insertions, 0 deletions
| diff --git a/zig.html.markdown b/zig.html.markdown new file mode 100644 index 00000000..0efe5f64 --- /dev/null +++ b/zig.html.markdown @@ -0,0 +1,980 @@ +--- +language: zig +filename: learnzig.zig +contributors: +    - ["Philippe Pittoli", "https://karchnu.fr/"] +--- + + +[Zig][ziglang] aims to be a replacement for the C programming language. + +**WARNING**: this document expects you to understand a few basic concepts in computer science, such as pointers, stack and heap memory, etc. + +**WARNING**: Zig isn't considered as ready for production. Bugs are expected. +DO NOT TRY ZIG AS YOUR FIRST PROGRAMMING EXPERIENCE. +The compiler, even the language and its libraries aren't ready, yet. +You've been warned. + +Prior knowledge of C is recommended. + + +## Quick overview: Zig compared to C + +- Syntax is mostly the same, with some improvements (less ambiguity). +- Zig introduces namespaces. +- Try and catch mechanism, which is both convenient, efficient and optional. +- Most of the C undefined behaviors (UBs) are fixed. +- Compared to C, raw pointers are safer to use and less likely to be needed. +  * The type system distinguishes between a pointer to a single value, or multiple values, etc. +  * Slices are preferred, which is a structure with a pointer and a runtime known size, which characterizes most uses of pointers in the first place. +- Some arbitrary language limitations are removed. For example, enumerations, structures and unions can have functions. +- Simple access to SIMD operations (basic maths on vectors). +- Zig provides both low-level features of C and the one provided through compiler extensions. +  For example: packed structures. +- An extensive standard library, including data structures and algorithms. +- Cross-compilation capability is provided by default, without any dependency. +  Different libc are provided to ease the process. +  Cross-compilation works from, and to, any operating system and architecture. + +## Zig language + + +```zig +//! Top-level documentation. + +/// Documentation comment. + +// Simple comment. +``` + + +### Hello world. +```zig +// Import standard library, reachable through the "std" constant. +const std = @import("std"); + +// "info" now refers to the "std.log.info" function. +const info = std.log.info; + +// Usual hello world. +// syntax: [pub] fn <function-name>(<arguments>) <return-type> { <body> } +pub fn main() void { +    // Contrary to C functions, Zig functions have a fixed number of arguments. +    // In C: "printf" takes any number of arguments. +    // In Zig: std.log.info takes a format and a list of elements to print. +    info("hello world", .{});  // .{} is an empty anonymous tuple. +} +``` + +### Booleans, integers and float. +```zig +// Booleans. +// Keywords are prefered to operators for boolean operations. +print("{}\n{}\n{}\n", .{ +    true and false, +    true or false, +    !true, +}); + +// Integers. +const one_plus_one: i32 = 1 + 1; +print("1 + 1 = {}\n", .{one_plus_one}); // 2 + +// Floats. +const seven_div_three: f32 = 7.0 / 3.0; +print("7.0 / 3.0 = {}\n", .{seven_div_three}); // 2.33333325e+00 + +// Integers have arbitrary value lengths. +var myvar: u10 = 5; // 10-bit unsigned integer +// Useful for example to read network packets, or complex binary formats. + +// Number representation is greatly improved compared to C. +const one_billion = 1_000_000_000;         // Decimal. +const binary_mask = 0b1_1111_1111;         // Binary. Ex: network mask. +const permissions = 0o7_5_5;               // Octal.  Ex: Unix permissions. +const big_address = 0xFF80_0000_0000_0000; // Hexa.   Ex: IPv6 address. + + +// Overflow operators: tell the compiler when it's okay to overflow. +var i: u8 = 0;  // "i" is an unsigned 8-bit integer +i  -= 1;        // runtime overflow error (unsigned value always are positive) +i -%= 1;        // okay (wrapping operator), i == 255 + +// Saturation operators: values will stick to their lower and upper bounds. +var i: u8 = 200;   // "i" is an unsigned 8-bit integer (values: from 0 to 255) +i  +| 100 == 255   // u8: won't go higher than 255 +i  -| 300 == 0     // unsigned, won't go lower than 0 +i  *| 2   == 255   // u8: won't go higher than 255 +i <<| 8   == 255   // u8: won't go higher than 255 +``` + +### Arrays. +```zig +// An array is a well-defined structure with a length attribute (len). + +// 5-byte array with undefined content (stack garbage). +var array1: [5]u8 = undefined; + +// 5-byte array with defined content. +var array2 = [_]u8{ 1, 2, 3, 4, 5 }; +// [_] means the compiler knows the length at compile-time. + +// 1000-byte array with defined content (0). +var array3 = [_]u8{0} ** 1000; + +// Another 1000-byte array with defined content. +// The content is provided by the "foo" function, called at compile-time and +// allows complex initializations. +var array4 = [_]u8{foo()} ** 1000; + +// In any case, array.len gives the length of the array, +// array1.len and array2.len produce 5, array3.len and array4.len produce 1000. + + +// Modifying and accessing arrays content. + +// Array of 10 32-bit undefined integers. +var some_integers: [10]i32 = undefined; + +some_integers[0] = 30; // first element of the array is now 30 + +var x = some_integers[0]; // "x" now equals to 30, its type is infered. +var y = some_integers[1]; // Second element of the array isn't defined. +                          // "y" got a stack garbage value (no runtime error). + +// Array of 10 32-bit undefined integers. +var some_integers: [10]i32 = undefined; + +var z = some_integers[20]; // index > array size, compilation error. + +// At runtime, we loop over the elements of "some_integers" with an index. +// Index i = 20, then we try: +try some_integers[i]; // Runtime error 'index out of bounds'. +                      // "try" keyword is necessary when accessing an array with +                      // an index, since there is a potential runtime error. +                      // More on that later. +``` + +### Multidimensional arrays. +```zig + +const mat4x4 = [4][4]f32{ +    [_]f32{ 1.0, 0.0, 0.0, 0.0 }, +    [_]f32{ 0.0, 1.0, 0.0, 1.0 }, +    [_]f32{ 0.0, 0.0, 1.0, 0.0 }, +    [_]f32{ 0.0, 0.0, 0.0, 1.0 }, +}; + +// Access the 2D array then the inner array through indexes. +try expect(mat4x4[1][1] == 1.0); + +// Here we iterate with for loops. +for (mat4x4) |row, row_index| { +    for (row) |cell, column_index| { +        // ... +    } +} +``` + +### Strings. +```zig + +// Simple string constant. +const greetings = "hello"; +// ... which is equivalent to: +const greetings: *const [5:0]u8 = "hello"; +// In words: "greetings" is a constant value, a pointer on a constant array of 5 +// elements (8-bit unsigned integers), with an extra '0' at the end. +// The extra "0" is called a "sentinel value". + +print("string: {s}\n", .{greetings}); + +// This represents rather faithfully C strings. Although, Zig strings are +// structures, no need for "strlen" to compute their size. +// greetings.len == 5 +``` + +### Slices. +```zig + +// A slice is a pointer and a size, an array without compile-time known size. +// Slices have runtime out-of-band verifications. + +const array = [_]u8{1,2,3,4,5};     // [_] = array with compile-time known size. +const slice = array[0..array.len];  // "slice" represents the whole array. +                                    // slice[10] gives a runtime error. +``` + +### Pointers. +```zig + +// Pointer on a value can be created with "&". +const x: i32 = 1; +const pointer: *i32 = &x;  // "pointer" is a pointer on the i32 var "x". +print("1 = {}, {}\n", .{x, pointer}); + +// Pointer values are accessed and modified with ".*". +if (pointer.* == 1) { +    print("x value == {}\n", .{pointer.*}); +} + +// ".?" is a shortcut for "orelse unreachable". +const foo = pointer.?; // Get the pointed value, otherwise crash. +``` + +### Optional values (?<type>). +```zig +// An optional is a value than can be of any type or null. + +// Example: "optional_value" can either be "null" or an unsigned 32-bit integer. +var optional_value: ?u32 = null; // optional_value == null +optional_value = 42;             // optional_value != null + +// "some_function" returns ?u32 +var x = some_function(); +if (x) |value| { +    // In case "some_function" returned a value. +    // Do something with 'value'. +} +``` + +### Errors. +```zig +// Zig provides an unified way to express errors. + +// Errors are defined in error enumerations, example: +const Error = error { +    WatchingAnyNetflixTVShow, +    BeOnTwitter, +}; + +// Normal enumerations are expressed the same way, but with "enum" keyword. +const SuccessStory = enum { +    DoingSport, +    ReadABook, +}; + + +// Error union (!). +// Either the value "mylife" is an an error or a normal value. +var mylife: Error!SuccessStory = Error.BeOnTwitter; +// mylife is an error. Sad. + +mylife = SuccessStory.ReadABook; +// Now mylife is an enum. + + +// Zig ships with many pre-defined errors. Example: +const value: anyerror!u32 = error.Broken; + + +// Handling errors. + +// Some error examples. +const Error = error { +    UnExpected, +    Authentication, +}; + +// "some_function" can either return an "Error" or an integer. +fn some_function() Error!u8 { +    return Error.UnExpected; // It returns an error. +} + +// Errors can be "catch" without intermediate variable. +var value = some_function() catch |err| switch(err) { +    Error.UnExpected     => return err,   // Returns the error. +    Error.Authentication => unreachable,  // Not expected. Crashes the program. +    else                 => unreachable, +}; + +// An error can be "catch" without giving it a name. +const unwrapped = some_function() catch 1234; // "unwrapped" = 1234 + +// "try" is a very handy shortcut for "catch |err| return err". +var value = try some_function(); +// If "some_function" fails, the current function stops and returns the error. +// "value" can only have a valid value, the error already is handled with "try". +``` + +### Control flow. + +```zig +// Conditional branching. + +if (condition) { +    ... +} +else { +    ... +} + +// Ternary. +var value = if (condition) x else y; + +// Shortcut for "if (x) x else 0" +var value = x orelse 0; + +// If "a" is an optional, which may contain a value. +if (a) |value| { +    print("value: {}\n", .{value}); +} +else { +    print("'a' is null\n", .{}); +} + +// Get a pointer on the value (if it exists). +if (a) |*value| { value.* += 1; } + + +// Loops. + +// Syntax examples: +//   while (condition) statement +//   while (condition) : (end-of-iteration-statement) statement +// +//   for (iterable) statement +//   for (iterable) |capture| statement +//   for (iterable) statement else statement + +// Note: loops work the same way over arrays or slices. + +// Simple "while" loop. +while (i < 10) { i += 1; } + +// While loop with a "continue expression" +// (expression executed as the last expression of the loop). +while (i < 10) : (i += 1) { ... } +// Same, with a more complex continue expression (block of code). +while (i * j < 2000) : ({ i *= 2; j *= 3; }) { ... } + +// To iterate over a portion of a slice, reslice. +for (items[0..1]) |value| { sum += value; } + +// Loop over every item of an array (or slice). +for (items) |value| { sum += value; } + +// Iterate and get pointers on values instead of copies. +for (items) |*value| { value.* += 1; } + +// Iterate with an index. +for (items) |value, i| { print("val[{}] = {}\n", .{i, value}); } + +// Iterate with pointer and index. +for (items) |*value, i| { print("val[{}] = {}\n", .{i, value}); value.* += 1; } + + +// Break and continue are supported. +for (items) |value| { +    if (value == 0)  { continue; } +    if (value >= 10) { break;    } +    // ... +} + +// For loops can also be used as expressions. +// Similar to while loops, when you break from a for loop, +// the else branch is not evaluated. +var sum: i32 = 0; +// The "for" loop has to provide a value, which will be the "else" value. +const result = for (items) |value| { +    if (value != null) { +        sum += value.?; // "result" will be the last "sum" value. +    } +} else 0;                  // Last value. +``` + +### Labels. +```zig + +// Labels are a way to name an instruction, a location in the code. +// Labels can be used to "continue" or "break" in a nested loop. +outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { +    for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { +        count += 1; +        continue :outer; // "continue" for the first loop. +    } +} // count = 8 +outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { +    for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { +        count += 1; +        break :outer; // "break" for the first loop. +    } +} // count = 1 + + +// Labels can also be used to return a value from a block. +var y: i32 = 5; +const x = blk: { +    y += 1; +    break :blk y; // Now "x" equals 6. +}; +// Relevant in cases like "for else" expression (explained in the following). + +// For loops can be used as expressions. +// When you break from a for loop, the else branch is not evaluated. +// WARNING: counter-intuitive. +//      The "for" loop will run, then the "else" block will run. +//      The "else" keyword has to be followed by the value to give to "result". +//      See later for another form. +var sum: u8 = 0; +const result = for (items) |value| { +    sum += value; +} else 8; // result = 8 + +// In this case, the "else" keyword is followed by a value, too. +// However, the syntax is different: it is labeled. +// Instead of a value, there is a label followed by a block of code, which +// allows to do stuff before returning the value (see the "break" invocation). +const result = for (items) |value| { // First: loop. +    sum += value; +} else blk: {                        // Second: "else" block. +    std.log.info("executed AFTER the loop!", .{}); +    break :blk sum; // The "sum" value will replace the label "blk". +}; +``` + +### Switch. +```zig + +// As a switch in C, but slightly more advanced. +// Syntax: +//   switch (value) { +//       pattern => expression, +//       pattern => expression, +//       else    => expression +//   }; + +// A switch only checking for simple values. +var x = switch(value) { +    Error.UnExpected     => return err, +    Error.Authentication => unreachable, +    else                 => unreachable, +}; + +// A slightly more advanced switch, accepting a range of values: +const foo: i32 = 0; +const bar = switch (foo) { +  0                        => "zero", +  1...std.math.maxInt(i32) => "positive", +  else                     => "negative", +}; +``` + +### Structures. +```zig + +// Structure containing a single value. +const Full = struct { +    number: u16, +}; + +// Packed structure, with guaranteed in-memory layout. +const Divided = packed struct { +    half1: u8, +    quarter3: u4, +    quarter4: u4, +}; + +// Point is a constant representing a structure containing two u32, "x" and "y". +// "x" has a default value, which wasn't possible in C. +const Point = struct { +    x: u32 = 1, // default value +    y: u32, +}; + +// Variable "p" is a new Point, with x = 1 (default value) and y = 2. +var p = Point{ .y = 2 }; + +// Fields are accessed as usual with the dot notation: variable.field. +print("p.x: {}\n", .{p.x}); // 1 +print("p.y: {}\n", .{p.y}); // 2 + + +// A structure can also contain public constants and functions. +const Point = struct { +    pub const some_constant = 30; + +    x: u32, +    y: u32, + +    // This function "init" creates a Point and returns it. +    pub fn init() Point { +        return Point{ .x = 0, .y = 0 }; +    } +}; + + +// How to access a structure public constant. +// The value isn't accessed from an "instance" of the structure, but from the +// constant representing the structure definition (Point). +print("constant: {}\n", .{Point.some_constant}); + +// Having an "init" function is rather idiomatic in the standard library. +// More on that later. +var p = Point.init(); +print("p.x: {}\n", .{p.x}); // p.x = 0 +print("p.y: {}\n", .{p.y}); // p.y = 0 + + +// Structures often have functions to modify their state, similar to +// object-oriented programming. +const Point = struct { +    const Self = @This(); // Refers to its own type (later called "Point"). + +    x: u32, +    y: u32, + +    // Take a look at the signature. First argument is of type *Self: "self" is +    // a pointer on the instance of the structure. +    // This allows the same "dot" notation as in OOP, like "instance.set(x,y)". +    // See the following example. +    pub fn set(self: *Self, x: u32, y: u32) void { +        self.x = x; +        self.y = y; +    } + +    // Again, look at the signature. First argument is of type Self (not *Self), +    // this isn't a pointer. In this case, "self" refers to the instance of the +    // structure, but can't be modified. +    pub fn getx(self: Self) u32 { +        return self.x; +    } + +    // PS: two previous functions may be somewhat useless. +    //     Attributes can be changed directly, no need for accessor functions. +    //     It was just an example. +}; + +// Let's use the previous structure. +var p = Point{ .x = 0, .y = 0 }; // "p" variable is a Point. + +p.set(10, 30); // x and y attributes of "p" are modified via the "set" function. +print("p.x: {}\n", .{p.x}); // 10 +print("p.y: {}\n", .{p.y}); // 30 + +// In C: +//   1. We would have written something like: point_set(p, 10, 30). +//   2. Since all functions are in the same namespace, it would have been +//      very cumbersome to create functions with different names for different +//      structures. Many long names, painful to read. +// +// In Zig, structures provide namespaces for their own functions. +// Different structures can have the same names for their functions, +// which brings clarity. +``` + +### Tuples. +```zig +// A tuple is a list of elements, possibly of different types. + +const foo = .{ "hello", true, 42 }; +// foo.len == 3 +``` + +### Enumerations. +```zig + +const Type = enum { ok, not_ok }; + +const CardinalDirections = enum { North, South, East, West }; +const direction: CardinalDirections = .North; +const x = switch (direction) { +  // shorthand for CardinalDirections.North +  .North => true, +  else => false +}; + +// Switch statements need exhaustiveness. +// WARNING: won't compile. East and West are missing. +const x = switch (direction) { +  .North => true, +  .South => true, +}; + + +// Switch statements need exhaustiveness. +// Won't compile: East and West are missing. +const x = switch (direction) { +  .North => true, +  .South => true, +  .East,          // Its value is the same as the following pattern: false. +  .West => false, +}; + + +// Enumerations are like structures: they can have functions. +``` + +### Unions. +```zig + +const Bar = union { +  boolean: bool, +  int: i16, +  float: f32, +}; + +// Both syntaxes are equivalent. +const foo = Bar{ .int = 42 }; +const foo: Bar = .{ .int = 42 }; + +// Unions, like enumerations and structures, can have functions. +``` + +### Tagged unions. +```zig + +// Unions can be declared with an enum tag type, allowing them to be used in +// switch expressions. + +const MaybeEnum = enum { +    success, +    failure, +}; + +const Maybe = union(MaybeEnum) { +    success: u8, +    failure: []const u8, +}; + +// First value: success! +const yay = Maybe{ .success = 42 }; +switch (yay) { +    .success => |value|     std.log.info("success: {}", .{value}), +    .failure => |err_msg|   std.log.info("failure: {}", .{err_msg}), +} + +// Second value: failure! :( +const nay = Maybe{ .failure = "I was too lazy" }; +switch (nay) { +    .success => |value|     std.log.info("success: {}", .{value}), +    .failure => |err_msg|   std.log.info("failure: {}", .{err_msg}), +} +``` + +### Defer and errdefer. +```zig + +// Make sure that an action (single instruction or block of code) is executed +// before the end of the scope (function, block of code). +// Even on error, that action will be executed. +// Useful for memory allocations, and resource management in general. + +pub fn main() void { +    // Should be executed at the end of the function. +    defer print("third!\n", .{}); + +    { +        // Last element of its scope: will be executed right away. +        defer print("first!\n", .{}); +    } + +    print("second!\n", .{}); +} + +fn hello_world() void { +    defer print("end of function\n", .{}); // after "hello world!" + +    print("hello world!\n", .{}); +} + +// errdefer executes the instruction (or block of code) only on error. +fn second_hello_world() !void { +    errdefer print("2. something went wrong!\n", .{}); // if "foo" fails. +    defer    print("1. second hello world\n", .{});    // executed after "foo" + +    try foo(); +} +// Defer statements can be seen as stacked: first one is executed last. +``` + +### Memory allocators. +Memory isn't managed directly in the standard library, instead an "allocator" is asked every time an operation on memory is required. +Thus, the standard library lets developers handle memory as they need, through structures called "allocators", handling all memory operations. + +**NOTE**: the choice of the allocator isn't in the scope of this document. +A whole book could be written about it. +However, here are some examples, to get an idea of what you can expect: +- page_allocator. +  Allocate a whole page of memory each time we ask for some memory. +  Very simple, very dumb, very wasteful. +- GeneralPurposeAllocator. +  Get some memory first and manage some buckets of memory in order to +  reduce the number of allocations. +  A bit complex. Can be combined with other allocators. +  Can detect leaks and provide useful information to find them. +- FixedBufferAllocator. +  Use a fixed buffer to get its memory, don't ask memory to the kernel. +  Very simple, limited and wasteful (can't deallocate), but very fast. +- ArenaAllocator. +  Allow to free all allocted memory at once. +  To use in combinaison with another allocator. +  Very simple way of avoiding leaks. + +A first example. +```zig +// "!void" means the function doesn't return any value except for errors. +// In this case we try to allocate memory, and this may fail. +fn foo() !void { +    // In this example we use a page allocator. +    var allocator = std.heap.page_allocator; + +    // "list" is an ArrayList of 8-bit unsigned integers. +    // An ArrayList is a contiguous, growable list of elements in memory. +    var list = try ArrayList(u8).initAllocated(allocator); +    defer list.deinit(); // Free the memory at the end of the scope. Can't leak. +    // "defer" allows to express memory release right after its allocation, +    // regardless of the complexity of the function (loops, conditions, etc.). + +    list.add(5); // Some memory is allocated here, with the provided allocator. + +    for (list.items) |item| { +        std.debug.print("item: {}\n", .{item}); +    } +} +``` + +### Memory allocation combined with error management and defer. +```zig + +fn some_memory_allocation_example() !void { +    // Memory allocation may fail, so we "try" to allocate the memory and +    // in case there is an error, the current function returns it. +    var buf = try page_allocator.alloc(u8, 10); +    // Defer memory release right after the allocation. +    // Will happen even if an error occurs. +    defer page_allocator.free(buf); + +    // Second allocation. +    // In case of a failure, the first allocation is correctly released. +    var buf2 = try page_allocator.alloc(u8, 10); +    defer page_allocator.free(buf2); + +    // In case of failure, both previous allocations are correctly deallocated. +    try foo(); +    try bar(); + +    // ... +} +``` + +### Memory allocators: a taste of the standard library. +```zig + +// Allocators: 4 main functions to know +//   single_value = create (type) +//   destroy (single_value) +//   slice = alloc (type, size) +//   free (slice) + +// Page Allocator +fn page_allocator_fn() !void { +    var slice = try std.heap.page_allocator.alloc(u8, 3); +    defer std.heap.page_allocator.free(slice); + +    // playing_with_a_slice(slice); +} + +// GeneralPurposeAllocator +fn general_purpose_allocator_fn() !void { +    // GeneralPurposeAllocator has to be configured. +    // In this case, we want to track down memory leaks. +    const config = .{.safety = true}; +    var gpa = std.heap.GeneralPurposeAllocator(config){}; +    defer _ = gpa.deinit(); + +    const allocator = gpa.allocator(); + +    var slice = try allocator.alloc(u8, 3); +    defer allocator.free(slice); + +    // playing_with_a_slice(slice); +} + +// FixedBufferAllocator +fn fixed_buffer_allocator_fn() !void { +    var buffer = [_]u8{0} ** 1000; // array of 1000 u8, all initialized at zero. +    var fba  = std.heap.FixedBufferAllocator.init(buffer[0..]); +    // Side note: buffer[0..] is a way to create a slice from an array. +    //            Since the function takes a slice and not an array, this makes +    //            the type system happy. + +    var allocator = fba.allocator(); + +    var slice = try allocator.alloc(u8, 3); +    // No need for "free", memory cannot be freed with a fixed buffer allocator. +    // defer allocator.free(slice); + +    // playing_with_a_slice(slice); +} + +// ArenaAllocator +fn arena_allocator_fn() !void { +    // Reminder: arena doesn't allocate memory, it uses an inner allocator. +    // In this case, we combine the arena allocator with the page allocator. +    var arena = std.heap.arena_allocator.init(std.heap.page_allocator); +    defer arena.deinit(); // end of function = all allocations are freed. + +    var allocator = arena.allocator(); + +    const slice = try allocator.alloc(u8, 3); +    // No need for "free", memory will be freed anyway. + +    // playing_with_a_slice(slice); +} + + +// Combining the general purpose and arena allocators. Both are very useful, +// and their combinaison should be in everyone's favorite cookbook. +fn gpa_arena_allocator_fn() !void { +    const config = .{.safety = true}; +    var gpa = std.heap.GeneralPurposeAllocator(config){}; +    defer _ = gpa.deinit(); + +    const gpa_allocator = gpa.allocator(); + +    var arena = arena_allocator.init(gpa_allocator); +    defer arena.deinit(); + +    const allocator = arena.allocator(); + +    var slice = try allocator.alloc(u8, 3); +    defer allocator.free(slice); + +    // playing_with_a_slice(slice); +} +``` + +### Comptime. +```zig + +// Comptime is a way to avoid the pre-processor. +// The idea is simple: run code at compilation. + +inline fn max(comptime T: type, a: T, b: T) T { +    return if (a > b) a else b; +} + +var res = max(u64, 1, 2); +var res = max(f32, 10.50, 32.19); + + +// Comptime: creating generic structures. + +fn List(comptime T: type) type { +    return struct { +        items: []T, + +        fn init()   ... { ... } +        fn deinit() ... { ... } +        fn do()     ... { ... } +    }; +} + +const MyList = List(u8); + + +// use +var list = MyList{ +    .items = ... // memory allocation +}; + +list.items[0] = 10; +``` + +### Conditional compilation. +```zig +const available_os = enum { OpenBSD, Linux }; +const myos = available_os.OpenBSD; + + +// The following switch is based on a constant value. +// This means that the only possible outcome is known at compile-time. +// Thus, there is no need to build the rest of the possibilities. +// Similar to the "#ifdef" in C, but without requiring a pre-processor. +const string = switch (myos) { +   .OpenBSD => "OpenBSD is awesome!", +   .Linux => "Linux rocks!", +}; + +// Also works in this case. +const myprint = switch(myos) { +    .OpenBSD => std.debug.print, +    .Linux => std.log.info, +} +``` + +### Testing our functions. +```zig +const std = @import("std"); +const expect = std.testing.expect; + +// Function to test. +pub fn some_function() bool { +    return true; +} + +// This "test" block can be run with "zig test". +// It will test the function at compile-time. +test "returns true" { +    expect(false == some_function()); +} +``` + +### Compiler built-ins. +The compiler has special functions called "built-ins", starting with an "@". +There are more than a hundred built-ins, allowing very low-level stuff: +- compile-time errors, logging, verifications +- type coercion and convertion, even in an unsafe way +- alignment management +- memory tricks (such as getting the byte offset of a field in a struct) +- calling functions at compile-time +- including C headers to transparently call C functions +- atomic operations +- embed files into the executable (@embedFile) +- frame manipulations (for async functions, for example) +- etc. + +Example: enums aren't integers, they have to be converted with a built-in. +```zig +const Value = enum { zero, stuff, blah }; +if (@enumToInt(Value.zero)  == 0) { ... } +if (@enumToInt(Value.stuff) == 1) { ... } +if (@enumToInt(Value.blah)  == 2) { ... } +``` + + +### A few "not yourself in the foot" measures in the Zig language. + +- Namespaces: names conflicts are easily avoided. +  In practice, that means an unified API between different structures (data types). +- Enumerations aren't integers. Comparing an enumeration to an integer requires a conversion. +- Explicit casts, coercion exists but is limited. +  Types are slightly more enforced than in C, just a taste: +    Pointers aren't integers, explicit conversion is necessary. +    You won't lose precision by accident, implicit coercions are only authorized in case no precision can be lost. +    Unions cannot be reinterpreted (in an union with an integer and a float, one cannot take a value for another by accident). +    Etc. +- Removing most of the C undefined behaviors (UBs), and when the compiler encounters one, it stops. +- Slice and Array structures are prefered to pointers. +  Types enforced by the compiler are less prone to errors than pointer manipulations. +- Numerical overflows produce an error, unless explicitly accepted using wrapping operators. +- Try and catch mechanism. +  It's both handy, trivially implemented (simple error enumeration), and it takes almost no space nor computation time. +- Unused variables are considered as errors by the compiler. +- Many pointer types exist in order to represent what is pointed. +  Example: is this a single value or an array, is the length known, etc. +- Structures need a value for their attributes, and it still is possible to give an undefined value (stack garbage), but at least it is explicitely undefined. + + +## Further Reading + +For a start, some concepts are presented on the [Zig learn website][ziglearn]. + +The [official website][zigdoc] provides a reference documentation to the language. + +For now, documentation for standard library is WIP. + +[ziglang]: https://ziglang.org +[ziglearn]: https://ziglearn.org/ +[zigdoc]: https://ziglang.org/documentation/ | 
