Style Guide

These coding conventions are not enforced by the compiler, but they are shipped in this documentation along with the compiler in order to provide a point of reference, should anyone wish to point to an authority on agreed upon Zig coding style.

Avoid Redundancy in Names

Avoid these words in type names:

  • Value
  • Data
  • Context
  • Manager
  • utils, misc, or somebody’s initials

Everything is a value, all types are data, everything is context, all logic manages state. Nothing is communicated by using a word that applies to all types.

Temptation to use “utilities”, “miscellaneous”, or somebody’s initials is a failure to categorize, or more commonly, overcategorization. Such declarations can live at the root of a module that needs them with no namespace needed.

Avoid Redundant Names in Fully-Qualified Namespaces

Every declaration is assigned a fully qualified namespace by the compiler, creating a tree structure. Choose names based on the fully-qualified namespace, and avoid redundant name segments.

redundant_fqn.zig

  1. const std = @import("std");
  2. pub const json = struct {
  3. pub const JsonValue = union(enum) {
  4. number: f64,
  5. boolean: bool,
  6. // ...
  7. };
  8. };
  9. pub fn main() void {
  10. std.debug.print("{s}\n", .{@typeName(json.JsonValue)});
  11. }

Shell

  1. $ zig build-exe redundant_fqn.zig
  2. $ ./redundant_fqn
  3. redundant_fqn.json.JsonValue

In this example, “json” is repeated in the fully-qualified namespace. The solution is to delete Json from JsonValue. In this example we have an empty struct named json but remember that files also act as part of the fully-qualified namespace.

This example is an exception to the rule specified in Avoid Redundancy in Names. The meaning of the type has been reduced to its core: it is a json value. The name cannot be any more specific without being incorrect.

Whitespace

  • 4 space indentation
  • Open braces on same line, unless you need to wrap.
  • If a list of things is longer than 2, put each item on its own line and exercise the ability to put an extra comma at the end.
  • Line length: aim for 100; use common sense.

Names

Roughly speaking: camelCaseFunctionName, TitleCaseTypeName, snake_case_variable_name. More precisely:

  • If x is a type then x should be TitleCase, unless it is a struct with 0 fields and is never meant to be instantiated, in which case it is considered to be a “namespace” and uses snake_case.
  • If x is callable, and x‘s return type is type, then x should be TitleCase.
  • If x is otherwise callable, then x should be camelCase.
  • Otherwise, x should be snake_case.

Acronyms, initialisms, proper nouns, or any other word that has capitalization rules in written English are subject to naming conventions just like any other word. Even acronyms that are only 2 letters long are subject to these conventions.

File names fall into two categories: types and namespaces. If the file (implicitly a struct) has top level fields, it should be named like any other struct with fields using TitleCase. Otherwise, it should use snake_case. Directory names should be snake_case.

These are general rules of thumb; if it makes sense to do something different, do what makes sense. For example, if there is an established convention such as ENOENT, follow the established convention.

Examples

style_example.zig

  1. const namespace_name = @import("dir_name/file_name.zig");
  2. const TypeName = @import("dir_name/TypeName.zig");
  3. var global_var: i32 = undefined;
  4. const const_name = 42;
  5. const primitive_type_alias = f32;
  6. const string_alias = []u8;
  7. const StructName = struct {
  8. field: i32,
  9. };
  10. const StructAlias = StructName;
  11. fn functionName(param_name: TypeName) void {
  12. var functionPointer = functionName;
  13. functionPointer();
  14. functionPointer = otherFunction;
  15. functionPointer();
  16. }
  17. const functionAlias = functionName;
  18. fn ListTemplateFunction(comptime ChildType: type, comptime fixed_size: usize) type {
  19. return List(ChildType, fixed_size);
  20. }
  21. fn ShortList(comptime T: type, comptime n: usize) type {
  22. return struct {
  23. field_name: [n]T,
  24. fn methodName() void {}
  25. };
  26. }
  27. // The word XML loses its casing when used in Zig identifiers.
  28. const xml_document =
  29. \\<?xml version="1.0" encoding="UTF-8"?>
  30. \\<document>
  31. \\</document>
  32. ;
  33. const XmlParser = struct {
  34. field: i32,
  35. };
  36. // The initials BE (Big Endian) are just another word in Zig identifier names.
  37. fn readU32Be() u32 {}

See the Zig Standard Library for more examples.

Doc Comment Guidance

  • Omit any information that is redundant based on the name of the thing being documented.
  • Duplicating information onto multiple similar functions is encouraged because it helps IDEs and other tools provide better help text.
  • Use the word assume to indicate invariants that cause Undefined Behavior when violated.
  • Use the word assert to indicate invariants that cause safety-checked Undefined Behavior when violated.