-
A general-purpose programming language & toolchain.
-
StdLib doc online.
-
Hello World Code. Has document comments on build/run.
- Source code are UTF-8 encoded files usually
*.zignamed.- Public
mainfunction is necessary for Zig compiler to infer start point. Unless a library.- Return types are explicit as
!voidfor main (full form is<error set type>!<any data type>).std.log's functions orstd.debug.printshall be used for stdout where failure to write don't need be handled.- Semicolons to end an expression/statement are necessary.
- No multiline comments.
//are single line comments to be tokenized out of context.
///3-slash are document comments and consecutive line multiple document comments are merged.- Document comments are only allowed at relevant locations
- Container level documentation, or top-level doc comments are to begin with
//!
- Primitive Types, check
fn sampleValues()in hello-values.zig
i8, i16, i32, i64, i128, isizefor signed x-bit & lastly sized integeres; C Equivalents be likeint8_t, ..., intptr_t
u8, u16, u32, u64, u128, usizefor unsigned x-bit & lastly sized integeres; C Equivalents be likeuint8_t, ..., uintptr_t
c_short, c_int, c_long, c_longlong, c_longdoublefor ABI compatibility with C for eqivalent likeshort, int, .., long double
c_ushort, c_uint, c_ulong, c_ulonglongfor ABI compatibility with C for unsigned eqivalents
f16, f32, f64, f80, f128for C Equivalents of_Float16, float, double, double, _Float128
boolfor true/false;anyopaquefor type-erased pointers void in C;voidforvoid{}
noreturntype ofbreak, continue, return, unreachable, while(true){..}
typeis type of types
anyerroran error code
comptime_intfor compile-time known int literals;comptime_floatfor compile time known floatsarbitrary bit-width integers are also available e.g.
i7oru7with65536being max width allowed
- Primitive Values are
true,false,nullfor allocating optional type to, andundefinedto leave a value unspecified.
varneed to be initialized at declaration, to leave them uninitialized and coerce-able.. assignundefinedto them.
Strings, check fn sampleStrings() in hello-strings.zig
- String literals are constant single-item Pointers to null-terminated byte arrays. Type encodes length and fact of null-terminated, thus can be coerced to Slices & Null-Terminated Pointers. Dereferencing converts to Arrays.
- Multi-lines have no escapes and start with
\\token from next line, end of line is not included in literal which has just;.- Unicode code point literals are of type
comptime_int.- Bytes are not modified by Compiler, though non-UTF-8 bytes can be embedded using
xNNnotation.- maximum valid Unicode points is
0x10ffff
Zig Test in hello-tests.zig
test "<test-name>" { test_block() }declarations to ensure behavior meets expectations; ran viazig test <source-file[s]>which builds and runs executable using Stdlib's default test runner as main entry point. Test declarations are omitted from build unlesszig testtool builds them.
- (convention) UnNamed tests should only invoke other tests, these can't be filtered.
- Implicit return type of tests is
anyerror!voiderror-union type.- Tests can be in same source file as tested functionality, or separate. Tests are top-level declarations, thus order independent.
- Tests can be Nested within Blocks, but unless referenced in a top-level test.. will not be resolved.
- all container tests can be referenced via
std.testing.refAllDecls(@This())in a top-level testtest "demo container.X"&test "demo container.Y"can be referenced individually as in comments- to run tests of an imported file, reference as well
_ = @import("other_test.zig");
-
Can skip tests programmatically via
return error.SkipZigTest;. Default test runner skip test withsuspend pointwhile test running using default blocking IO mode. Evented IO mode could be enabled via--test-evented-io -
Code allocating memory using
std.testing.allocator, default test runner to report leaks found. -
std.builtin.is_testchecks if build is in test mode or run -
std.log.warnlets you log statements to stderr for testing namespace -
Including
std.testing.expect, we also haveexpectEqual,expectDeepEqualfor container/custom types,expectEqualSlices,expectEqualStrings,expectError,expectStringStartsWith,expectStringEndsWith, etc.
Variables in hello-variables.zig
- Var identifiers never shadow identifiers from outer scope.
@".."syntax to be used for non-conventional Indentifier names; also for linking with external variables.
-
Container level variables have static lifetime, are order independent & lazily analyzed. Init value is implicitly comptime.
-
Local static variables can be used with containers within functions.
-
externstructs are required when need layout of local struct to match layout of say C ABI. Because default structs can reorder struct fields, add hidden fields for safety/debug and perform other transformations.@externbult-in function is available to, for linking against a variable from another object. -
@exportfn orexportkeyword to make a variable available to other objects at linking, types shall be C ABI compatible. -
comptime var idx: i32 = 0;causesidxto enforce all loads & store happen at compile time; thus can't be used in an expression that also uses simple variable. -
noreturntype is forbreak, continue, return, unreachable, while(true) {}; allowingconst num = if (cond) 10 else return;
Integers & Floats in hello-numbers.zig
- Integers have no size limitation. If value is not comptime, then it's vulnerable to Int Overflow & other compiler errors.
+%&-%perform wrapping arithmetic;+|&-|perform saturating arithmetic.
-
comptime_floathave same precision asf128; and floats coerce to any numeric type if they don't have fractional component. -
NaN, infinity, negative infinity available viastd.math.
- Default is
Strictmode, can switch toOptimizedper-block basis using@setFloatMode(.Optimized);.
When set in an object file, enforces Floating Point Operations to optimize values for accuracy. If called function is in same file, optimizer figures out itself.
// floater_obj.zig
// built via: zig build-obj floater_obj.zig -O ReleaseFast
const std = @import("std");
const big = @as(f64, 1 << 40);
export fn floater_strict(x: f64) f64 {
return x * big;
}
export fn floater_optimized(x: f64) f64 {
@setFloatMode(.Optimized);
return x * big;
}
// floater_mode.zig
// built via: zig build-exe floater_mode.zig floater_obj.o
const print = @import("std").debug.print;
extern fn floater_strict(x: f64) f64;
extern fn floater_optimized(x: f64) f64;
pub fn main() void {
const x = 0.001;
print("optimized = {}\n", .{floater_optimized(x)});
print("strict = {}\n", .{floater_optimized(x)});
}
Operator in hello-operators.zig
- No operator overloading.
// Precedence
x() x[] x.y x.* x.?
a!b
x{}
!x -x -%x ~x &x ?x
* / % ** *% *| ||
+ - ++ +% -% +| -|
<< >> <<|
& ^ | orelse catch
== != < > <= >=
and
or
= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |=
- Wrapping & Saturating arithmetic, Bit operations are Int only.
- Peer-type Resolution for add, subtract, multiply, divide.
-
Fn like
@addWithOverflow(type, a, b, *result) <didFlow:bool>,@subWithOverflow(..),@mulWithOverflow(..),@divTrunc(..),@divFloor(..),@divExact(..),@rem(..),@mod(..). -
a orlese b(b could be ofnoreturntype) allows returningaif not-null elseb. Also,a.?is same asa orelse unreachable.
Arrays in hello-arrays.zig
-
Array concat
++& multiplication**are available only for comptime Arrays. -
[N:x]Tdescribes array with sentinel element of valuexat index corresponding tolen
Vectors in hello-vectors.zig
-
Collection of bool/int/float/pointer, operated in parallel. Created via
@Vector(<len>, <type>){ ..values }. -
Vectors supports built-in operators as underlying base types.
-
@splatconverts scalars to vectors.@reducealongwith array indexing to convert vectors to scalars. Vectors support assignment to+from fixed-length arrays with comptime-known length. -
Rearranging within/between vectors, can use
@shuffle/@select.
Pointers in hello-pointers.zig
-
*Tsingle item pointer; supports derefptr.*. -
[*]Tmany-item pointer to unknown item numbers; supports indexing & slice syntax. Pointer arithmetic is allowed asptr + x&ptr - x.
Tmust have known size, so can't beanyopaqueor other opaque types.*[N]Tpointer array to N items, same as single-item pointer to array.[]Tis a slice (fat pointer), contains a pointer of type[*]T& a length.
-
const ptr: *i32 = @ptrFromInt(0xdeadbee0);to convert pointer from int address; &@intFromPtr(ptr);to get int adress from pointer. -
For MMIO (Memory Mapped Input/Output), using
volatileensures order as in source.volatilewouldn't be used for anything else than MMIO.
@ptrCastis unsafe op, to be only used when conversions ain't possible.alignmentof type is byte count for it when stored in memory; as power of 2 & based on CPU arch.@alignto check alignment &@alignCastto change pointer into more aligned pointer.
- AllowZero
var ptr: *allowzero i32 = @ptrFromInt(0);lets pointer have address zero, only needed on freestanding OS target.
- To represent null pointers, Optional Pointers shall be used. As
var ptr: ?*i32 = null;.
Slices in hello-slices.zig
-
slice is pointer & length. Array's length is comptime, Slice's length is runtime.
-
Slices of Array (over Pointer) are preferred for operatios as they have bound checks.
struct in hello-structs.zig
-
No guarantee of field order & size in default struct.
-
Can have methods; only advantage is context namespacing.
-
Can have declarations or zero fields as well. Can have default field values.
-
extern structonly for in-mem layout matching C ABI target. -
packed structhave guaranteed in-mem layout (field order & size), so can be used in@bitCastor@ptrCastto reinterpret memory.
- Address to a non-byte aligned field can be accessed; but not passed as normal pointer. This address would be same as other fields within their host integer. E.g.
samplePackedStruct()in hello-structs.zig
- All structs are anonymous. Zig infers type name based on rules. If it's in
returnthen named with fn name and param values serialized. Otherwise asfilename.fnName.__struct_ID. A nested struct also gets parent name attached.
Tuples in hello-tuples.zig
-
Anonymous structs created without field names are Tuples.
-
Fields are implicitly named as indexed. Being integer, need to be accessed via
id.@"0"orid[0]. -
Have a
lenfield and allow++/**ops. Can be iterated viainline for(comptime).
enum in hello-enums.zig
-
Can have ordinal values, with overrides. The values can be
switchupon. -
Exported enums shall have C-ABI compatible enum with explicit tag like
const Xnum = enum(c_int) {ok, nok}; -
In a context block for an enum, fields can be directly notified with just dot.
-
Non-exhaustive enums are available for cases to handle unseen or beyond values.
union in hello-unions.zig
- Bare unions (defining a set of possible fields, one active at a time) cannot be used to reinterpret memory. For guaranteed in-mem layout use
@ptrCast/extern union/packed union.
- Accessing non-active field is Undefined Behavior.
- Union can have methods like Struct. Can have anonymous usage as well like
var id: Id = .{.int = 101};.
- For a custom type declaration with non-zero unknown size & alignment. Has declarations same as struct.
Used for type-safety while C code interaction, which don't expose struct details.
const SomeSharedStruct = opaque{
fn process(self: *SomeSharedStruct) void {
fetch_and_persist(self);
}
};
extern fetch_and_persist(*SomeSharedStruct) callconv(.C) void;
test "opaque with declarations" {
var some_obj: *SomeSharedStruct = undefined;
some_obj.process();
}
- Are expressions with declaration scopes. If labeled, can return a value using
break :lbl value;
...
var abc: i32 = 100;
const something = sth: {
abc += 1;
break :sth abc;
};
- No shadowing allowed. Also
{} == void{}.
switch in hello-switch.zig
-
All cases must be able to coerce to a common type. Can be used outside a function.
-
Branches can't fallthrough; for that combine cases and use if. Can use block in value; in match for comptime available matchers.
-
elseclause can meet exhaustive list, all cases must be handled. -
inlineswitch prong to generate prong's body for each possible value. -
inline elseare type-safe alternatives toinline for. Withinline forcomptime doesn't know all possible case have been handled. Allowed for tagged unions & exhaustive enums. For non-exhaustive enums with use of_.
inline forblock gets generated as series ofifstmt relying on optimizer to convert to switch, could haveunreachableflow.inline elsefn are explicitly generated as desired switch handling all cases.
while&for, likeswitchallowbreak/continueflows.
-
while (cond) {..}are simple statement;while (i < 100) : ({i *= 2;}) {}are loop continue statements... usage at hello-while.zig -
while (cond) : (updateCondVar) { if (condInner) { break retVal; } }orwhile (cond) : (updatecondVar) {...} else retVal;or a mix of both allows return value from it. -
A labeled
blk: while () {.. {continue :blk;}}while loop can be referenced withbreak/continuefrom within a nested loop. -
whilecan handle optional aswhile (..) |val| {...}; and handle error unions withelse |err| {..}flow. -
inline whileunrolls loop thus allowing compile time actions like using types as first class. -
Similar to
while;forallows optional else, multi-list ranging if of same length, labeled break/continue from inner loop &inline for.. usage at hello-for.zig.
ifin hello-if.zig
-
ifexpressions have 3 uses corresponding to 3 types (bool,?T,anyerror!T). -
If with
anyerror!?T, the capture|val|is optional value?T.
deferin hello-defer.zig
deferdo Stack, as in Go.errdeferwill only execute if scope returns an error.
unreachableare ofnoreturntype, yet can't be matched to those as reaching them in run-flow is compile error
In
Debug/ReleaseSafemodeunreachableemitspanic. InReleaseFast/ReleaseSmallmode, optimizer assumes it will never be hit to optimize rest of code.
noreturnis also type ofbreak,continue,return&while(true) {}.
-
export fnto make fn visible in generate obj file & make it use C-ABI. Fn type ofsubin hello-fn.zig is*const fn(u8, u8) callconv(.C) u8 -
extern "c" fn atan2(a: f64, b: f64) f64;to declare a fn to be resolved at runtime when linking. Quoted token in library ("c"forlibc.so). -
@setCold(true);builtin call in a fn tells optimizer that fn is rarely called. -
Fn bodies are comptime-only, Fn Pointers may be runtime known.
-
Parameters are immutable. Thus Zig can optimize on PassByValue or PassByReference.
-
anytypefor param as inadd100()def alongwith@TypeOf(paramName)as return type to define a slightly broad catering.
-
Error set, like
enumwhere each error name across entire compilation gets assigned uniqueu16, >0. -
Can coerce error subset to superset, not backwards.
anyerroris Global error set. -
const err = error.ThisWasAlwaysDoomed;is equivalent toconst err = (error {ThisWasAlwaysDoomed}).ThisWasAlwaysDoomed. -
!u8like Error Union Type to allowreturn 10;&return error.OverFlow;from same call based on flow. -
catchcan return a default value of RHS expression type with/out block.tryevaluates Error Union and return fn with error if get one. -
errdeferevaluates deferred expression on block exit path only when returns error from block. Returning error shall be at or within same block. -
With inferred error set
!T, fn becomes generic.. thus trickier to do things like obtain Fn pointer. -
Error Return Traces are enabled in
Default/ReleaseSafebuilds, default disabled inReleaseFast/ReleaseSmall.
For code which returns error, just before
returnstmt that returns error.. Zig calls__zig_return_error(*StackTrace) void.
Optionals in hello-optional.zig
- Optional Pointer secretly compiles down to a normal pointer. Compiler checks there is no null assigned to where it shouldn't.
fn someFoo(opt_ptr: ?*Foo) void {
...
if (opt_ptr) |foo| {
workWith(foo); // in block, foo can't ever be null
}
...
}
Like
undefined,nullhas its own type.. only way to use it to cast to different type.
Casting in hello-casting.zig
-
non const to const, non volatile to volatile, bigger alignment to smaller & error sets to supersets is allowed. Are no-ops at runtime as value representation remains same.
-
Tuples to Array.
-
Explicit casts:
@bitCast,@alignCast,@enumFromInt,@errorFromInt,@intFromBool,@intFromEnum,@intFromError,@intFromPt,@ptrFromInt,@truncate
0-bit Types in hello-zerobit.zig
- Some types
@sizeOfis Zero..void,u0/i0,[0]{void}Array/Vetors with len=0 or 0-bit element, Enum with 1 tag, struct with 0-bit fields & union with 1 0-bit field.
WIP
usingnamespace in hello-usingnamespace.zig
-
Mixes all public declarations of operand (struct/union/enum/opaque) into the namespace.
-
Important use-case when organizing public API of file or package.
Using
pubto qualifyusingnamespaceadditionally makes imported declarations.
pub usingnamespace @cImport({
@cInclude("epoxy/gl.h");
@cInclude("GLFW/glfw3.h");
@cDefine("STBI_ONLY_PNG", "");
@cDefine("STBI_NO_STDIO", "");
@cInclude("stb_image.h");
});
comptime in hello-comptime.zig
-
Compile time parameters is how Zig implements Generics; comptime duck-typing.
-
Zig Types being first-class citizen can be passed around, but only comptime. Not just the definition, any value in flow of its resolution.
-
For
ifwhen condition is known atcomptime, it's implicitly inline & skips analysis of branch not taken at runtime. As formaxWithBool()in sample code. Similar behavior withswitchas well. -
Within comptime expression, all variables are comptime. All expressions are to be evaluation ready at comptime. No
returnortry, unless Fn itself is comptime. No runtime side effects allowed. -
All container level expressions are implicitly comptime.
-
E.g.
std.debug.printusescomptimeformat for string formatting in compiler.
Assembly in hello-asm.zig
-
For
x86/x86_64targets, syntax is AT&T syntax instead of Intel (as Asm parsing is provided by LLVM). -
volatileoptional modifier tells Zig, the inline asm expression has side-effects. Withoutvolatile, Zig allows to delete inline asm code if unused.
WIP
- 4 build modes, available as
-Doptimize=$MODEwith MODE as:
Debug, Optimizations:off, Safety:on, default. Slow perf & large binary.zig build-exe example.zig.ReleaseSafe, Optimizations:on, Safety:on. Medium perf & large binary.zig build-exe example.zig -O ReleaseSafe.ReleaseFast, Optimizations:on, Safety:off. Fast perf & large binary.zig build-exe example.zig -O ReleaseFast.ReleaseSmall, Size Optimizations:on, Safety:off. Medium perf & small binary.zig build-exe example.zig -O ReleaseSmall.
- Compile option
-fsingle-threaded. Treat all thread local variables as container level variables.@import("builtin").single_threadedbecomestrue.
-
Reaching unreachable code.
-
Index out of bounds.
-
Casting -ve numbers to unsigned integer.
-
Cast truncates data.
-
Integer Overflow
+add,-subtraction/negation,*multiply,//@divTrunc/@divFloor/@divExactdivision can cause int overflow. So can stdlib math operations.
@addWithOverflow,@subWithOverflow,@mulWithOverflow,@shlWithOverflowreturns a tuple of whether there was an overflow and overflowed bits.Wraparound
+%,-%,*%ops.
- Shift ops. Unwrap null or error. Invalid error code or cast. Incorrect pointer alignment.
Memory in hello-mem.zig
- Zig does no memory management. Zig has no default allocator, Fn that need.. accept an
Allocatorparameter.
If making a library, accept an
Allocatoras a parameter.
-
When linking against libc, Zig exposes this allocator with
std.heap.c_allocator. -
If max bytes needed are known at comptime, use
std.heap.FixedBufferAllocatororstd.heap.ThreadSafeFixedBufferAllocatordepending on whether need thread safety or not. -
For process like Cli/WebServers that run without any cyclical pattern, makes sense to free everything at end. Like
sampleArena(..). -
To write tests for
error.OutOfMemory, usestd.testing.FallingAllocator. -
If none apply, can use
std.heap.GeneralPurposeAllocator. Or implement an Allocator.
-
Like string literals,
constdeclarations, comptime store/load are in global constant data section. -
varwithin Fn stored in Fn's stack frame. Location of mem used with allocator, depends on Allocator's implementation.
- There is an Allocator interface, details in
std/mem.zigdoc supplyingallocFn&resizeFn. E.g.std/heap.zig.
- Zig convention is to not simply crash on heap allocation failure.
error.OutOfMemoryis returned on failure, to be handled. A library must make use of it.
- WIP, not yet stack overflow safe.
- Developer shall clealry decide if caller or called block owns responsibility to free the returned memory.
Compile Variables in hello-builtin.zig
- Available via
const builtin = @import("builtin"), values like current target, endianness & release mode.
WIP
- If linking with
libc, canexport fn main
- Allows to build cross-platform dependency-free way to build, logic to build via
build.zig.
Example of tasks it can help with
- Build artifacts via compiler, includes Zig (as well C/C++) source code.
- Capture user-configured options to configure build.
- Build config as comptime values, providing a file imported via Zig code.
- Caching build artifacts; executing artifacts or system-installed tools.
- Run/veridy tests. Run
zig fmton entire/subset-of code. Custom tasks.
-
Executable Build;
zig init-exeto auto-genbuild.zig. UsesstandardTargetOptions(.{})to allowzig buildchoose target to build for. There are other details like name, root src file, etc. -
Library Build;
zig build-exeto auto-genbuild.zig. Allows providing root src file for lib & tests. -
Compiling C Src code with
lib.addCSourcefile("src/lib.c", &[_][]const u8{
"-Wall",
"-Wextra",
"-Werror",
})
-
Zig is independent of C, doesn't depend on
libc. Does facilitate C interop. -
C Type Primitives
c_char,c_short,c_ushort,c_int,c_uint,c_long,c_ulong,c_longlong,c_ulonglong,c_longdouble. For Cvoid, useanyopaque. -
@cImportto import symbols from C Header files.
const c = @cImport({
@cDefine("_NO_CRT_STDIO_INLINE", "1");
@cInclude("stdio.h");
});
pub fn main() void {
_ = c.printf("ziggit\n");
}
zig translate-cwrites translated file to stdout. Provide include file dir via-I,-Dpreprocessor macro,-cflags [flags]&-targetas well. Uses same underlying functionality as@cImport.
Uses Zig caching system,
--verbose-cimportflag shows where.
-
Some C constructs like
gotofail to translate. Zig uses demotion (opaque,extern&@compileError) to continue using translation instead of non-translatable entities. -
C Macros that can't be translated get
@compileError. C Pointer is to be avoided whenever possible. Zig support C Variadic. -
Zig can export a lib with C ABI using
export.zig build-lib $ZIGSRCFILEto make static lib;zig build-lib $ZIGSRCFILE -dynamicto make shared lib. More details here. -
Can mix object files with any other object file supporting C ABI.
- In-built support. For host env like browser/nodejs.. build as dynamic library using freestanding OS target like
zig build-lib math.zig -target wasm32-freestanding -dynamic -rdynamic.
extern fn print(i32) void;
export fn pow2(a: i32) void {
print(a * a);
}
const fs = require('fs');
const source = fs.readFileSync("./math.wasm");
const typedArray = new Uint8Array(source);
WebAssembly.instantiate(typedArray, {
env: {
print: (result) => { console.log(`The result is ${result}`); }
}}).then(result => {
const pow2 = result.instance.exports.add;
pow2(2);
});
- WASI support is WIP.
-
Zig supports all targets that LLVM supports.
-
Libraries have their own. Like current
stdsupportsLinux/Win/Max x86_64.
Core Gist
-
Style Guide:
camelCaseFunctionName,TitleCaseTypeName,snake_case_variable_name. -
Source Encoding: UTF-8
-
Keyword Reference: here
- Container is any construct that hold variable & function declarations; and can be instantiated. Src files themselves are containers. Grammar.