diff --git a/build.zig b/build.zig
index 959f328..ba94cfb 100644
--- a/build.zig
+++ b/build.zig
@@ -5,7 +5,7 @@ const std = @import("std");
const zig_version = @import("builtin").zig_version;
// Used for any unversioned content (for now just /blog)
-const current_minor_version = 13;
+const current_minor_version = 14;
const version_path = std.fmt.comptimePrint(
"website/versioned_docs/version-0.{}/",
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js
index 5d4e575..b3bb13d 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -48,7 +48,7 @@ const config = {
maxHits: 7,
disableVersioning: true,
excludeRoutes: [
- '/master/**/*', '/0.11/**/*', '/0.12/**/*',
+ '/master/**/*', '/0.11/**/*', '/0.12/**/*', '/0.13/**/*',
],
}]
],
@@ -66,19 +66,23 @@ const config = {
editUrl: 'https://github.com/Sobeston/zig.guide/tree/master/website/',
showLastUpdateAuthor: true,
showLastUpdateTime: true,
- lastVersion: "0.13",
+ lastVersion: "0.14",
admonitions: {
keywords: ['cpp', 'go', 'js'],
extendDefaults: true
},
versions: {
- "0.14": {
- label: 'Zig 0.14.0 (dev)',
+ "0.15": {
+ label: 'Zig 0.15.0 (dev)',
path: 'master',
},
+ "0.14": {
+ label: 'Zig 0.14.0',
+ path: '/',
+ },
"0.13": {
label: 'Zig 0.13.0',
- path: '/',
+ path: '/0.13',
},
"0.12": {
label: 'Zig 0.12.0',
diff --git a/website/versioned_docs/version-0.13/00-getting-started/01-welcome.mdx b/website/versioned_docs/version-0.13/00-getting-started/01-welcome.mdx
index 6ee794d..903582a 100644
--- a/website/versioned_docs/version-0.13/00-getting-started/01-welcome.mdx
+++ b/website/versioned_docs/version-0.13/00-getting-started/01-welcome.mdx
@@ -11,7 +11,7 @@ toolchain for maintaining **robust**, **optimal**, and **reusable** software.
:::warning
-The latest release of Zig is 0.13.0 and is currently unstable.
+The latest release of Zig is 0.15.0 and is currently unstable.
:::
diff --git a/website/versioned_docs/version-0.14/00-getting-started/01-welcome.mdx b/website/versioned_docs/version-0.14/00-getting-started/01-welcome.mdx
index 6ee794d..903582a 100644
--- a/website/versioned_docs/version-0.14/00-getting-started/01-welcome.mdx
+++ b/website/versioned_docs/version-0.14/00-getting-started/01-welcome.mdx
@@ -11,7 +11,7 @@ toolchain for maintaining **robust**, **optimal**, and **reusable** software.
:::warning
-The latest release of Zig is 0.13.0 and is currently unstable.
+The latest release of Zig is 0.15.0 and is currently unstable.
:::
diff --git a/website/versioned_docs/version-0.15/00-getting-started/01-welcome.mdx b/website/versioned_docs/version-0.15/00-getting-started/01-welcome.mdx
new file mode 100644
index 0000000..903582a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/00-getting-started/01-welcome.mdx
@@ -0,0 +1,25 @@
+---
+slug: /
+description: Get started with the Zig programming language. Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
+pagination_prev: null
+---
+
+# Welcome
+
+[Zig](https://ziglang.org) is a general-purpose programming language and
+toolchain for maintaining **robust**, **optimal**, and **reusable** software.
+
+:::warning
+
+The latest release of Zig is 0.15.0 and is currently unstable.
+
+:::
+
+To follow this guide, we assume you have:
+
+- Prior experience programming
+- Some understanding of low-level programming concepts
+
+Knowing a language like C, C++, Rust, Go, Pascal, or similar will help you follow
+this guide. You must have an editor, terminal, and internet connection available
+to you.
diff --git a/website/versioned_docs/version-0.15/00-getting-started/02-installation.mdx b/website/versioned_docs/version-0.15/00-getting-started/02-installation.mdx
new file mode 100644
index 0000000..9c84476
--- /dev/null
+++ b/website/versioned_docs/version-0.15/00-getting-started/02-installation.mdx
@@ -0,0 +1,128 @@
+---
+description: Installation instructions for the Zig programming language on Linux, Windows, and macOS.
+---
+
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
+# Installation
+
+
+
+ Consider getting Zig from your distribution's [package manager](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager). Most major linux distros package the latest Zig release.
+
+ ### Installing manually
+
+ 1. [Download](https://ziglang.org/download/#release-0.15.0) a prebuilt version of Zig.
+
+ Choose a build of Zig 0.14 for Linux that matches your CPU architecture. If you're unsure which architecture you're using, this can be found with:
+
+ ```bash
+ uname -m
+ ```
+
+ 2. Extract the archive using tar, e.g.
+
+ ```bash
+ tar xf zig-linux-x86_64-0.15.0.tar.xz
+ ```
+
+ 3. Add the location of your Zig binary to your path, e.g.
+
+ ```bash
+ echo 'export PATH="$HOME/zig-linux-x86_64-0.15.0:$PATH"' >> ~/.bashrc
+ ```
+
+
+ Consider getting Zig from a package manager such as [chocolatey](https://chocolatey.org/), [scoop](https://scoop.sh/), or [winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget).
+
+ All commands shown are to be used inside Powershell.
+
+ ```powershell
+ choco install zig
+ ```
+ ```
+ winget install zig.zig
+ ```
+ ```
+ scoop install zig
+ ```
+
+ ### Installing manually
+
+ 1. [Download](https://ziglang.org/download/#release-0.14.0) a prebuilt version of Zig.
+
+ Choose a build of Zig 0.14 for Windows that matches your CPU architecture. Most Windows systems use `x86_64`, also known as `AMD64`. If you're unsure which architecture you're using, this can be found with:
+
+ ```powershell
+ $Env:PROCESSOR_ARCHITECTURE
+ ```
+
+ 2. Extract Zig.
+
+ 3. Add Zig to your path:
+
+
+
+
+ ```powershell
+ [Environment]::SetEnvironmentVariable(
+ "Path",
+ [Environment]::GetEnvironmentVariable("Path", "User") + ";C:\_\zig-windows-_",
+ "User"
+ )
+ ```
+
+
+
+
+ ```powershell
+ [Environment]::SetEnvironmentVariable(
+ "Path",
+ [Environment]::GetEnvironmentVariable("Path", "Machine") + ";C:\_\zig-windows-_",
+ "Machine"
+ )
+ ```
+
+
+
+ Close your terminal and create a new one.
+
+
+
+
+
+ Consider getting Zig from a package manager such as [brew](https://brew.sh/).
+
+ ```
+ brew install zig
+ ```
+
+
+
+
+### Verifying your install
+
+Verify your installation with `zig version`. The output should look like this:
+
+```
+$ zig version
+0.14.0
+```
+
+### Extras
+
+For completions and go-to-definition in your editor, consider installing the [Zig Language Server](https://github.com/zigtools/zls/#installation).
+
+Consider joining a [Zig community](https://github.com/ziglang/zig/wiki/Community).
diff --git a/website/versioned_docs/version-0.15/00-getting-started/03-hello-world.mdx b/website/versioned_docs/version-0.15/00-getting-started/03-hello-world.mdx
new file mode 100644
index 0000000..cdfe499
--- /dev/null
+++ b/website/versioned_docs/version-0.15/00-getting-started/03-hello-world.mdx
@@ -0,0 +1,22 @@
+---
+description: Write your first program using the Zig programming language.
+---
+
+# Hello World
+
+Create a file called `main.zig`, with the following contents:
+
+```zig
+const std = @import("std");
+
+pub fn main() void {
+ std.debug.print("Hello, {s}!\n", .{"World"});
+}
+```
+
+Use `zig run main.zig` to build and run it. In this example, `Hello, World!`
+will be written to stderr, and is assumed to never fail.
+
+:::warning found 'invalid bytes'
+Make sure your `main.zig` file is UTF-8 encoded as the Zig compiler does not currently support other encodings. To re-encode your file as UTF-8, run `zig fmt main.zig` and reopen the file in your editor.
+:::
diff --git a/website/versioned_docs/version-0.15/00-getting-started/04-running-tests.mdx b/website/versioned_docs/version-0.15/00-getting-started/04-running-tests.mdx
new file mode 100644
index 0000000..a18c980
--- /dev/null
+++ b/website/versioned_docs/version-0.15/00-getting-started/04-running-tests.mdx
@@ -0,0 +1,51 @@
+---
+pagination_next: language-basics/assignment
+---
+
+import CodeBlock from "@theme/CodeBlock";
+
+import Success from "!!raw-loader!./04.running-tests-success.zig";
+
+# Running Tests
+
+In this guide, code examples are often given as runnable tests. Before proceeding, make sure that you can run them successfully.
+
+### Success
+
+Save the following text as `test_pass.zig`, and run `zig test test_pass.zig`; you should read `All 1 tests passed.` in your terminal.
+
+{Success}
+
+:::note
+
+Some code examples in this guide will have their imports at the top hidden, make sure to get them by clicking the copy button on the top-right of the code block.
+
+:::
+
+Try the same test without the `try` keyword. What happened?
+
+### Failure
+
+Now, save the following text as `test_fail.zig` and observe it fail.
+
+```zig
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "always fails" {
+ try expect(false);
+}
+```
+
+Which should output something like:
+
+```
+Test [1/1] test.always fails... FAIL (TestUnexpectedResult)
+/usr/lib/zig/std/testing.zig:515:14: 0x2241ef in expect (test)
+ if (!ok) return error.TestUnexpectedResult;
+ ^
+[...]/test_fail:5:5: 0x224305 in test.always fails (test)
+ try expect(false);
+ ^
+0 passed; 0 skipped; 1 failed.
+```
diff --git a/website/versioned_docs/version-0.15/00-getting-started/04.running-tests-success.zig b/website/versioned_docs/version-0.15/00-getting-started/04.running-tests-success.zig
new file mode 100644
index 0000000..5697b8e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/00-getting-started/04.running-tests-success.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "always succeeds" {
+ try expect(true);
+}
diff --git a/website/versioned_docs/version-0.15/00-getting-started/_category_.json b/website/versioned_docs/version-0.15/00-getting-started/_category_.json
new file mode 100644
index 0000000..f0252aa
--- /dev/null
+++ b/website/versioned_docs/version-0.15/00-getting-started/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Getting Started",
+ "link": {
+ "description": "zig.guide - A Guide & Tutorial for the Zig programming language. Install and get started with ziglang here."
+ }
+}
\ No newline at end of file
diff --git a/website/versioned_docs/version-0.15/01-language-basics/01-assignment.mdx b/website/versioned_docs/version-0.15/01-language-basics/01-assignment.mdx
new file mode 100644
index 0000000..33480e2
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/01-assignment.mdx
@@ -0,0 +1,46 @@
+---
+pagination_prev: getting-started/running-tests
+---
+
+import CodeBlock from "@theme/CodeBlock";
+
+import Assignment from "!!raw-loader!./01.assignment.zig";
+import Undefined from "!!raw-loader!./01.undefined.zig";
+
+# Assignment
+
+Value assignment has the following syntax:
+`(const|var) identifier[: type] = value`.
+
+- `const` indicates that `identifier` is a **constant** that stores an immutable
+ value.
+- `var` indicates that `identifier` is a **variable** that stores a mutable
+ value.
+- `: type` is a type annotation for `identifier`, and may be omitted if the data
+ type of `value` can be inferred.
+
+{Assignment}
+
+Constants and variables _must_ have a value. If no known value can be given, the
+[`undefined`](https://ziglang.org/documentation/master/#undefined) value, which
+coerces to any type, may be used as long as a type annotation is provided.
+
+{Undefined}
+
+:::js
+
+If you're familiar with JavaScript, you might be used to using `undefined` as a
+way to represent a variable that hasn't been initialised, or to represent an
+absense of value.
+
+However in Zig, using `undefined` like this is a bad idea as `undefined` works
+very differently. In JavaScript, values can be checked for being undefined,
+whereas in Zig, an undefined value is impossible to detect. Usage of undefined
+values is not safe. Zig's undefined is "undefined" as in "undefined behaviour",
+and should not be used as a stand-in for an optional.
+
+Need optionals? These are [covered later](./23-optionals.mdx).
+
+:::
+
+Where possible, `const` values are preferred over `var` values.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/01.assignment.zig b/website/versioned_docs/version-0.15/01-language-basics/01.assignment.zig
new file mode 100644
index 0000000..3493ece
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/01.assignment.zig
@@ -0,0 +1,6 @@
+const constant: i32 = 5; // signed 32-bit constant
+var variable: u32 = 5000; // unsigned 32-bit variable
+
+// @as performs an explicit type coercion
+const inferred_constant = @as(i32, 5);
+var inferred_variable = @as(u32, 5000);
diff --git a/website/versioned_docs/version-0.15/01-language-basics/01.undefined.zig b/website/versioned_docs/version-0.15/01-language-basics/01.undefined.zig
new file mode 100644
index 0000000..b45accb
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/01.undefined.zig
@@ -0,0 +1,2 @@
+const a: i32 = undefined;
+var b: u32 = undefined;
diff --git a/website/versioned_docs/version-0.15/01-language-basics/02-arrays.md b/website/versioned_docs/version-0.15/01-language-basics/02-arrays.md
new file mode 100644
index 0000000..256dd99
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/02-arrays.md
@@ -0,0 +1,18 @@
+# Arrays
+
+Arrays are denoted by `[N]T`, where `N` is the number of elements in the array
+and `T` is the type of those elements (i.e., the array's child type).
+
+For array literals, `N` may be replaced by `_` to infer the size of the array.
+
+```zig
+const a = [5]u8{ 'h', 'e', 'l', 'l', 'o' };
+const b = [_]u8{ 'w', 'o', 'r', 'l', 'd' };
+```
+
+To get the size of an array, simply access the array's `len` field.
+
+```zig
+const array = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
+const length = array.len; // 5
+```
diff --git a/website/versioned_docs/version-0.15/01-language-basics/03-if.mdx b/website/versioned_docs/version-0.15/01-language-basics/03-if.mdx
new file mode 100644
index 0000000..451e0a7
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/03-if.mdx
@@ -0,0 +1,17 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Expect from "!!raw-loader!./03.expect.zig";
+import IfExpression from "!!raw-loader!./03.if-expression.zig";
+
+# If Expressions
+
+Zig's if statements accept `bool` values (i.e. `true` or `false`). Unlike languages
+like C or JavaScript, there are no values that implicitly coerce to bool values.
+
+Ternary conditional operators (cond ? a : b) do not exist in zig.
+
+{Expect}
+
+If statements also work as expressions.
+
+{IfExpression}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/03.expect.zig b/website/versioned_docs/version-0.15/01-language-basics/03.expect.zig
new file mode 100644
index 0000000..00ab3fc
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/03.expect.zig
@@ -0,0 +1,12 @@
+const expect = @import("std").testing.expect;
+
+test "if statement" {
+ const a = true;
+ var x: u16 = 0;
+ if (a) {
+ x += 1;
+ } else {
+ x += 2;
+ }
+ try expect(x == 1);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/03.if-expression.zig b/website/versioned_docs/version-0.15/01-language-basics/03.if-expression.zig
new file mode 100644
index 0000000..2e398a1
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/03.if-expression.zig
@@ -0,0 +1,10 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "if statement expression" {
+ const a = true;
+ var x: u16 = 0;
+ x += if (a) 1 else 2;
+ try expect(x == 1);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/04-while-loops.mdx b/website/versioned_docs/version-0.15/01-language-basics/04-while-loops.mdx
new file mode 100644
index 0000000..1fba934
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/04-while-loops.mdx
@@ -0,0 +1,27 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import While from "!!raw-loader!./04.while.zig";
+import WhileContinueExpression from "!!raw-loader!./04.while-continue-expression.zig";
+import WhileContinue from "!!raw-loader!./04.while-continue.zig";
+import WhileBreak from "!!raw-loader!./04.while-break.zig";
+
+# While loops
+
+Zig's while loop has three parts - a condition, a block and a continue
+expression.
+
+Without a continue expression.
+
+{While}
+
+With a continue expression.
+
+{WhileContinueExpression}
+
+With a `continue`.
+
+{WhileContinue}
+
+With a `break`.
+
+{WhileBreak}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/04.while-break.zig b/website/versioned_docs/version-0.15/01-language-basics/04.while-break.zig
new file mode 100644
index 0000000..e29d80d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/04.while-break.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "while with break" {
+ var sum: u8 = 0;
+ var i: u8 = 0;
+ while (i <= 3) : (i += 1) {
+ if (i == 2) break;
+ sum += i;
+ }
+ try expect(sum == 1);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/04.while-continue-expression.zig b/website/versioned_docs/version-0.15/01-language-basics/04.while-continue-expression.zig
new file mode 100644
index 0000000..8b2a828
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/04.while-continue-expression.zig
@@ -0,0 +1,12 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "while with continue expression" {
+ var sum: u8 = 0;
+ var i: u8 = 1;
+ while (i <= 10) : (i += 1) {
+ sum += i;
+ }
+ try expect(sum == 55);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/04.while-continue.zig b/website/versioned_docs/version-0.15/01-language-basics/04.while-continue.zig
new file mode 100644
index 0000000..f3f947a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/04.while-continue.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "while with continue" {
+ var sum: u8 = 0;
+ var i: u8 = 0;
+ while (i <= 3) : (i += 1) {
+ if (i == 2) continue;
+ sum += i;
+ }
+ try expect(sum == 4);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/04.while.zig b/website/versioned_docs/version-0.15/01-language-basics/04.while.zig
new file mode 100644
index 0000000..1cadec2
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/04.while.zig
@@ -0,0 +1,11 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "while" {
+ var i: u8 = 2;
+ while (i < 100) {
+ i *= 2;
+ }
+ try expect(i == 128);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/05-for-loops.mdx b/website/versioned_docs/version-0.15/01-language-basics/05-for-loops.mdx
new file mode 100644
index 0000000..047117b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/05-for-loops.mdx
@@ -0,0 +1,12 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import For from "!!raw-loader!./05.for.zig";
+
+# For loops
+
+For loops are used to iterate over arrays (and other types, to be discussed
+later). For loops follow this syntax. Like while, for loops can use `break` and
+`continue`. Here, we've had to assign values to `_`, as Zig does not allow us to
+have unused values.
+
+{For}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/05.for.zig b/website/versioned_docs/version-0.15/01-language-basics/05.for.zig
new file mode 100644
index 0000000..06367cb
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/05.for.zig
@@ -0,0 +1,23 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "for" {
+ //character literals are equivalent to integer literals
+ const string = [_]u8{ 'a', 'b', 'c' };
+
+ for (string, 0..) |character, index| {
+ _ = character;
+ _ = index;
+ }
+
+ for (string) |character| {
+ _ = character;
+ }
+
+ for (string, 0..) |_, index| {
+ _ = index;
+ }
+
+ for (string) |_| {}
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/06-functions.mdx b/website/versioned_docs/version-0.15/01-language-basics/06-functions.mdx
new file mode 100644
index 0000000..10ce954
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/06-functions.mdx
@@ -0,0 +1,29 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Functions from "!!raw-loader!./06.functions.zig";
+import FunctionsRecursion from "!!raw-loader!./06.functions-recursion.zig";
+
+# Functions
+
+**All function arguments are immutable** - if a copy is desired the user must
+explicitly make one. Unlike variables, which are snake_case, functions are
+camelCase. Here's an example of declaring and calling a simple function.
+
+{Functions}
+
+Recursion is allowed:
+
+{FunctionsRecursion}
+
+When recursion happens, the compiler is no longer able to work out the maximum
+stack size, which may result in unsafe behaviour - a stack overflow. Details on
+how to achieve safe recursion will be covered in the future.
+
+Values can be ignored using `_` instead of a variable or const declaration. This
+does not work at the global scope (i.e. it only works inside functions and
+blocks) and is useful for ignoring the values returned from functions if you do
+not need them.
+
+```zig
+_ = 10;
+```
diff --git a/website/versioned_docs/version-0.15/01-language-basics/06.functions-recursion.zig b/website/versioned_docs/version-0.15/01-language-basics/06.functions-recursion.zig
new file mode 100644
index 0000000..9f68e9f
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/06.functions-recursion.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+fn fibonacci(n: u16) u16 {
+ if (n == 0 or n == 1) return n;
+ return fibonacci(n - 1) + fibonacci(n - 2);
+}
+
+test "function recursion" {
+ const x = fibonacci(10);
+ try expect(x == 55);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/06.functions.zig b/website/versioned_docs/version-0.15/01-language-basics/06.functions.zig
new file mode 100644
index 0000000..71f54c8
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/06.functions.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+fn addFive(x: u32) u32 {
+ return x + 5;
+}
+
+test "function" {
+ const y = addFive(0);
+ try expect(@TypeOf(y) == u32);
+ try expect(y == 5);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/07-defer.mdx b/website/versioned_docs/version-0.15/01-language-basics/07-defer.mdx
new file mode 100644
index 0000000..c7d1c0e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/07-defer.mdx
@@ -0,0 +1,19 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Defer from "!!raw-loader!./07.defer.zig";
+import DeferMulti from "!!raw-loader!./07.defer-multi.zig";
+
+# Defer
+
+Defer is used to execute a statement upon exiting the current block.
+
+{Defer}
+
+When there are multiple defers in a single block, they are executed in reverse
+order.
+
+{DeferMulti}
+
+Defer is useful to ensure that resources are cleaned up when they are no longer needed.
+Instead of needing to remember to manually free up the resource,
+you can add a defer statement right next to the statement that allocates the resource.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/07.defer-multi.zig b/website/versioned_docs/version-0.15/01-language-basics/07.defer-multi.zig
new file mode 100644
index 0000000..ce50e5c
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/07.defer-multi.zig
@@ -0,0 +1,12 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "multi defer" {
+ var x: f32 = 5;
+ {
+ defer x += 2;
+ defer x /= 2;
+ }
+ try expect(x == 4.5);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/07.defer.zig b/website/versioned_docs/version-0.15/01-language-basics/07.defer.zig
new file mode 100644
index 0000000..fe0177b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/07.defer.zig
@@ -0,0 +1,12 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "defer" {
+ var x: i16 = 5;
+ {
+ defer x += 2;
+ try expect(x == 5);
+ }
+ try expect(x == 7);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08-errors.mdx b/website/versioned_docs/version-0.15/01-language-basics/08-errors.mdx
new file mode 100644
index 0000000..3dbbabe
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08-errors.mdx
@@ -0,0 +1,72 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import ErrorSet from "!!raw-loader!./08.error-set.zig";
+import ErrorSetCoercion from "!!raw-loader!./08.error-set-coercion.zig";
+import ErrorUnion from "!!raw-loader!./08.error-union.zig";
+import ErrorPayloadCapture from "!!raw-loader!./08.error-payload-capture.zig";
+import ErrorTry from "!!raw-loader!./08.error-try.zig";
+import Errdefer from "!!raw-loader!./08.errdefer.zig";
+import ErrorSetInferred from "!!raw-loader!./08.error-set-inferred.zig";
+import ErrorSetMerge from "!!raw-loader!./08.error-set-merge.zig";
+
+# Errors
+
+An error set is like an enum (details on Zig's enums later), where each error in
+the set is a value. There are no exceptions in Zig; errors are values. Let's
+create an error set.
+
+{ErrorSet}
+
+Error sets coerce to their supersets.
+
+{ErrorSetCoercion}
+
+An error set type and another type can be combined with the `!` operator to form
+an error union type. Values of these types may be an error value or a value of
+the other type.
+
+Let's create a value of an error union type. Here
+[`catch`](https://ziglang.org/documentation/master/#catch) is used, which is
+followed by an expression which is evaluated when the value preceding it is an
+error. The catch here is used to provide a fallback value, but could instead be
+a [`noreturn`](https://ziglang.org/documentation/master/#noreturn) - the type of
+`return`, `while (true)` and others.
+
+{ErrorUnion}
+
+Functions often return error unions. Here's one using a catch, where the `|err|`
+syntax receives the value of the error. This is called **payload capturing**,
+and is used similarly in many places. We'll talk about it in more detail later
+in the chapter. Side note: some languages use similar syntax for lambdas - this
+is not true for Zig.
+
+{ErrorPayloadCapture}
+
+`try x` is a shortcut for `x catch |err| return err`, and is commonly used where
+handling an error isn't appropriate. Zig's
+[`try`](https://ziglang.org/documentation/master/#try) and
+[`catch`](https://ziglang.org/documentation/master/#catch) are unrelated to
+try-catch in other languages.
+
+{ErrorTry}
+
+[`errdefer`](https://ziglang.org/documentation/master/#errdefer) works like
+[`defer`](https://ziglang.org/documentation/master/#defer), but only executing
+when the function is returned from with an error inside of the
+[`errdefer`](https://ziglang.org/documentation/master/#errdefer)'s block.
+
+{Errdefer}
+
+Error unions returned from a function can have their error sets inferred by not
+having an explicit error set. This inferred error set contains all possible
+errors that the function may return.
+
+{ErrorSetInferred}
+
+Error sets can be merged.
+
+{ErrorSetMerge}
+
+`anyerror` is the global error set, which due to being the superset of all error
+sets, can have an error from any set coerced to it. Its usage should be
+generally avoided.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.errdefer.zig b/website/versioned_docs/version-0.15/01-language-basics/08.errdefer.zig
new file mode 100644
index 0000000..d8aa773
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.errdefer.zig
@@ -0,0 +1,22 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+fn failingFunction() error{Oops}!void {
+ return error.Oops;
+}
+
+//hide-end
+var problems: u32 = 98;
+
+fn failFnCounter() error{Oops}!void {
+ errdefer problems += 1;
+ try failingFunction();
+}
+
+test "errdefer" {
+ failFnCounter() catch |err| {
+ try expect(err == error.Oops);
+ try expect(problems == 99);
+ return;
+ };
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.error-payload-capture.zig b/website/versioned_docs/version-0.15/01-language-basics/08.error-payload-capture.zig
new file mode 100644
index 0000000..f15b986
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.error-payload-capture.zig
@@ -0,0 +1,14 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+fn failingFunction() error{Oops}!void {
+ return error.Oops;
+}
+
+test "returning an error" {
+ failingFunction() catch |err| {
+ try expect(err == error.Oops);
+ return;
+ };
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.error-set-coercion.zig b/website/versioned_docs/version-0.15/01-language-basics/08.error-set-coercion.zig
new file mode 100644
index 0000000..5151b61
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.error-set-coercion.zig
@@ -0,0 +1,15 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+const FileOpenError = error{
+ AccessDenied,
+ OutOfMemory,
+ FileNotFound,
+};
+// hide-end
+const AllocationError = error{OutOfMemory};
+
+test "coerce error from a subset to a superset" {
+ const err: FileOpenError = AllocationError.OutOfMemory;
+ try expect(err == FileOpenError.OutOfMemory);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.error-set-inferred.zig b/website/versioned_docs/version-0.15/01-language-basics/08.error-set-inferred.zig
new file mode 100644
index 0000000..48422e7
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.error-set-inferred.zig
@@ -0,0 +1,12 @@
+fn createFile() !void {
+ return error.AccessDenied;
+}
+
+test "inferred error set" {
+ //type coercion successfully takes place
+ const x: error{AccessDenied}!void = createFile();
+
+ //Zig does not let us ignore error unions via _ = x;
+ //we must unwrap it with "try", "catch", or "if" by any means
+ _ = x catch {};
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.error-set-merge.zig b/website/versioned_docs/version-0.15/01-language-basics/08.error-set-merge.zig
new file mode 100644
index 0000000..56249dc
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.error-set-merge.zig
@@ -0,0 +1,3 @@
+const A = error{ NotDir, PathNotFound };
+const B = error{ OutOfMemory, PathNotFound };
+const C = A || B;
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.error-set.zig b/website/versioned_docs/version-0.15/01-language-basics/08.error-set.zig
new file mode 100644
index 0000000..c7a6488
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.error-set.zig
@@ -0,0 +1,5 @@
+const FileOpenError = error{
+ AccessDenied,
+ OutOfMemory,
+ FileNotFound,
+};
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.error-try.zig b/website/versioned_docs/version-0.15/01-language-basics/08.error-try.zig
new file mode 100644
index 0000000..45fa4da
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.error-try.zig
@@ -0,0 +1,20 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+fn failingFunction() error{Oops}!void {
+ return error.Oops;
+}
+
+//hide-end
+fn failFn() error{Oops}!i32 {
+ try failingFunction();
+ return 12;
+}
+
+test "try" {
+ const v = failFn() catch |err| {
+ try expect(err == error.Oops);
+ return;
+ };
+ try expect(v == 12); // is never reached
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/08.error-union.zig b/website/versioned_docs/version-0.15/01-language-basics/08.error-union.zig
new file mode 100644
index 0000000..ea43a1d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/08.error-union.zig
@@ -0,0 +1,18 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+const FileOpenError = error{
+ AccessDenied,
+ OutOfMemory,
+ FileNotFound,
+};
+const AllocationError = error{OutOfMemory};
+
+// hide-end
+test "error union" {
+ const maybe_error: AllocationError!u16 = 10;
+ const no_error = maybe_error catch 0;
+
+ try expect(@TypeOf(no_error) == u16);
+ try expect(no_error == 10);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/09-switch.mdx b/website/versioned_docs/version-0.15/01-language-basics/09-switch.mdx
new file mode 100644
index 0000000..be515a1
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/09-switch.mdx
@@ -0,0 +1,26 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import SwitchStatement from "!!raw-loader!./09.switch-statement.zig";
+import SwitchExpression from "!!raw-loader!./09.switch-expression.zig";
+
+# Switch
+
+Zig's `switch` works as both a statement and an expression. The types of all
+branches must coerce to the type which is being switched upon. All possible
+values must have an associated branch - values cannot be left out. Cases cannot
+fall through to other branches.
+
+An example of a switch statement. The else is required to satisfy the
+exhaustiveness of this switch.
+
+{SwitchStatement}
+
+Here is the former, but as a switch expression.
+
+{SwitchExpression}
+
+:::info
+
+Now is the perfect time to use what you've learned and [solve a problem together](/posts/fizz-buzz).
+
+:::
diff --git a/website/versioned_docs/version-0.15/01-language-basics/09.switch-expression.zig b/website/versioned_docs/version-0.15/01-language-basics/09.switch-expression.zig
new file mode 100644
index 0000000..f2a6115
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/09.switch-expression.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "switch expression" {
+ var x: i8 = 10;
+ x = switch (x) {
+ -1...1 => -x,
+ 10, 100 => @divExact(x, 10),
+ else => x,
+ };
+ try expect(x == 1);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/09.switch-statement.zig b/website/versioned_docs/version-0.15/01-language-basics/09.switch-statement.zig
new file mode 100644
index 0000000..2f87e57
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/09.switch-statement.zig
@@ -0,0 +1,19 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "switch statement" {
+ var x: i8 = 10;
+ switch (x) {
+ -1...1 => {
+ x = -x;
+ },
+ 10, 100 => {
+ //special considerations must be made
+ //when dividing signed integers
+ x = @divExact(x, 10);
+ },
+ else => {},
+ }
+ try expect(x == 1);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/10-runtime-safety.mdx b/website/versioned_docs/version-0.15/01-language-basics/10-runtime-safety.mdx
new file mode 100644
index 0000000..02f47d5
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/10-runtime-safety.mdx
@@ -0,0 +1,82 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import SwitchUnreachable from "!!raw-loader!./10.switch-unreachable.zig";
+
+# Runtime Safety
+
+Zig provides a level of safety, where problems may be found during execution.
+Safety can be left on, or turned off. Zig has many cases of so-called
+**detectable illegal behaviour**, meaning that illegal behaviour will be caught
+(causing a panic) with safety on, but will result in undefined behaviour with
+safety off. Users are strongly recommended to develop and test their software
+with safety on, despite its speed penalties.
+
+For example, runtime safety protects you from out of bounds indices.
+
+```zig
+test "out of bounds" {
+ const a = [3]u8{ 1, 2, 3 };
+ var index: u8 = 5;
+ const b = a[index];
+
+ _ = b;
+ index = index;
+}
+```
+
+```
+test "out of bounds"...index out of bounds
+.\tests.zig:43:14: 0x7ff698cc1b82 in test "out of bounds" (test.obj)
+ const b = a[index];
+ ^
+```
+
+The user may disable runtime safety for the current block using the built-in
+function
+[`@setRuntimeSafety`](https://ziglang.org/documentation/master/#setRuntimeSafety).
+
+```zig
+test "out of bounds, no safety" {
+ @setRuntimeSafety(false);
+ const a = [3]u8{ 1, 2, 3 };
+ var index: u8 = 5;
+ const b = a[index];
+
+ _ = b;
+ index = index;
+}
+```
+
+Safety is off for some build modes (to be discussed later).
+
+# Unreachable
+
+[`unreachable`](https://ziglang.org/documentation/master/#unreachable) is an
+assertion to the compiler that this statement will not be reached. It can tell
+the compiler that a branch is impossible, which the optimiser can then take
+advantage of. Reaching an
+[`unreachable`](https://ziglang.org/documentation/master/#unreachable) is
+detectable illegal behaviour.
+
+As it is of the type
+[`noreturn`](https://ziglang.org/documentation/master/#noreturn), it is
+compatible with all other types. Here it coerces to u32.
+
+```zig
+test "unreachable" {
+ const x: i32 = 1;
+ const y: u32 = if (x == 2) 5 else unreachable;
+ _ = y;
+}
+```
+
+```
+test "unreachable"...reached unreachable code
+.\tests.zig:211:39: 0x7ff7e29b2049 in test "unreachable" (test.obj)
+ const y: u32 = if (x == 2) 5 else unreachable;
+ ^
+```
+
+Here is an unreachable being used in a switch.
+
+{SwitchUnreachable}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/10.switch-unreachable.zig b/website/versioned_docs/version-0.15/01-language-basics/10.switch-unreachable.zig
new file mode 100644
index 0000000..282511d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/10.switch-unreachable.zig
@@ -0,0 +1,16 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+fn asciiToUpper(x: u8) u8 {
+ return switch (x) {
+ 'a'...'z' => x + 'A' - 'a',
+ 'A'...'Z' => x,
+ else => unreachable,
+ };
+}
+
+test "unreachable switch" {
+ try expect(asciiToUpper('a') == 'A');
+ try expect(asciiToUpper('A') == 'A');
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/11-pointers.mdx b/website/versioned_docs/version-0.15/01-language-basics/11-pointers.mdx
new file mode 100644
index 0000000..abf5776
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/11-pointers.mdx
@@ -0,0 +1,50 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Pointers from "!!raw-loader!./11.pointers.zig";
+
+# Pointers
+
+Normal pointers in Zig cannot have 0 or null as a value. They follow the syntax
+`*T`, where `T` is the child type.
+
+Referencing is done with `&variable`, and dereferencing is done with
+`variable.*`.
+
+{Pointers}
+
+Trying to set a `*T` to the value 0 is detectable illegal behaviour.
+
+```zig
+test "naughty pointer" {
+ var x: u16 = 5;
+ x -= 5;
+ var y: *u8 = @ptrFromInt(x);
+ y = y;
+}
+```
+
+```
+Test [23/126] test.naughty pointer... thread 21598 panic: cast causes pointer to be null
+./test-c1.zig:252:18: 0x260a91 in test.naughty pointer (test)
+ var y: *u8 = @ptrFromInt(x);
+ ^
+```
+
+Zig also has const pointers, which cannot be used to modify the referenced data.
+Referencing a const variable will yield a const pointer.
+
+```zig
+test "const pointers" {
+ const x: u8 = 1;
+ var y = &x;
+ y.* += 1;
+}
+```
+
+```
+error: cannot assign to constant
+ y.* += 1;
+ ^
+```
+
+A `*T` coerces to a `*const T`.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/11.pointers.zig b/website/versioned_docs/version-0.15/01-language-basics/11.pointers.zig
new file mode 100644
index 0000000..b6e99a3
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/11.pointers.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+fn increment(num: *u8) void {
+ num.* += 1;
+}
+
+test "pointers" {
+ var x: u8 = 1;
+ increment(&x);
+ try expect(x == 2);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/12-pointer-sized-integers.md b/website/versioned_docs/version-0.15/01-language-basics/12-pointer-sized-integers.md
new file mode 100644
index 0000000..f693f80
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/12-pointer-sized-integers.md
@@ -0,0 +1,11 @@
+# Pointer Sized Integers
+
+`usize` and `isize` are given as unsigned and signed integers which are the same
+size as pointers.
+
+```zig
+test "usize" {
+ try expect(@sizeOf(usize) == @sizeOf(*u8));
+ try expect(@sizeOf(isize) == @sizeOf(*u8));
+}
+```
diff --git a/website/versioned_docs/version-0.15/01-language-basics/13-many-item-pointers.mdx b/website/versioned_docs/version-0.15/01-language-basics/13-many-item-pointers.mdx
new file mode 100644
index 0000000..d6dbb0f
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/13-many-item-pointers.mdx
@@ -0,0 +1,37 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import ManyItemPointers from "!!raw-loader!./13.many-item-pointers.zig";
+
+# Many-item Pointers
+
+Most programs need to keep track of buffers which don't have compile-time known
+lengths. Many-item pointers are used for these. These act similarly to their
+single-item counterparts, using the syntax `[*]T` instead of `*T`.
+
+Here's a rundown of the differences between single and multi-item pointers.
+
+| | Single-item pointers | Multi-item pointers |
+| ----------------------------- | --------------------------- | -------------------------------- |
+| Dereferenceable | Yes, e.g. `ptr.*` | No |
+| Indexable | No | Yes, e.g. `ptr[0]` |
+| Supports Arithmetic | No | Yes, e.g. `ptr + 1` or `ptr - 1` |
+| Item size | Any size, including unknown | Must be known |
+| Coerces from an array pointer | No | Yes |
+
+Many-item pointers can have all of the same attributes, such as `const`, as
+single-item pointers.
+
+In this example code, we've written a function that can take in a buffer of any
+length. Notice how a single-item pointer to an array of bytes coerces into a
+many-item pointer of bytes.
+
+{ManyItemPointers}
+
+Think about what might happen if you passed that function the incorrect
+`byte_count`. The programmer is expected to keep track of (or otherwise know)
+the length of these buffers. It's worth noting that this function is effectively
+trusting us to pass us a valid length for the given buffer.
+
+We can convert from a many-item pointer to a single-item pointer by either
+indexing an element and dereferencing that, or by using `@ptrCast` to cast the
+pointer type. This is only valid when the buffer has a length of at least 1.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/13.many-item-pointers.zig b/website/versioned_docs/version-0.15/01-language-basics/13.many-item-pointers.zig
new file mode 100644
index 0000000..a658b62
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/13.many-item-pointers.zig
@@ -0,0 +1,21 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+fn doubleAllManypointer(buffer: [*]u8, byte_count: usize) void {
+ var i: usize = 0;
+ while (i < byte_count) : (i += 1) buffer[i] *= 2;
+}
+
+test "many-item pointers" {
+ var buffer: [100]u8 = [_]u8{1} ** 100;
+ const buffer_ptr: *[100]u8 = &buffer;
+
+ const buffer_many_ptr: [*]u8 = buffer_ptr;
+ doubleAllManypointer(buffer_many_ptr, buffer.len);
+ for (buffer) |byte| try expect(byte == 2);
+
+ const first_elem_ptr: *u8 = &buffer_many_ptr[0];
+ const first_elem_ptr_2: *u8 = @ptrCast(buffer_many_ptr);
+ try expect(first_elem_ptr == first_elem_ptr_2);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/14-slices.mdx b/website/versioned_docs/version-0.15/01-language-basics/14-slices.mdx
new file mode 100644
index 0000000..6bfa031
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/14-slices.mdx
@@ -0,0 +1,76 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Slices from "!!raw-loader!./14.slices.zig";
+import Slices2 from "!!raw-loader!./14.slices-2.zig";
+
+# Slices
+
+Slices can be thought of many-item pointers (`[*]T`) with a length (`usize`).
+These use the syntax `[]T`. Slices are easier to use safely and more convinient
+than many-item pointers, as they store the valid length of the buffer with them.
+Slices are sometimes referred to as "fat pointers" as they're typically double
+the size of a normal pointer. Slices are the most common way to pass around
+buffers in Zig.
+
+:::go
+Slicing in Zig is similar to slicing in Go, but you replace `array[start:end]`
+with `array[start..end]`.
+
+Moreover, in Go, there is no explicit ownership or memory management, meaning
+that slices point to memory owned by the garbage collector. However in Zig,
+slices point to manually-managed memory; slices are not tied to memory
+allocation. This has important implications:
+
+- The validity and lifetime of the backing memory is in the hands of the
+ programmer.
+- Zig slices do not have a Cap field, as they do not resize.
+
+For a resizeable/appendable buffer with ownership, have a look at
+[ArrayList](../02-standard-library/02-arraylist.mdx).
+
+:::
+
+Unlike many-item pointers, `for` loops work on slices.
+
+The syntax `x[n..m]` is used to create a slice from an array, an operation known
+as **slicing**. This creates a "slice" - a view into the array consisting of a
+pointer and a length. Slicing includes the first element (`n`), but excludes the
+last element (`m`).
+
+In this example, a `const` slice is used in the `total` function as it doesn't
+write to the slice's buffer.
+
+{Slices}
+
+When these `n` and `m` values are both known at compile time, slicing will
+actually produce a pointer to an array. This is not an issue as a pointer to an
+array i.e. `*[N]T` will coerce to a slice - `[]T`.
+
+{Slices2}
+
+The syntax `x[n..]` can also be used when you want to slice to the end.
+
+```zig
+test "slices 3" {
+ var array = [_]u8{ 1, 2, 3, 4, 5 };
+ var slice = array[0..];
+ _ = slice;
+}
+```
+
+Let's again compare our pointer types.
+
+| Feature | `*T` | `*[N]T` | `[*]T` | `[]T` |
+| --------------- | ----------------- | ------------------ | -------------------------------- | ------------------- |
+| Dereferenceable | Yes, e.g. `ptr.*` | No | No | No |
+| Indexable | No | Yes | Yes | Yes |
+| Sliceable | No | Yes | Yes | Yes |
+| Element Count | Always 1 | Compile-time known | Unknown | Runtime known |
+| Arithmetic | No | No | Yes, e.g. `ptr + 1` or `ptr - 1` | No |
+| Size | @sizeOf(usize) | @sizeOf(usize) | @sizeOf(usize) | @sizeOf(usize) \* 2 |
+| Attributes | Yes | Yes | Yes | Yes |
+
+:::info
+We can now apply our knowledge and [make another program together](/posts/fahrenheit-to-celsius).
+
+:::
diff --git a/website/versioned_docs/version-0.15/01-language-basics/14.slices-2.zig b/website/versioned_docs/version-0.15/01-language-basics/14.slices-2.zig
new file mode 100644
index 0000000..ec60ec9
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/14.slices-2.zig
@@ -0,0 +1,9 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+test "slices 2" {
+ const array = [_]u8{ 1, 2, 3, 4, 5 };
+ const slice = array[0..3];
+ try expect(@TypeOf(slice) == *const [3]u8);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/14.slices.zig b/website/versioned_docs/version-0.15/01-language-basics/14.slices.zig
new file mode 100644
index 0000000..6fdb74a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/14.slices.zig
@@ -0,0 +1,15 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+fn total(values: []const u8) usize {
+ var sum: usize = 0;
+ for (values) |v| sum += v;
+ return sum;
+}
+
+test "slices" {
+ const array = [_]u8{ 1, 2, 3, 4, 5 };
+ const slice = array[0..3];
+ try expect(total(slice) == 6);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/15-enums.mdx b/website/versioned_docs/version-0.15/01-language-basics/15-enums.mdx
new file mode 100644
index 0000000..f6c606b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/15-enums.mdx
@@ -0,0 +1,43 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import EnumOrdinal from "!!raw-loader!./15.enum-ordinal.zig";
+import EnumOrdinalOverride from "!!raw-loader!./15.enum-ordinal-override.zig";
+import EnumMethods from "!!raw-loader!./15.enum-methods.zig";
+import EnumDeclarations from "!!raw-loader!./15.enum-declarations.zig";
+
+# Enums
+
+Zig's enums allow you to define types with a restricted set of named values.
+
+Let's declare an enum.
+
+```zig
+const Direction = enum { north, south, east, west };
+```
+
+Enums types may have specified (integer) tag types.
+
+```zig
+const Value = enum(u2) { zero, one, two };
+```
+
+Enum's ordinal values start at 0. They can be accessed with the built-in
+function
+[`@intFromEnum`](https://ziglang.org/documentation/master/#intFromEnum).
+
+{EnumOrdinal}
+
+Values can be overridden, with the next values continuing from there.
+
+{EnumOrdinalOverride}
+
+Enums can be given methods. These act as namespaced functions that can be called
+with the dot syntax.
+
+{EnumMethods}
+
+Enums can also be given `var` and `const` declarations. These act as namespaced
+globals and their values are unrelated and unattached to instances of the enum
+type.
+
+{EnumDeclarations}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/15.enum-declarations.zig b/website/versioned_docs/version-0.15/01-language-basics/15.enum-declarations.zig
new file mode 100644
index 0000000..2c08db9
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/15.enum-declarations.zig
@@ -0,0 +1,14 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+const Mode = enum {
+ var count: u32 = 0;
+ on,
+ off,
+};
+
+test "hmm" {
+ Mode.count += 1;
+ try expect(Mode.count == 1);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/15.enum-methods.zig b/website/versioned_docs/version-0.15/01-language-basics/15.enum-methods.zig
new file mode 100644
index 0000000..d267311
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/15.enum-methods.zig
@@ -0,0 +1,17 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+const Suit = enum {
+ clubs,
+ spades,
+ diamonds,
+ hearts,
+ pub fn isClubs(self: Suit) bool {
+ return self == Suit.clubs;
+ }
+};
+
+test "enum method" {
+ try expect(Suit.spades.isClubs() == Suit.isClubs(.spades));
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/15.enum-ordinal-override.zig b/website/versioned_docs/version-0.15/01-language-basics/15.enum-ordinal-override.zig
new file mode 100644
index 0000000..dd1c450
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/15.enum-ordinal-override.zig
@@ -0,0 +1,17 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+// hide-end
+const Value2 = enum(u32) {
+ hundred = 100,
+ thousand = 1000,
+ million = 1000000,
+ next,
+};
+
+test "set enum ordinal value" {
+ try expect(@intFromEnum(Value2.hundred) == 100);
+ try expect(@intFromEnum(Value2.thousand) == 1000);
+ try expect(@intFromEnum(Value2.million) == 1000000);
+ try expect(@intFromEnum(Value2.next) == 1000001);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/15.enum-ordinal.zig b/website/versioned_docs/version-0.15/01-language-basics/15.enum-ordinal.zig
new file mode 100644
index 0000000..8304f65
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/15.enum-ordinal.zig
@@ -0,0 +1,11 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+const Value = enum(u2) { zero, one, two };
+
+// hide-end
+test "enum ordinal value" {
+ try expect(@intFromEnum(Value.zero) == 0);
+ try expect(@intFromEnum(Value.one) == 1);
+ try expect(@intFromEnum(Value.two) == 2);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/16-structs.mdx b/website/versioned_docs/version-0.15/01-language-basics/16-structs.mdx
new file mode 100644
index 0000000..750fa0b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/16-structs.mdx
@@ -0,0 +1,46 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Struct from "!!raw-loader!./16.struct.zig";
+import StructDefaults from "!!raw-loader!./16.struct-defaults.zig";
+import StructDeclarations from "!!raw-loader!./16.struct-declarations.zig";
+
+# Structs
+
+Structs are Zig's most common kind of composite data type, allowing you to
+define types that can store a fixed set of named fields. Zig gives no guarantees
+about the in-memory order of fields in a struct or its size. Like arrays,
+structs are also neatly constructed with `T{}` syntax. Here is an example of
+declaring and filling a struct.
+
+{Struct}
+
+Struct fields cannot be implicitly uninitialised:
+
+```zig
+test "missing struct field" {
+ const my_vector = Vec3{
+ .x = 0,
+ .z = 50,
+ };
+ _ = my_vector;
+}
+```
+
+```
+error: missing field: 'y'
+ const my_vector = Vec3{
+ ^
+```
+
+Fields may be given defaults:
+
+{StructDefaults}
+
+Like enums, structs may also contain functions and declarations.
+
+Structs have the unique property that when given a pointer to a struct, one
+level of dereferencing is done automatically when accessing fields. Notice how,
+in this example, `self.x` and `self.y` are accessed in the swap function without
+needing to dereference the self pointer.
+
+{StructDeclarations}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/16.struct-declarations.zig b/website/versioned_docs/version-0.15/01-language-basics/16.struct-declarations.zig
new file mode 100644
index 0000000..ea0e4a0
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/16.struct-declarations.zig
@@ -0,0 +1,20 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+const Stuff = struct {
+ x: i32,
+ y: i32,
+ fn swap(self: *Stuff) void {
+ const tmp = self.x;
+ self.x = self.y;
+ self.y = tmp;
+ }
+};
+
+test "automatic dereference" {
+ var thing = Stuff{ .x = 10, .y = 20 };
+ thing.swap();
+ try expect(thing.x == 20);
+ try expect(thing.y == 10);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/16.struct-defaults.zig b/website/versioned_docs/version-0.15/01-language-basics/16.struct-defaults.zig
new file mode 100644
index 0000000..4084ecc
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/16.struct-defaults.zig
@@ -0,0 +1,9 @@
+const Vec4 = struct { x: f32 = 0, y: f32 = 0, z: f32 = 0, w: f32 = 0 };
+
+test "struct defaults" {
+ const my_vector = Vec4{
+ .x = 25,
+ .y = -50,
+ };
+ _ = my_vector;
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/16.struct.zig b/website/versioned_docs/version-0.15/01-language-basics/16.struct.zig
new file mode 100644
index 0000000..7746856
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/16.struct.zig
@@ -0,0 +1,10 @@
+const Vec3 = struct { x: f32, y: f32, z: f32 };
+
+test "struct usage" {
+ const my_vector = Vec3{
+ .x = 0,
+ .y = 100,
+ .z = 50,
+ };
+ _ = my_vector;
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/17-unions.mdx b/website/versioned_docs/version-0.15/01-language-basics/17-unions.mdx
new file mode 100644
index 0000000..e55f7a1
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/17-unions.mdx
@@ -0,0 +1,55 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import TaggedUnion from "!!raw-loader!./17.union-tagged.zig";
+
+# Unions
+
+Zig's unions allow you to define types that store one value of many possible
+typed fields; only one field may be active at one time.
+
+Bare union types do not have a guaranteed memory layout. Because of this, bare
+unions cannot be used to reinterpret memory. Accessing a field in a union that
+is not active is detectable illegal behaviour.
+
+```zig
+const Result = union {
+ int: i64,
+ float: f64,
+ bool: bool,
+};
+
+test "simple union" {
+ var result = Result{ .int = 1234 };
+ result.float = 12.34;
+}
+```
+
+```
+test "simple union"...access of inactive union field
+.\tests.zig:342:12: 0x7ff62c89244a in test "simple union" (test.obj)
+ result.float = 12.34;
+ ^
+```
+
+Tagged unions are unions that use an enum to detect which field is active. Here
+we make use of payload capturing again, to switch on the tag type of a union
+while also capturing the value it contains. Here we use a _pointer capture_;
+captured values are immutable, but with the `|*value|` syntax, we can capture a
+pointer to the values instead of the values themselves. This allows us to use
+dereferencing to mutate the original value.
+
+{TaggedUnion}
+
+The tag type of a tagged union can also be inferred. This is equivalent to the
+Tagged type above.
+
+```zig
+const Tagged = union(enum) { a: u8, b: f32, c: bool };
+```
+
+`void` member types can have their type omitted from the syntax. Here, none is
+of type `void`.
+
+```zig
+const Tagged2 = union(enum) { a: u8, b: f32, c: bool, none };
+```
diff --git a/website/versioned_docs/version-0.15/01-language-basics/17.union-tagged.zig b/website/versioned_docs/version-0.15/01-language-basics/17.union-tagged.zig
new file mode 100644
index 0000000..ea37e93
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/17.union-tagged.zig
@@ -0,0 +1,17 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+const Tag = enum { a, b, c };
+
+const Tagged = union(Tag) { a: u8, b: f32, c: bool };
+
+test "switch on tagged union" {
+ var value = Tagged{ .b = 1.5 };
+ switch (value) {
+ .a => |*byte| byte.* += 1,
+ .b => |*float| float.* *= 2,
+ .c => |*b| b.* = !b.*,
+ }
+ try expect(value.b == 3);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/18-integer-rules.mdx b/website/versioned_docs/version-0.15/01-language-basics/18-integer-rules.mdx
new file mode 100644
index 0000000..f44e84b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/18-integer-rules.mdx
@@ -0,0 +1,55 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import IntegerWidening from "!!raw-loader!./18.integer-widening.zig";
+import IntegerCasting from "!!raw-loader!./18.integer-casting.zig";
+import IntegerSafeOverflow from "!!raw-loader!./18.integer-safe-overflow.zig";
+
+# Integer Rules
+
+Zig supports hex, octal and binary integer literals.
+
+```zig
+const decimal_int: i32 = 98222;
+const hex_int: u8 = 0xff;
+const another_hex_int: u8 = 0xFF;
+const octal_int: u16 = 0o755;
+const binary_int: u8 = 0b11110000;
+```
+
+Underscores may also be placed between digits as a visual separator.
+
+```zig
+const one_billion: u64 = 1_000_000_000;
+const binary_mask: u64 = 0b1_1111_1111;
+const permissions: u64 = 0o7_5_5;
+const big_address: u64 = 0xFF80_0000_0000_0000;
+```
+
+"Integer Widening" is allowed, which means that integers of a type may coerce to
+an integer of another type, providing that the new type can fit all of the
+values that the old type can.
+
+{IntegerWidening}
+
+If you have a value stored in an integer that cannot coerce to the type that you
+want, [`@intCast`](https://ziglang.org/documentation/master/#intCast) may be
+used to explicitly convert from one type to the other. If the value given is out
+of the range of the destination type, this is detectable illegal behaviour.
+
+{IntegerCasting}
+
+Integers, by default, are not allowed to overflow. Overflows are detectable
+illegal behaviour. Sometimes, being able to overflow integers in a well-defined
+manner is a wanted behaviour. For this use case, Zig provides overflow
+operators.
+
+| Normal Operator | Wrapping Operator |
+| --------------- | ----------------- |
+| + | +% |
+| - | -% |
+| \* | \*% |
+| += | +%= |
+| -= | -%= |
+| \*= | \*%= |
+
+{IntegerSafeOverflow}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/18.integer-casting.zig b/website/versioned_docs/version-0.15/01-language-basics/18.integer-casting.zig
new file mode 100644
index 0000000..3ff5c71
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/18.integer-casting.zig
@@ -0,0 +1,9 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "@intCast" {
+ const x: u64 = 200;
+ const y = @as(u8, @intCast(x));
+ try expect(@TypeOf(y) == u8);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/18.integer-safe-overflow.zig b/website/versioned_docs/version-0.15/01-language-basics/18.integer-safe-overflow.zig
new file mode 100644
index 0000000..4d8ab3e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/18.integer-safe-overflow.zig
@@ -0,0 +1,9 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "well defined overflow" {
+ var a: u8 = 255;
+ a +%= 1;
+ try expect(a == 0);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/18.integer-widening.zig b/website/versioned_docs/version-0.15/01-language-basics/18.integer-widening.zig
new file mode 100644
index 0000000..86a7377
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/18.integer-widening.zig
@@ -0,0 +1,10 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "integer widening" {
+ const a: u8 = 250;
+ const b: u16 = a;
+ const c: u32 = b;
+ try expect(c == a);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/19-floats.mdx b/website/versioned_docs/version-0.15/01-language-basics/19-floats.mdx
new file mode 100644
index 0000000..a7e9655
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/19-floats.mdx
@@ -0,0 +1,34 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import FloatsWidening from "!!raw-loader!./19.floats-widening.zig";
+import FloatsLiterals from "!!raw-loader!./19.floats-literals.zig";
+import FloatsHexUnderscores from "!!raw-loader!./19.floats-hex-underscores.zig";
+import FloatsConversion from "!!raw-loader!./19.floats-conversion.zig";
+
+# Floats
+
+Zig's floats are strictly IEEE-compliant unless
+[`@setFloatMode(.Optimized)`](https://ziglang.org/documentation/master/#setFloatMode)
+is used, which is equivalent to GCC's `-ffast-math`. Floats coerce to larger
+float types.
+
+{FloatsWidening}
+
+Floats support multiple kinds of literal.
+
+{FloatsLiterals}
+
+Underscores may also be placed between digits.
+
+{FloatsHexUnderscores}
+
+Integers and floats may be converted using the built-in functions
+[`@floatFromInt`](https://ziglang.org/documentation/0.12.0/#floatFromInt) and
+[`@intFromFloat`](https://ziglang.org/documentation/0.12.0/#intFromFloat).
+[`@floatFromInt`](https://ziglang.org/documentation/0.12.0/#floatFromInt) is
+always safe, whereas
+[`@intFromFloat`](https://ziglang.org/documentation/0.12.0/#intFromFloat) is
+detectable illegal behaviour if the float value cannot fit in the integer
+destination type.
+
+{FloatsConversion}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/19.floats-conversion.zig b/website/versioned_docs/version-0.15/01-language-basics/19.floats-conversion.zig
new file mode 100644
index 0000000..d264b92
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/19.floats-conversion.zig
@@ -0,0 +1,10 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "int-float conversion" {
+ const a: i32 = 0;
+ const b = @as(f32, @floatFromInt(a));
+ const c = @as(i32, @intFromFloat(b));
+ try expect(c == a);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/19.floats-hex-underscores.zig b/website/versioned_docs/version-0.15/01-language-basics/19.floats-hex-underscores.zig
new file mode 100644
index 0000000..c5baf08
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/19.floats-hex-underscores.zig
@@ -0,0 +1,3 @@
+const lightspeed: f64 = 299_792_458.000_000;
+const nanosecond: f64 = 0.000_000_001;
+const more_hex: f64 = 0x1234_5678.9ABC_CDEFp-10;
diff --git a/website/versioned_docs/version-0.15/01-language-basics/19.floats-literals.zig b/website/versioned_docs/version-0.15/01-language-basics/19.floats-literals.zig
new file mode 100644
index 0000000..1e86653
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/19.floats-literals.zig
@@ -0,0 +1,7 @@
+const floating_point: f64 = 123.0E+77;
+const another_float: f64 = 123.0;
+const yet_another: f64 = 123.0e+77;
+
+const hex_floating_point: f64 = 0x103.70p-5;
+const another_hex_float: f64 = 0x103.70;
+const yet_another_hex_float: f64 = 0x103.70P-5;
diff --git a/website/versioned_docs/version-0.15/01-language-basics/19.floats-widening.zig b/website/versioned_docs/version-0.15/01-language-basics/19.floats-widening.zig
new file mode 100644
index 0000000..033d2bd
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/19.floats-widening.zig
@@ -0,0 +1,10 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "float widening" {
+ const a: f16 = 0;
+ const b: f32 = a;
+ const c: f128 = b;
+ try expect(c == @as(f128, a));
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/20-labelled-blocks.md b/website/versioned_docs/version-0.15/01-language-basics/20-labelled-blocks.md
new file mode 100644
index 0000000..272ef6d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/20-labelled-blocks.md
@@ -0,0 +1,31 @@
+# Labelled Blocks
+
+Blocks in Zig are expressions and can be given labels, which are used to yield
+values. Here, we are using a label called `blk`. Blocks yield values, meaning
+they can be used in place of a value. The value of an empty block `{}` is a
+value of the type `void`.
+
+```zig
+test "labelled blocks" {
+ const count = blk: {
+ var sum: u32 = 0;
+ var i: u32 = 0;
+ while (i < 10) : (i += 1) sum += i;
+ break :blk sum;
+ };
+ try expect(count == 45);
+ try expect(@TypeOf(count) == u32);
+}
+```
+
+This can be seen as being equivalent to C's `i++`.
+
+
+
+```zig
+blk: {
+ const tmp = i;
+ i += 1;
+ break :blk tmp;
+}
+```
diff --git a/website/versioned_docs/version-0.15/01-language-basics/21-labelled-loops.md b/website/versioned_docs/version-0.15/01-language-basics/21-labelled-loops.md
new file mode 100644
index 0000000..408893b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/21-labelled-loops.md
@@ -0,0 +1,17 @@
+# Labelled Loops
+
+Loops can be given labels, allowing you to `break` and `continue` to outer
+loops.
+
+```zig
+test "nested continue" {
+ var count: usize = 0;
+ outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| {
+ for ([_]i32{ 1, 2, 3, 4, 5 }) |_| {
+ count += 1;
+ continue :outer;
+ }
+ }
+ try expect(count == 8);
+}
+```
diff --git a/website/versioned_docs/version-0.15/01-language-basics/22-loops-as-expressions.md b/website/versioned_docs/version-0.15/01-language-basics/22-loops-as-expressions.md
new file mode 100644
index 0000000..5c2732c
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/22-loops-as-expressions.md
@@ -0,0 +1,20 @@
+# Loops as Expressions
+
+Like `return`, `break` accepts a value. This can be used to yield a value from a
+loop. Loops in Zig also have an `else` branch, which is evaluated when the loop
+is not exited with a `break`.
+
+```zig
+fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
+ var i = begin;
+ return while (i < end) : (i += 1) {
+ if (i == number) {
+ break true;
+ }
+ } else false;
+}
+
+test "while loop expression" {
+ try expect(rangeHasNumber(0, 10, 3));
+}
+```
diff --git a/website/versioned_docs/version-0.15/01-language-basics/23-optionals.mdx b/website/versioned_docs/version-0.15/01-language-basics/23-optionals.mdx
new file mode 100644
index 0000000..73f51c3
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/23-optionals.mdx
@@ -0,0 +1,51 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import OptionalsFind from "!!raw-loader!./23.optionals-find.zig";
+import OptionalsOrelse from "!!raw-loader!./23.optionals-orelse.zig";
+import OptionalsOrelseUnreachable from "!!raw-loader!./23.optionals-orelse-unreachable.zig";
+import OptionalsIfPayload from "!!raw-loader!./23.optionals-if-payload.zig";
+import OptionalsWhileCapture from "!!raw-loader!./23.optionals-while-capture.zig";
+
+# Optionals
+
+Optionals use the syntax `?T` and are used to store the data
+[`null`](https://ziglang.org/documentation/master/#null), or a value of type
+`T`.
+
+{OptionalsFind}
+
+Optionals support the `orelse` expression, which acts when the optional is
+[`null`](https://ziglang.org/documentation/master/#null). This unwraps the
+optional to its child type.
+
+{OptionalsOrelse}
+
+`.?` is a shorthand for `orelse unreachable`. This is used for when you know it
+is impossible for an optional value to be null, and using this to unwrap a
+[`null`](https://ziglang.org/documentation/master/#null) value is detectable
+illegal behaviour.
+
+{OptionalsOrelseUnreachable}
+
+Both `if` expressions and `while` loops support taking optional values as conditions,
+allowing you to "capture" the inner non-null value.
+
+Here we use an `if` optional payload capture; a and b are equivalent here.
+`if (b) |value|` captures the value of `b` (in the cases where `b` is not null),
+and makes it available as `value`. As in the union example, the captured value
+is immutable, but we can still use a pointer capture to modify the value stored
+in `b`.
+
+{OptionalsIfPayload}
+
+And with `while`:
+
+{OptionalsWhileCapture}
+
+Optional pointer and optional slice types do not take up any extra memory
+compared to non-optional ones. This is because internally they use the 0 value
+of the pointer for `null`.
+
+This is how null pointers in Zig work - they must be unwrapped to a non-optional
+before dereferencing, which stops null pointer dereferences from happening
+accidentally.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/23.optionals-find.zig b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-find.zig
new file mode 100644
index 0000000..c88f073
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-find.zig
@@ -0,0 +1,12 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "optional" {
+ var found_index: ?usize = null;
+ const data = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 12 };
+ for (data, 0..) |v, i| {
+ if (v == 10) found_index = i;
+ }
+ try expect(found_index == null);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/23.optionals-if-payload.zig b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-if-payload.zig
new file mode 100644
index 0000000..45f461a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-if-payload.zig
@@ -0,0 +1,17 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "if optional payload capture" {
+ const a: ?i32 = 5;
+ if (a != null) {
+ const value = a.?;
+ _ = value;
+ }
+
+ var b: ?i32 = 5;
+ if (b) |*value| {
+ value.* += 1;
+ }
+ try expect(b.? == 6);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/23.optionals-orelse-unreachable.zig b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-orelse-unreachable.zig
new file mode 100644
index 0000000..5a115e8
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-orelse-unreachable.zig
@@ -0,0 +1,11 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "orelse unreachable" {
+ const a: ?f32 = 5;
+ const b = a orelse unreachable;
+ const c = a.?;
+ try expect(b == c);
+ try expect(@TypeOf(c) == f32);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/23.optionals-orelse.zig b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-orelse.zig
new file mode 100644
index 0000000..ad17b3d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-orelse.zig
@@ -0,0 +1,11 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "orelse" {
+ const a: ?f32 = null;
+ const fallback_value: f32 = 0;
+ const b = a orelse fallback_value;
+ try expect(b == 0);
+ try expect(@TypeOf(b) == f32);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/23.optionals-while-capture.zig b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-while-capture.zig
new file mode 100644
index 0000000..5e92b71
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/23.optionals-while-capture.zig
@@ -0,0 +1,18 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+var numbers_left: u32 = 4;
+fn eventuallyNullSequence() ?u32 {
+ if (numbers_left == 0) return null;
+ numbers_left -= 1;
+ return numbers_left;
+}
+
+test "while null capture" {
+ var sum: u32 = 0;
+ while (eventuallyNullSequence()) |value| {
+ sum += value;
+ }
+ try expect(sum == 6); // 3 + 2 + 1
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24-comptime.mdx b/website/versioned_docs/version-0.15/01-language-basics/24-comptime.mdx
new file mode 100644
index 0000000..4f6fa7b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24-comptime.mdx
@@ -0,0 +1,84 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import ComptimeBlocks from "!!raw-loader!./24.comptime-blocks.zig";
+import ComptimeInts from "!!raw-loader!./24.comptime-ints.zig";
+import ComptimeTypeBranching from "!!raw-loader!./24.comptime-type-branching.zig";
+import ComptimeReturningType from "!!raw-loader!./24.comptime-returning-type.zig";
+import ComptimeTypeinfo from "!!raw-loader!./24.comptime-typeinfo.zig";
+import ComptimeType from "!!raw-loader!./24.comptime-type.zig";
+import ComptimeReturnStruct from "!!raw-loader!./24.comptime-return-struct.zig";
+import ComptimeAnytype from "!!raw-loader!./24.comptime-anytype.zig";
+import ComptimeConcatRepeat from "!!raw-loader!./24.comptime-concat-repeat.zig";
+
+# Comptime
+
+Blocks of code may be forcibly executed at compile time using the
+[`comptime`](https://ziglang.org/documentation/master/#comptime) keyword. In
+this example, the variables x and y are equivalent.
+
+{ComptimeBlocks}
+
+Integer literals are of the type `comptime_int`. These are special in a way that
+they have no size (they cannot be used at runtime!), and they have arbitrary
+precision. `comptime_int` values coerce to any integer type that can hold them.
+They also coerce to floats. Character literals are of this type.
+
+{ComptimeInts}
+
+`comptime_float` is also available, which internally is an `f128`. These cannot
+be coerced to integers, even if they hold an integer value.
+
+Types in Zig are values of the type `type`. These are available at compile time.
+We have previously encountered them by checking
+[`@TypeOf`](https://ziglang.org/documentation/master/#TypeOf) and comparing with
+other types, but we can do more.
+
+{ComptimeTypeBranching}
+
+Function parameters in Zig can be tagged as being
+[`comptime`](https://ziglang.org/documentation/master/#comptime). This means
+that the value passed to that function parameter must be known at compile time.
+Let's make a function that returns a type. Notice how this function is
+PascalCase, as it returns a type.
+
+{ComptimeReturningType}
+
+We can reflect upon types using the built-in
+[`@typeInfo`](https://ziglang.org/documentation/master/#typeInfo), which takes
+in a `type` and returns a tagged union. This tagged union type can be found in
+[`std.builtin.Type`](https://ziglang.org/documentation/master/std/#std.builtin.Type)
+(info on how to make use of imports and std later).
+
+{ComptimeTypeinfo}
+
+We can use the [`@Type`](https://ziglang.org/documentation/master/#Type)
+function to create a type from a
+[`@typeInfo`](https://ziglang.org/documentation/master/#typeInfo).
+[`@Type`](https://ziglang.org/documentation/master/#Type) is implemented for
+most types but is notably unimplemented for enums, unions, functions, and
+structs.
+
+Here anonymous struct syntax is used with `.{}`, because the `T` in `T{}` can be
+inferred. Anonymous structs will be covered in detail later. In this example we
+will get a compile error if the `Int` tag isn't set.
+
+{ComptimeType}
+
+Returning a struct type is how you make generic data structures in Zig. The
+usage of [`@This`](https://ziglang.org/documentation/master/#This) is required
+here, which gets the type of the innermost struct, union, or enum. Here
+[`std.mem.eql`](https://ziglang.org/documentation/master/std/#std;mem.eql) is
+also used which compares two slices.
+
+{ComptimeReturnStruct}
+
+The types of function parameters can also be inferred by using `anytype` in
+place of a type. [`@TypeOf`](https://ziglang.org/documentation/master/#TypeOf)
+can then be used on the parameter.
+
+{ComptimeAnytype}
+
+Comptime also introduces the operators `++` and `**` for concatenating and
+repeating arrays and slices. These operators do not work at runtime.
+
+{ComptimeConcatRepeat}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-anytype.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-anytype.zig
new file mode 100644
index 0000000..b4ca46e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-anytype.zig
@@ -0,0 +1,11 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+fn plusOne(x: anytype) @TypeOf(x) {
+ return x + 1;
+}
+
+test "inferred function parameter" {
+ try expect(plusOne(@as(u32, 1)) == 2);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-blocks.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-blocks.zig
new file mode 100644
index 0000000..814b9ab
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-blocks.zig
@@ -0,0 +1,17 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+fn fibonacci(n: u16) u16 {
+ if (n == 0 or n == 1) return n;
+ return fibonacci(n - 1) + fibonacci(n - 2);
+}
+
+test "comptime blocks" {
+ const x = comptime fibonacci(10);
+ const y = comptime blk: {
+ break :blk fibonacci(10);
+ };
+ try expect(y == 55);
+ try expect(x == 55);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-concat-repeat.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-concat-repeat.zig
new file mode 100644
index 0000000..f5f1498
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-concat-repeat.zig
@@ -0,0 +1,20 @@
+// hide-start
+const expect = @import("std").testing.expect;
+const eql = @import("std").mem.eql;
+//hide-end
+test "++" {
+ const x: [4]u8 = undefined;
+ const y = x[0..];
+
+ const a: [6]u8 = undefined;
+ const b = a[0..];
+
+ const new = y ++ b;
+ try expect(new.len == 10);
+}
+
+test "**" {
+ const pattern = [_]u8{ 0xCC, 0xAA };
+ const memory = pattern ** 3;
+ try expect(eql(u8, &memory, &[_]u8{ 0xCC, 0xAA, 0xCC, 0xAA, 0xCC, 0xAA }));
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-ints.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-ints.zig
new file mode 100644
index 0000000..541d083
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-ints.zig
@@ -0,0 +1,14 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "comptime_int" {
+ const a = 12;
+ const b = a + 10;
+
+ const c: u4 = a;
+ const d: f32 = b;
+
+ try expect(c == 12);
+ try expect(d == 22);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-return-struct.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-return-struct.zig
new file mode 100644
index 0000000..f0c5ba1
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-return-struct.zig
@@ -0,0 +1,36 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+fn Vec(
+ comptime count: comptime_int,
+ comptime T: type,
+) type {
+ return struct {
+ data: [count]T,
+ const Self = @This();
+
+ fn abs(self: Self) Self {
+ var tmp = Self{ .data = undefined };
+ for (self.data, 0..) |elem, i| {
+ tmp.data[i] = if (elem < 0)
+ -elem
+ else
+ elem;
+ }
+ return tmp;
+ }
+
+ fn init(data: [count]T) Self {
+ return Self{ .data = data };
+ }
+ };
+}
+
+const eql = @import("std").mem.eql;
+
+test "generic vector" {
+ const x = Vec(3, f32).init([_]f32{ 10, -10, 5 });
+ const y = x.abs();
+ try expect(eql(f32, &y.data, &[_]f32{ 10, 10, 5 }));
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-returning-type.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-returning-type.zig
new file mode 100644
index 0000000..2839c70
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-returning-type.zig
@@ -0,0 +1,15 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+fn Matrix(
+ comptime T: type,
+ comptime width: comptime_int,
+ comptime height: comptime_int,
+) type {
+ return [height][width]T;
+}
+
+test "returning a type" {
+ try expect(Matrix(f32, 4, 4) == [4][4]f32);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-type-branching.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-type-branching.zig
new file mode 100644
index 0000000..3f0d968
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-type-branching.zig
@@ -0,0 +1,10 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "branching on types" {
+ const a = 5;
+ const b: if (a < 10) f32 else i32 = 5;
+ try expect(b == 5);
+ try expect(@TypeOf(b) == f32);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-type.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-type.zig
new file mode 100644
index 0000000..900df4d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-type.zig
@@ -0,0 +1,17 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+fn GetBiggerInt(comptime T: type) type {
+ return @Type(.{
+ .int = .{
+ .bits = @typeInfo(T).int.bits + 1,
+ .signedness = @typeInfo(T).int.signedness,
+ },
+ });
+}
+
+test "@Type" {
+ try expect(GetBiggerInt(u8) == u9);
+ try expect(GetBiggerInt(i31) == i32);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/24.comptime-typeinfo.zig b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-typeinfo.zig
new file mode 100644
index 0000000..1616b99
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/24.comptime-typeinfo.zig
@@ -0,0 +1,20 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+fn addSmallInts(comptime T: type, a: T, b: T) T {
+ return switch (@typeInfo(T)) {
+ .comptime_int => a + b,
+ .int => |info| if (info.bits <= 16)
+ a + b
+ else
+ @compileError("ints too large"),
+ else => @compileError("only ints accepted"),
+ };
+}
+
+test "typeinfo switch" {
+ const x = addSmallInts(u16, 20, 30);
+ try expect(@TypeOf(x) == u16);
+ try expect(x == 50);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25-payload-captures.mdx b/website/versioned_docs/version-0.15/01-language-basics/25-payload-captures.mdx
new file mode 100644
index 0000000..3545940
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25-payload-captures.mdx
@@ -0,0 +1,49 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import PayloadCapturesOptionalIf from "!!raw-loader!./25.payload-captures-optional-if.zig";
+import PayloadCapturesErrorUnionIf from "!!raw-loader!./25.payload-captures-error-union-if.zig";
+import PayloadCapturesWhileOptional from "!!raw-loader!./25.payload-captures-while-optional.zig";
+import PayloadCapturesWhileErrorUnion from "!!raw-loader!./25.payload-captures-while-error-union.zig";
+import PayloadCapturesForCapture from "!!raw-loader!./25.payload-captures-for-capture.zig";
+import PayloadCapturesSwitchCapture from "!!raw-loader!./25.payload-captures-switch-capture.zig";
+import PayloadCapturesForPointerCapture from "!!raw-loader!./25.payload-captures-for-pointer-capture.zig";
+
+# Payload Captures
+
+Payload captures use the syntax `|value|` and appear in many places, some of
+which we've seen already. Wherever they appear, they are used to "capture" the
+value from something.
+
+With if statements and optionals.
+
+{PayloadCapturesOptionalIf}
+
+With if statements and error unions. The else with the error capture is required
+here.
+
+{PayloadCapturesErrorUnionIf}
+
+With while loops and optionals. This may have an else block.
+
+{PayloadCapturesWhileOptional}
+
+With while loops and error unions. The else with the error capture is required
+here.
+
+{PayloadCapturesWhileErrorUnion}
+
+For loops.
+
+{PayloadCapturesForCapture}
+
+Switch cases on tagged unions.
+
+{PayloadCapturesSwitchCapture}
+
+As we saw in the Union and Optional sections above, values captured with the
+`|val|` syntax are immutable (similar to function arguments), but we can use
+pointer capture to modify the original values. This captures the values as
+pointers that are themselves still immutable, but because the value is now a
+pointer, we can modify the original value by dereferencing it:
+
+{PayloadCapturesForPointerCapture}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-error-union-if.zig b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-error-union-if.zig
new file mode 100644
index 0000000..864b814
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-error-union-if.zig
@@ -0,0 +1,14 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "error union if" {
+ const ent_num: error{UnknownEntity}!u32 = 5;
+ if (ent_num) |entity| {
+ try expect(@TypeOf(entity) == u32);
+ try expect(entity == 5);
+ } else |err| {
+ _ = err catch {};
+ unreachable;
+ }
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-for-capture.zig b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-for-capture.zig
new file mode 100644
index 0000000..7c9183c
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-for-capture.zig
@@ -0,0 +1,8 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "for capture" {
+ const x = [_]i8{ 1, 5, 120, -5 };
+ for (x) |v| try expect(@TypeOf(v) == i8);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-for-pointer-capture.zig b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-for-pointer-capture.zig
new file mode 100644
index 0000000..caeb37b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-for-pointer-capture.zig
@@ -0,0 +1,10 @@
+// hide-start
+const expect = @import("std").testing.expect;
+const eql = @import("std").mem.eql;
+
+//hide-end
+test "for with pointer capture" {
+ var data = [_]u8{ 1, 2, 3 };
+ for (&data) |*byte| byte.* += 1;
+ try expect(eql(u8, &data, &[_]u8{ 2, 3, 4 }));
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-optional-if.zig b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-optional-if.zig
new file mode 100644
index 0000000..af74746
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-optional-if.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "optional-if" {
+ const maybe_num: ?usize = 10;
+ if (maybe_num) |n| {
+ try expect(@TypeOf(n) == usize);
+ try expect(n == 10);
+ } else {
+ unreachable;
+ }
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-switch-capture.zig b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-switch-capture.zig
new file mode 100644
index 0000000..9e29ad4
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-switch-capture.zig
@@ -0,0 +1,28 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+const Info = union(enum) {
+ a: u32,
+ b: []const u8,
+ c,
+ d: u32,
+};
+
+test "switch capture" {
+ const b = Info{ .a = 10 };
+ const x = switch (b) {
+ .b => |str| blk: {
+ try expect(@TypeOf(str) == []const u8);
+ break :blk 1;
+ },
+ .c => 2,
+ //if these are of the same type, they
+ //may be inside the same capture group
+ .a, .d => |num| blk: {
+ try expect(@TypeOf(num) == u32);
+ break :blk num * 2;
+ },
+ };
+ try expect(x == 20);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-while-error-union.zig b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-while-error-union.zig
new file mode 100644
index 0000000..915e0bb
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-while-error-union.zig
@@ -0,0 +1,22 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+var numbers_left2: u32 = undefined;
+
+fn eventuallyErrorSequence() !u32 {
+ return if (numbers_left2 == 0) error.ReachedZero else blk: {
+ numbers_left2 -= 1;
+ break :blk numbers_left2;
+ };
+}
+
+test "while error union capture" {
+ var sum: u32 = 0;
+ numbers_left2 = 3;
+ while (eventuallyErrorSequence()) |value| {
+ sum += value;
+ } else |err| {
+ try expect(err == error.ReachedZero);
+ }
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-while-optional.zig b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-while-optional.zig
new file mode 100644
index 0000000..1735b21
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/25.payload-captures-while-optional.zig
@@ -0,0 +1,15 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "while optional" {
+ const sequence = [_]?u8{ 0xFF, 0xCC, 0x00, null };
+
+ var i: usize = 0;
+ while (sequence[i]) |num| : (i += 1) {
+ try expect(@TypeOf(num) == u8);
+ }
+
+ try expect(i == 3);
+ try expect(sequence[i] == null);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/26-inline-loops.md b/website/versioned_docs/version-0.15/01-language-basics/26-inline-loops.md
new file mode 100644
index 0000000..0afb9f2
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/26-inline-loops.md
@@ -0,0 +1,20 @@
+# Inline Loops
+
+`inline` loops are unrolled, and allow some things to happen that only work at
+compile time. Here we use a
+[`for`](https://ziglang.org/documentation/master/#inline-for), but a
+[`while`](https://ziglang.org/documentation/master/#inline-while) works
+similarly.
+
+```zig
+test "inline for" {
+ const types = [_]type{ i32, f32, u8, bool };
+ var sum: usize = 0;
+ inline for (types) |T| sum += @sizeOf(T);
+ try expect(sum == 10);
+}
+```
+
+Using these for performance reasons is inadvisable unless you've tested that
+explicitly unrolling is faster; the compiler tends to make better decisions here
+than you.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/27-opaque.md b/website/versioned_docs/version-0.15/01-language-basics/27-opaque.md
new file mode 100644
index 0000000..a3b0dd0
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/27-opaque.md
@@ -0,0 +1,55 @@
+# Opaque
+
+[`opaque`](https://ziglang.org/documentation/master/#opaque) types in Zig have
+an unknown (albeit non-zero) size and alignment. Because of this these data
+types cannot be stored directly. These are used to maintain type safety with
+pointers to types that we don't have information about.
+
+
+
+```zig
+const Window = opaque {};
+const Button = opaque {};
+
+extern fn show_window(*Window) callconv(.C) void;
+
+test "opaque" {
+ var main_window: *Window = undefined;
+ show_window(main_window);
+
+ var ok_button: *Button = undefined;
+ show_window(ok_button);
+}
+```
+
+```
+./test-c1.zig:653:17: error: expected type '*Window', found '*Button'
+ show_window(ok_button);
+ ^
+./test-c1.zig:653:17: note: pointer type child 'Button' cannot cast into pointer type child 'Window'
+ show_window(ok_button);
+ ^
+```
+
+Opaque types may have declarations in their definitions (the same as structs,
+enums and unions).
+
+
+
+```zig
+const Window = opaque {
+ fn show(self: *Window) void {
+ show_window(self);
+ }
+};
+
+extern fn show_window(*Window) callconv(.C) void;
+
+test "opaque with declarations" {
+ var main_window: *Window = undefined;
+ main_window.show();
+}
+```
+
+The typical usecase of opaque is to maintain type safety when interoperating
+with C code that does not expose complete type information.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/28-anonymous-structs.mdx b/website/versioned_docs/version-0.15/01-language-basics/28-anonymous-structs.mdx
new file mode 100644
index 0000000..a26cb6d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/28-anonymous-structs.mdx
@@ -0,0 +1,31 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import AnonymousStructsLiteral from "!!raw-loader!./28.anonymous-structs-literal.zig";
+import AnonymousStructsFullyAnonymous from "!!raw-loader!./28.anonymous-structs-fully-anonymous.zig";
+import AnonymousStructsTuple from "!!raw-loader!./28.anonymous-structs-tuple.zig";
+
+# Anonymous Structs
+
+The struct type may be omitted from a struct literal. These literals may coerce
+to other struct types.
+
+{AnonymousStructsLiteral}
+
+Anonymous structs may be completely anonymous i.e. without being coerced to
+another struct type.
+
+{AnonymousStructsFullyAnonymous}
+
+{/* TODO: mention tuple slicing when it's implemented */}
+
+Anonymous structs without field names may be created and are referred to as
+**tuples**. These have many of the properties that arrays do; tuples can be
+iterated over, indexed, can be used with the `++` and `**` operators, and have a
+len field. Internally, these have numbered field names starting at `"0"`, which
+may be accessed with the special syntax `@"0"` which acts as an escape for the
+syntax - things inside `@""` are always recognised as identifiers.
+
+An `inline` loop must be used to iterate over the tuple here, as the type of
+each tuple field may differ.
+
+{AnonymousStructsTuple}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-fully-anonymous.zig b/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-fully-anonymous.zig
new file mode 100644
index 0000000..a34c480
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-fully-anonymous.zig
@@ -0,0 +1,20 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "fully anonymous struct" {
+ try dump(.{
+ .int = @as(u32, 1234),
+ .float = @as(f64, 12.34),
+ .b = true,
+ .s = "hi",
+ });
+}
+
+fn dump(args: anytype) !void {
+ try expect(args.int == 1234);
+ try expect(args.float == 12.34);
+ try expect(args.b);
+ try expect(args.s[0] == 'h');
+ try expect(args.s[1] == 'i');
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-literal.zig b/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-literal.zig
new file mode 100644
index 0000000..2575cab
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-literal.zig
@@ -0,0 +1,14 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "anonymous struct literal" {
+ const Point = struct { x: i32, y: i32 };
+
+ const pt: Point = .{
+ .x = 13,
+ .y = 67,
+ };
+ try expect(pt.x == 13);
+ try expect(pt.y == 67);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-tuple.zig b/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-tuple.zig
new file mode 100644
index 0000000..5473797
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/28.anonymous-structs-tuple.zig
@@ -0,0 +1,20 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "tuple" {
+ const values = .{
+ @as(u32, 1234),
+ @as(f64, 12.34),
+ true,
+ "hi",
+ } ++ .{false} ** 2;
+ try expect(values[0] == 1234);
+ try expect(values[4] == false);
+ inline for (values, 0..) |v, i| {
+ if (i != 2) continue;
+ try expect(v);
+ }
+ try expect(values.len == 6);
+ try expect(values.@"3"[0] == 'h');
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/29-sentinel-termination.mdx b/website/versioned_docs/version-0.15/01-language-basics/29-sentinel-termination.mdx
new file mode 100644
index 0000000..92f1c0c
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/29-sentinel-termination.mdx
@@ -0,0 +1,41 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import SentinelTerminationPtrcast from "!!raw-loader!./29.sentinel-termination-ptrcast.zig";
+import SentinelTerminationStringLiteral from "!!raw-loader!./29.sentinel-termination-string-literal.zig";
+import SentinelTerminationCString from "!!raw-loader!./29.sentinel-termination-c-string.zig";
+import SentinelTerminationCoercion from "!!raw-loader!./29.sentinel-termination-coercion.zig";
+import SentinelTerminationSlicing from "!!raw-loader!./29.sentinel-termination-slicing.zig";
+
+# Sentinel Termination
+
+Arrays, slices and many pointers may be terminated by a value of their child
+type. This is known as sentinel termination. These follow the syntax `[N:t]T`,
+`[:t]T`, and `[*:t]T`, where `t` is a value of the child type `T`.
+
+An example of a sentinel terminated array. The built-in
+[`@ptrCast`](https://ziglang.org/documentation/master/#ptrCast) is used to
+perform an unsafe type conversion. This shows us that the last element of the
+array is followed by a 0 byte.
+
+{SentinelTerminationPtrcast}
+
+The types of string literals is `*const [N:0]u8`, where N is the length of the
+string. This allows string literals to coerce to sentinel terminated slices, and
+sentinel terminated many pointers. Note: string literals are UTF-8 encoded.
+
+{SentinelTerminationStringLiteral}
+
+`[*:0]u8` and `[*:0]const u8` perfectly model C's strings.
+
+{SentinelTerminationCString}
+
+Sentinel terminated types coerce to their non-sentinel-terminated counterparts.
+
+{SentinelTerminationCoercion}
+
+Sentinel terminated slicing is provided which can be used to create a sentinel
+terminated slice with the syntax `x[n..m:t]`, where `t` is the terminator value.
+Doing this is an assertion from the programmer that the memory is terminated
+where it should be - getting this wrong is detectable illegal behaviour.
+
+{SentinelTerminationSlicing}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-c-string.zig b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-c-string.zig
new file mode 100644
index 0000000..84223dc
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-c-string.zig
@@ -0,0 +1,13 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "C string" {
+ const c_string: [*:0]const u8 = "hello";
+ var array: [5]u8 = undefined;
+
+ var i: usize = 0;
+ while (c_string[i] != 0) : (i += 1) {
+ array[i] = c_string[i];
+ }
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-coercion.zig b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-coercion.zig
new file mode 100644
index 0000000..cae8285
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-coercion.zig
@@ -0,0 +1,16 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "coercion" {
+ const a: [*:0]u8 = undefined;
+ const b: [*]u8 = a;
+
+ const c: [5:0]u8 = undefined;
+ const d: [5]u8 = c;
+
+ const e: [:0]f32 = undefined;
+ const f: []f32 = e;
+
+ _ = .{ b, d, f }; //ignore unused
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-ptrcast.zig b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-ptrcast.zig
new file mode 100644
index 0000000..338c324
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-ptrcast.zig
@@ -0,0 +1,9 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "sentinel termination" {
+ const terminated = [3:0]u8{ 3, 2, 1 };
+ try expect(terminated.len == 3);
+ try expect(@as(*const [4]u8, @ptrCast(&terminated))[3] == 0);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-slicing.zig b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-slicing.zig
new file mode 100644
index 0000000..f6aa47f
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-slicing.zig
@@ -0,0 +1,9 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "sentinel terminated slicing" {
+ var x = [_:0]u8{255} ** 3;
+ const y = x[0..3 :0];
+ _ = y;
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-string-literal.zig b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-string-literal.zig
new file mode 100644
index 0000000..3896a1e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/29.sentinel-termination-string-literal.zig
@@ -0,0 +1,7 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "string literal" {
+ try expect(@TypeOf("hello") == *const [5:0]u8);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/30-vectors.mdx b/website/versioned_docs/version-0.15/01-language-basics/30-vectors.mdx
new file mode 100644
index 0000000..0d301fd
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/30-vectors.mdx
@@ -0,0 +1,50 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import VectorsAdd from "!!raw-loader!./30.vectors-add.zig";
+import VectorsIndexing from "!!raw-loader!./30.vectors-indexing.zig";
+import VectorsSplatScalar from "!!raw-loader!./30.vectors-splat-scalar.zig";
+import VectorsLooping from "!!raw-loader!./30.vectors-looping.zig";
+import VectorsCoercion from "!!raw-loader!./30.vectors-coercion.zig";
+
+# Vectors
+
+Zig provides vector types for SIMD. These are not to be conflated with vectors
+in a mathematical sense, or vectors like C++'s std::vector (for this, see
+"Arraylist" in chapter 2). Vectors may be created using the
+[`@Type`](https://ziglang.org/documentation/master/#Type) built-in we used
+earlier, and
+[`std.meta.Vector`](https://ziglang.org/documentation/master/std/#std;meta.Vector)
+provides a shorthand for this.
+
+Vectors can only have child types of booleans, integers, floats and pointers.
+
+Operations between vectors with the same child type and length can take place.
+These operations are performed on each of the values in the
+vector.[`std.meta.eql`](https://ziglang.org/documentation/master/std/#std;meta.eql)
+is used here to check for equality between two vectors (also useful for other
+types like structs).
+
+{VectorsAdd}
+
+Vectors are indexable.
+
+{VectorsIndexing}
+
+The built-in function
+[`@splat`](https://ziglang.org/documentation/master/#splat) may be used to
+construct a vector where all of the values are the same. Here we use it to
+multiply a vector by a scalar.
+
+{VectorsSplatScalar}
+
+Vectors do not have a `len` field like arrays, but may still be looped over.
+
+{VectorsLooping}
+
+Vectors coerce to their respective arrays.
+
+{VectorsCoercion}
+
+It is worth noting that using explicit vectors may result in slower software if
+you do not make the right decisions - the compiler's auto-vectorisation is
+fairly smart as-is.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/30.vectors-add.zig b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-add.zig
new file mode 100644
index 0000000..53c69ac
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-add.zig
@@ -0,0 +1,12 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+const meta = @import("std").meta;
+
+test "vector add" {
+ const x: @Vector(4, f32) = .{ 1, -10, 20, -1 };
+ const y: @Vector(4, f32) = .{ 2, 10, 0, 1 };
+ const z = x + y;
+ try expect(meta.eql(z, @Vector(4, f32){ 3, 0, 20, 0 }));
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/30.vectors-coercion.zig b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-coercion.zig
new file mode 100644
index 0000000..885eb38
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-coercion.zig
@@ -0,0 +1,5 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+const arr: [4]f32 = @Vector(4, f32){ 1, 2, 3, 4 };
diff --git a/website/versioned_docs/version-0.15/01-language-basics/30.vectors-indexing.zig b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-indexing.zig
new file mode 100644
index 0000000..d38fff1
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-indexing.zig
@@ -0,0 +1,8 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "vector indexing" {
+ const x: @Vector(4, u8) = .{ 255, 0, 255, 0 };
+ try expect(x[0] == 255);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/30.vectors-looping.zig b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-looping.zig
new file mode 100644
index 0000000..4fa2af0
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-looping.zig
@@ -0,0 +1,14 @@
+// hide-start
+const expect = @import("std").testing.expect;
+
+//hide-end
+test "vector looping" {
+ const x = @Vector(4, u8){ 255, 0, 255, 0 };
+ const sum = blk: {
+ var tmp: u10 = 0;
+ var i: u8 = 0;
+ while (i < 4) : (i += 1) tmp += x[i];
+ break :blk tmp;
+ };
+ try expect(sum == 510);
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/30.vectors-splat-scalar.zig b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-splat-scalar.zig
new file mode 100644
index 0000000..dc5bbb1
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/30.vectors-splat-scalar.zig
@@ -0,0 +1,10 @@
+// hide-start
+const expect = @import("std").testing.expect;
+const meta = @import("std").meta;
+
+//hide-end
+test "vector * scalar" {
+ const x: @Vector(3, f32) = .{ 12.5, 37.5, 2.5 };
+ const y = x * @as(@Vector(3, f32), @splat(2));
+ try expect(meta.eql(y, @Vector(3, f32){ 25, 75, 5 }));
+}
diff --git a/website/versioned_docs/version-0.15/01-language-basics/31-imports.md b/website/versioned_docs/version-0.15/01-language-basics/31-imports.md
new file mode 100644
index 0000000..84ceb62
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/31-imports.md
@@ -0,0 +1,17 @@
+---
+pagination_next: standard-library/allocators
+---
+
+# Imports
+
+The built-in function
+[`@import`](https://ziglang.org/documentation/master/#import) takes in a file,
+and gives you a struct type based on that file. All declarations labelled as
+`pub` (for public) will end up in this struct type, ready for use.
+
+`@import("std")` is a special case in the compiler, and gives you access to the
+standard library. Other
+[`@import`](https://ziglang.org/documentation/master/#import)s will take in a
+file path, or a package name (more on packages in a later chapter).
+
+We will explore more of the standard library in later chapters.
diff --git a/website/versioned_docs/version-0.15/01-language-basics/_category_.json b/website/versioned_docs/version-0.15/01-language-basics/_category_.json
new file mode 100644
index 0000000..b6ed471
--- /dev/null
+++ b/website/versioned_docs/version-0.15/01-language-basics/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Language",
+ "link": {
+ "description": "Getting started with the Zig programming language."
+ }
+}
\ No newline at end of file
diff --git a/website/versioned_docs/version-0.15/02-standard-library/01-allocators.mdx b/website/versioned_docs/version-0.15/02-standard-library/01-allocators.mdx
new file mode 100644
index 0000000..bb5be60
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/01-allocators.mdx
@@ -0,0 +1,69 @@
+---
+pagination_prev: language-basics/imports
+---
+
+import CodeBlock from "@theme/CodeBlock";
+
+import AllocatorsAlloc from "!!raw-loader!./01.allocators-alloc.zig";
+import AllocatorsFba from "!!raw-loader!./01.allocators-fba.zig";
+import AllocatorsArena from "!!raw-loader!./01.allocators-arena.zig";
+import AllocatorsCreate from "!!raw-loader!./01.allocators-create.zig";
+import AllocatorsGpa from "!!raw-loader!./01.allocators-gpa.zig";
+
+# Allocators
+
+The Zig standard library provides a pattern for allocating memory, which allows
+the programmer to choose precisely how memory allocations are done within the
+standard library - no allocations happen behind your back in the standard
+library.
+
+The most basic allocator is
+[`std.heap.page_allocator`](https://ziglang.org/documentation/master/std/#std.heap.page_allocator).
+Whenever this allocator makes an allocation, it will ask your OS for entire
+pages of memory; an allocation of a single byte will likely reserve multiple
+kibibytes. As asking the OS for memory requires a system call, this is also
+extremely inefficient for speed.
+
+Here, we allocate 100 bytes as a `[]u8`. Notice how defer is used in conjunction
+with a free - this is a common pattern for memory management in Zig.
+
+{AllocatorsAlloc}
+
+The
+[`std.heap.FixedBufferAllocator`](https://ziglang.org/documentation/master/std/#std.heap.FixedBufferAllocator)
+is an allocator that allocates memory into a fixed buffer and does not make any
+heap allocations. This is useful when heap usage is not wanted, for example,
+when writing a kernel. It may also be considered for performance reasons. It
+will give you the error `OutOfMemory` if it has run out of bytes.
+
+{AllocatorsFba}
+
+[`std.heap.ArenaAllocator`](https://ziglang.org/documentation/master/std/#std.heap.ArenaAllocator)
+takes in a child allocator and allows you to allocate many times and only free
+once. Here, `.deinit()` is called on the arena, which frees all memory. Using
+`allocator.free` in this example would be a no-op (i.e. does nothing).
+
+{AllocatorsArena}
+
+`alloc` and `free` are used for slices. For single items, consider using
+`create` and `destroy`.
+
+{AllocatorsCreate}
+
+The Zig standard library also has a general-purpose allocator. This is a safe
+allocator that can prevent double-free, use-after-free and can detect leaks.
+Safety checks and thread safety can be turned off via its configuration struct
+(left empty below). Zig's GPA is designed for safety over performance, but may
+still be many times faster than page_allocator.
+
+{AllocatorsGpa}
+
+For high performance (but very few safety features!),
+[`std.heap.c_allocator`](https://ziglang.org/documentation/master/std/#std.heap.c_allocator)
+may be considered. This,however, has the disadvantage of requiring linking Libc,
+which can be done with `-lc`.
+
+Benjamin Feng's talk
+[_What's a Memory Allocator Anyway?_](https://www.youtube.com/watch?v=vHWiDx_l4V0)
+goes into more detail on this topic, and covers the implementation of
+allocators.
diff --git a/website/versioned_docs/version-0.15/02-standard-library/01.allocators-alloc.zig b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-alloc.zig
new file mode 100644
index 0000000..6d77993
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-alloc.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "allocation" {
+ const allocator = std.heap.page_allocator;
+
+ const memory = try allocator.alloc(u8, 100);
+ defer allocator.free(memory);
+
+ try expect(memory.len == 100);
+ try expect(@TypeOf(memory) == []u8);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/01.allocators-arena.zig b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-arena.zig
new file mode 100644
index 0000000..4d37bec
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-arena.zig
@@ -0,0 +1,14 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+
+// hide-end
+test "arena allocator" {
+ var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ defer arena.deinit();
+ const allocator = arena.allocator();
+
+ _ = try allocator.alloc(u8, 1);
+ _ = try allocator.alloc(u8, 10);
+ _ = try allocator.alloc(u8, 100);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/01.allocators-create.zig b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-create.zig
new file mode 100644
index 0000000..0cb0b5d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-create.zig
@@ -0,0 +1,10 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+
+// hide-end
+test "allocator create/destroy" {
+ const byte = try std.heap.page_allocator.create(u8);
+ defer std.heap.page_allocator.destroy(byte);
+ byte.* = 128;
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/01.allocators-fba.zig b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-fba.zig
new file mode 100644
index 0000000..74a70e5
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-fba.zig
@@ -0,0 +1,16 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+
+// hide-end
+test "fixed buffer allocator" {
+ var buffer: [1000]u8 = undefined;
+ var fba = std.heap.FixedBufferAllocator.init(&buffer);
+ const allocator = fba.allocator();
+
+ const memory = try allocator.alloc(u8, 100);
+ defer allocator.free(memory);
+
+ try expect(memory.len == 100);
+ try expect(@TypeOf(memory) == []u8);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/01.allocators-gpa.zig b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-gpa.zig
new file mode 100644
index 0000000..0d64e3f
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/01.allocators-gpa.zig
@@ -0,0 +1,17 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+
+// hide-end
+test "GPA" {
+ var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
+ const allocator = gpa.allocator();
+ defer {
+ const deinit_status = gpa.deinit();
+ //fail test; can't try in defer as defer is executed after we return
+ if (deinit_status == .leak) expect(false) catch @panic("TEST FAIL");
+ }
+
+ const bytes = try allocator.alloc(u8, 100);
+ defer allocator.free(bytes);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/02-arraylist.mdx b/website/versioned_docs/version-0.15/02-standard-library/02-arraylist.mdx
new file mode 100644
index 0000000..7582707
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/02-arraylist.mdx
@@ -0,0 +1,21 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import ArrayList from "!!raw-loader!./02.arraylist.zig";
+
+# ArrayList
+
+The
+[`std.ArrayList`](https://ziglang.org/documentation/master/std/#std.ArrayList)
+is commonly used throughout Zig, and serves as a buffer that can change in
+size. `std.ArrayList(T)` is similar to C++'s `std::vector` and Rust's
+`Vec`. The `deinit()` method frees all of the ArrayList's memory. The memory
+can be read from and written to via its slice field - `.items`.
+
+Here we will introduce the usage of the testing allocator. This is a special
+allocator that only works in tests and can detect memory leaks. In your code,
+use whatever allocator is appropriate.
+
+{ArrayList}
+
+:::cpp
+Zig's `std.ArrayList` is very comparable to C++'s `std::vector`.
diff --git a/website/versioned_docs/version-0.15/02-standard-library/02.arraylist.zig b/website/versioned_docs/version-0.15/02-standard-library/02.arraylist.zig
new file mode 100644
index 0000000..8ff7aaa
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/02.arraylist.zig
@@ -0,0 +1,21 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+
+// hide-end
+const eql = std.mem.eql;
+const ArrayList = std.ArrayList;
+const test_allocator = std.testing.allocator;
+
+test "arraylist" {
+ var list = ArrayList(u8).init(test_allocator);
+ defer list.deinit();
+ try list.append('H');
+ try list.append('e');
+ try list.append('l');
+ try list.append('l');
+ try list.append('o');
+ try list.appendSlice(" World!");
+
+ try expect(eql(u8, list.items, "Hello World!"));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/03-filesystem.mdx b/website/versioned_docs/version-0.15/02-standard-library/03-filesystem.mdx
new file mode 100644
index 0000000..610cf01
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/03-filesystem.mdx
@@ -0,0 +1,32 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import CwdCreate from "!!raw-loader!./03.filesystem-cwd-create.zig";
+import Stat from "!!raw-loader!./03.filesystem-stat.zig";
+import MakeDirIterable from "!!raw-loader!./03.filesystem-make-dir-iterable.zig";
+
+# Filesystem
+
+Let's create and open a file in our current working directory, write to it, and
+then read from it. Here we have to use `.seekTo` to go back to the start of the
+file before reading what we have written.
+
+{CwdCreate}
+
+The functions
+[`std.fs.openFileAbsolute`](https://ziglang.org/documentation/master/std/#std.fs.openFileAbsolute)
+and similar absolute functions exist, but we will not test them here.
+
+We can get various information about files by using `.stat()` on them. `Stat`
+also contains fields for .inode and .mode, but they are not tested here as they
+rely on the current OS' types.
+
+When the Enum type is known from context, it can be omitted, so we can
+compare `stat.kind` to `.file` instead of `Kind.file`.
+
+{Stat}
+
+We can make directories and iterate over their contents. Here we will use an
+iterator (discussed later). This directory (and its contents) will be deleted
+after this test finishes.
+
+{MakeDirIterable}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-cwd-create.zig b/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-cwd-create.zig
new file mode 100644
index 0000000..d875aca
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-cwd-create.zig
@@ -0,0 +1,20 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+test "createFile, write, seekTo, read" {
+ const file = try std.fs.cwd().createFile(
+ "junk_file.txt",
+ .{ .read = true },
+ );
+ defer file.close();
+
+ try file.writeAll("Hello File!");
+
+ var buffer: [100]u8 = undefined;
+ try file.seekTo(0);
+ const bytes_read = try file.readAll(&buffer);
+
+ try expect(eql(u8, buffer[0..bytes_read], "Hello File!"));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-make-dir-iterable.zig b/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-make-dir-iterable.zig
new file mode 100644
index 0000000..ab82466
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-make-dir-iterable.zig
@@ -0,0 +1,28 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+test "make dir" {
+ try std.fs.cwd().makeDir("test-tmp");
+ var iter_dir = try std.fs.cwd().openDir(
+ "test-tmp",
+ .{ .iterate = true },
+ );
+ defer {
+ iter_dir.close();
+ std.fs.cwd().deleteTree("test-tmp") catch unreachable;
+ }
+
+ _ = try iter_dir.createFile("x", .{});
+ _ = try iter_dir.createFile("y", .{});
+ _ = try iter_dir.createFile("z", .{});
+
+ var file_count: usize = 0;
+ var iter = iter_dir.iterate();
+ while (try iter.next()) |entry| {
+ if (entry.kind == .file) file_count += 1;
+ }
+
+ try expect(file_count == 3);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-stat.zig b/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-stat.zig
new file mode 100644
index 0000000..58655c3
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/03.filesystem-stat.zig
@@ -0,0 +1,18 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+test "file stat" {
+ const file = try std.fs.cwd().createFile(
+ "junk_file2.txt",
+ .{ .read = true },
+ );
+ defer file.close();
+ const stat = try file.stat();
+ try expect(stat.size == 0);
+ try expect(stat.kind == .file);
+ try expect(stat.ctime <= std.time.nanoTimestamp());
+ try expect(stat.mtime <= std.time.nanoTimestamp());
+ try expect(stat.atime <= std.time.nanoTimestamp());
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/04-readers-and-writers.mdx b/website/versioned_docs/version-0.15/02-standard-library/04-readers-and-writers.mdx
new file mode 100644
index 0000000..2200239
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/04-readers-and-writers.mdx
@@ -0,0 +1,76 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Writer from "!!raw-loader!./04.readers-and-writers-writer.zig";
+import Reader from "!!raw-loader!./04.readers-and-writers-reader.zig";
+import Custom from "!!raw-loader!./04.readers-and-writers-custom.zig";
+
+# Readers and Writers
+
+[`std.io.Writer`](https://ziglang.org/documentation/master/std/#std.io.Writer)
+and
+[`std.io.Reader`](https://ziglang.org/documentation/master/std/#std.io.Reader)
+provide standard ways of making use of IO. `std.ArrayList(u8)` has a `writer`
+method which gives us a writer. Let's use it.
+
+{Writer}
+
+Here we will use a reader to copy the file's contents into an allocated buffer.
+The second argument of
+[`readAllAlloc`](https://ziglang.org/documentation/master/std/#std.io.Reader.readAllAlloc)
+is the maximum size that it may allocate; if the file is larger than this, it
+will return `error.StreamTooLong`.
+
+{Reader}
+
+A common usecase for readers is to read until the next line (e.g. for user
+input). Here we will do this with the
+[`std.io.getStdIn()`](https://ziglang.org/documentation/master/std/#std.io.getStdIn)
+file.
+
+{/* Code snippet not tested as it uses stdin/stdout */}
+
+```zig
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+
+fn nextLine(reader: anytype, buffer: []u8) !?[]const u8 {
+ var line = (try reader.readUntilDelimiterOrEof(
+ buffer,
+ '\n',
+ )) orelse return null;
+ // trim annoying windows-only carriage return character
+ if (@import("builtin").os.tag == .windows) {
+ return std.mem.trimRight(u8, line, "\r");
+ } else {
+ return line;
+ }
+}
+
+test "read until next line" {
+ const stdout = std.io.getStdOut();
+ const stdin = std.io.getStdIn();
+
+ try stdout.writeAll(
+ \\ Enter your name:
+ );
+
+ var buffer: [100]u8 = undefined;
+ const input = (try nextLine(stdin.reader(), &buffer)).?;
+ try stdout.writer().print(
+ "Your name is: \"{s}\"\n",
+ .{input},
+ );
+}
+```
+
+An
+[`std.io.Writer`](https://ziglang.org/documentation/master/std/#std.io.Writer)
+type consists of a context type, error set, and a write function. The write
+function must take in the context type and a byte slice. The write function must
+also return an error union of the Writer type's error set and the number of
+bytes written. Let's create a type that implements a writer.
+
+{Custom}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-custom.zig b/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-custom.zig
new file mode 100644
index 0000000..74a0a16
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-custom.zig
@@ -0,0 +1,43 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+// Don't create a type like this! Use an
+// arraylist with a fixed buffer allocator
+const MyByteList = struct {
+ data: [100]u8 = undefined,
+ items: []u8 = &[_]u8{},
+
+ const Writer = std.io.Writer(
+ *MyByteList,
+ error{EndOfBuffer},
+ appendWrite,
+ );
+
+ fn appendWrite(
+ self: *MyByteList,
+ data: []const u8,
+ ) error{EndOfBuffer}!usize {
+ if (self.items.len + data.len > self.data.len) {
+ return error.EndOfBuffer;
+ }
+ @memcpy(
+ self.data[self.items.len..][0..data.len],
+ data,
+ );
+ self.items = self.data[0 .. self.items.len + data.len];
+ return data.len;
+ }
+
+ fn writer(self: *MyByteList) Writer {
+ return .{ .context = self };
+ }
+};
+
+test "custom writer" {
+ var bytes = MyByteList{};
+ _ = try bytes.writer().write("Hello");
+ _ = try bytes.writer().write(" Writer!");
+ try expect(eql(u8, bytes.items, "Hello Writer!"));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-reader.zig b/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-reader.zig
new file mode 100644
index 0000000..8a46f6a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-reader.zig
@@ -0,0 +1,26 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+const test_allocator = std.testing.allocator;
+// hide-end
+test "io reader usage" {
+ const message = "Hello File!";
+
+ const file = try std.fs.cwd().createFile(
+ "junk_file2.txt",
+ .{ .read = true },
+ );
+ defer file.close();
+
+ try file.writeAll(message);
+ try file.seekTo(0);
+
+ const contents = try file.reader().readAllAlloc(
+ test_allocator,
+ message.len,
+ );
+ defer test_allocator.free(contents);
+
+ try expect(eql(u8, contents, message));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-writer.zig b/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-writer.zig
new file mode 100644
index 0000000..90b450b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/04.readers-and-writers-writer.zig
@@ -0,0 +1,17 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+const ArrayList = std.ArrayList;
+const test_allocator = std.testing.allocator;
+
+test "io writer usage" {
+ var list = ArrayList(u8).init(test_allocator);
+ defer list.deinit();
+ const bytes_written = try list.writer().write(
+ "Hello World!",
+ );
+ try expect(bytes_written == 12);
+ try expect(eql(u8, list.items, "Hello World!"));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/05-formatting.mdx b/website/versioned_docs/version-0.15/02-standard-library/05-formatting.mdx
new file mode 100644
index 0000000..cb35611
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/05-formatting.mdx
@@ -0,0 +1,55 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Fmt from "!!raw-loader!./05.formatting-fmt.zig";
+import Print from "!!raw-loader!./05.formatting-print.zig";
+import ArrayPrint from "!!raw-loader!./05.formatting-array-print.zig";
+import Custom from "!!raw-loader!./05.formatting-custom.zig";
+
+# Formatting
+
+[`std.fmt`](https://ziglang.org/documentation/master/std/#std.fmt) provides
+ways to format data to and from strings.
+
+A basic example of creating a formatted string. The format string must be
+compile-time known. The `d` here denotes that we want a decimal number.
+
+{Fmt}
+
+Writers conveniently have a `print` method, which works similarly.
+
+{Print}
+
+Take a moment to appreciate that you now know from top to bottom how printing
+Hello World works.
+[`std.debug.print`](https://ziglang.org/documentation/master/std/#std.debug.print)
+works the same, except it writes to stderr and is protected by a mutex.
+
+{/* Code snippet not tested as it uses stdin/stdout */}
+
+```zig
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+test "hello world" {
+ const out_file = std.io.getStdOut();
+ try out_file.writer().print(
+ "Hello, {s}!\n",
+ .{"World"},
+ );
+}
+```
+
+We have used the `{s}` format specifier up until this point to print strings.
+Here, we will use `{any}`, which gives us the default formatting.
+
+{ArrayPrint}
+
+Let's create a type with custom formatting by giving it a `format` function.
+This function must be marked as `pub` so that std.fmt can access it (more on
+packages later). You may notice the usage of `{s}` instead of `{}` - this is the
+format specifier for strings (more on format specifiers later). This is used
+here as `{}` defaults to array printing over string printing.
+
+{Custom}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/05.formatting-array-print.zig b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-array-print.zig
new file mode 100644
index 0000000..353cd6a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-array-print.zig
@@ -0,0 +1,24 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+const test_allocator = std.testing.allocator;
+// hide-end
+test "array printing" {
+ const string = try std.fmt.allocPrint(
+ test_allocator,
+ "{any} + {any} = {any}",
+ .{
+ @as([]const u8, &[_]u8{ 1, 4 }),
+ @as([]const u8, &[_]u8{ 2, 5 }),
+ @as([]const u8, &[_]u8{ 3, 9 }),
+ },
+ );
+ defer test_allocator.free(string);
+
+ try expect(eql(
+ u8,
+ string,
+ "{ 1, 4 } + { 2, 5 } = { 3, 9 }",
+ ));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/05.formatting-custom.zig b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-custom.zig
new file mode 100644
index 0000000..9025bf4
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-custom.zig
@@ -0,0 +1,70 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+const test_allocator = std.testing.allocator;
+// hide-end
+const Person = struct {
+ name: []const u8,
+ birth_year: i32,
+ death_year: ?i32,
+ pub fn format(
+ self: Person,
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = fmt;
+ _ = options;
+
+ try writer.print("{s} ({}-", .{
+ self.name, self.birth_year,
+ });
+
+ if (self.death_year) |year| {
+ try writer.print("{}", .{year});
+ }
+
+ try writer.writeAll(")");
+ }
+};
+
+test "custom fmt" {
+ const john = Person{
+ .name = "John Carmack",
+ .birth_year = 1970,
+ .death_year = null,
+ };
+
+ const john_string = try std.fmt.allocPrint(
+ test_allocator,
+ "{s}",
+ .{john},
+ );
+ defer test_allocator.free(john_string);
+
+ try expect(eql(
+ u8,
+ john_string,
+ "John Carmack (1970-)",
+ ));
+
+ const claude = Person{
+ .name = "Claude Shannon",
+ .birth_year = 1916,
+ .death_year = 2001,
+ };
+
+ const claude_string = try std.fmt.allocPrint(
+ test_allocator,
+ "{s}",
+ .{claude},
+ );
+ defer test_allocator.free(claude_string);
+
+ try expect(eql(
+ u8,
+ claude_string,
+ "Claude Shannon (1916-2001)",
+ ));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/05.formatting-fmt.zig b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-fmt.zig
new file mode 100644
index 0000000..3ad9509
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-fmt.zig
@@ -0,0 +1,17 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+// hide-end
+const test_allocator = std.testing.allocator;
+
+test "fmt" {
+ const string = try std.fmt.allocPrint(
+ test_allocator,
+ "{d} + {d} = {d}",
+ .{ 9, 10, 19 },
+ );
+ defer test_allocator.free(string);
+
+ try expect(eql(u8, string, "9 + 10 = 19"));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/05.formatting-print.zig b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-print.zig
new file mode 100644
index 0000000..44ab440
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/05.formatting-print.zig
@@ -0,0 +1,15 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+const test_allocator = std.testing.allocator;
+// hide-end
+test "print" {
+ var list = std.ArrayList(u8).init(test_allocator);
+ defer list.deinit();
+ try list.writer().print(
+ "{} + {} = {}",
+ .{ 9, 10, 19 },
+ );
+ try expect(eql(u8, list.items, "9 + 10 = 19"));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/06-json.mdx b/website/versioned_docs/version-0.15/02-standard-library/06-json.mdx
new file mode 100644
index 0000000..13fe846
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/06-json.mdx
@@ -0,0 +1,20 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import JsonParse from "!!raw-loader!./06.json-parse.zig";
+import JsonStringify from "!!raw-loader!./06.json-stringify.zig";
+import JsonStringifyStrings from "!!raw-loader!./06.json-stringify-strings.zig";
+
+# JSON
+
+Let's parse a JSON string into a struct type, using the streaming parser.
+
+{JsonParse}
+
+And using stringify to turn arbitrary data into a string.
+
+{JsonStringify}
+
+The JSON parser requires an allocator for JavaScript's string, array, and map
+types.
+
+{JsonStringifyStrings}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/06.json-parse.zig b/website/versioned_docs/version-0.15/02-standard-library/06.json-parse.zig
new file mode 100644
index 0000000..82a4525
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/06.json-parse.zig
@@ -0,0 +1,22 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const test_allocator = std.testing.allocator;
+// hide-end
+const Place = struct { lat: f32, long: f32 };
+
+test "json parse" {
+ const parsed = try std.json.parseFromSlice(
+ Place,
+ test_allocator,
+ \\{ "lat": 40.684540, "long": -74.401422 }
+ ,
+ .{},
+ );
+ defer parsed.deinit();
+
+ const place = parsed.value;
+
+ try expect(place.lat == 40.684540);
+ try expect(place.long == -74.401422);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/06.json-stringify-strings.zig b/website/versioned_docs/version-0.15/02-standard-library/06.json-stringify-strings.zig
new file mode 100644
index 0000000..17a285e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/06.json-stringify-strings.zig
@@ -0,0 +1,19 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+const test_allocator = std.testing.allocator;
+// hide-end
+test "json parse with strings" {
+ const User = struct { name: []u8, age: u16 };
+
+ const parsed = try std.json.parseFromSlice(User, test_allocator,
+ \\{ "name": "Joe", "age": 25 }
+ , .{});
+ defer parsed.deinit();
+
+ const user = parsed.value;
+
+ try expect(eql(u8, user.name, "Joe"));
+ try expect(user.age == 25);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/06.json-stringify.zig b/website/versioned_docs/version-0.15/02-standard-library/06.json-stringify.zig
new file mode 100644
index 0000000..9297304
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/06.json-stringify.zig
@@ -0,0 +1,22 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+const test_allocator = std.testing.allocator;
+const Place = struct { lat: f32, long: f32 };
+// hide-end
+test "json stringify" {
+ const x = Place{
+ .lat = 51.997664,
+ .long = -0.740687,
+ };
+
+ var buf: [100]u8 = undefined;
+ var fba = std.heap.FixedBufferAllocator.init(&buf);
+ var string = std.ArrayList(u8).init(fba.allocator());
+ try std.json.stringify(x, .{}, string.writer());
+
+ try expect(eql(u8, string.items,
+ \\{"lat":5.199766540527344e1,"long":-7.406870126724243e-1}
+ ));
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/07-random-numbers.mdx b/website/versioned_docs/version-0.15/02-standard-library/07-random-numbers.mdx
new file mode 100644
index 0000000..fe6935e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/07-random-numbers.mdx
@@ -0,0 +1,22 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import RandomNumbers from "!!raw-loader!./07.random-numbers.zig";
+import RandomNumbersCrypto from "!!raw-loader!./07.random-numbers-crypto.zig";
+
+# Random Numbers
+
+Here, we create a new prng using a 64-bit random seed. a, b, c, and d are given
+random values via this prng. The expressions giving c and d values are
+equivalent. `DefaultPrng` is `Xoroshiro128`; there are other prngs available in
+`std.Random`.
+
+{RandomNumbers}
+
+Cryptographically secure random is also available.
+
+{RandomNumbersCrypto}
+
+:::info
+We can now use our knowledge of `std.Random` and [make a guessing game together](/posts/a-guessing-game).
+
+:::
diff --git a/website/versioned_docs/version-0.15/02-standard-library/07.random-numbers-crypto.zig b/website/versioned_docs/version-0.15/02-standard-library/07.random-numbers-crypto.zig
new file mode 100644
index 0000000..a254a39
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/07.random-numbers-crypto.zig
@@ -0,0 +1,15 @@
+// hide-start
+const std = @import("std");
+
+// hide-end
+test "crypto random numbers" {
+ const rand = std.crypto.random;
+
+ const a = rand.float(f32);
+ const b = rand.boolean();
+ const c = rand.int(u8);
+ const d = rand.intRangeAtMost(u8, 0, 255);
+
+ //suppress unused constant compile error
+ _ = .{ a, b, c, d };
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/07.random-numbers.zig b/website/versioned_docs/version-0.15/02-standard-library/07.random-numbers.zig
new file mode 100644
index 0000000..e6f9feb
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/07.random-numbers.zig
@@ -0,0 +1,20 @@
+// hide-start
+const std = @import("std");
+
+// hide-end
+test "random numbers" {
+ var prng = std.Random.DefaultPrng.init(blk: {
+ var seed: u64 = undefined;
+ try std.posix.getrandom(std.mem.asBytes(&seed));
+ break :blk seed;
+ });
+ const rand = prng.random();
+
+ const a = rand.float(f32);
+ const b = rand.boolean();
+ const c = rand.int(u8);
+ const d = rand.intRangeAtMost(u8, 0, 255);
+
+ //suppress unused constant compile error
+ _ = .{ a, b, c, d };
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/08-crypto.md b/website/versioned_docs/version-0.15/02-standard-library/08-crypto.md
new file mode 100644
index 0000000..b0fcf2f
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/08-crypto.md
@@ -0,0 +1,15 @@
+# Crypto
+
+[`std.crypto`](https://ziglang.org/documentation/master/std/#std.crypto)
+includes many cryptographic utilities, including:
+
+- AES (Aes128, Aes256)
+- Diffie-Hellman key exchange (x25519)
+- Elliptic-curve arithmetic (curve25519, edwards25519, ristretto255)
+- Crypto secure hashing (blake2, Blake3, Gimli, Md5, sha1, sha2, sha3)
+- MAC functions (Ghash, Poly1305)
+- Stream ciphers (ChaCha20IETF, ChaCha20With64BitNonce, XChaCha20IETF, Salsa20,
+ XSalsa20)
+
+This list is inexhaustive. For more in-depth information, try
+[A tour of std.crypto in Zig 0.7.0 - Frank Denis](https://www.youtube.com/watch?v=9t6Y7KoCvyk).
diff --git a/website/versioned_docs/version-0.15/02-standard-library/09-threads.md b/website/versioned_docs/version-0.15/02-standard-library/09-threads.md
new file mode 100644
index 0000000..0a016ae
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/09-threads.md
@@ -0,0 +1,27 @@
+# Threads
+
+While Zig provides more advanced ways of writing concurrent and parallel code,
+[`std.Thread`](https://ziglang.org/documentation/master/std/#std.Thread) is
+available for making use of OS threads. Let's make use of an OS thread.
+
+```zig
+fn ticker(step: u8) void {
+ while (true) {
+ std.time.sleep(1 * std.time.ns_per_s);
+ tick += @as(isize, step);
+ }
+}
+
+var tick: isize = 0;
+
+test "threading" {
+ var thread = try std.Thread.spawn(.{}, ticker, .{@as(u8, 1)});
+ _ = thread;
+ try expect(tick == 0);
+ std.time.sleep(3 * std.time.ns_per_s / 2);
+ try expect(tick == 1);
+}
+```
+
+Threads, however, aren't particularly useful without strategies for thread
+safety.
diff --git a/website/versioned_docs/version-0.15/02-standard-library/10-hashmaps.md b/website/versioned_docs/version-0.15/02-standard-library/10-hashmaps.md
new file mode 100644
index 0000000..771e898
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/10-hashmaps.md
@@ -0,0 +1,87 @@
+# Hash Maps
+
+The standard library provides
+[`std.AutoHashMap`](https://ziglang.org/documentation/master/std/#std.AutoHashMap),
+which lets you easily create a hash map type from a key type and a value type.
+These must be initiated with an allocator.
+
+Let's put some values in a hash map.
+
+```zig
+test "hashing" {
+ const Point = struct { x: i32, y: i32 };
+
+ var map = std.AutoHashMap(u32, Point).init(
+ test_allocator,
+ );
+ defer map.deinit();
+
+ try map.put(1525, .{ .x = 1, .y = -4 });
+ try map.put(1550, .{ .x = 2, .y = -3 });
+ try map.put(1575, .{ .x = 3, .y = -2 });
+ try map.put(1600, .{ .x = 4, .y = -1 });
+
+ try expect(map.count() == 4);
+
+ var sum = Point{ .x = 0, .y = 0 };
+ var iterator = map.iterator();
+
+ while (iterator.next()) |entry| {
+ sum.x += entry.value_ptr.x;
+ sum.y += entry.value_ptr.y;
+ }
+
+ try expect(sum.x == 10);
+ try expect(sum.y == -10);
+}
+```
+
+`.fetchPut` puts a value in the hash map, returning a value if there was
+previously a value for that key.
+
+```zig
+test "fetchPut" {
+ var map = std.AutoHashMap(u8, f32).init(
+ test_allocator,
+ );
+ defer map.deinit();
+
+ try map.put(255, 10);
+ const old = try map.fetchPut(255, 100);
+
+ try expect(old.?.value == 10);
+ try expect(map.get(255).? == 100);
+}
+```
+
+[`std.StringHashMap`](https://ziglang.org/documentation/master/std/#std.StringHashMap)
+is also provided for when you need strings as keys.
+
+```zig
+test "string hashmap" {
+ var map = std.StringHashMap(enum { cool, uncool }).init(
+ test_allocator,
+ );
+ defer map.deinit();
+
+ try map.put("loris", .uncool);
+ try map.put("me", .cool);
+
+ try expect(map.get("me").? == .cool);
+ try expect(map.get("loris").? == .uncool);
+}
+```
+
+[`std.StringHashMap`](https://ziglang.org/documentation/master/std/#std.StringHashMap)
+and
+[`std.AutoHashMap`](https://ziglang.org/documentation/master/std/#std.AutoHashMap)
+are just wrappers for
+[`std.HashMap`](https://ziglang.org/documentation/master/std/#std.HashMap). If
+these two do not fulfil your needs, using
+[`std.HashMap`](https://ziglang.org/documentation/master/std/#std.HashMap)
+directly gives you much more control.
+
+If having your elements backed by an array is wanted behaviour, try
+[`std.ArrayHashMap`](https://ziglang.org/documentation/master/std/#std.ArrayHashMap)
+and its wrapper
+[`std.AutoArrayHashMap`](https://ziglang.org/documentation/master/std/#std.AutoArrayHashMap).
diff --git a/website/versioned_docs/version-0.15/02-standard-library/11-stacks.md b/website/versioned_docs/version-0.15/02-standard-library/11-stacks.md
new file mode 100644
index 0000000..8d7461d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/11-stacks.md
@@ -0,0 +1,39 @@
+# Stacks
+
+[`std.ArrayList`](https://ziglang.org/documentation/master/std/#std.ArrayList)
+provides the methods necessary to use it as a stack. Here's an example of
+creating a list of matched brackets.
+
+```zig
+test "stack" {
+ const string = "(()())";
+ var stack = std.ArrayList(usize).init(
+ test_allocator,
+ );
+ defer stack.deinit();
+
+ const Pair = struct { open: usize, close: usize };
+ var pairs = std.ArrayList(Pair).init(
+ test_allocator,
+ );
+ defer pairs.deinit();
+
+ for (string, 0..) |char, i| {
+ if (char == '(') try stack.append(i);
+ if (char == ')')
+ try pairs.append(.{
+ .open = stack.pop(),
+ .close = i,
+ });
+ }
+
+ for (pairs.items, 0..) |pair, i| {
+ try expect(std.meta.eql(pair, switch (i) {
+ 0 => Pair{ .open = 1, .close = 2 },
+ 1 => Pair{ .open = 3, .close = 4 },
+ 2 => Pair{ .open = 0, .close = 5 },
+ else => unreachable,
+ }));
+ }
+}
+```
diff --git a/website/versioned_docs/version-0.15/02-standard-library/12-sorting.md b/website/versioned_docs/version-0.15/02-standard-library/12-sorting.md
new file mode 100644
index 0000000..751e0ed
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/12-sorting.md
@@ -0,0 +1,22 @@
+# Sorting
+
+The standard library provides utilities for in-place sorting slices. Its basic
+usage is as follows.
+
+```zig
+test "sorting" {
+ var data = [_]u8{ 10, 240, 0, 0, 10, 5 };
+ std.mem.sort(u8, &data, {}, comptime std.sort.asc(u8));
+ try expect(eql(u8, &data, &[_]u8{ 0, 0, 5, 10, 10, 240 }));
+ std.mem.sort(u8, &data, {}, comptime std.sort.desc(u8));
+ try expect(eql(u8, &data, &[_]u8{ 240, 10, 10, 5, 0, 0 }));
+}
+```
+
+[`std.sort.asc`](https://ziglang.org/documentation/master/std/#std.sort.asc)
+and [`.desc`](https://ziglang.org/documentation/master/std/#std.sort.desc)
+create a comparison function for the given type at comptime; if non-numerical
+types should be sorted, the user must provide their own comparison function.
+
+[`std.mem.sort`](https://ziglang.org/documentation/master/std/#std.mem.sort)
+has a best case of O(n), and an average and worst case of O(n*log(n)).
diff --git a/website/versioned_docs/version-0.15/02-standard-library/13-iterators.mdx b/website/versioned_docs/version-0.15/02-standard-library/13-iterators.mdx
new file mode 100644
index 0000000..febc208
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/13-iterators.mdx
@@ -0,0 +1,31 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import IteratorsSplit from "!!raw-loader!./13.iterators-split.zig";
+import IteratorsLooping from "!!raw-loader!./13.iterators-looping.zig";
+import IteratorsCustom from "!!raw-loader!./13.iterators-custom.zig";
+
+# Iterators
+
+It is a common idiom to have a struct type with a `next` function with an
+optional in its return type, so that the function may return a null to indicate
+that iteration is finished.
+
+[`std.mem.SplitIterator`](https://ziglang.org/documentation/master/std/#std.mem.SplitIterator)
+(and the subtly different
+[`std.mem.TokenIterator`](https://ziglang.org/documentation/master/std/#std.mem.TokenIterator))
+is an example of this pattern.
+
+{IteratorsSplit}
+
+Some iterators have a `!?T` return type, as opposed to ?T. `!?T` requires that
+we unpack the error union before the optional, meaning that the work done to get
+to the next iteration may error. Here is an example of doing this with a loop.
+[`cwd`](https://ziglang.org/documentation/master/std/#std;fs.cwd) has to be
+opened with iterate permissions for the directory iterator to work.
+
+{IteratorsLooping}
+
+Here we will implement a custom iterator. This will iterate over a slice of
+strings, yielding the strings which contain a given string.
+
+{IteratorsCustom}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/13.iterators-custom.zig b/website/versioned_docs/version-0.15/02-standard-library/13.iterators-custom.zig
new file mode 100644
index 0000000..617a736
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/13.iterators-custom.zig
@@ -0,0 +1,31 @@
+// hide-start
+const std = @import("std");
+const eql = std.mem.eql;
+const expect = std.testing.expect;
+// hide-end
+const ContainsIterator = struct {
+ strings: []const []const u8,
+ needle: []const u8,
+ index: usize = 0,
+ fn next(self: *ContainsIterator) ?[]const u8 {
+ const index = self.index;
+ for (self.strings[index..]) |string| {
+ self.index += 1;
+ if (std.mem.indexOf(u8, string, self.needle)) |_| {
+ return string;
+ }
+ }
+ return null;
+ }
+};
+
+test "custom iterator" {
+ var iter = ContainsIterator{
+ .strings = &[_][]const u8{ "one", "two", "three" },
+ .needle = "e",
+ };
+
+ try expect(eql(u8, iter.next().?, "one"));
+ try expect(eql(u8, iter.next().?, "three"));
+ try expect(iter.next() == null);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/13.iterators-looping.zig b/website/versioned_docs/version-0.15/02-standard-library/13.iterators-looping.zig
new file mode 100644
index 0000000..df6c0dd
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/13.iterators-looping.zig
@@ -0,0 +1,17 @@
+// hide-start
+const std = @import("std");
+const expect = std.testing.expect;
+// hide-end
+test "iterator looping" {
+ var iter = (try std.fs.cwd().openDir(
+ ".",
+ .{ .iterate = true },
+ )).iterate();
+
+ var file_count: usize = 0;
+ while (try iter.next()) |entry| {
+ if (entry.kind == .file) file_count += 1;
+ }
+
+ try expect(file_count > 0);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/13.iterators-split.zig b/website/versioned_docs/version-0.15/02-standard-library/13.iterators-split.zig
new file mode 100644
index 0000000..9856e1a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/13.iterators-split.zig
@@ -0,0 +1,15 @@
+// hide-start
+const std = @import("std");
+const eql = std.mem.eql;
+const expect = std.testing.expect;
+// hide-end
+test "split iterator" {
+ const text = "robust, optimal, reusable, maintainable, ";
+ var iter = std.mem.splitSequence(u8, text, ", ");
+ try expect(eql(u8, iter.next().?, "robust"));
+ try expect(eql(u8, iter.next().?, "optimal"));
+ try expect(eql(u8, iter.next().?, "reusable"));
+ try expect(eql(u8, iter.next().?, "maintainable"));
+ try expect(eql(u8, iter.next().?, ""));
+ try expect(iter.next() == null);
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14-formatting-specifiers.mdx b/website/versioned_docs/version-0.15/02-standard-library/14-formatting-specifiers.mdx
new file mode 100644
index 0000000..e098026
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14-formatting-specifiers.mdx
@@ -0,0 +1,57 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Hex from "!!raw-loader!./14.formatting-specifiers-hex.zig";
+import Decimal from "!!raw-loader!./14.formatting-specifiers-decimal.zig";
+import Ascii from "!!raw-loader!./14.formatting-specifiers-ascii.zig";
+import ByteSizes from "!!raw-loader!./14.formatting-specifiers-byte-sizes.zig";
+import BinaryOctal from "!!raw-loader!./14.formatting-specifiers-binary-octal.zig";
+import Pointer from "!!raw-loader!./14.formatting-specifiers-pointer.zig";
+import Scientific from "!!raw-loader!./14.formatting-specifiers-scientific.zig";
+import Strings from "!!raw-loader!./14.formatting-specifiers-strings.zig";
+
+# Formatting specifiers
+
+[`std.fmt`](https://ziglang.org/documentation/master/std/#std.fmt) provides
+options for formatting various data types.
+
+From here on, we'll be using `std.testing.expectEqualStrings`. This prints out
+information if our strings aren't equal, and better conveys our intent over
+using `std.testing.expect` and `std.mem.eql`. It's worth noting that the
+"strings" we're operating on are just `[]const u8` values where we trust that
+the buffer is valid UTF-8.
+
+`std.fmt.fmtSliceHexLower` and `std.fmt.fmtSliceHexUpper` provide hex formatting
+for strings as well as `{x}` and `{X}` for ints.
+
+{Hex}
+
+`{d}` performs decimal formatting for numeric types.
+
+{Decimal}
+
+`{c}` formats a byte into an ascii character.
+
+{Ascii}
+
+`std.fmt.fmtIntSizeDec` and `std.fmt.fmtIntSizeBin` output memory sizes in
+metric (1000) and power-of-two (1024) based notation.
+
+{ByteSizes}
+
+`{b}` and `{o}` output integers in binary and octal format.
+
+{BinaryOctal}
+
+`{*}` performs pointer formatting, printing the address rather than the value.
+
+{Pointer}
+
+`{e}` outputs floats in scientific notation.
+
+{Scientific}
+
+`{s}` outputs strings.
+
+{Strings}
+
+This list is non-exhaustive.
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-ascii.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-ascii.zig
new file mode 100644
index 0000000..9fc4706
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-ascii.zig
@@ -0,0 +1,12 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "ascii fmt" {
+ var b: [1]u8 = undefined;
+ try expectEqualStrings(
+ "B",
+ try bufPrint(&b, "{c}", .{66}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-binary-octal.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-binary-octal.zig
new file mode 100644
index 0000000..78b964b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-binary-octal.zig
@@ -0,0 +1,18 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "binary, octal fmt" {
+ var b: [8]u8 = undefined;
+
+ try expectEqualStrings(
+ "11111110",
+ try bufPrint(&b, "{b}", .{254}),
+ );
+
+ try expectEqualStrings(
+ "376",
+ try bufPrint(&b, "{o}", .{254}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-byte-sizes.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-byte-sizes.zig
new file mode 100644
index 0000000..36bdb67
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-byte-sizes.zig
@@ -0,0 +1,26 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+const fmtIntSizeDec = std.fmt.fmtIntSizeDec;
+const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
+
+test "B Bi" {
+ var b: [32]u8 = undefined;
+
+ try expectEqualStrings("1B", try bufPrint(&b, "{}", .{fmtIntSizeDec(1)}));
+ try expectEqualStrings("1B", try bufPrint(&b, "{}", .{fmtIntSizeBin(1)}));
+
+ try expectEqualStrings("1.024kB", try bufPrint(&b, "{}", .{fmtIntSizeDec(1024)}));
+ try expectEqualStrings("1KiB", try bufPrint(&b, "{}", .{fmtIntSizeBin(1024)}));
+
+ try expectEqualStrings(
+ "1.073741824GB",
+ try bufPrint(&b, "{}", .{fmtIntSizeDec(1024 * 1024 * 1024)}),
+ );
+ try expectEqualStrings(
+ "1GiB",
+ try bufPrint(&b, "{}", .{fmtIntSizeBin(1024 * 1024 * 1024)}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-decimal.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-decimal.zig
new file mode 100644
index 0000000..9533468
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-decimal.zig
@@ -0,0 +1,12 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "decimal float" {
+ var b: [4]u8 = undefined;
+ try expectEqualStrings(
+ "16.5",
+ try bufPrint(&b, "{d}", .{16.5}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-hex.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-hex.zig
new file mode 100644
index 0000000..06c577e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-hex.zig
@@ -0,0 +1,34 @@
+// hide-start
+const std = @import("std");
+const eql = std.mem.eql;
+const expect = std.testing.expect;
+
+// hide-end
+const bufPrint = std.fmt.bufPrint;
+const expectEqualStrings = std.testing.expectEqualStrings;
+
+test "hex" {
+ var b: [10]u8 = undefined;
+
+ try expectEqualStrings(
+ "FFFFFFFE",
+ try bufPrint(&b, "{X}", .{4294967294}),
+ );
+
+ try expectEqualStrings(
+ "fffffffe",
+ try bufPrint(&b, "{x}", .{4294967294}),
+ );
+
+ try expectEqualStrings(
+ "0xAAAAAAAA",
+ try bufPrint(&b, "0x{X}", .{2863311530}),
+ );
+
+ try expectEqualStrings(
+ "5a696721",
+ try bufPrint(&b, "{}", .{
+ std.fmt.fmtSliceHexLower("Zig!"),
+ }),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-pointer.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-pointer.zig
new file mode 100644
index 0000000..1454dcb
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-pointer.zig
@@ -0,0 +1,12 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "pointer fmt" {
+ var b: [16]u8 = undefined;
+ try expectEqualStrings(
+ "u8@deadbeef",
+ try bufPrint(&b, "{*}", .{@as(*u8, @ptrFromInt(0xDEADBEEF))}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-scientific.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-scientific.zig
new file mode 100644
index 0000000..edfaa97
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-scientific.zig
@@ -0,0 +1,13 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "scientific" {
+ var b: [16]u8 = undefined;
+
+ try expectEqualStrings(
+ try bufPrint(&b, "{e}", .{3.14159}),
+ "3.14159e0",
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-strings.zig b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-strings.zig
new file mode 100644
index 0000000..80cb1c4
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/14.formatting-specifiers-strings.zig
@@ -0,0 +1,15 @@
+// hide-start
+const std = @import("std");
+const eql = std.mem.eql;
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "string fmt" {
+ var b: [6]u8 = undefined;
+ const hello: [*:0]const u8 = "hello!";
+
+ try expectEqualStrings(
+ "hello!",
+ try bufPrint(&b, "{s}", .{hello}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/15-advanced-formatting.mdx b/website/versioned_docs/version-0.15/02-standard-library/15-advanced-formatting.mdx
new file mode 100644
index 0000000..2d2a826
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/15-advanced-formatting.mdx
@@ -0,0 +1,38 @@
+---
+pagination_next: build-system/build-modes
+---
+
+import CodeBlock from "@theme/CodeBlock";
+
+import Position from "!!raw-loader!./15.advanced-formatting-position.zig";
+import FillAlignmentWidth from "!!raw-loader!./15.advanced-formatting-fill-alignment-width.zig";
+import Precision from "!!raw-loader!./15.advanced-formatting-precision.zig";
+
+# Advanced Formatting
+
+So far we have only covered formatting specifiers. Format strings actually
+follow this format, where between each pair of square brackets is a parameter
+you have to replace with something.
+
+`{[position][specifier]:[fill][alignment][width].[precision]}`
+
+| Name | Meaning |
+| --------- | -------------------------------------------------------------------------------- |
+| Position | The index of the argument that should be inserted |
+| Specifier | A type-dependent formatting option |
+| Fill | A single character used for padding |
+| Alignment | One of three characters < ^ or >; these are for left, middle and right alignment |
+| Width | The total width of the field (characters) |
+| Precision | How many decimals a formatted number should have |
+
+Position usage.
+
+{Position}
+
+Fill, alignment and width being used.
+
+{FillAlignmentWidth}
+
+Using a specifier with precision.
+
+{Precision}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-fill-alignment-width.zig b/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-fill-alignment-width.zig
new file mode 100644
index 0000000..d96b2af
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-fill-alignment-width.zig
@@ -0,0 +1,23 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "fill, alignment, width" {
+ var b: [6]u8 = undefined;
+
+ try expectEqualStrings(
+ "hi! ",
+ try bufPrint(&b, "{s: <5}", .{"hi!"}),
+ );
+
+ try expectEqualStrings(
+ "_hi!__",
+ try bufPrint(&b, "{s:_^6}", .{"hi!"}),
+ );
+
+ try expectEqualStrings(
+ "!hi!",
+ try bufPrint(&b, "{s:!>4}", .{"hi!"}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-position.zig b/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-position.zig
new file mode 100644
index 0000000..79776ec
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-position.zig
@@ -0,0 +1,13 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "position" {
+ var b: [3]u8 = undefined;
+
+ try expectEqualStrings(
+ "aab",
+ try bufPrint(&b, "{0s}{0s}{1s}", .{ "a", "b" }),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-precision.zig b/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-precision.zig
new file mode 100644
index 0000000..0eff1d4
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/15.advanced-formatting-precision.zig
@@ -0,0 +1,12 @@
+// hide-start
+const std = @import("std");
+const expectEqualStrings = std.testing.expectEqualStrings;
+const bufPrint = std.fmt.bufPrint;
+// hide-end
+test "precision" {
+ var b: [4]u8 = undefined;
+ try expectEqualStrings(
+ "3.14",
+ try bufPrint(&b, "{d:.2}", .{3.14159}),
+ );
+}
diff --git a/website/versioned_docs/version-0.15/02-standard-library/_category_.json b/website/versioned_docs/version-0.15/02-standard-library/_category_.json
new file mode 100644
index 0000000..2b8098e
--- /dev/null
+++ b/website/versioned_docs/version-0.15/02-standard-library/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Standard Library",
+ "link": {
+ "description": "Zig's standard library in detail."
+ }
+}
\ No newline at end of file
diff --git a/website/versioned_docs/version-0.15/03-build-system/01-build-modes.md b/website/versioned_docs/version-0.15/03-build-system/01-build-modes.md
new file mode 100644
index 0000000..1328a2b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/01-build-modes.md
@@ -0,0 +1,21 @@
+---
+pagination_prev: standard-library/advanced-formatting
+---
+
+# Build Modes
+
+Zig provides four build modes, with debug being the default as it produces the
+shortest compile times.
+
+| | Runtime Safety | Optimizations |
+| ------------ | -------------- | ------------- |
+| Debug | Yes | No |
+| ReleaseSafe | Yes | Yes, Speed |
+| ReleaseSmall | No | Yes, Size |
+| ReleaseFast | No | Yes, Speed |
+
+These may be used with `zig run` and `zig test` with the arguments
+`-O ReleaseSafe`, `-O ReleaseSmall`, and `-O ReleaseFast`.
+
+Users are recommended to develop their software with runtime safety enabled,
+despite its small speed disadvantage.
diff --git a/website/versioned_docs/version-0.15/03-build-system/02-emitting-an-executable.md b/website/versioned_docs/version-0.15/03-build-system/02-emitting-an-executable.md
new file mode 100644
index 0000000..2064700
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/02-emitting-an-executable.md
@@ -0,0 +1,26 @@
+# Emitting an Executable
+
+The commands `zig build-exe`, `zig build-lib`, and `zig build-obj` can be used
+to output executables, libraries, and objects, respectively. These commands take
+in a source file and arguments.
+
+Some common arguments:
+
+- `-fsingle-threaded`, which asserts the binary is single-threaded. This will
+ turn thread-safety measures such as mutexes into no-ops.
+- `-fstrip`, which removes debug info from the binary.
+- `--dynamic`, which is used in conjunction with `zig build-lib` to output a
+ dynamic/shared library.
+
+Let's create a tiny hello world. Save this as `tiny-hello.zig`, and run
+`zig build-exe tiny-hello.zig -O ReleaseSmall -fstrip -fsingle-threaded`.
+
+```zig
+const std = @import("std");
+
+pub fn main() void {
+ std.io.getStdOut().writeAll(
+ "Hello World!",
+ ) catch unreachable;
+}
+```
diff --git a/website/versioned_docs/version-0.15/03-build-system/03-cross-compilation.md b/website/versioned_docs/version-0.15/03-build-system/03-cross-compilation.md
new file mode 100644
index 0000000..ef344f2
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/03-cross-compilation.md
@@ -0,0 +1,52 @@
+# Cross-compilation
+
+By default, Zig will compile for your combination of CPU and OS. This can be
+overridden by `-target`. Let's compile our tiny hello world to a 64-bit arm
+Linux platform.
+
+`zig build-exe .\tiny-hello.zig -O ReleaseSmall -fstrip -fsingle-threaded -target aarch64-linux`
+
+[QEMU](https://www.qemu.org/) or similar may be used to conveniently test
+executables made for foreign platforms.
+
+Some CPU architectures that you can cross-compile for:
+
+- `x86_64`
+- `arm`
+- `aarch64`
+- `i386`
+- `riscv64`
+- `wasm32`
+
+Some operating systems you can cross-compile for:
+
+- `linux`
+- `macos`
+- `windows`
+- `freebsd`
+- `netbsd`
+- `dragonfly`
+- `UEFI`
+
+Many other targets are available for compilation but aren't as well tested as
+of now. See
+[Zig's support table](https://ziglang.org/learn/overview/#wide-range-of-targets-supported)
+for more information; the list of well-tested targets is slowly expanding.
+
+As Zig compiles for your specific CPU by default, these binaries may not run on
+other computers with slightly different CPU architectures. It may be useful to
+instead specify a specific baseline CPU model for greater compatibility. Note:
+Choosing an older CPU architecture will bring greater compatibility, but means
+you also miss out on newer CPU instructions; there is an efficiency/speed versus
+compatibility trade-off here.
+
+Let's compile a binary for a sandybridge CPU (Intel x86_64, circa 2011), so we
+can be reasonably sure that someone with an x86_64 CPU can run our binary. Here
+we can use `native` in place of our CPU or OS, to use our system's.
+
+`zig build-exe tiny-hello.zig -target x86_64-native -mcpu sandybridge`
+
+Details on what architectures, OSes, CPUs, and ABIs (details on ABIs in the next
+chapter) are available can be found by running `zig targets`. Note: the output
+is long, and you may want to pipe it to a file, e.g.
+`zig targets > targets.json`.
diff --git a/website/versioned_docs/version-0.15/03-build-system/04-zig-build.mdx b/website/versioned_docs/version-0.15/03-build-system/04-zig-build.mdx
new file mode 100644
index 0000000..325746c
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/04-zig-build.mdx
@@ -0,0 +1,161 @@
+import CodeBlock from "@theme/CodeBlock";
+
+import Build from "!!raw-loader!./04.zig-build-hello/build.zig";
+import Hello from "!!raw-loader!./04.zig-build-hello/src/main.zig";
+
+# Zig Build
+
+The `zig build` system allows people to do more advanced things with their Zig
+projects, including:
+
+- Pulling in dependencies
+- Building multiple artifacts (e.g. building both a static and a dynamic library)
+- Providing additional configuration
+- Doing custom tasks at build time
+- Building with multiple steps (e.g. fetching and processing data before compiling)
+
+The Zig build system allows you to fulfil these more complex use cases, without
+bringing in any additional build tools or languages (e.g. cmake, python), all while making good
+use of the compiler's in-built caching system.
+
+# Hello Zig Build
+
+Using the Zig build system requires writing some Zig code. Let's create a
+directory structure as follows.
+
+```
+.
+├── build.zig
+└── src
+ └── main.zig
+```
+
+Defining a build function as shown below acts as our entry point to the build
+system, which will allow us to define a graph of "steps" for the _build runner_
+to perform. Place this code into `build.zig`.
+
+{Build}
+
+Place your executable's entry point in `src/main.zig`.
+
+{Hello}
+
+We can now run `zig build` which will output our executable.
+
+```
+$ zig build
+$ ./zig-out/bin/hello
+Hello, Zig Build!
+```
+
+# Target & Optimisation Options
+
+Previously, we've used `zig build-exe` with `-target` and `-O` to tell Zig what
+target and optimisation mode to use. When using the Zig build system, these settings are
+now passed into `b.addExecutable`.
+
+Most Zig projects will want to use these standard options.
+
+```zig
+ .target = b.standardTargetOptions(.{}),
+ .optimize = b.standardOptimizeOption(.{}),
+```
+
+When using `standardTargetOptions` and `standardOptimizeOption` your target will
+default to native, meaning that the target of the executable will match the
+computer that it was built on. The optimisation mode will default to debug.
+
+If you run `zig build --help`, you can see that these functions have registered
+project-specific build options.
+
+```
+Project-Specific Options:
+ -Dtarget=[string] The CPU architecture, OS, and ABI to build for
+ -Dcpu=[string] Target CPU features to add or subtract
+ -Doptimize=[enum] Prioritize performance, safety, or binary size (-O flag)
+ Supported Values:
+ Debug
+ ReleaseSafe
+ ReleaseFast
+ ReleaseSmall
+```
+
+We can now supply them via arguments, e.g.
+
+```
+zig build -Dtarget=x86_64-windows -Dcpu=x86_64_v3 -Doptimize=ReleaseSafe
+```
+
+# Adding an Option
+
+Thanks to the standard target and optimise options, we already have some useful
+build options. In more advanced projects, you may want to add your own
+project-specific options; here is a basic example of creating and using an option
+that changes the executable's name.
+
+```zig
+ const exe_name = b.option(
+ []const u8,
+ "exe_name",
+ "Name of the executable",
+ ) orelse "hello";
+
+ const exe = b.addExecutable(.{
+ .name = exe_name,
+ .root_source_file = b.path("src/main.zig"),
+ .target = b.standardTargetOptions(.{}),
+ .optimize = b.standardOptimizeOption(.{}),
+ });
+```
+
+If you now run `zig build --help`, we can see that the project-specific build
+options have been expanded to include `exe_name`.
+
+```
+Project-Specific Options:
+ -Dexe_name=[string] Name of the executable
+ -Dtarget=[string] The CPU architecture, OS, and ABI to build for
+```
+
+```
+$ zig build -Dtarget=x86_64-windows -Dexe_name="Hello!"
+$ file zig-out/bin/Hello\!.exe
+zig-out/bin/Hello!.exe: PE32+ executable (console) x86-64, for MS Windows, 7 sections
+```
+
+# Adding a Run Step
+
+We've previously used `zig run` as a convenient shortcut for calling `zig build-exe`
+and then running the resulting binary. We can quite easily do something similar
+using the Zig build system.
+
+```zig
+ b.installArtifact(exe);
+
+ const run_exe = b.addRunArtifact(exe);
+
+ const run_step = b.step("run", "Run the application");
+ run_step.dependOn(&run_exe.step);
+```
+
+```
+$ zig build run
+Hello, Zig Build!
+```
+
+The Zig build system uses a DAG (directed acyclic graph) of steps that it runs
+concurrently. Here we've created a step called "run", which depends on the
+`run_exe` step, which depends on our compile step.
+
+Let's have a look at the breakdown of steps in our build.
+
+```
+$ zig build run --summary all
+Hello, Zig Build!
+Build Summary: 3/3 steps succeeded
+run success
+└─ run hello success 471us MaxRSS:3M
+ └─ zig build-exe hello Debug native success 881ms MaxRSS:220M
+```
+
+We will see more advanced build graphs as we progress.
diff --git a/website/versioned_docs/version-0.15/03-build-system/04.zig-build-hello/build.zig b/website/versioned_docs/version-0.15/03-build-system/04.zig-build-hello/build.zig
new file mode 100644
index 0000000..fc8ce26
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/04.zig-build-hello/build.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+ const exe = b.addExecutable(.{
+ .name = "hello",
+ .root_source_file = b.path("src/main.zig"),
+ .target = b.standardTargetOptions(.{}),
+ .optimize = b.standardOptimizeOption(.{}),
+ });
+
+ b.installArtifact(exe);
+}
diff --git a/website/versioned_docs/version-0.15/03-build-system/04.zig-build-hello/src/main.zig b/website/versioned_docs/version-0.15/03-build-system/04.zig-build-hello/src/main.zig
new file mode 100644
index 0000000..c12d570
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/04.zig-build-hello/src/main.zig
@@ -0,0 +1,5 @@
+const std = @import("std");
+
+pub fn main() void {
+ std.debug.print("Hello, {s}!\n", .{"Zig Build"});
+}
diff --git a/website/versioned_docs/version-0.15/03-build-system/06-generating-documentation.mdx b/website/versioned_docs/version-0.15/03-build-system/06-generating-documentation.mdx
new file mode 100644
index 0000000..e8c9890
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/06-generating-documentation.mdx
@@ -0,0 +1,47 @@
+---
+pagination_next: working-with-c/abi
+title: Generating Documentation
+---
+
+import CodeBlock from "@theme/CodeBlock";
+
+import Main from "!!raw-loader!./06.generating-documentation/src/main.zig";
+import Build from "!!raw-loader!./06.generating-documentation/build.zig";
+
+# Generating Documentation
+
+Zig's documentation generation makes use of _doc comments_, using `///`. These
+doc comments will "attach" themselves to the declarations found under them.
+File-level doc comments can also be made at the top of a file with `//!`.
+
+The Zig compiler comes with documentation generation. This can be invoked by
+adding `-femit-docs` to your `zig build-{exe, lib, obj}` or `zig run`command.
+This documentation is saved into `./docs`, as a small static website.
+
+Here we will save this file as `src/main.zig` and build documentation for it with
+`zig build-lib -femit-docs src/main.zig`.
+
+{Main}
+
+We should now have a folder called `docs/`. To view these docs nicely, you may
+want to start a (local) static web server. Here's some commands you might be
+able to use on your system without installing anything new:
+
+- `caddy file-server --root docs/ --listen :8000`
+- `python -m http.server 8000 -d docs/`
+
+Take a few minutes to browse around. Notice that the left side of an error set
+merge will take precedence over the right side, when it comes to differing doc
+comments on the same error name. In our case this means that
+`OpenError.InvalidSheet` will have the doc comment from
+`OpenFileError.InvalidSheet`, rather than from `ParseError.InvalidSheet`.
+
+The Zig compiler itself has the `zig std` command, which automatically serves a
+web server with the standard library documentation, however as of writing this
+server isn't exposed to the build system. The Zig standard library documentation
+can also be found [here](https://ziglang.org/documentation/master/std/).
+
+When using Zig Build, generated docs can be found by using `getEmittedDocs()` on
+a Compile step. Here's an example of a `build.zig` that generates documentation:
+
+{Build}
diff --git a/website/versioned_docs/version-0.15/03-build-system/06.generating-documentation/build.zig b/website/versioned_docs/version-0.15/03-build-system/06.generating-documentation/build.zig
new file mode 100644
index 0000000..31e6326
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/06.generating-documentation/build.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const lib = b.addStaticLibrary(.{
+ .name = "lib",
+ .root_source_file = b.path("src/main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+ b.installArtifact(lib);
+
+ const install_docs = b.addInstallDirectory(.{
+ .source_dir = lib.getEmittedDocs(),
+ .install_dir = .prefix,
+ .install_subdir = "docs",
+ });
+
+ const docs_step = b.step("docs", "Install docs into zig-out/docs");
+ docs_step.dependOn(&install_docs.step);
+}
diff --git a/website/versioned_docs/version-0.15/03-build-system/06.generating-documentation/src/main.zig b/website/versioned_docs/version-0.15/03-build-system/06.generating-documentation/src/main.zig
new file mode 100644
index 0000000..17da5a4
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/06.generating-documentation/src/main.zig
@@ -0,0 +1,40 @@
+//! This module provides functions for dealing with spreadsheets.
+
+const std = @import("std");
+
+/// A spreadsheet position
+pub const Pos = struct {
+ /// (0-indexed) row
+ x: u32,
+ /// (0-indexed) column
+ y: u32,
+
+ /// The top-left position
+ pub const zero: Pos = .{ .x = 0, .y = 0 };
+
+ /// Illegal position
+ pub const invalid_pos: Pos = .{
+ .x = std.math.maxInt(u32),
+ .y = std.math.maxInt(u32),
+ };
+};
+
+pub const OpenFileError = error{
+ /// Unexpected file extension
+ InvalidSheet,
+ FileNotFound,
+};
+
+pub const ParseError = error{
+ /// File header invalid
+ InvalidSheet,
+ InvalidPosition,
+};
+
+pub const OpenError = OpenFileError || ParseError;
+
+pub fn readCell(file: std.fs.File, pos: Pos) OpenError![]const u8 {
+ _ = file;
+ _ = pos;
+ @panic("todo");
+}
diff --git a/website/versioned_docs/version-0.15/03-build-system/_category_.json b/website/versioned_docs/version-0.15/03-build-system/_category_.json
new file mode 100644
index 0000000..8349dc6
--- /dev/null
+++ b/website/versioned_docs/version-0.15/03-build-system/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Build System",
+ "link": {
+ "description": "Getting started with the Zig programming language."
+ }
+}
\ No newline at end of file
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/01-abi.md b/website/versioned_docs/version-0.15/04-working-with-c/01-abi.md
new file mode 100644
index 0000000..937bf7f
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/01-abi.md
@@ -0,0 +1,29 @@
+---
+pagination_prev: build-system/generating-documentation
+---
+
+
+# ABI
+
+An ABI _(application binary interface)_ is a standard, pertaining to:
+
+- The in-memory layout of types (i.e. a type's size, alignment, offsets, and the
+ layouts of its fields)
+- The in-linker naming of symbols (e.g. name mangling)
+- The calling conventions of functions (i.e. how a function call works at a
+ binary level)
+
+By defining these rules and not breaking them, an ABI is said to be stable, and
+this can be used to, for example, reliably link together multiple libraries,
+executables, or objects which were compiled separately (potentially on different
+machines or using different compilers). This allows for FFI _(foreign function
+interface)_ to take place, where we can share code between programming
+languages.
+
+Zig natively supports C ABIs for `extern` things; which C ABI is used depends on
+the target you are compiling for (e.g. CPU architecture, operating system). This
+allows for near-seamless interoperation with code that was not written in Zig;
+the usage of C ABIs is standard amongst programming languages.
+
+Zig internally does not use an ABI, meaning code should explicitly conform to a
+C ABI where reproducible and defined binary-level behaviour is needed.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/02-c-primitive-types.md b/website/versioned_docs/version-0.15/04-working-with-c/02-c-primitive-types.md
new file mode 100644
index 0000000..de72fa5
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/02-c-primitive-types.md
@@ -0,0 +1,20 @@
+# C Primitive Types
+
+Zig provides special `c_` prefixed types for conforming to the C ABI. These do
+not have fixed sizes but rather change in size depending on the ABI being used.
+
+| Type | C Equivalent | Minimum Size (bits) |
+| ------------ | ------------------ | ------------------- |
+| c_short | short | 16 |
+| c_ushort | unsigned short | 16 |
+| c_int | int | 16 |
+| c_uint | unsigned int | 16 |
+| c_long | long | 32 |
+| c_ulong | unsigned long | 32 |
+| c_longlong | long long | 64 |
+| c_ulonglong | unsigned long long | 64 |
+| c_longdouble | long double | N/A |
+| anyopaque | void | N/A |
+
+Note: C's void (and Zig's `anyopaque`) has an unknown non-zero size. Zig's
+`void` is a true zero-sized type.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/03-calling-conventions.md b/website/versioned_docs/version-0.15/04-working-with-c/03-calling-conventions.md
new file mode 100644
index 0000000..ebae90d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/03-calling-conventions.md
@@ -0,0 +1,19 @@
+# Calling conventions
+
+Calling conventions describe how functions are called. This includes how
+arguments are supplied to the function (i.e. where they go - in registers or on
+the stack, and how), and how the return value is received.
+
+In Zig, the attribute `callconv` may be given to a function. The calling
+conventions available may be found in
+[std.builtin.CallingConvention](https://ziglang.org/documentation/master/std/#std.builtin.CallingConvention).
+Here we make use of the cdecl calling convention.
+
+```zig
+fn add(a: u32, b: u32) callconv(.C) u32 {
+ return a + b;
+}
+```
+
+Marking your functions with the C calling convention is crucial when you're
+calling Zig from C.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/04-extern-structs.md b/website/versioned_docs/version-0.15/04-working-with-c/04-extern-structs.md
new file mode 100644
index 0000000..5eacb87
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/04-extern-structs.md
@@ -0,0 +1,44 @@
+# Extern Structs
+
+Normal structs in Zig do not have a defined layout; `extern` structs are
+required for when you want the layout of your struct to match the layout of your
+C ABI.
+
+Let's create an extern struct. This test should be run with `x86_64` with a
+`gnu` ABI, which can be done with `-target x86_64-native-gnu`.
+
+```zig
+const expect = @import("std").testing.expect;
+
+const Data = extern struct { a: i32, b: u8, c: f32, d: bool, e: bool };
+
+test "hmm" {
+ const x = Data{
+ .a = 10005,
+ .b = 42,
+ .c = -10.5,
+ .d = false,
+ .e = true,
+ };
+ const z = @as([*]const u8, @ptrCast(&x));
+
+ try expect(@as(*const i32, @ptrCast(@alignCast(z))).* == 10005);
+ try expect(@as(*const u8, @ptrCast(@alignCast(z + 4))).* == 42);
+ try expect(@as(*const f32, @ptrCast(@alignCast(z + 8))).* == -10.5);
+ try expect(@as(*const bool, @ptrCast(@alignCast(z + 12))).* == false);
+ try expect(@as(*const bool, @ptrCast(@alignCast(z + 13))).* == true);
+}
+```
+
+This is what the memory inside our `x` value looks like.
+
+| Field | a | a | a | a | b | | | | c | c | c | c | d | e | | |
+| ----- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
+| Bytes | 15 | 27 | 00 | 00 | 2A | 00 | 00 | 00 | 00 | 00 | 28 | C1 | 00 | 01 | 00 | 00 |
+
+Note how there are gaps in the middle and at the end - this is called "padding".
+The data in this padding is undefined memory, and won't always be zero.
+
+As our `x` value is that of an extern struct, we could safely pass it into a C
+function expecting a `Data`, providing the C function was also compiled with the
+same `gnu` ABI and CPU arch.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/05-alignment.md b/website/versioned_docs/version-0.15/04-working-with-c/05-alignment.md
new file mode 100644
index 0000000..062e024
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/05-alignment.md
@@ -0,0 +1,51 @@
+# Alignment
+
+For circuitry reasons, CPUs access primitive values at specific multiples in
+memory. This could mean, for example, that the address of an `f32` value must be
+a multiple of 4, meaning `f32` has an alignment of 4. This so-called "natural
+alignment" of primitive data types depends on CPU architecture. All alignments
+are powers of 2.
+
+Data of a larger alignment also has the alignment of every smaller alignment;
+for example, a value which has an alignment of 16 also has an alignment of 8, 4,
+2 and 1.
+
+We can make specially aligned data by using the `align(x)` property. Here we are
+making data with a greater alignment.
+
+```zig
+const a1: u8 align(8) = 100;
+const a2 align(8) = @as(u8, 100);
+```
+
+And making data with a lesser alignment. Note: Creating data of a lesser
+alignment isn't particularly useful.
+
+```zig
+const b1: u64 align(1) = 100;
+const b2 align(1) = @as(u64, 100);
+```
+
+Like `const`, `align` is also a property of pointers.
+
+```zig
+test "aligned pointers" {
+ const a: u32 align(8) = 5;
+ try expect(@TypeOf(&a) == *align(8) const u32);
+}
+```
+
+Let's make use of a function expecting an aligned pointer.
+
+```zig
+fn total(a: *align(64) const [64]u8) u32 {
+ var sum: u32 = 0;
+ for (a) |elem| sum += elem;
+ return sum;
+}
+
+test "passing aligned data" {
+ const x align(64) = [_]u8{10} ** 64;
+ try expect(total(&x) == 640);
+}
+```
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/06-packed-structs.md b/website/versioned_docs/version-0.15/04-working-with-c/06-packed-structs.md
new file mode 100644
index 0000000..dabe613
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/06-packed-structs.md
@@ -0,0 +1,34 @@
+# Packed Structs
+
+By default, all struct fields in Zig are naturally aligned to that of
+[`@alignOf(FieldType)`](https://ziglang.org/documentation/master/#alignOf) (the
+ABI size), but without a defined layout. Sometimes you may want to have struct
+fields with a defined layout that do not conform to your C ABI. `packed` structs
+allow you to have extremely precise control of your struct fields, allowing you
+to place your fields on a bit-by-bit basis.
+
+Inside packed structs, Zig's integers take their bit-width in space (i.e. a
+`u12` has an [`@bitSizeOf`](https://ziglang.org/documentation/master/#bitSizeOf)
+of 12, meaning it will take up 12 bits in the packed struct). Bools also take up
+1 bit, meaning you can implement bit flags easily.
+
+```zig
+const MovementState = packed struct {
+ running: bool,
+ crouching: bool,
+ jumping: bool,
+ in_air: bool,
+};
+
+test "packed struct size" {
+ try expect(@sizeOf(MovementState) == 1);
+ try expect(@bitSizeOf(MovementState) == 4);
+ const state = MovementState{
+ .running = true,
+ .crouching = true,
+ .jumping = true,
+ .in_air = true,
+ };
+ _ = state;
+}
+```
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/07-bit-aligned-pointers.md b/website/versioned_docs/version-0.15/04-working-with-c/07-bit-aligned-pointers.md
new file mode 100644
index 0000000..c313c36
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/07-bit-aligned-pointers.md
@@ -0,0 +1,33 @@
+# Bit Aligned Pointers
+
+Similar to aligned pointers, bit-aligned pointers have extra information in
+their type, which informs how to access the data. These are necessary when the
+data is not byte-aligned. Bit alignment information is often needed to address
+fields inside of packed structs.
+
+```zig
+test "bit aligned pointers" {
+ var x = MovementState{
+ .running = false,
+ .crouching = false,
+ .jumping = false,
+ .in_air = false,
+ };
+
+ const running = &x.running;
+ running.* = true;
+
+ const crouching = &x.crouching;
+ crouching.* = true;
+
+ try expect(@TypeOf(running) == *align(1:0:1) bool);
+ try expect(@TypeOf(crouching) == *align(1:1:1) bool);
+
+ try expect(@import("std").meta.eql(x, .{
+ .running = true,
+ .crouching = true,
+ .jumping = false,
+ .in_air = false,
+ }));
+}
+```
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/08-c-pointers.md b/website/versioned_docs/version-0.15/04-working-with-c/08-c-pointers.md
new file mode 100644
index 0000000..c706b61
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/08-c-pointers.md
@@ -0,0 +1,16 @@
+# C Pointers
+
+Up until now, we have used the following kinds of pointers:
+
+- single item pointers - `*T`
+- many item pointers - `[*]T`
+- slices - `[]T`
+
+Unlike the aforementioned pointers, C pointers cannot deal with specially
+aligned data and may point to the address `0`. C pointers coerce back and forth
+between integers, and also coerce to single and multi item pointers. When a C
+pointer of value `0` is coerced to a non-optional pointer, this is detectable
+illegal behaviour.
+
+Outside of automatically translated C code, the usage of `[*c]` is almost always
+a bad idea, and should almost never be used.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/09-translate-c.md b/website/versioned_docs/version-0.15/04-working-with-c/09-translate-c.md
new file mode 100644
index 0000000..45f899c
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/09-translate-c.md
@@ -0,0 +1,35 @@
+# Translate-C
+
+Zig provides the command `zig translate-c` for automatic translation from C
+source code.
+
+Create the file `main.c` with the following contents.
+
+```c
+#include
+
+void int_sort(int* array, size_t count) {
+ for (int i = 0; i < count - 1; i++) {
+ for (int j = 0; j < count - i - 1; j++) {
+ if (array[j] > array[j+1]) {
+ int temp = array[j];
+ array[j] = array[j+1];
+ array[j+1] = temp;
+ }
+ }
+ }
+}
+```
+
+Run the command `zig translate-c main.c` to get the equivalent Zig code output
+to your console (stdout). You may wish to pipe this into a file with
+`zig translate-c main.c > int_sort.zig` (warning for Windows users: piping in
+PowerShell will produce a file with the incorrect encoding - use your editor to
+correct this).
+
+In another file you could use `@import("int_sort.zig")` to use this function.
+
+Currently the code produced may be unnecessarily verbose, though translate-c
+successfully translates most C code into Zig. You may wish to use translate-c to
+produce Zig code before editing it into more idiomatic code; a gradual transfer
+from C to Zig within a codebase is a supported use case.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/10-c-import.md b/website/versioned_docs/version-0.15/04-working-with-c/10-c-import.md
new file mode 100644
index 0000000..7c00168
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/10-c-import.md
@@ -0,0 +1,25 @@
+# cImport
+
+Zig's [`@cImport`](https://ziglang.org/documentation/master/#cImport) builtin is
+unique in that it takes in an expression, which can only take in
+[`@cInclude`](https://ziglang.org/documentation/master/#cInclude),
+[`@cDefine`](https://ziglang.org/documentation/master/#cDefine), and
+[`@cUndef`](https://ziglang.org/documentation/master/#cUndef). This works
+similarly to translate-c, translating C code to Zig under the hood.
+
+[`@cInclude`](https://ziglang.org/documentation/master/#cInclude) takes in a
+path string and adds the path to the includes list.
+
+[`@cDefine`](https://ziglang.org/documentation/master/#cDefine) and
+[`@cUndef`](https://ziglang.org/documentation/master/#cUndef) define and
+undefine things for the import.
+
+These three functions work exactly as you'd expect them to work within C code.
+
+Similar to [`@import`](https://ziglang.org/documentation/master/#import), this
+returns a struct type with declarations. It is typically recommended to only use
+one instance of [`@cImport`](https://ziglang.org/documentation/master/#cImport)
+in an application to avoid symbol collisions; the types generated within one
+cImport will not be equivalent to those generated in another.
+
+cImport is only available when linking libc.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/11-linking-libc.md b/website/versioned_docs/version-0.15/04-working-with-c/11-linking-libc.md
new file mode 100644
index 0000000..f65ebd0
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/11-linking-libc.md
@@ -0,0 +1,5 @@
+# Linking libc
+
+Linking libc can be done via the command line via `-lc`, or via `build.zig`
+using `exe.linkLibC();`. The libc used is that of the compilation's target; Zig
+provides libc for many targets.
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/12-zig-cc.md b/website/versioned_docs/version-0.15/04-working-with-c/12-zig-cc.md
new file mode 100644
index 0000000..adc090d
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/12-zig-cc.md
@@ -0,0 +1,24 @@
+---
+pagination_next: async/introduction
+---
+
+# Zig cc, Zig c++
+
+The Zig executable comes with Clang embedded inside it alongside libraries and
+headers required to cross-compile for other operating systems and architectures.
+
+This means that not only can `zig cc` and `zig c++` compile C and C++ code (with
+Clang-compatible arguments), but it can also do so while respecting Zig's target
+triple argument; the single Zig binary that you have installed has the power to
+compile for several different targets without the need to install multiple
+versions of the compiler or any addons. Using `zig cc` and `zig c++` also makes
+use of Zig's caching system to speed up your workflow.
+
+Using Zig, one can easily construct a cross-compiling toolchain for languages
+that use a C and/or C++ compiler.
+
+Some examples in the wild:
+
+- [Using zig cc to cross compile LuaJIT to aarch64-linux from x86_64-linux](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html)
+
+- [Using zig cc and zig c++ in combination with cgo to cross compile hugo from aarch64-macos to x86_64-linux, with full static linking](https://twitter.com/croloris/status/1349861344330330114)
diff --git a/website/versioned_docs/version-0.15/04-working-with-c/_category_.json b/website/versioned_docs/version-0.15/04-working-with-c/_category_.json
new file mode 100644
index 0000000..605b9bf
--- /dev/null
+++ b/website/versioned_docs/version-0.15/04-working-with-c/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Working with C",
+ "link": {
+ "description": "Zig has been designed from the ground up with C interop as a first-class feature. In this section, we will go over how this works."
+ }
+}
\ No newline at end of file
diff --git a/website/versioned_docs/version-0.15/05-async/01-introduction.md b/website/versioned_docs/version-0.15/05-async/01-introduction.md
new file mode 100644
index 0000000..776d36b
--- /dev/null
+++ b/website/versioned_docs/version-0.15/05-async/01-introduction.md
@@ -0,0 +1,44 @@
+---
+pagination_prev: working-with-c/zig-cc
+---
+
+# Introduction
+
+:::danger
+
+Zig's async features have not been present in the compiler for multiple major
+versions. There is currently no estimate on when async will be added back to the
+compiler; async's future is unclear. The following code will not compile with Zig
+0.11 or Zig 0.12.
+
+:::
+
+A functioning understanding of Zig's async requires familiarity with the concept
+of the call stack. If you have not heard of this before,
+[check out the wikipedia page](https://en.wikipedia.org/wiki/Call_stack).
+
+
+
+A traditional function call comprises of three things:
+
+1. Initiate the called function with its arguments, pushing the function's stack
+ frame
+2. Transfer control to the function
+3. Upon function completion, hand control back to the caller, retrieving the
+ function's return value and popping the function's stack frame
+
+With Zig's async functions we can do more than this, with the transfer of
+control being an ongoing two-way conversation (i.e. we can give control to the
+function and take it back multiple times). Because of this, special
+considerations must be made when calling a function in an async context; we can
+no longer push and pop the stack frame as normal (as the stack is volatile, and
+things "above" the current stack frame may be overwritten), instead explicitly
+storing the async function's frame. While most people won't make use of its full
+feature set, this style of async is useful for creating more powerful constructs
+such as event loops.
+
+The style of Zig's async may be described as suspendible stackless coroutines.
+Zig's async is very different to something like an OS thread which has a stack,
+and can only be suspended by the kernel. Furthermore, Zig's async is there to
+provide you with control flow structures and code generation; async does not
+imply parallelism or the usage of threads.
diff --git a/website/versioned_docs/version-0.15/05-async/02-suspend-resume.md b/website/versioned_docs/version-0.15/05-async/02-suspend-resume.md
new file mode 100644
index 0000000..16b8650
--- /dev/null
+++ b/website/versioned_docs/version-0.15/05-async/02-suspend-resume.md
@@ -0,0 +1,55 @@
+# Suspend / Resume
+
+In the previous section we talked of how async functions can give control back
+to the caller, and how the async function can later take control back. This
+functionality is provided by the keywords
+[`suspend`, and `resume`](https://ziglang.org/documentation/master/#Suspend-and-Resume).
+When a function suspends, control flow returns to wherever it was last resumed;
+when a function is called via an `async` invocation, this is an implicit resume.
+
+The comments in these examples indicate the order of execution. There are a few
+things to take in here:
+
+- The `async` keyword is used to invoke functions in an async context.
+- `async func()` returns the function's frame.
+- We must store this frame.
+- The `resume` keyword is used on the frame, whereas `suspend` is used from the
+ called function.
+
+This example has a suspend, but no matching resume.
+
+```zig
+const expect = @import("std").testing.expect;
+
+var foo: i32 = 1;
+
+test "suspend with no resume" {
+ var frame = async func(); //1
+ _ = frame;
+ try expect(foo == 2); //4
+}
+
+fn func() void {
+ foo += 1; //2
+ suspend {} //3
+ foo += 1; //never reached!
+}
+```
+
+In well formed code, each suspend is matched with a resume.
+
+```zig
+var bar: i32 = 1;
+
+test "suspend with resume" {
+ var frame = async func2(); //1
+ resume frame; //4
+ try expect(bar == 3); //6
+}
+
+fn func2() void {
+ bar += 1; //2
+ suspend {} //3
+ bar += 1; //5
+}
+```
diff --git a/website/versioned_docs/version-0.15/05-async/03-async-await.md b/website/versioned_docs/version-0.15/05-async/03-async-await.md
new file mode 100644
index 0000000..4e245a1
--- /dev/null
+++ b/website/versioned_docs/version-0.15/05-async/03-async-await.md
@@ -0,0 +1,29 @@
+# Async / Await
+
+Similar to how well formed code has a suspend for every resume, each `async`
+function invocation with a return value must be matched with an `await`. The
+value yielded by `await` on the async frame corresponds to the function's
+return.
+
+You may notice that `func3` here is a normal function (i.e. it has no suspend
+points - it is not an async function). Despite this, `func3` can work as an
+async function when called from an async invocation; the calling convention of
+`func3` doesn't have to be changed to async - `func3` can be of any calling
+convention.
+
+```zig
+fn func3() u32 {
+ return 5;
+}
+
+test "async / await" {
+ var frame = async func3();
+ try expect(await frame == 5);
+}
+```
+
+Using `await` on an async frame of a function which may suspend is only possible
+from async functions. As such, functions that use `await` on the frame of an
+async function are also considered async functions. If you can be sure that the
+potential suspend doesn't happen, `nosuspend await` will stop this from
+happening.
diff --git a/website/versioned_docs/version-0.15/05-async/04-nosuspend.md b/website/versioned_docs/version-0.15/05-async/04-nosuspend.md
new file mode 100644
index 0000000..0eb6531
--- /dev/null
+++ b/website/versioned_docs/version-0.15/05-async/04-nosuspend.md
@@ -0,0 +1,77 @@
+# Nosuspend
+
+When calling a function which is determined to be async (i.e. it may suspend)
+without an `async` invocation, the function which called it is also treated as
+being async. When a function of a concrete (non-async) calling convention is
+determined to have suspend points, this is a compile error as async requires its
+own calling convention. This means, for example, that main cannot be async.
+
+
+
+```zig
+pub fn main() !void {
+ suspend {}
+}
+```
+
+(compiled from windows)
+
+```
+C:\zig\lib\zig\std\start.zig:165:1: error: function with calling convention 'Stdcall' cannot be async
+fn WinStartup() callconv(.Stdcall) noreturn {
+^
+C:\zig\lib\zig\std\start.zig:173:65: note: async function call here
+ std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain());
+ ^
+C:\zig\lib\zig\std\start.zig:276:12: note: async function call here
+ return @call(.{ .modifier = .always_inline }, callMain, .{});
+ ^
+C:\zig\lib\zig\std\start.zig:334:37: note: async function call here
+ const result = root.main() catch |err| {
+ ^
+.\main.zig:12:5: note: suspends here
+ suspend {}
+ ^
+```
+
+If you want to call an async function without using an `async` invocation, and
+without the caller of the function also being async, the `nosuspend` keyword
+comes in handy. This allows the caller of the async function to not also be
+async, by asserting that the potential suspends do not happen.
+
+
+
+```zig
+const std = @import("std");
+
+fn doTicksDuration(ticker: *u32) i64 {
+ const start = std.time.milliTimestamp();
+
+ while (ticker.* > 0) {
+ suspend {}
+ ticker.* -= 1;
+ }
+
+ return std.time.milliTimestamp() - start;
+}
+
+pub fn main() !void {
+ var ticker: u32 = 0;
+ const duration = nosuspend doTicksDuration(&ticker);
+}
+```
+
+In the above code if we change the value of `ticker` to be above 0, this is
+detectable illegal behaviour. If we run that code, we will have an error like
+this in safe build modes. Similar to other illegal behaviours in Zig, having
+these happen in unsafe modes will result in undefined behaviour.
+
+```
+async function called in nosuspend scope suspended
+.\main.zig:16:47: 0x7ff661dd3414 in main (main.obj)
+ const duration = nosuspend doTicksDuration(&ticker);
+ ^
+C:\zig\lib\zig\std\start.zig:173:65: 0x7ff661dd18ce in std.start.WinStartup (main.obj)
+ std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain());
+ ^
+```
diff --git a/website/versioned_docs/version-0.15/05-async/05-frames-suspend.md b/website/versioned_docs/version-0.15/05-async/05-frames-suspend.md
new file mode 100644
index 0000000..9adbd9a
--- /dev/null
+++ b/website/versioned_docs/version-0.15/05-async/05-frames-suspend.md
@@ -0,0 +1,91 @@
+# Async Frames, Suspend Blocks
+
+`@Frame(function)` returns the frame type of the function. This works for async
+functions, and functions without a specific calling convention.
+
+```zig
+fn add(a: i32, b: i32) i64 {
+ return a + b;
+}
+
+test "@Frame" {
+ var frame: @Frame(add) = async add(1, 2);
+ try expect(await frame == 3);
+}
+```
+
+[`@frame()`](https://ziglang.org/documentation/master/#frame) returns a pointer
+to the frame of the current function. Similar to `suspend` points, if this call
+is found in a function then it is inferred as being async. All pointers to
+frames coerce to the special type `anyframe`, which you can use `resume` upon.
+
+This allows us to, for example, write a function that resumes itself.
+
+```zig
+fn double(value: u8) u9 {
+ suspend {
+ resume @frame();
+ }
+ return value * 2;
+}
+
+test "@frame 1" {
+ var f = async double(1);
+ try expect(nosuspend await f == 2);
+}
+```
+
+Or, more interestingly, we can use it to tell other functions to resume us. Here
+we're introducing **suspend blocks**. Upon entering a suspend block, the async
+function is already considered suspended (i.e. it can be resumed). This means
+that we can have our function resumed by something other than the last resumer.
+
+```zig
+const std = @import("std");
+
+fn callLater(comptime laterFn: fn () void, ms: u64) void {
+ suspend {
+ wakeupLater(@frame(), ms);
+ }
+ laterFn();
+}
+
+fn wakeupLater(frame: anyframe, ms: u64) void {
+ std.time.sleep(ms * std.time.ns_per_ms);
+ resume frame;
+}
+
+fn alarm() void {
+ std.debug.print("Time's Up!\n", .{});
+}
+
+test "@frame 2" {
+ nosuspend callLater(alarm, 1000);
+}
+```
+
+Using the `anyframe` data type can be thought of as a kind of type erasure, in
+that we are no longer sure of the concrete type of the function or the function
+frame. This is useful as it still allows us to resume the frame - in a lot of
+code we will not care about the details and will just want to resume it. This
+gives us a single concrete type which we can use for our async logic.
+
+The natural drawback of `anyframe` is that we have lost type information, and we
+no longer know what the return type of the function is. This means we cannot
+await an `anyframe`. Zig's solution to this is the `anyframe->T` types, where
+the `T` is the return type of the frame.
+
+```zig
+fn zero(comptime x: anytype) x {
+ return 0;
+}
+
+fn awaiter(x: anyframe->f32) f32 {
+ return nosuspend await x;
+}
+
+test "anyframe->T" {
+ var frame = async zero(f32);
+ try expect(awaiter(&frame) == 0);
+}
+```
diff --git a/website/versioned_docs/version-0.15/05-async/06-basic-event-loop.md b/website/versioned_docs/version-0.15/05-async/06-basic-event-loop.md
new file mode 100644
index 0000000..1c94354
--- /dev/null
+++ b/website/versioned_docs/version-0.15/05-async/06-basic-event-loop.md
@@ -0,0 +1,114 @@
+# Basic Event Loop Implementation
+
+An event loop is a design pattern in which events are dispatched and/or waited
+upon. This will mean some kind of service or runtime that resumes suspended
+async frames when conditions are met. This is the most powerful and useful use
+case of Zig's async.
+
+Here we will implement a basic event loop. This one will allow us to submit
+tasks to be executed in a given amount of time. We will use this to submit pairs
+of tasks which will print the time since the program's start. Here is an example
+of the output.
+
+```
+[task-pair b] it is now 499 ms since start!
+[task-pair a] it is now 1000 ms since start!
+[task-pair b] it is now 1819 ms since start!
+[task-pair a] it is now 2201 ms since start!
+```
+
+Here is the implementation.
+
+
+
+```zig
+const std = @import("std");
+
+// used to get monotonic time, as opposed to wall-clock time
+var timer: ?std.time.Timer = null;
+fn nanotime() u64 {
+ if (timer == null) {
+ timer = std.time.Timer.start() catch unreachable;
+ }
+ return timer.?.read();
+}
+
+// holds the frame, and the nanotime of
+// when the frame should be resumed
+const Delay = struct {
+ frame: anyframe,
+ expires: u64,
+};
+
+// suspend the caller, to be resumed later by the event loop
+fn waitForTime(time_ms: u64) void {
+ suspend timer_queue.add(Delay{
+ .frame = @frame(),
+ .expires = nanotime() + (time_ms * std.time.ns_per_ms),
+ }) catch unreachable;
+}
+
+fn waitUntilAndPrint(
+ time1: u64,
+ time2: u64,
+ name: []const u8,
+) void {
+ const start = nanotime();
+
+ // suspend self, to be woken up when time1 has passed
+ waitForTime(time1);
+ std.debug.print(
+ "[{s}] it is now {} ms since start!\n",
+ .{ name, (nanotime() - start) / std.time.ns_per_ms },
+ );
+
+ // suspend self, to be woken up when time2 has passed
+ waitForTime(time2);
+ std.debug.print(
+ "[{s}] it is now {} ms since start!\n",
+ .{ name, (nanotime() - start) / std.time.ns_per_ms },
+ );
+}
+
+fn asyncMain() void {
+ // stores the async frames of our tasks
+ var tasks = [_]@Frame(waitUntilAndPrint){
+ async waitUntilAndPrint(1000, 1200, "task-pair a"),
+ async waitUntilAndPrint(500, 1300, "task-pair b"),
+ };
+ // |*t| is used, as |t| would be a *const @Frame(...)
+ // which cannot be awaited upon
+ for (tasks) |*t| await t;
+}
+
+// priority queue of tasks
+// lower .expires => higher priority => to be executed before
+var timer_queue: std.PriorityQueue(Delay, void, cmp) = undefined;
+fn cmp(context: void, a: Delay, b: Delay) std.math.Order {
+ _ = context;
+ return std.math.order(a.expires, b.expires);
+}
+
+pub fn main() !void {
+ timer_queue = std.PriorityQueue(Delay, void, cmp).init(
+ std.heap.page_allocator, undefined
+ );
+ defer timer_queue.deinit();
+
+ var main_task = async asyncMain();
+
+ // the body of the event loop
+ // pops the task which is to be next executed
+ while (timer_queue.removeOrNull()) |delay| {
+ // wait until it is time to execute next task
+ const now = nanotime();
+ if (now < delay.expires) {
+ std.time.sleep(delay.expires - now);
+ }
+ // execute next task
+ resume delay.frame;
+ }
+
+ nosuspend await main_task;
+}
+```
diff --git a/website/versioned_docs/version-0.15/05-async/_category_.json b/website/versioned_docs/version-0.15/05-async/_category_.json
new file mode 100644
index 0000000..3de56e7
--- /dev/null
+++ b/website/versioned_docs/version-0.15/05-async/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Async",
+ "link": {
+ "description": "Details on how Zig's async features work."
+ }
+}
\ No newline at end of file
diff --git a/website/versioned_sidebars/version-0.15-sidebars.json b/website/versioned_sidebars/version-0.15-sidebars.json
new file mode 100644
index 0000000..e6a5d91
--- /dev/null
+++ b/website/versioned_sidebars/version-0.15-sidebars.json
@@ -0,0 +1,8 @@
+{
+ "tutorialSidebar": [
+ {
+ "type": "autogenerated",
+ "dirName": "."
+ }
+ ]
+}
\ No newline at end of file