# Learn Rux
::warning
Rux is still in active development. As the language matures some syntax and features may change, and the course changes with it — every lesson is checked against the current compiler.
::
Welcome to Rux — a fast, compiled, strongly typed, multi-paradigm language that ships as a single small binary. This course takes you from installing the toolchain to writing complete programs. It assumes no Rux at all, and only a little programming: if you know what a variable and a loop are, you are ready.
## How the course works
Every lesson teaches **one idea** and is backed by **one small package** in the [Rux Examples repository](https://github.com/rux-lang/Examples){rel=""nofollow""}. The package is a real program: it compiles, it runs, and its comments explain each line. A lesson page walks you through that program, shows what it prints, and points out the mistakes beginners usually make.
- **Read** the lesson page, top to bottom.
- **Run** the package with `rux run` and compare the output with the page.
- **Change** something — a value, a condition, a type — and run it again. Breaking a program on purpose and reading the compiler's message is one of the fastest ways to learn.
- **Move on** with the *Next* link at the bottom of the page. Each lesson lists the lessons it needs, so you can also jump around.
```mermaid
flowchart LR
read["Read the lesson"] --> run["rux run"]
run --> compare["Compare the output"]
compare --> change["Change the code"]
change --> check["rux check"]
check -- "error: read the message" --> change
check -- "it compiles" --> run
compare -- "understood" --> next["Next lesson"]
```
## The road map
Parts 1–17 build the language step by step and are best read in order. Parts 18–24 tour the standard packages: once the language parts are done, take them in any order. Part 25 holds complete small programs, each one a *checkpoint* you can attempt as soon as you have finished the part marked with its flag.
:learn-roadmap
## Start here
::steps{level="3"}
### Install the toolchain
Install `rux` on [Windows](https://rux-lang.dev/docs/learn/install/windows), [Linux](https://rux-lang.dev/docs/learn/install/linux), [macOS](https://rux-lang.dev/docs/learn/install/macos) or [FreeBSD](https://rux-lang.dev/docs/learn/install/freebsd) — or [build it from source](https://rux-lang.dev/docs/learn/build) on any other platform. Prebuilt binaries cover x86-64 and AArch64.
### Set up your editor
Add Rux syntax highlighting to [Visual Studio Code](https://rux-lang.dev/docs/learn/editors/vscode), [Sublime Text](https://rux-lang.dev/docs/learn/editors/sublime) or [Zed](https://rux-lang.dev/docs/learn/editors/zed).
### Build your first project
[Create, build and run a project](https://rux-lang.dev/docs/learn/first-project) of your own, then clone the Examples repository the lessons are built on.
### Take the first lesson
Start with [1.1 Hello, World](https://rux-lang.dev/docs/learn/hello) and follow the *Next* links from there.
::
::tip
**No install yet?**:br
The [Playground](https://rux-lang.dev/play) runs the compiler in your browser. Paste any snippet from a lesson to try it straight away.
::
## Checkpoint projects
Each project in Part 25 combines what the earlier parts taught into one complete program. Try to write it yourself first, then compare with the lesson.
| After | Projects |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 3. Control flow | [Thanks](https://rux-lang.dev/docs/learn/thanks), [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz) |
| 4. Functions | [Temperature](https://rux-lang.dev/docs/learn/temperature) |
| 5. Sequences | [Prime](https://rux-lang.dev/docs/learn/prime) |
| 9. Errors | [Calculator](https://rux-lang.dev/docs/learn/calculator) |
| 16. Numbers | [Circle](https://rux-lang.dev/docs/learn/circle), [Quadratic](https://rux-lang.dev/docs/learn/quadratic) |
| 17. Collections | [Word count](https://rux-lang.dev/docs/learn/word-count), [Inventory](https://rux-lang.dev/docs/learn/inventory) |
| 18. Algorithms | [Statistics](https://rux-lang.dev/docs/learn/statistics) |
| 20. Utilities | [Guess](https://rux-lang.dev/docs/learn/guess), [Age](https://rux-lang.dev/docs/learn/age), [Password](https://rux-lang.dev/docs/learn/password), [Launch](https://rux-lang.dev/docs/learn/launch) |
| 21. Data formats | [Notes](https://rux-lang.dev/docs/learn/notes) |
| 24. Platform | [Melody](https://rux-lang.dev/docs/learn/melody) |
## Learn with an AI assistant
Claude Code, Codex and similar coding assistants make good study partners: they can explain a lesson line by line, quiz you, and review your own version of a program. Rux is a new language, so they need a little context first — [Learn Rux with AI](https://rux-lang.dev/docs/learn/ai) shows how to set that up. Every lesson page also has **Copy page** and **Open in Claude / ChatGPT** buttons that hand the lesson to an assistant for you.
## Going further
The course teaches by example. When you want the full rules, the [Rux Reference](https://rux-lang.dev/docs/lang) specifies the language, the [CLI Reference](https://rux-lang.dev/docs/cli) documents every `rux` command, and the [API Reference](https://rux-lang.dev/docs/api) lists the standard packages. The [cheat sheet](https://rux-lang.dev/docs/learn/cheatsheet) puts the syntax from the whole course on one page.
# Install on FreeBSD
This guide explains how to install the Rux compiler on FreeBSD from the
prebuilt release tarball. There is no native `pkg` package yet.
## Prerequisites
- FreeBSD 14.4 or newer, on x86-64 or AArch64
- `fetch` or `curl` to download the release
- `tar` to extract the archive
## Manual Install
Download the tarball for your architecture — `x86_64` for amd64 machines,
`aarch64` for arm64:
```sh
fetch https://github.com/rux-lang/Rux/releases/latest/download/rux-freebsd-x86_64.tar.gz
```
Extract the archive:
```sh
tar -xzf rux-freebsd-x86_64.tar.gz
```
Move the extracted `rux` binary somewhere on your `PATH`:
```sh
su -m root -c 'install -m 755 rux /usr/local/bin/rux'
```
## Verify the Installation
```sh
rux version
```
You should see the installed Rux compiler version.
## Other BSDs
OpenBSD, NetBSD, and DragonFly BSD have no prebuilt binaries and are not
covered by continuous testing. On those systems,
[build the compiler from source](https://rux-lang.dev/docs/learn/build).
## Releases
All Rux releases are available on [GitHub](https://github.com/rux-lang/Rux/releases){rel=""nofollow""}.
# Install on Linux
This guide explains how to install the Rux compiler on Linux — using the
universal install script, your distribution's package manager, or the prebuilt
release tarball.
## Prerequisites
- A 64-bit (x86-64) Linux distribution
- `curl` or `wget` to download the release
- `tar` to extract the archive (for the manual install)
## Quick Install (Script)
The fastest way to install on **any** distribution is the per-user install
script. It downloads the latest release, installs the `rux` binary into
`~/.local/bin` (no root required), and adds that directory to your `PATH`:
```sh
curl -fsSL https://rux-lang.dev/install.sh | sh
```
The script accepts a few options — for example, install a specific version or a
custom directory:
```sh
curl -fsSL https://rux-lang.dev/install.sh | sh -s -- --version 0.3.0 --dir /usr/local/bin
```
Run it with `--help` to see all options.
## Manual Install
On Ubuntu, Debian, or any other distribution without a dedicated package, use
the prebuilt tarball.
Download the latest Linux tarball:
```sh
curl -LO https://github.com/rux-lang/Rux/releases/latest/download/rux-linux.tar.gz
```
Extract the archive:
```sh
tar -xzf rux-linux.tar.gz
```
Move the extracted `rux` binary somewhere on your `PATH`:
```sh
sudo mv rux /usr/local/bin/rux
```
## Verify the Installation
```sh
rux version
```
You should see the installed Rux compiler version.
## Releases
All Rux releases are available on [GitHub](https://github.com/rux-lang/Rux/releases){rel=""nofollow""}.
# Install on macOS
::warning
The macOS release is still in development. Since there is no native installer or package yet, these instructions will change.
::
A dedicated macOS package is not available at this time. While native support is being worked on, you can run Rux on macOS by [building the compiler from source](https://rux-lang.dev/docs/learn/build) — the build toolchain works anywhere a supported C++ compiler and CMake are available, including macOS.
## Verify the Installation
Once built and on your `PATH`, confirm the compiler is available:
```sh
rux version
```
You should see the installed Rux compiler version.
## Releases
All Rux releases are available on [GitHub](https://github.com/rux-lang/Rux/releases){rel=""nofollow""}.
# Install on Windows
This guide explains how to install the Rux compiler on Windows, either with the MSI installer or using [Scoop](https://scoop.sh){rel=""nofollow""}, a command-line package manager for Windows.
## Prerequisites
- Windows 11
- Windows Server 2025
## Install with the MSI Installer
The MSI installer is the simplest option if you prefer a graphical installer.
1. Download `rux-windows.msi` from the [Download](https://rux-lang.dev/download) page or the [latest release](https://github.com/rux-lang/Rux/releases/latest){rel=""nofollow""}.
2. Double-click the downloaded `.msi` file to launch the installer.
3. Follow the setup wizard to complete the installation.
The installer places the `rux` executable on your system `PATH`, so you can run it from any new terminal window once setup finishes.
::tip
You can also install silently from a terminal — useful for scripted or unattended setups:
```sh
msiexec /i rux-windows.msi /quiet
```
::
To update later, download the newer `.msi` and run it again; to remove Rux, uninstall it from **Settings → Apps → Installed apps**.
## Install with Scoop
### Add the Rux Bucket
Scoop uses buckets to organize packages. Add the official Rux bucket:
```sh
scoop bucket add rux-lang https://github.com/rux-lang/Scoop
```
### Install the Rux Compiler
```sh
scoop install rux
```
## Verify the Installation
```sh
rux version
```
You should see the installed Rux compiler version.
## Updating with Scoop
To update a Scoop installation to the latest version:
```sh
scoop update rux
```
## Releases
All Rux releases are available on [GitHub](https://github.com/rux-lang/Rux/releases){rel=""nofollow""}.
# Build From Source
This guide explains how to build the Rux compiler from source with [CMake](https://cmake.org){rel=""nofollow""}, [Ninja](https://ninja-build.org){rel=""nofollow""}, and [Clang](https://clang.llvm.org){rel=""nofollow""}. It works on every platform Rux supports, including platforms without a prebuilt package.
## Supported Platforms
- BSD
- Illumos
- Linux
- macOS
- Windows
## Requirements
Rux is built with Clang C++ compiler. We recommend the latest stable release of each tool:
- [Clang 22.1](https://clang.llvm.org){rel=""nofollow""} or later
- [CMake 4.3](https://cmake.org){rel=""nofollow""} or later
- [Ninja 1.13](https://ninja-build.org){rel=""nofollow""} or later
- [Git 2.54](https://git-scm.com){rel=""nofollow""} or later
## Install the Build Tools
Install Clang, CMake, Ninja, and Git for your platform:
::code-group
```sh [Arch]
sudo pacman -S clang cmake ninja git
```
```sh [BSD]
pkg install llvm cmake ninja git
```
```sh [Debian]
sudo apt install clang cmake ninja-build git
```
```sh [Fedora]
sudo dnf install clang cmake ninja-build git
```
```sh [macOS]
brew install llvm cmake ninja git
```
```sh [Ubuntu]
sudo apt install clang cmake ninja-build git
```
```sh [Windows]
scoop install llvm cmake ninja git
```
::
::tip
On macOS, the system `clang` is Apple Clang. To build with Homebrew's LLVM instead, prepend it to your `PATH` — `export PATH="$(brew --prefix llvm)/bin:$PATH"` — before configuring.
::
## Clone the Repository
The Rux compiler has two branches:
- **main** — the most stable release of the compiler
- **dev** — the latest community updates; may be unstable
::code-group
```sh [main]
git clone https://github.com/rux-lang/rux
cd rux
```
```sh [dev]
git clone -b dev https://github.com/rux-lang/rux rux-dev
cd rux-dev
```
::
## Configure and Build
Configure the project with the Ninja generator and Clang, then build:
```sh
cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -B build
cmake --build build
```
The compiled `rux` binary is written to the `build` directory.
## Add the Binary to Your PATH
::code-group
```sh [BSD / Illumos / macOS / Linux]
cp build/rux ~/.local/bin/
```
```sh [Windows]
# Open Environment Variables and add the build directory to your global Path,
# or copy rux.exe into a directory that is already on your PATH.
```
::
On Unix-like systems, make sure `~/.local/bin` is on your `PATH`, adding it in your shell's profile if needed.
## Verify the Installation
```sh
rux version
```
You should see the installed Rux compiler version.
## Releases
All Rux releases are available on [GitHub](https://github.com/rux-lang/Rux/releases){rel=""nofollow""}.
# Visual Studio Code
[Visual Studio Code](https://code.visualstudio.com){rel=""nofollow""} is the recommended editor for Rux. The official [Rux Language](https://marketplace.visualstudio.com/items?itemName=rux-lang.vscode-rux){rel=""nofollow""} extension adds syntax highlighting for `.rux` source files.
## Installing the Extension
::code-group
```sh [Quick Open]
# Press Ctrl+P in VS Code and paste:
ext install rux-lang.vscode-rux
```
```sh [Command Line]
code --install-extension rux-lang.vscode-rux
```
::
Alternatively, open the **Extensions** panel (`Ctrl+Shift+X`), search for **Rux Language**, and click **Install**.
## Features
- Syntax highlighting for `.rux` source files
- Markdown code fence injection — Rux code blocks in `.md` files are highlighted automatically
## Creating a New Project
```sh
rux new MyApp
cd MyApp
code .
```
# Sublime Text
The official [Rux](https://packagecontrol.io/packages/Rux){rel=""nofollow""} package for [Sublime Text](https://www.sublimetext.com){rel=""nofollow""} adds syntax highlighting for `.rux` source files.
## Installing the Package
The package is distributed through [Package Control](https://packagecontrol.io){rel=""nofollow""}. If Package Control is not yet installed, add it via **Tools → Install Package Control…** in Sublime Text.
Once Package Control is available:
1. Open the Command Palette — `Ctrl+Shift+P` on Windows/Linux, `Cmd+Shift+P` on macOS.
2. Type **Package Control: Install Package** and press `Enter`.
3. Search for **Rux** and press `Enter` to install.
## Features
- Syntax highlighting for `.rux` source files
## Creating a New Project
```sh
rux new MyApp
cd MyApp
```
Then open the `MyApp` folder in Sublime Text via **File → Open Folder…**
# Zed Editor
[Zed](https://zed.dev){rel=""nofollow""} is a fast, multiplayer code editor for macOS and Linux. The official [Rux extension](https://github.com/rux-lang/Zed){rel=""nofollow""} adds syntax highlighting for `.rux` source files.
## Installing the Extension
Open the extensions view from the Command Palette (`Cmd+Shift+P` on macOS, `Ctrl+Shift+P` on Linux) and run **zed: extensions**, then search for **Rux** and click **Install**.
### Install from source
If the extension is not yet available in the registry, you can install it directly from its [GitHub repository](https://github.com/rux-lang/Zed){rel=""nofollow""} as a dev extension.
1. Clone the repository:
```sh
git clone https://github.com/rux-lang/Zed.git rux-zed
```
2. Open the extensions view (`zed: extensions`) and click **Install Dev Extension**.
3. Select the cloned `rux-zed` folder.
Zed compiles the extension and loads it immediately. It reloads automatically whenever you change the source, which is handy while the extension is still evolving.
## Features
- Syntax highlighting for `.rux` source files
## Creating a New Project
```sh
rux new MyApp
cd MyApp
```
Then open the `MyApp` folder in Zed via **File → Open…**, or from the terminal:
```sh
zed MyApp
```
# Your First Project
This page takes you from an empty folder to a running program, and then sets up the [Examples repository](https://github.com/rux-lang/Examples){rel=""nofollow""} that every lesson of the course is built on. It assumes `rux` is [installed](https://rux-lang.dev/docs/learn/install/windows) and on your `PATH`; check with:
```sh
rux --version
```
## How a Rux project is built
Rux code lives in **packages**. A package is a folder with a manifest, `Rux.toml`, that names the package and lists what it depends on, and a `Src/` folder with the source files. The `rux` tool reads the manifest, compiles the sources together with their dependencies, and writes the program into `Bin/`.
```mermaid
flowchart LR
new["rux new Hello"] --> toml["Rux.toml
name, type, dependencies"]
new --> src["Src/Main.rux
your code"]
toml --> build["rux build"]
src --> build
std["Standard packages
Io, Core, Text, …"] --> build
build --> exe["Bin/Debug/<OS>/<Arch>/Hello"]
exe --> run["rux run"]
```
## 1. Create the project
```sh
rux new Hello
cd Hello
```
`rux new` creates three files:
```text
Hello/
├── .gitignore ignores the Bin/ and Temp/ build folders
├── Rux.toml the package manifest
└── Src/
└── Main.rux the program
```
The generated program is the smallest one Rux accepts — a `Main` function that does nothing and reports success:
```rux [Src/Main.rux]
func Main() -> int {
return 0;
}
```
`Main` is the program's [entry point](https://rux-lang.dev/docs/lang/functions/main). The `int` it returns becomes the process exit status, and `0` means *success*.
## 2. Print a line
To print you need `PrintLine`, which lives in the standard `Io` package. A package can only use what its manifest lists, so first add `Io` to `Rux.toml`:
```toml [Rux.toml]
[Manifest]
Version = 1
[Package]
Name = "Hello"
Version = "0.1.0"
Type = "Executable"
[Dependencies]
Io = { Namespace = "Rux", Version = "*" }
```
`Namespace = "Rux"` marks `Io` as one of the standard packages that ship with the compiler, and `Version = "*"` accepts whichever version is installed. Now replace `Src/Main.rux`:
```rux [Src/Main.rux]
import Io::PrintLine;
func Main() -> int {
PrintLine("Hello, World!");
return 0;
}
```
`import Io::PrintLine;` brings one function from the `Io` package into scope, so the program can call it by its short name.
## 3. Build and run
```sh
rux run
```
```text
Hello, World!
```
`rux run` builds the package if anything changed and then starts the program. To only compile, use `rux build`; it reports where the executable went:
```text
Built Hello (Debug, Windows x86-64) in 525 ms
Output: Bin\Debug\Windows\x86-64\Hello.exe
```
Two more commands you will use all the time:
| Command | What it does |
| ------------------- | -------------------------------------------------------------------- |
| `rux check` | Compiles without writing a program — the fastest way to find errors. |
| `rux run --release` | Builds an optimised program into `Bin/Release/` and runs it. |
The [CLI Reference](https://rux-lang.dev/docs/cli) documents every command and flag.
## 4. Read your first error
Delete the semicolon after `PrintLine("Hello, World!")` and run `rux check`:
```text
Src\Main.rux:5:5: error: expected ';' after expression, but found 'return'
5 | return 0;
| ^
note: compiler phase: Parsing
```
Every message names the file, line and column, quotes the line, and points at the spot. Here the compiler reached `return` while still waiting for the `;` that ends the previous statement. Put the semicolon back and the error goes away.
::tip
**Make mistakes on purpose.**:br
Throughout the course, when a lesson says "the compiler refuses this", try it. Reading real error messages is a large part of learning a language, and the lessons quote the exact messages so you know what to expect.
::
## 5. Get the course examples
Every lesson is a package in the [Rux Examples repository](https://github.com/rux-lang/Examples){rel=""nofollow""}. Clone it once:
```sh
git clone https://github.com/rux-lang/Examples.git
cd Examples
```
The repository is organised by course part, one folder per lesson:
```text
Examples/
├── Basics/
│ ├── Hello/
│ │ ├── README.md the lesson's goal and expected output
│ │ ├── Rux.toml
│ │ └── Src/
│ │ └── Main.rux the program, explained in its comments
│ ├── Comment/
│ └── …
├── ControlFlow/
├── …
├── Projects/
├── Run.ps1 check or test every lesson (PowerShell)
└── Run.sh the same for POSIX shells
```
To run a lesson, move into its folder and run it:
```sh
cd Basics/Hello
rux run
```
To check that every lesson compiles with your installed `rux` — useful after upgrading — use the runner script from the repository root:
::code-group
```powershell [Windows]
./Run.ps1 check
./Run.ps1 test -Filter Basics
```
```sh [Linux / macOS / FreeBSD]
sh Run.sh check
sh Run.sh test --filter Basics
```
::
`check` type-checks every package; `test` also runs them and reports any that exit with an error.
## Next
You are set up. Start the course with [1.1 Hello, World](https://rux-lang.dev/docs/learn/hello) — or, if you would like an AI assistant to study with, read [Learn Rux with AI](https://rux-lang.dev/docs/learn/ai) first.
# Learn Rux with AI
A coding assistant makes a patient study partner. It can explain a lesson line by line, answer "why?" as often as you ask, quiz you, invent exercises, and review the program you wrote. This page shows how to use one with the course — and how to avoid its one big weakness with a language as new as Rux.
## The one rule: the compiler has the last word
Assistants learned to program from millions of examples in older languages, and very few in Rux. Left to guess, they fill gaps with Rust, C or Swift — a `_` default arm where Rux wants `else`, a `fn` where Rux writes `func`, a library function that does not exist. Three habits keep you safe:
1. **Give the assistant Rux context** — the lesson, the Examples repository, the documentation. The setup below does this.
2. **Ask it to run `rux check`** on anything it writes, and to fix what the compiler reports.
3. **Trust the compiler and the docs over the assistant.** When they disagree, the assistant is wrong — and working out *why* teaches you more than the right answer would.
```mermaid
flowchart LR
you(["You"]) -- "question or exercise" --> ai["Assistant"]
docs["Lesson, Examples repo,
rux-lang.dev docs"] -. context .-> ai
ai -- "explanation and code" --> check{"rux check"}
check -- "error" --> ai
check -- "compiles" --> run["rux run"]
run -- "output" --> you
```
## Option 1: a coding agent in the Examples repository
A coding agent runs in your terminal or editor, reads the files in the folder you open it in, and can run commands such as `rux check` and `rux run` itself. Open it in the [Examples repository](https://github.com/rux-lang/Examples){rel=""nofollow""} and every lesson is already in its reach.
### Install an agent
::tabs
:::tabs-item{icon="i-simple-icons-anthropic" label="Claude Code"}
[Claude Code](https://claude.com/claude-code){rel=""nofollow""} is Anthropic's coding agent. It needs a Claude subscription or a Claude Console account.
::::code-group
```sh [macOS / Linux]
curl -fsSL https://claude.ai/install.sh | bash
```
```powershell [Windows]
irm https://claude.ai/install.ps1 | iex
```
::::
Then start it inside the repository with `claude`. Claude Code is also available as a desktop app and as extensions for VS Code and JetBrains IDEs — see the [Claude Code documentation](https://code.claude.com/docs){rel=""nofollow""}.
:::
:::tabs-item{icon="i-simple-icons-openai" label="Codex"}
[Codex](https://developers.openai.com/codex){rel=""nofollow""} is OpenAI's coding agent. It signs in with a ChatGPT account.
::::code-group
```sh [npm]
npm install -g @openai/codex
```
```sh [macOS / Linux]
curl -fsSL https://chatgpt.com/codex/install.sh | sh
```
```powershell [Windows]
powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"
```
::::
Then start it inside the repository with `codex`. Codex also has an IDE extension — see the [Codex documentation](https://developers.openai.com/codex){rel=""nofollow""} and the [source on GitHub](https://github.com/openai/codex){rel=""nofollow""}.
:::
::
### Open the course in it
```sh
git clone https://github.com/rux-lang/Examples.git
cd Examples
claude # or: codex
```
The repository root carries an `AGENTS.md` file — Codex reads it automatically, and Claude Code reads it through the repository's `CLAUDE.md`. It tells the agent what Rux is, how the course is laid out, which syntax differs from the languages it knows, and to verify everything with `rux check`. You do not have to explain any of that yourself.
### Add the Rux skill (optional)
The Examples repository also publishes a Rux *skill*: a compact reference an agent loads when it works with Rux code, in any project. Install it once and your agent knows Rux everywhere, not just inside the course:
```sh
npx skills add rux-lang/Examples
```
## Option 2: a chat assistant, straight from a lesson
You do not need to install anything to study with a chat assistant. Every lesson page has a **Copy page** button and a menu next to it:
- **Open in Claude** or **Open in ChatGPT** starts a conversation that already points the assistant at the lesson and its program, and asks it to explain the lesson and check your understanding.
- **Copy page** puts the lesson's Markdown on your clipboard, to paste into any assistant you like.
A chat assistant cannot run the compiler for you, so paste what it writes into a lesson package — or the [Playground](https://rux-lang.dev/play) — and check it yourself.
## Prompts that work
Copy these into your agent, replacing the lesson name. Each one keeps the assistant tied to the course and the compiler.
**Explain a lesson**
```text
Read the lesson in ControlFlow/DoWhile (README.md and Src/Main.rux).
Explain the program step by step for a beginner, then ask me one
question at a time to check I understood. Do not move on until I answer.
```
**Practise**
```text
Give me a small exercise that uses only what lessons 1.1 to 3.5 teach.
Do not show a solution. When I say I am done, read my code, run
rux check and rux run in that folder, and review it.
```
**Understand an error**
```text
I ran rux check in Basics/Convert and got the error below. Explain what
the compiler means in plain words and give me a hint — not the fix.
```
**Compare with a language you know**
```text
I know Python. Compare how Rux handles optional values (lessons in
Optionals/) with Python's None. Only use Rux syntax that appears in
this repository, and point to the lesson for each claim.
```
**Review your own program**
```text
Here is my FizzBuzz solution in Projects/MyFizzBuzz. Run rux check,
then review it against Projects/FizzBuzz: what is idiomatic Rux, what
is not, and why?
```
::tip
**Ask for hints, not answers.**:br
An assistant that writes every exercise for you teaches you to read code, not to write it. Ask it to wait for your attempt, to give the smallest useful hint, and to explain its reasoning — then type the code yourself.
::
## Context for any assistant
When you work outside the Examples repository, point your assistant at these:
| Source | What it gives the assistant |
| ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| [llms-full.txt](https://rux-lang.dev/llms-full.txt) | The whole site — course, reference, CLI and API — as one Markdown file |
| [llms.txt](https://rux-lang.dev/llms.txt) | An index of every page, each linked to its Markdown source |
| [Learn Rux](https://rux-lang.dev/docs/learn) | This course, lesson by lesson |
| [github.com/rux-lang/Examples](https://github.com/rux-lang/Examples){rel=""nofollow""} | Every lesson as a package that compiles with the current `rux` |
| [Rux Reference](https://rux-lang.dev/docs/lang) | The full language rules |
| [API Reference](https://rux-lang.dev/docs/api) | The standard packages and their functions |
`https://rux-lang.dev/llms-full.txt` is the quickest way to give an assistant that has never heard of Rux the whole language at once: attach it to a chat, or tell an agent to fetch it before it writes any code. It is large, so for a single lesson the lesson's own page is the better fit.
## Next
Start the course with [1.1 Hello, World](https://rux-lang.dev/docs/learn/hello), with or without a study partner.
# Rux Cheat Sheet
The syntax from the whole course on one page. The snippets are cut from programs that compile with the current `rux` — `…` marks a body left out — and each section links to the lessons that explain it. For the complete rules, see the [Rux Reference](https://rux-lang.dev/docs/lang).
## A program
```rux [Src/Main.rux]
import Io::{ Print, PrintLine }; // bring names from a package into scope
func Main() -> int { // the entry point
PrintLine("Hello, {}!", "World");
return 0; // the exit status: 0 means success
}
```
```toml [Rux.toml]
[Dependencies]
Io = { Namespace = "Rux", Version = "*" } # every package you import from
```
Lessons: [Hello, World](https://rux-lang.dev/docs/learn/hello), [Console](https://rux-lang.dev/docs/learn/console), [Comment](https://rux-lang.dev/docs/learn/comment)
## Bindings and constants
```rux
let year = 2026; // immutable, type inferred (int)
var score = 10; // mutable
score += 5;
let small: int8 = 100; // explicit type
const Limit = 100; // folded in at compile time
const Pi: float32 = 3.1415927f32;
```
Lessons: [Variable](https://rux-lang.dev/docs/learn/variable), [Mutable](https://rux-lang.dev/docs/learn/mutable), [Const](https://rux-lang.dev/docs/learn/const)
## Types
| Kind | Types | Literals |
| ------------------ | ----------------------------------------------- | -------------------------------------- |
| Signed integers | `int8` … `int512`, `int` (pointer-sized) | `42`, `-7`, `5i64` |
| Unsigned integers | `uint8` … `uint512`, `uint`, `byte` (= `uint8`) | `200u8`, `0xFF`, `0b1010`, `1_000_000` |
| Floating point | `float32`, `float64`, `float` (= `float64`) | `0.75f32`, `2.998e8` |
| Boolean | `bool` | `true`, `false` |
| Character | `char` (= `char32`), `char8`, `char16` | `'R'`, `'\u{263A}'`, `c8'R'` |
| Text | `char8[..]` — a slice of UTF-8 bytes | `"hello"` |
| Array, slice | `T[N]`, `T[..]`, `var T[..]` | `[2, 3, 5]`, `[0; 16]` |
| Tuple | `(A, B)` | `(3, true)` |
| Optional | `T?` | `none` |
| Fallible | `T ! E`, `! E` | — |
| Sum | `A | B` | — |
| Reference, pointer | `&T`, `&var T`, `*T`, `*var T` | `@x`, `null` |
| Function | `func(int32) -> int32` | a function's name |
```rux
let big = small as int32; // convert with `as`
```
Lessons: [Integer](https://rux-lang.dev/docs/learn/integer), [Float](https://rux-lang.dev/docs/learn/float), [Character](https://rux-lang.dev/docs/learn/character), [Literal](https://rux-lang.dev/docs/learn/literal), [Convert](https://rux-lang.dev/docs/learn/convert)
## Control flow
```rux
if score > 10 { // braces always, parentheses optional
PrintLine("big");
} else if score > 5 {
PrintLine("medium");
} else {
PrintLine("small");
}
let label = n % 2 == 0 ? "even" : "odd";
while n > 0 { n -= 1; }
do { n += 1; } while n < 3; // runs at least once — note the `;`
loop { break; } // until a `break`
for i in 0..3 { } // 0, 1, 2
for i in 1..=3 { } // 1, 2, 3
outer: for a in 1..10 { // a labelled loop
for b in a..10 {
if a * b == 12 { break outer; }
}
}
```
Lessons: [If](https://rux-lang.dev/docs/learn/if), [Ternary](https://rux-lang.dev/docs/learn/ternary), [While](https://rux-lang.dev/docs/learn/while), [Do-while](https://rux-lang.dev/docs/learn/do-while), [Loop](https://rux-lang.dev/docs/learn/loop), [For](https://rux-lang.dev/docs/learn/for), [Label](https://rux-lang.dev/docs/learn/label)
## match
```rux
let kind = match score {
0 => "none",
1..=9 => "few", // a range pattern
n if n > 1000 => "huge", // a guard
else => "many" // the default arm is `else`, never `_`
};
```
Lessons: [Match](https://rux-lang.dev/docs/learn/match), [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Guard](https://rux-lang.dev/docs/learn/guard), [Range pattern](https://rux-lang.dev/docs/learn/range-pattern), [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive)
## Functions
```rux
func Add(left: int, right: int) -> int {
return left + right;
}
func Clamp(value: int, low: int = 0, high: int = 100) -> int { … } // default arguments
func Sum(args: int32...) -> int32 { … } // variadic
func Divide(a: int32, b: int32) -> (int32, int32) { // several results
return (a / b, a % b);
}
let (q, r) = Divide(17, 5); // destructure
func Larger(a: T, b: T) -> T { // generic
return a > b ? a : b;
}
```
Lessons: [Function](https://rux-lang.dev/docs/learn/function), [Default argument](https://rux-lang.dev/docs/learn/default-argument), [Variadic](https://rux-lang.dev/docs/learn/variadic), [Tuple](https://rux-lang.dev/docs/learn/tuple), [Destructure](https://rux-lang.dev/docs/learn/destructure), [Generic](https://rux-lang.dev/docs/learn/generic)
## Arrays and slices
```rux
let primes: int32[4] = [2, 3, 5, 7];
let zeros: int32[8] = [0; 8]; // repeat a value
let part = primes[1..3]; // a slice: a view of elements 1 and 2
PrintLine("{}", primes.length);
for p in primes { }
```
Lessons: [Array](https://rux-lang.dev/docs/learn/array), [Array repeat](https://rux-lang.dev/docs/learn/array-repeat), [Slice](https://rux-lang.dev/docs/learn/slice), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice)
## Structs and methods
```rux
struct Point {
x: int;
y: int;
}
extend Point {
func Point(x: int, y: int) -> Point { // a constructor: named after the type
return Point { x: x, y: y };
}
func Length2(self: &Point) -> int { // reads its receiver
return self.x * self.x + self.y * self.y;
}
func Move(self: &var Point, dx: int) { // changes its receiver
self.x += dx;
}
}
var p = Point(3, 4);
p.Move(1);
```
Lessons: [Struct](https://rux-lang.dev/docs/learn/struct), [Method](https://rux-lang.dev/docs/learn/method), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method), [Constructor](https://rux-lang.dev/docs/learn/constructor), [Reference](https://rux-lang.dev/docs/learn/reference)
## Enums and variants
```rux
enum Direction { North, East, South, West }
enum Status: uint16 { Ok = 200, NotFound = 404 }
variant Shape {
Circle(float64),
Rectangle { width: float64; height: float64; },
Empty
}
func Area(shape: Shape) -> float64 {
return match shape {
.Circle(r) => 3.14 * r * r,
.Rectangle { width, height } => width * height,
.Empty => 0.0
};
}
```
Lessons: [Enum](https://rux-lang.dev/docs/learn/enum), [Enum value](https://rux-lang.dev/docs/learn/enum-value), [Variant](https://rux-lang.dev/docs/learn/variant), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern)
## Optionals
```rux
func FirstEven(values: int[..]) -> int? {
for v in values {
if v % 2 == 0 { return v; }
}
return none;
}
let found = FirstEven(numbers) ?? -1; // a fallback
match FirstEven(numbers) {
value? => PrintLine("{}", value),
none => PrintLine("none")
}
```
Lessons: [Optional](https://rux-lang.dev/docs/learn/optional), [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit), [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate)
## Errors
```rux
variant ParseError { Empty, Bad(int) }
func Half(n: int) -> int ! ParseError { // a value, or an error
if n == 0 { fail ParseError::Empty; }
if n % 2 != 0 { fail ParseError::Bad(n); }
return n / 2;
}
func Quarter(n: int) -> int ! ParseError {
let half = Half(n)?; // pass a failure to the caller
return Half(half)?;
}
match Quarter(8) {
.Success(v) => PrintLine("{}", v),
.Failure(e) => PrintLine("failed")
}
let safe = Half(3) catch { else => 0 }; // a default on failure
```
Lessons: [Fallible](https://rux-lang.dev/docs/learn/fallible), [Fail](https://rux-lang.dev/docs/learn/fail), [Propagate](https://rux-lang.dev/docs/learn/propagate), [Catch](https://rux-lang.dev/docs/learn/catch), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback)
## Sum types
```rux
type Setting = int32 | bool;
func Describe(s: Setting) -> char8[..] {
return match s {
n: int32 => "number",
flag: bool => "flag"
};
}
if limit is bool { }
```
Lessons: [Sum type](https://rux-lang.dev/docs/learn/sum-type), [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern), [The is operator](https://rux-lang.dev/docs/learn/is)
## Interfaces
```rux
interface Named {
func Name() -> char8[..];
}
extend Point : Named {
func Name(self: &Point) -> char8[..] {
return "point";
}
}
```
Lessons: [Interface](https://rux-lang.dev/docs/learn/interface), [Interface value](https://rux-lang.dev/docs/learn/interface-value), [Generic bound](https://rux-lang.dev/docs/learn/generic-bound)
## Ownership
```rux
let b <- a; // move: `a` can no longer be used
Take(<-b); // move into a call
extend Token {
func ~Token(self: &var Token) { … } // a destructor, run when the value goes away
}
defer PrintLine("done"); // runs when the scope ends
```
Lessons: [Copy](https://rux-lang.dev/docs/learn/copy), [Move](https://rux-lang.dev/docs/learn/move), [Destructor](https://rux-lang.dev/docs/learn/destructor), [Defer](https://rux-lang.dev/docs/learn/defer)
## Pointers
```rux
var score = 10;
let reader: *int = @score; // `@` takes an address
let writer: *var int = @score;
*writer = 25; // `*` reads or writes through it
```
Lessons: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Out parameter](https://rux-lang.dev/docs/learn/out-parameter), [Optional pointer](https://rux-lang.dev/docs/learn/optional-pointer)
## Formatting
| Placeholder | Result |
| --------------------------------------- | -------------------------------------------------- |
| `{}` | the value |
| `{{}}` | a literal `{}` |
| `{:>6}`, `{:<6}`, `{:^6}`, `{:*^8}` | right, left, centre aligned; with a fill character |
| `{:.2}` | two decimals |
| `{:x}`, `{:X}`, `{:o}`, `{:b}`, `{:#x}` | hexadecimal, octal, binary; with a prefix |
| `{:08}`, `{:+}`, `{:e}` | zero padded, with a sign, in scientific notation |
Lessons: [Console](https://rux-lang.dev/docs/learn/console), [Format](https://rux-lang.dev/docs/learn/format), [Format number](https://rux-lang.dev/docs/learn/format-number)
## The rux tool
| Command | What it does |
| -------------- | -------------------------------------------------- |
| `rux new Name` | Create a package |
| `rux check` | Compile without building — the fastest error check |
| `rux build` | Build into `Bin/Debug///` |
| `rux run` | Build and run; `--release` for an optimised build |
| `rux fmt` | Format the sources |
| `rux lint` | Report style problems |
| `rux test` | Build and run the packages under `Tests/` |
| `rux doc` | Generate documentation from `///` comments |
Lessons: [Your first project](https://rux-lang.dev/docs/learn/first-project), [Tooling](https://rux-lang.dev/docs/learn/tooling) · Reference: [CLI](https://rux-lang.dev/docs/cli)
# Part 1: Basics
Your first programs: printing, naming values, and the types those values have. By the end of this part you can write a program that prints a formatted report of numbers, characters and text — and you will have met the compiler's error messages often enough to read them without fear.
## What you will learn
- The shape of every Rux program: `import`, `func Main() -> int`, and the exit status.
- Comments of all three kinds, including documentation comments.
- Naming values with `let`, `var` and `const`, and when to use each.
- The primitive types — integers, floats, booleans and characters — and how literals get their types.
- Printing values with `Print`, `PrintLine` and `{}` placeholders.
- Converting between types with `as`, and what happens when a value does not fit.
## The primitive types at a glance
```mermaid
flowchart TB
v(["A value"]) --> num["Numbers"]
v --> b["Booleans
bool (= bool8), bool16 … bool64"]
v --> c["Characters
char (= char32), char8, char16, char64"]
num --> i["Integers"]
num --> f["Floats
float64 (= float), float32"]
i --> s["Signed
int8 … int512, int"]
i --> u["Unsigned
uint8 … uint512, uint, byte"]
```
## Lessons
| | Lesson | What you will learn |
| ---- | ------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| 1.1 | [Hello, World](https://rux-lang.dev/docs/learn/hello) | print "Hello, World!", the minimal Rux application |
| 1.2 | [Comment](https://rux-lang.dev/docs/learn/comment) | explain code with `//` and `/* */` comments, which can nest |
| 1.3 | [Variable](https://rux-lang.dev/docs/learn/variable) | name a value with `let`, and let the compiler infer its type |
| 1.4 | [Mutable](https://rux-lang.dev/docs/learn/mutable) | declare a binding with `var` so it can be reassigned |
| 1.5 | [Integer](https://rux-lang.dev/docs/learn/integer) | whole numbers: signed and unsigned widths, `int` and `uint` |
| 1.6 | [Float](https://rux-lang.dev/docs/learn/float) | fractional numbers: `float32`, `float64`, and why `0.1 + 0.2` is not `0.3` |
| 1.7 | [Boolean](https://rux-lang.dev/docs/learn/boolean) | `true`, `false` and the `bool` type |
| 1.8 | [Character](https://rux-lang.dev/docs/learn/character) | single characters, character literals and the `char` type |
| 1.9 | [Literal](https://rux-lang.dev/docs/learn/literal) | write numbers in other bases, with separators and suffixes |
| 1.10 | [Console](https://rux-lang.dev/docs/learn/console) | write to the console with `Print` and `PrintLine`, and fill `{}` placeholders |
| 1.11 | [Const](https://rux-lang.dev/docs/learn/const) | name a value the compiler folds in, and see where it differs from `let` |
| 1.12 | [Convert](https://rux-lang.dev/docs/learn/convert) | convert between numeric types with `as`, and see what a value that does not fit becomes |
## Before you start
You need `rux` installed and the Examples repository cloned — [Your first project](https://rux-lang.dev/docs/learn/first-project) covers both. Each lesson's package is in the repository's `Basics/` folder:
```sh
cd Examples/Basics/Hello
rux run
```
## After this part
[Part 2: Operators](https://rux-lang.dev/docs/learn/operators) combines the values you can now name into new ones, and [Part 3: Control flow](https://rux-lang.dev/docs/learn/control-flow) makes programs choose and repeat. After Part 3 you are ready for the first checkpoint projects, [Thanks](https://rux-lang.dev/docs/learn/thanks) and [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz).
For the full rules behind this part, see [Variables](https://rux-lang.dev/docs/lang/bindings/overview), [Literals](https://rux-lang.dev/docs/lang/lexical/literals) and the [primitive types](https://rux-lang.dev/docs/lang/appendix/primitives) in the Rux Reference.
# Hello, World
::note
**You'll need**: nothing — this is the first lesson.
::
Every programming course starts by printing one line, and for good reason: it proves the whole toolchain works — the compiler, the standard library and your terminal — before you write anything harder. This lesson's program is only four lines, but each one shows something you will use in every Rux program you write.
## Anatomy of a program
```rux
import Io::PrintLine;
func Main() -> int {
PrintLine("Hello, World!");
return 0;
}
```
| Line | What it does |
| ----------------------------- | ------------------------------------------------------------------------------- |
| `import Io::PrintLine;` | Brings the name `PrintLine` from the standard `Io` package into this file. |
| `func Main() -> int {` | Declares the function `Main`, where every program starts. It returns an `int`. |
| `PrintLine("Hello, World!");` | Calls `PrintLine` with one piece of text. It prints the text and ends the line. |
| `return 0;` | Ends `Main` and hands `0` back to the operating system: *success*. |
Statements end with a semicolon, and a function's body sits between braces.
## Imports and dependencies
`PrintLine` is not built into the language. It lives in `Io`, one of the standard packages that ship with the compiler. Two things make it usable:
1. The package's manifest, `Rux.toml`, lists `Io` under `[Dependencies]`:
```toml
[Dependencies]
Io = { Namespace = "Rux", Version = "*" }
```
2. The source file imports the name it needs with `import Io::PrintLine;`. The `::` separates the package from the name inside it.
Together they keep every dependency visible: the manifest says which packages a program uses, and each file says which names it takes from them.
## Main and the exit status
When the program starts, the operating system calls `Main`. When `Main` returns, the number it returns becomes the program's **exit status** — the value scripts and other programs see. By convention `0` means the program succeeded and anything else means it failed.
```mermaid
sequenceDiagram
participant OS as Operating system
participant Main
participant Io as Io::PrintLine
OS->>Main: start the program
Main->>Io: PrintLine("Hello, World!")
Io-->>OS: "Hello, World!" on the console
Main-->>OS: return 0 (success)
```
You can see the exit status yourself. Run the program, then ask the shell for it — `echo $?` in Bash or zsh, `$LASTEXITCODE` in PowerShell. Change `return 0;` to `return 3;`, run again, and the shell reports 3.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Hello){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The smallest complete Rux program: an import, an entry point and one line of output. Running
// `rux run` in this directory builds the program and then runs it.
//
// `import` brings a name from another package into this file. `PrintLine` lives in `Io`, the
// standard input and output package, and the `[Dependencies]` table in Rux.toml is what makes
// `Io` available to import from.
import Io::PrintLine;
// `Main` is where every program starts. The `int` it returns is the exit status, and zero means
// the program finished successfully.
func Main() -> int {
// `PrintLine` writes the text in the quotes and then ends the line.
PrintLine("Hello, World!");
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Hello
rux run
```
```text
Hello, World!
```
## Common mistakes
::warning
**Forgetting the dependency.**:br
Remove the `Io = …` line from `Rux.toml` and the import has nothing to import from. The compiler says the package `Io` is not listed in `[Dependencies]` and suggests adding it.
::
::warning
**Leaving out a semicolon.**:br`PrintLine("Hello, World!")` with no `;` makes the compiler stop at the next line: `error: expected ';' after expression, but found 'return'`. The error points at the token *after* the gap, so look one line up.
::
## Try it yourself
1. Print your own name on a second line, with a second `PrintLine` call.
2. Make `Main` return `1`, run the program and read the exit status in your shell.
3. Change `import Io::PrintLine;` to `import Io::Print;` and replace both calls with `Print`. What happens to the line breaks? The [Console](https://rux-lang.dev/docs/learn/console) lesson explains it.
## Learn more
- [The `Main` function](https://rux-lang.dev/docs/lang/functions/main) in the Rux Reference
- [Imports](https://rux-lang.dev/docs/lang/modules/imports) and [modules](https://rux-lang.dev/docs/lang/modules/overview)
- [Your first project](https://rux-lang.dev/docs/learn/first-project) — create this package yourself with `rux new`
# Comment
::note
**You'll need**: [Hello, World](https://rux-lang.dev/docs/learn/hello)
::
A comment is text the compiler skips. It is written for people — the next person to read the code, or you in three months — and good comments explain *why* the code is the way it is, which the code itself cannot say. Rux has three kinds, and this lesson's program uses all of them.
| Kind | Syntax | Used for |
| --------------------- | ------------------------------------------ | ------------------------------------------- |
| Line comment | `// …` to the end of the line | Everyday notes, and switching a line off |
| Block comment | `/* … */`, may span lines and nest | Longer notes, or a note inside a statement |
| Documentation comment | `/// …` or `/** … */` before a declaration | Describing a function or type for `rux doc` |
## Line comments
Two slashes start a comment that runs to the end of the line. It can have a line to itself or follow code:
```rux
// A comment can have a line of its own, or follow code on the same line.
PrintLine("Comments never reach the program."); // This one sits beside a statement.
```
Putting `//` in front of a line of code is the quickest way to switch it off without deleting it:
```rux
// PrintLine("This line never runs.");
```
## Block comments — and why nesting matters
A block comment starts with `/*` and ends with `*/`. It can cover several lines or sit in the middle of one, even inside a call:
```rux
PrintLine(/* the message */ "A block comment can sit inside a call.");
```
Unlike C, Java or JavaScript, Rux block comments **nest**: every `/*` needs its own `*/`. That means you can wrap a block comment around code that already contains one, and it still works:
```rux
/* Block comments nest, which is less common than it sounds. …
/* an inner comment */
so this line is still inside the outer comment.
*/
```
```mermaid
flowchart LR
open1["/* outer opens"] --> open2["/* inner opens"] --> close2["*/ inner closes"] --> text["…still a comment…"] --> close1["*/ outer closes"]
```
In a language without nesting, the first `*/` would end the whole comment and leave the rest as broken code.
## Documentation comments
Three slashes start a **documentation comment**. It describes the declaration right after it, and tools read it: `rux doc` turns these comments into a package's reference pages.
```rux
/// A documentation comment starts with three slashes. It describes the declaration that follows
/// it — here `Main` — and tools read it: …
func Main() -> int {
```
`/** … */` is the block form of the same thing. You will write many more of these in the [Documentation](https://rux-lang.dev/docs/learn/documentation) lesson.
## Comment markers inside text
Between double quotes, `//` and `/*` are just characters. The program prints them like any other text:
```rux
PrintLine("// is not a comment here");
PrintLine("/* and neither is this */");
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Comment){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A comment is text the compiler skips. It is there for the people who read the code, and Rux has
// three kinds of it. This file uses all three.
//
// A line comment, like this one, starts with two slashes and runs to the end of the line. It is
// the everyday kind: use it to say why the code is the way it is.
import Io::PrintLine;
/* A block comment starts with a slash and a star and ends with a star and a slash. It can cover
several lines, or sit in the middle of one. */
/* Block comments nest, which is less common than it sounds. In many languages the first closing
star-slash below would end the whole comment and leave the line after it as broken code. In
Rux every opening pair needs its own closing pair:
/* an inner comment */
so this line is still inside the outer comment.
*/
/// A documentation comment starts with three slashes. It describes the declaration that follows
/// it — here `Main` — and tools read it: `rux doc` turns these comments into a package's
/// reference pages. `/** ... */` is the block form of the same thing.
func Main() -> int {
// A comment can have a line of its own, or follow code on the same line.
PrintLine("Comments never reach the program."); // This one sits beside a statement.
// A block comment can even sit inside a statement, between any two pieces of it.
PrintLine(/* the message */ "A block comment can sit inside a call.");
// Inside quotes, the comment markers are only characters, and they print like any others.
PrintLine("// is not a comment here");
PrintLine("/* and neither is this */");
// Putting `//` in front of a line switches it off without deleting it:
// PrintLine("This line never runs.");
// A documentation comment must document a declaration. Written inside a body, as in
//
// /// Prints the total.
// PrintLine("total");
//
// it is refused, because there is no declaration for it to describe:
//
// error: documentation comment inside a block is not attached to a declaration
//
// Inside a function, use `//`.
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Comment
rux run
```
```text
Comments never reach the program.
A block comment can sit inside a call.
// is not a comment here
/* and neither is this */
```
## Common mistakes
::warning
**A documentation comment with nothing to document.**:br`///` must sit directly before a declaration. Inside a function body there is none, and the compiler refuses it: `error: documentation comment inside a block is not attached to a declaration`. Inside a function, use `//`.
::
::warning
**An unclosed block comment.**:br
Because block comments nest, a stray `/*` inside a comment needs its own `*/`. If the rest of your file suddenly seems to vanish, count the openers and closers.
::
## Try it yourself
1. Comment out the second `PrintLine` with `//` and run again: one line fewer.
2. Wrap three lines of `Main` in a single `/* … */`, then add a nested `/* … */` inside it. Does the program still compile?
3. Move the `///` comment above `func Main` so it sits inside the body instead, and read the compiler's message.
## Learn more
- [Comments](https://rux-lang.dev/docs/lang/lexical/comments) in the Rux Reference
- [Documentation](https://rux-lang.dev/docs/learn/documentation) — writing `///` comments for `rux doc`
# Variable
::note
**You'll need**: [Hello, World](https://rux-lang.dev/docs/learn/hello), [Comment](https://rux-lang.dev/docs/learn/comment)
::
A variable gives a value a name. Once named, the value can be printed, combined with others, or passed around — and the name makes the code say what the value *means*. In Rux, `let` makes a variable that never changes after it is set. That sounds limiting, but it is the right default: a name that cannot change is one less thing to keep track of while you read.
## Declaring a variable with let
A declaration is `let`, a name, `=`, and a value:
```rux
let year = 2026;
let price = 4.99;
let language = "Rux";
let ready = true;
```
## Type inference
None of those lines names a type, yet every variable has one. The compiler **infers** it from the value:
| Value | Inferred type |
| -------------------------------------- | ---------------------- |
| `2026` — a whole number | `int` |
| `4.99` — a number with a decimal point | `float64` |
| `"Rux"` — text in double quotes | a string (`char8[..]`) |
| `true` or `false` | `bool` |
You can also write the type yourself, after a colon. Then the value has to fit it:
```rux
let small: int8 = 100;
let large: int64 = 100;
```
`100` fits in an `int8`, which holds −128 to 127; `300` would not, and the compiler would refuse it. The [Integer](https://rux-lang.dev/docs/learn/integer) lesson covers every integer type.
## Printing a value
Each `{}` in the text passed to `PrintLine` is replaced by the next value after it:
```rux
PrintLine("year {}", year);
```
The [Console](https://rux-lang.dev/docs/learn/console) lesson covers placeholders in detail — for now, this is how a value gets printed.
## Values built from other variables
The value can be any expression, including one that uses earlier variables:
```rux
let nextYear = year + 1;
```
## One name, one meaning
Rux refuses two things you may be used to from other languages. A `let` variable cannot be assigned again, and a name cannot be declared twice in the same scope:
```mermaid
flowchart TD
decl["let total = 5;"] --> assign{"total = 6;"}
assign -- "refused" --> err1["cannot modify immutable variable 'total'
help: declare 'total' with 'var'"]
decl2["let name = 1;"] --> redecl{"let name = 2;"}
redecl -- "refused" --> err2["variable 'name' is already
declared in this scope"]
```
The second rule matters if you come from a language where declaring a name again quietly hides the old one ("shadowing"). In Rux a name means one thing for the whole scope it lives in.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Variable){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A variable gives a value a name. `let` makes one: a name, an `=` and a value, and from then on
// the name stands for that value. A `let` variable never changes — the next lesson, Mutable,
// introduces the kind that does. Reach for `let` first: a name that cannot change is one less
// thing to keep track of while reading the code.
import Io::PrintLine;
func Main() -> int {
// With no type written, the compiler infers one from the value. A whole number makes an
// `int`, a number with a decimal point makes a `float64`, text in double quotes makes a
// string, and `true` or `false` makes a `bool`.
let year = 2026;
let price = 4.99;
let language = "Rux";
let ready = true;
// Each `{}` in the text is replaced by the next value after it. The Console lesson has the
// details; until then, this is how a value gets printed.
PrintLine("year {}", year);
PrintLine("price {}", price);
PrintLine("language {}", language);
PrintLine("ready {}", ready);
// A type can also be written after the name, following a colon. Then the value has to fit
// that type: 100 fits in an `int8`, which holds -128 to 127, but 300 would not.
let small: int8 = 100;
let large: int64 = 100;
PrintLine("small {}", small);
PrintLine("large {}", large);
// The value can be any expression, including one that uses earlier variables.
let nextYear = year + 1;
PrintLine("next year {}", nextYear);
// Two things the compiler refuses. Neither can appear in a program that builds, so they are
// written here rather than run:
//
// let total = 5;
// total = 6;
// error: cannot modify immutable variable 'total'
// help: declare 'total' with 'var' to make it mutable
//
// let name = 1;
// let name = 2;
// error: variable 'name' is already declared in this scope
//
// The second is worth meeting early if you come from a language where declaring a name again
// quietly hides the old one. In Rux a name means one thing for the whole scope it lives in.
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Variable
rux run
```
```text
year 2026
price 4.99
language Rux
ready true
small 100
large 100
next year 2027
```
## Common mistakes
::warning
**Assigning to a `let`.**:br`let total = 5;` followed by `total = 6;` fails with `error: cannot modify immutable variable 'total'`, and the compiler suggests `var` — the subject of the [next lesson](https://rux-lang.dev/docs/learn/mutable).
::
::warning
**Declaring the same name twice.**:br`let name = 1; let name = 2;` fails with `error: variable 'name' is already declared in this scope`. Pick a second name that says how the two values differ.
::
::warning
**A value that does not fit its type.**:br`let small: int8 = 300;` is refused: an `int8` holds only −128 to 127.
::
## Try it yourself
1. Add a variable `month` and print a line like `2026-10`.
2. Write `let celsius = 21.5;` and a second variable `fahrenheit` computed from it (`celsius * 9.0 / 5.0 + 32.0`). Print both.
3. Try each refused line from this lesson and read the messages.
## Learn more
- [Variables](https://rux-lang.dev/docs/lang/bindings/overview) and [`let`](https://rux-lang.dev/docs/lang/bindings/overview#let) in the Rux Reference
- [Mutable](https://rux-lang.dev/docs/learn/mutable) — variables that can change
# Mutable
::note
**You'll need**: [Variable](https://rux-lang.dev/docs/learn/variable)
::
A `let` variable keeps its first value for good. A `var` variable may change: after it is declared, `=` gives it a new value as often as the program needs. That is the whole difference — and the reason `let` is the default. Writing `var` tells the reader "watch this name, it moves".
## Declaring and assigning
`var` is declared exactly like `let`, with the type inferred the same way. Assignment then replaces the value:
```rux
var score = 10;
score = 25;
score = score + 5;
```
The right-hand side is worked out first, so `score = score + 5` reads the old value (25) and stores the new one (30).
## Declaring now, assigning later
A `var` may be declared with a type and no value — as long as it gets one before anything reads it:
```rux
var bonus: int;
bonus = score * 2;
```
## Assignment copies the value
`saved` takes the value `level` holds *at that moment*. Changing `level` afterwards does not reach back into it:
```rux
var level = 1;
let saved = level;
level = 2;
```
```mermaid
flowchart LR
subgraph before ["after let saved = level;"]
l1["level: 1"]
s1["saved: 1"]
end
subgraph after ["after level = 2;"]
l2["level: 2"]
s2["saved: 1"]
end
before --> after
```
Each variable owns its own copy. Later, the [Ownership](https://rux-lang.dev/docs/learn/copy) part shows what happens with larger values, and how to *move* one instead of copying it.
## let or var?
| Use | When |
| ----- | ---------------------------------------------------------------- |
| `let` | The value is set once — most variables. |
| `var` | The value has to change: a counter, a running total, a position. |
Start with `let`. If the compiler tells you a variable needs to change, switch that one to `var`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Mutable){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `let` variable keeps its first value for good. A `var` variable may change: after it is
// declared, `=` gives it a new value, as often as the program needs. That is the whole difference,
// and it is why `let` is the default — `var` says "watch this name, it moves".
import Io::PrintLine;
func Main() -> int {
// `var` is declared exactly like `let`, and its type is inferred the same way.
var score = 10;
PrintLine("score {}", score);
// Assignment replaces the value. The right-hand side is worked out first, so it may use the
// variable's own old value.
score = 25;
PrintLine("score {}", score);
score = score + 5;
PrintLine("score {}", score);
// A `var` may be declared with a type and no value, so long as it is given one before
// anything reads it.
var bonus: int;
bonus = score * 2;
PrintLine("bonus {}", bonus);
// Assigning copies the value. `saved` took the 1 that `level` held at that moment, and
// changing `level` afterwards does not reach back into it.
var level = 1;
let saved = level;
level = 2;
PrintLine("level {}", level);
PrintLine("saved {}", saved);
// What may change is the value, never the type. `score` became an `int` when it was declared
// and stays one, and a variable cannot be read before it has a value:
//
// score = 2.5;
// error: cannot assign 'float64' to 'int'
//
// var total: int;
// PrintLine("{}", total);
// error: variable 'total' is used before it is initialized
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Mutable
rux run
```
```text
score 10
score 25
score 30
bonus 60
level 2
saved 1
```
## Common mistakes
::warning
**Changing the type.**:br
The value of a `var` may change; its type never does. `score` became an `int` when it was declared, so `score = 2.5;` fails with `error: cannot assign 'float64' to 'int'`.
::
::warning
**Reading a variable before it has a value.**:br`var total: int;` followed by `PrintLine("{}", total);` fails with `error: variable 'total' is used before it is initialized`.
::
## Try it yourself
1. Start a `var count = 0;`, add 1 to it three times, and print it after each step.
2. Declare `var message: char8[..];` with no value, assign it on the next line, and print it.
3. Change `var score` back to `let score` and read the message the compiler gives for the first assignment.
## Learn more
- [`var`](https://rux-lang.dev/docs/lang/bindings/overview#var) and [mutability](https://rux-lang.dev/docs/lang/bindings/overview#mutability) in the Rux Reference
- [Assignment](https://rux-lang.dev/docs/learn/assignment) — `+=`, `++` and the other ways to update a variable
# Integer
::note
**You'll need**: [Variable](https://rux-lang.dev/docs/learn/variable)
::
An integer is a whole number stored in a fixed number of bits, and in Rux the type's name says how many. Choosing a width is a trade between range and space — and choosing *signed* or *unsigned* decides whether negative numbers are allowed.
## Signed and unsigned
| Family | Types | Range |
| -------- | ---------------------------------------------------------------------- | ------------------------- |
| Signed | `int8`, `int16`, `int32`, `int64`, `int128`, `int256`, `int512` | negative and positive |
| Unsigned | `uint8`, `uint16`, `uint32`, `uint64`, `uint128`, `uint256`, `uint512` | zero and up, twice as far |
An unsigned type spends the bit a signed type uses for the sign on reaching twice as far. Each extra byte of width multiplies the range by 256:
| Type | Smallest | Largest |
| ------- | ----------------- | ---------------- |
| `int8` | −128 | 127 |
| `uint8` | 0 | 255 |
| `int16` | −32 768 | 32 767 |
| `int32` | −2 147 483 648 | 2 147 483 647 |
| `int64` | about −9.2 × 10¹⁸ | about 9.2 × 10¹⁸ |
The wide types — `int128` up to `uint512` — are for numbers no machine register holds, such as the ones cryptography and exact arithmetic work with. They are ordinary integers all the same.
## Asking a type for its limits
Every integer type knows its own limits as `Min` and `Max`, and its size as `Bits`. Those names come from the standard `Core` package, so the lesson imports each type it asks about:
```rux
import Core::{ int, int128, int16, int32, int64, int8 };
import Core::{ uint, uint16, uint32, uint512, uint64, uint8 };
```
```rux
PrintLine("int8 {} to {}", int8::Min, int8::Max);
```
and lists `Core` next to `Io` in `Rux.toml`.
## int, uint and byte
`int` and `uint` have no number in their name because the target machine picks their width: they are as wide as a memory address — 64 bits on today's desktop systems. A whole number with no type written is an `int`, which is why `Main` returns one.
```rux
PrintLine("int {} bits", int::Bits);
```
`byte` is another name for `uint8`, used when a value is raw storage rather than a number:
```rux
let level: byte = 200;
```
## Which one should I use?
```mermaid
flowchart TD
start{"What is the number?"} --> count["A count, an index,
everyday arithmetic"] --> intT["int"]
start --> neg{"Its size matters:
a file format, hardware"}
neg -- "never negative" --> uintN["uint8 … uint64"]
neg -- "can be negative" --> intN["int8 … int64"]
start --> raw["Raw bytes of data"] --> byteT["byte"]
start --> huge["Larger than 64 bits"] --> wide["int128 … uint512"]
```
When in doubt, use `int`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Integer){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An integer is a whole number stored in a fixed number of bits, and the type's name says how
// many. `int8`, `int16`, `int32`, `int64`, `int128`, `int256` and `int512` are signed: they hold
// negative and positive numbers. `uint8` through `uint512` are unsigned: they hold only zero and
// up, and spend the bit a signed type uses for the sign on reaching twice as far.
//
// Every integer type knows its own limits, as `Min` and `Max`. Those two names are declared in the
// standard `Core` package, so this lesson imports each type it asks about, and its Rux.toml lists
// `Core` as a dependency next to `Io`.
import Core::{ int, int128, int16, int32, int64, int8 };
import Core::{ uint, uint16, uint32, uint512, uint64, uint8 };
import Io::PrintLine;
func Main() -> int {
// Signed: each extra byte of width multiplies the range by 256.
PrintLine("int8 {} to {}", int8::Min, int8::Max);
PrintLine("int16 {} to {}", int16::Min, int16::Max);
PrintLine("int32 {} to {}", int32::Min, int32::Max);
PrintLine("int64 {} to {}", int64::Min, int64::Max);
// Unsigned: the same widths, starting at zero.
PrintLine("uint8 {} to {}", uint8::Min, uint8::Max);
PrintLine("uint16 {} to {}", uint16::Min, uint16::Max);
PrintLine("uint32 {} to {}", uint32::Min, uint32::Max);
PrintLine("uint64 {} to {}", uint64::Min, uint64::Max);
// The wide types are for numbers no machine register holds, such as the ones cryptography
// and exact arithmetic work with. They are ordinary integers all the same.
PrintLine("int128 max {}", int128::Max);
PrintLine("uint512 max {}", uint512::Max);
// `int` and `uint` have no number in their name because the target machine picks their
// width: they are as wide as a memory address, 64 bits on today's desktop systems. A whole
// number with no type written is an `int`, which is why `Main` returns one.
PrintLine("int {} bits", int::Bits);
PrintLine("uint {} bits", uint::Bits);
// `byte` is another name for `uint8`, used when a value is raw storage rather than a number.
let level: byte = 200;
PrintLine("byte {}", level);
// A value has to fit the type it is given, and the compiler checks every literal:
//
// let tooBig: uint8 = 256;
// error: integer literal is out of range for type 'uint8'
//
// And `Min`, `Max` and `Bits` exist only once their type is imported from `Core`. Without
// the import the compiler reports "'Max' not found in extend for type 'int8'".
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Basics/Integer
rux run
```
```text
int8 -128 to 127
int16 -32768 to 32767
int32 -2147483648 to 2147483647
int64 -9223372036854775808 to 9223372036854775807
uint8 0 to 255
uint16 0 to 65535
uint32 0 to 4294967295
uint64 0 to 18446744073709551615
int128 max 170141183460469231731687303715884105727
uint512 max 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095
int 64 bits
uint 64 bits
byte 200
```
The `int` and `uint` lines show 64 bits on a 64-bit target.
## Common mistakes
::warning
**A literal that does not fit.**:br
The compiler checks every literal against its type: `let tooBig: uint8 = 256;` fails with `error: integer literal is out of range for type 'uint8'`.
::
::warning
**Asking for limits without importing the type.**:br`Min`, `Max` and `Bits` exist only once the type is imported from `Core`. Without the import the compiler reports `'Max' not found in extend for type 'int8'`.
::
## Try it yourself
1. Print the limits of `int16` and `uint16` side by side.
2. Add `uint128` to the imports and print its `Max`. How many digits does it have?
3. Declare `let level: byte = 255;`, then try `256` and read the error.
## Learn more
- [Signed integers](https://rux-lang.dev/docs/lang/types/integers) and [unsigned integers](https://rux-lang.dev/docs/lang/types/integers) in the Rux Reference
- [Literal](https://rux-lang.dev/docs/learn/literal) — writing numbers in hexadecimal, binary and with separators
- [Number limit](https://rux-lang.dev/docs/learn/number-limit) and [Wide integer](https://rux-lang.dev/docs/learn/wide-integer) — more in Part 16
# Float
::note
**You'll need**: [Variable](https://rux-lang.dev/docs/learn/variable), [Integer](https://rux-lang.dev/docs/learn/integer)
::
A floating-point number — a *float* — has a fractional part and an enormous range. The price is exactness: a float is stored in binary with a fixed number of significant digits, so most decimal fractions are kept as the *nearest value it can represent*, not the value you wrote.
## Two widths
| Type | Size | Significant digits | Literal |
| ------------------------ | ------- | ------------------ | ------------------ |
| `float64` (also `float`) | 8 bytes | about 16 | `4.99`, `384400.0` |
| `float32` | 4 bytes | about 7 | `0.75f32` |
A number with a decimal point is a `float64` unless something says otherwise:
```rux
let price = 4.99;
let ratio: float = 0.75;
let distance: float64 = 384400.0;
```
A `float32` value is written with the `f32` suffix. Without it the literal is a `float64` — and a `float64` is never squeezed into a `float32` behind your back, because digits would be lost:
```rux
let single: float32 = 0.75f32;
```
## Precision you can see
A third has no exact binary form, so each type stops where its digits run out:
```rux
let third64 = 1.0 / 3.0;
let third32 = 1.0f32 / 3.0f32;
```
```text
1/3 0.3333333333333333 as float64
1/3 0.33333334 as float32
```
## Why 0.1 + 0.2 is not 0.3
Neither 0.1 nor 0.2 is exact in binary, and their two tiny errors add up to a sum that is not quite 0.3:
```rux
let sum = 0.1 + 0.2;
```
```text
0.1 + 0.2 0.30000000000000004
```
```mermaid
flowchart LR
a["0.1 as written"] --> a2["stored as
0.1000000000000000055…"]
b["0.2 as written"] --> b2["stored as
0.2000000000000000111…"]
a2 --> sum["sum ≈ 0.3000000000000000444…"]
b2 --> sum
sum --> shown["printed as
0.30000000000000004"]
```
This is how floats behave in every language that uses them, not a Rux quirk. It is why money is usually counted in whole cents, with an integer.
## Floats always look like floats
A float always prints with a decimal point, even when the value is whole, so it cannot be mistaken for an integer: `let whole = 2.0;` prints `2.0`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Float){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A floating-point number, or float, has a fractional part and an enormous range. The price is
// exactness: a float is stored in binary with a fixed number of significant digits, so most
// decimal fractions are kept as the nearest value it can represent, not the value written.
//
// Rux has two float types. `float64` keeps about 16 significant digits and `float32` about 7, in
// half the space. `float` is another name for `float64`, and it is what a number with a decimal
// point becomes when nothing says otherwise.
import Io::PrintLine;
func Main() -> int {
// Three ways to end up with a float64.
let price = 4.99;
let ratio: float = 0.75;
let distance: float64 = 384400.0;
PrintLine("price {}", price);
PrintLine("ratio {}", ratio);
PrintLine("distance {}", distance);
// A float32 value is written with the `f32` suffix. Without it the literal is a float64, and
// a float64 is never squeezed into a float32 behind your back, because digits would be lost.
let single: float32 = 0.75f32;
PrintLine("single {}", single);
// The same division at both widths shows the difference in precision. A third has no exact
// binary form, so each type stops where its digits run out.
let third64 = 1.0 / 3.0;
let third32 = 1.0f32 / 3.0f32;
PrintLine("1/3 {} as float64", third64);
PrintLine("1/3 {} as float32", third32);
// Neither 0.1 nor 0.2 is exact in binary either, and their two tiny errors add up to a sum
// that is not quite 0.3. This is how floats behave in every language that uses them, not a
// Rux quirk. It is why money is usually counted in whole cents with an integer.
let sum = 0.1 + 0.2;
PrintLine("0.1 + 0.2 {}", sum);
// A float always prints with a decimal point, even when the value is whole, so it cannot be
// mistaken for an integer.
let whole = 2.0;
PrintLine("whole {}", whole);
// A float and an integer are different types even when they hold the same number:
//
// let count: int = 2.0;
// error: cannot assign 'float64' to 'int'
//
// let single: float32 = 0.75;
// error: cannot assign 'float64' to 'float32'
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Float
rux run
```
```text
price 4.99
ratio 0.75
distance 384400.0
single 0.75
1/3 0.3333333333333333 as float64
1/3 0.33333334 as float32
0.1 + 0.2 0.30000000000000004
whole 2.0
```
## Common mistakes
::warning
**Mixing floats and integers.**:br
They are different types even when they hold the same number: `let count: int = 2.0;` fails with `error: cannot assign 'float64' to 'int'`. Write `2`, or convert with [`as`](https://rux-lang.dev/docs/learn/convert).
::
::warning
**Forgetting the `f32` suffix.**:br`let single: float32 = 0.75;` fails with `error: cannot assign 'float64' to 'float32'`. Write `0.75f32`.
::
::warning
**Comparing floats for exact equality.**:br
After arithmetic, `0.1 + 0.2 == 0.3` is false. Compare against a small tolerance instead — Part 16 covers [special float values](https://rux-lang.dev/docs/learn/float-special) and limits such as `Epsilon`.
::
## Try it yourself
1. Compute the area of a circle with radius `2.5`, using `3.14159` for π.
2. Repeat the `1/3` experiment with `2.0 / 3.0`. Where does each width round?
3. Add `0.1` to a `var total = 0.0;` ten times and print it. Is it exactly `1.0`?
## Learn more
- [`float64`](https://rux-lang.dev/docs/lang/types/floating-point) and [`float32`](https://rux-lang.dev/docs/lang/types/floating-point) in the Rux Reference
- [Float special](https://rux-lang.dev/docs/learn/float-special) — infinity, NaN and negative zero
- [Math](https://rux-lang.dev/docs/learn/math) — square roots, powers and trigonometry
# Boolean
::note
**You'll need**: [Variable](https://rux-lang.dev/docs/learn/variable), [Integer](https://rux-lang.dev/docs/learn/integer)
::
A boolean holds one of exactly two values: `true` or `false`. It is the answer to a yes-or-no question — is the file open, has the player won — and the rest of the course leans on it constantly: every comparison produces one, and `if` and `while` choose what to run by one.
## true and false
`true` and `false` are the only boolean literals, and either one makes a `bool`:
```rux
let ready = true;
let finished = false;
let visible: bool = true;
```
## The boolean family
`bool` is the everyday spelling. It is another name for `bool8`, a boolean stored in one byte. The wider types hold the same two values in more space:
| Type | Size | Use |
| ---------------- | ------- | ---------------------------------- |
| `bool` = `bool8` | 1 byte | Everyday code |
| `bool16` | 2 bytes | Talking to code in other languages |
| `bool32` | 4 bytes | The Windows API's `BOOL`, for one |
| `bool64` | 8 bytes | Matching other 8-byte layouts |
```rux
let flag32: bool32 = true;
```
They exist for [interoperating with C and operating-system APIs](https://rux-lang.dev/docs/learn/c-interop), where a boolean's size is part of the agreement. In your own code, use `bool`.
## Where booleans come from
You will rarely write `true` or `false` by hand. Most booleans are produced by questions the program asks:
```mermaid
flowchart LR
cmp["score > 10
(a comparison)"] --> b(("bool"))
logic["ready && !finished
(logic)"] --> b
b --> ifs["if … { }"]
b --> wh["while … { }"]
b --> tern["cond ? a : b"]
```
[Comparison](https://rux-lang.dev/docs/learn/comparison) and [Logical](https://rux-lang.dev/docs/learn/logical) show how to produce them; [If](https://rux-lang.dev/docs/learn/if) and [While](https://rux-lang.dev/docs/learn/while) show how to use them.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Boolean){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A boolean holds one of exactly two values, `true` or `false`. It is the answer to a yes-or-no
// question — is the file open, has the player won — and later parts lean on it constantly: a
// comparison produces one, and `if` and `while` choose what to run by one.
//
// `bool` is the everyday spelling. It is another name for `bool8`, a boolean stored in one byte.
// `bool16`, `bool32` and `bool64` hold the same two values in two, four and eight bytes. They
// exist for talking to code written in other languages: the Windows API, for one, uses a
// four-byte boolean, which is a `bool32` here.
import Io::PrintLine;
func Main() -> int {
// `true` and `false` are the only boolean literals, and either one makes a `bool`.
let ready = true;
let finished = false;
PrintLine("ready {}", ready);
PrintLine("finished {}", finished);
// The type can be written out, as with any variable.
let visible: bool = true;
PrintLine("visible {}", visible);
// The wider types print the same way. Only their size differs.
let flag8: bool8 = true;
let flag16: bool16 = false;
let flag32: bool32 = true;
let flag64: bool64 = false;
PrintLine("bool8 {}", flag8);
PrintLine("bool16 {}", flag16);
PrintLine("bool32 {}", flag32);
PrintLine("bool64 {}", flag64);
// A boolean is not a number. Some languages treat 0 as false and 1 as true; Rux does not mix
// them up on its own:
//
// let on: bool = 1;
// error: cannot assign 'int' to 'bool8'
//
// The message names `bool8` because that is what `bool` is. Turning a number into a boolean,
// or back, is a conversion you ask for, and the Convert lesson shows how.
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Boolean
rux run
```
```text
ready true
finished false
visible true
bool8 true
bool16 false
bool32 true
bool64 false
```
## Common mistakes
::warning
**Using a number as a boolean.**:br
Some languages treat 0 as false and 1 as true; Rux does not mix them up on its own. `let on: bool = 1;` fails with `error: cannot assign 'int' to 'bool8'` — the message names `bool8` because that is what `bool` is. Converting is something you ask for with `as`, as the [Convert](https://rux-lang.dev/docs/learn/convert) lesson shows.
::
## Try it yourself
1. Declare `let raining = true;` and `let umbrella = false;` and print a sentence using both.
2. Try `let on: bool = 1;` and read the message.
3. Look ahead: in the [Comparison](https://rux-lang.dev/docs/learn/comparison) lesson, find the line that produces a `bool` without writing `true` or `false`.
## Learn more
- [`bool`](https://rux-lang.dev/docs/lang/types/booleans) in the Rux Reference
- [Logical](https://rux-lang.dev/docs/learn/logical) — combining booleans with `&&`, `||` and `!`
# Character
::note
**You'll need**: [Variable](https://rux-lang.dev/docs/learn/variable), [Integer](https://rux-lang.dev/docs/learn/integer)
::
A character is one Unicode *code point*: a letter, a digit, a symbol, an emoji. A character literal is written in **single** quotes, `'A'`. Double quotes make a string instead — a sequence of characters, and a topic of its own in [Part 14](https://rux-lang.dev/docs/learn/text).
## The char type
`char` is the everyday type. It is another name for `char32`: four bytes, enough for every code point there is. Any character fits:
```rux
let letter = 'A';
let accented = 'é';
let greek: char = 'π';
let emoji = '😀';
```
## Escapes
A backslash starts an escape, for characters that are awkward to type between quotes:
| Escape | Character |
| ------------ | ----------------------------------------------------- |
| `'\''` | a single quote |
| `'\\'` | a backslash |
| `'\n'` | a line break |
| `'\t'` | a tab |
| `'\u{263A}'` | any code point, by its number in hexadecimal — here ☺ |
```rux
let quote = '\'';
let backslash = '\\';
let smile = '\u{263A}';
```
## Code units and code points
A prefix picks another character type, and two of them are different in kind:
```rux
let unit8 = c8'R';
let unit16 = c16'Ж';
let scalar32 = c32'€';
let scalar64 = c64'😀';
```
| Prefix | Type | Holds |
| --------------- | ----------------- | ------------------------------------ |
| `c8` | `char8` | One **byte** of UTF-8 text |
| `c16` | `char16` | One **unit** of UTF-16 text |
| `c32` (or none) | `char32` = `char` | One whole code point |
| `c64` | `char64` | One whole code point, in eight bytes |
A `char8` or `char16` is a *piece* of an encoding, and a piece is a whole character only when the code point is small enough to fit in one:
```mermaid
flowchart LR
R["'R' — U+0052"] --> r8["UTF-8: 1 byte
fits a char8"]
E["'é' — U+00E9"] --> e8["UTF-8: 2 bytes
does not fit a char8"]
E --> e16["UTF-16: 1 unit
fits a char16"]
S["'😀' — U+1F600"] --> s16["UTF-16: 2 units
does not fit a char16"]
S --> s32["fits a char32"]
```
Plain ASCII fits a `char8`; most living scripts fit a `char16`; everything fits a `char`. The [Encoding](https://rux-lang.dev/docs/learn/encoding) and [UTF-8](https://rux-lang.dev/docs/learn/utf8) lessons go deeper.
## A literal takes its binding's type
Without a prefix, a literal is a `char32` — unless its binding names a narrower type, which it then takes. So `let initial: char8 = 'R';` works too, as long as the character fits in one unit.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Character){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A character is one Unicode code point: a letter, a digit, a symbol, an emoji. A character
// literal is written in single quotes, 'A'. Double quotes make a string instead, which is a
// sequence of characters and a topic of its own.
//
// `char` is the everyday type. It is another name for `char32`, four bytes, enough for every code
// point there is, and it is the type a quoted character gets when nothing says otherwise.
import Io::PrintLine;
func Main() -> int {
// Any code point fits in a `char`, from plain ASCII to an emoji.
let letter = 'A';
let accented = 'é';
let greek: char = 'π';
let emoji = '😀';
PrintLine("letter {}", letter);
PrintLine("accented {}", accented);
PrintLine("greek {}", greek);
PrintLine("emoji {}", emoji);
// A backslash starts an escape, for characters that are awkward to type between quotes:
// '\'' is a single quote, '\\' a backslash, '\n' a line break and '\t' a tab. `\u{...}`
// names any code point by its number in hexadecimal.
let quote = '\'';
let backslash = '\\';
let smile = '\u{263A}';
PrintLine("quote {}", quote);
PrintLine("backslash {}", backslash);
PrintLine("smile {}", smile);
// A prefix picks another width. `c32` spells the default out, and `c64` holds the same code
// points in eight bytes. `c8` and `c16` are different in kind: a `char8` holds one byte of
// UTF-8 text and a `char16` one unit of UTF-16 text. Those are pieces of an encoding, and a
// piece is a whole character only for the code points small enough to fit in one: plain
// ASCII for `char8`, and most living scripts for `char16`.
let unit8 = c8'R';
let unit16 = c16'Ж';
let scalar32 = c32'€';
let scalar64 = c64'😀';
PrintLine("char8 {}", unit8);
PrintLine("char16 {}", unit16);
PrintLine("char32 {}", scalar32);
PrintLine("char64 {}", scalar64);
// Without a prefix, a literal is a `char32` unless its binding names a narrower type; then it
// takes that type, so `let initial: char8 = 'R';` works too. Either way the character has to
// fit in one unit, and one that does not is refused:
//
// let accent = c8'é';
// error: character 'é' (U+00E9) does not fit one 'char8' code unit
// help: write a string literal such as c8"é", or a byte such as 0xE9u8
//
// é takes two bytes of UTF-8, and one `char8` holds only one of them. Text that needs several
// units is a string, which has a lesson of its own.
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Character
rux run
```
```text
letter A
accented é
greek π
emoji 😀
quote '
backslash \
smile ☺
char8 R
char16 Ж
char32 €
char64 😀
```
## Common mistakes
::warning
**A character too big for its code unit.**:br`let accent = c8'é';` fails with `error: character 'é' (U+00E9) does not fit one 'char8' code unit`, and the compiler suggests a string literal such as `c8"é"` instead. é takes two bytes of UTF-8, and one `char8` holds only one of them.
::
::warning
**Double quotes for a character.**:br`"A"` is a string, not a character. Use single quotes for one character.
::
## Try it yourself
1. Print the first letter of your name, a digit and an emoji, each as a `char`.
2. Print a tab character between two letters using `'\t'`.
3. Find a code point in a table (for example U+2665, a heart) and print it with `'\u{…}'`.
4. Try `c8'ж'` and read the message. Which prefix makes it fit?
## Learn more
- [`char`](https://rux-lang.dev/docs/lang/types/characters) and [`char8`](https://rux-lang.dev/docs/lang/types/characters) in the Rux Reference
- [Character pattern](https://rux-lang.dev/docs/learn/character-pattern) — matching characters with `match`
- [Encoding](https://rux-lang.dev/docs/learn/encoding) and [UTF-8](https://rux-lang.dev/docs/learn/utf8) — characters inside text
# Literal
::note
**You'll need**: [Integer](https://rux-lang.dev/docs/learn/integer), [Float](https://rux-lang.dev/docs/learn/float)
::
A *literal* is a value written straight into the source: `42`, `2.5`, `'A'`, `true`. This lesson is about the numeric ones — the many ways one number can be spelled — and about a rule that surprises people: how a literal gets its **type**.
## Four bases
A prefix picks the base. The base is only how the source spells the value, so all four variables hold the same 255:
```rux
let decimal = 255;
let hex = 0xFF;
let octal = 0o377;
let binary = 0b11111111;
```
| Prefix | Base | Typical use |
| ------ | ---- | -------------------------------------- |
| none | 10 | Everyday numbers |
| `0x` | 16 | Colours, memory addresses, byte values |
| `0o` | 8 | File permissions |
| `0b` | 2 | Bit flags and masks |
## Digit separators
An underscore between digits is ignored. It groups them the way a comma does on paper:
```rux
let population = 8_100_000_000;
let mask = 0xFFFF_0000;
let flags = 0b1010_0101;
```
## Exponents
A float literal may carry an exponent: `e8` means "times ten to the eighth".
```rux
let light = 2.998e8;
let charge = 1.6e-19;
```
## Suffixes
A suffix fixes the type inside the literal itself:
```rux
let small = 200u8;
let wide = 5i64;
let single = 0.5f32;
```
`u8` makes a `uint8`, `i64` an `int64`, `f32` a `float32`; plain `u` and `i` mean `uint` and `int`.
## How a literal gets its type
This is the part worth slowing down for:
```mermaid
flowchart LR
lit["An unsuffixed literal,
such as 100"] --> q{"Is it next to a value
that already has a type?"}
q -- "no — it stands alone
or beside another literal" --> def["int, or float64 for 2.5"]
q -- "yes" --> partner["It takes that value's type"]
partner --> fit{"Does it fit that type?"}
fit -- "yes" --> ok["Compiled in that type"]
fit -- "no" --> err["error: integer literal is
out of range for type …"]
```
Here `100` sits next to `level`, a `uint8`, so it becomes a `uint8` too, and the sum is worked out in eight bits. 300 does not fit in eight bits: the result wraps around past 255 and lands on 44.
```rux
let level: uint8 = 200;
let raised = level + 100;
```
Two literals together have no typed partner, so both are `int` and the sum is plain 300:
```rux
let plain = 200 + 100;
```
Wrapping is covered properly in [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic) and [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Literal){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A literal is a value written straight into the source: 42, 2.5, 'A', true. This lesson is about
// the numeric ones — the many ways one number can be spelled, and how a literal gets its type.
import Io::PrintLine;
func Main() -> int {
// The same number in four bases. A prefix picks the base: `0x` for hexadecimal, `0o` for
// octal and `0b` for binary. The base is only how the source spells the value, so all four
// variables hold the same 255 and print it the same way.
let decimal = 255;
let hex = 0xFF;
let octal = 0o377;
let binary = 0b11111111;
PrintLine("bases {} {} {} {}", decimal, hex, octal, binary);
// An underscore between digits is ignored. It groups them the way a comma does on paper.
let population = 8_100_000_000;
let mask = 0xFFFF_0000;
let flags = 0b1010_0101;
PrintLine("separators {} {} {}", population, mask, flags);
// A float literal may carry an exponent: `e8` means "times ten to the eighth".
let light = 2.998e8;
let charge = 1.6e-19;
PrintLine("exponents {} {}", light, charge);
// A suffix fixes the type inside the literal itself: `u8` makes a uint8, `i64` an int64 and
// `f32` a float32. Plain `u` and `i` mean `uint` and `int`.
let small = 200u8;
let wide = 5i64;
let single = 0.5f32;
PrintLine("suffixes {} {} {}", small, wide, single);
// With no suffix, a literal standing alone is an `int` or a `float64`. Next to a value that
// already has a type, it takes that value's type instead. Here 100 becomes a uint8, the sum
// is worked out in eight bits, and 300 does not fit in eight bits: the result wraps around
// past 255 and lands on 44.
let level: uint8 = 200;
let raised = level + 100;
PrintLine("uint8 {}", raised);
// Two literals together have no typed partner, so both are `int` and the sum is plain 300.
let plain = 200 + 100;
PrintLine("int {}", plain);
// Because the literal takes the other side's type, it must also fit that type. These two are
// refused rather than quietly turned into something else:
//
// let over = level + 300;
// error: integer literal is out of range for type 'uint8'
//
// let count: uint = 3;
// let missing = count == -1;
// error: integer literal is out of range for type 'uint'
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Literal
rux run
```
```text
bases 255 255 255 255
separators 8100000000 4294901760 165
exponents 299800000.0 1.6e-19
suffixes 200 5 0.5
uint8 44
int 300
```
## Common mistakes
::warning
**A literal that does not fit its partner's type.**:br
Because the literal takes the other side's type, it must also fit it. When `level` is a `uint8`, `level + 300` fails with `error: integer literal is out of range for type 'uint8'` — 300 is not a `uint8`.
::
::warning
**A negative literal next to an unsigned value.**:br
With `let count: uint = 3;`, the comparison `count == -1` fails the same way: `-1` would have to be a `uint`, and no `uint` is negative.
::
## Try it yourself
1. Print `0b1111_0000`, `0xF0` and `0o360`. Are they the same number?
2. Write the speed of light in km/s as `2.998e5` and the mass of an electron as `9.109e-31`.
3. Change `level + 100` to `level + 300` and read the error. Then make it compile by giving `level` a wider type.
## Learn more
- [Literals](https://rux-lang.dev/docs/lang/lexical/literals) in the Rux Reference
- [Integer](https://rux-lang.dev/docs/learn/integer) and [Float](https://rux-lang.dev/docs/learn/float) — the types literals become
- [Format number](https://rux-lang.dev/docs/learn/format-number) — printing numbers in hexadecimal and binary
# Console
::note
**You'll need**: [Hello, World](https://rux-lang.dev/docs/learn/hello), [Variable](https://rux-lang.dev/docs/learn/variable), [Literal](https://rux-lang.dev/docs/learn/literal)
::
Every lesson so far has printed with `PrintLine`. This one looks at it closely, together with its partner `Print` — and at the `{}` placeholders that put values inside text.
## Print and PrintLine
The difference is one thing: `PrintLine` ends the line it writes, and `Print` leaves the cursor where it stopped, so the next output carries on the same line.
```rux
Print("one ");
Print("two ");
PrintLine("three");
```
```text
one two three
```
A `\n` inside the text is a line break too, so `PrintLine` is `Print` with one added:
```rux
Print("four\nfive\n");
```
With nothing to print, `PrintLine` writes only the line ending — a blank line:
```rux
PrintLine();
```
Both live in `Io`, and the lesson imports them together with braces:
```rux
import Io::{ Print, PrintLine };
```
## Printing values
A value can be printed on its own, with no text around it — `PrintLine(42);`, `PrintLine(2.5);`, `PrintLine(true);`. More often the value goes inside a message. Each `{}` is filled by the next argument, in the order they are written:
```rux
let name = "Rux";
let major = 0;
let minor = 4;
PrintLine("{} version {}.{}", name, major, minor);
```
```mermaid
flowchart LR
fmt["{} version {}.{}"] --> out["Rux version 0.4"]
a1["name → Rux"] -- "1st {}" --> fmt
a2["major → 0"] -- "2nd {}" --> fmt
a3["minor → 4"] -- "3rd {}" --> fmt
```
`Print` takes placeholders as well, so a line can be built from pieces:
```rux
Print("{} + {} = ", 2, 3);
PrintLine("{}", 2 + 3);
```
## Printing a brace
To print a brace itself in text that has placeholders, double it. Text passed with *no* arguments is printed exactly as written, so there a single brace is fine:
```rux
PrintLine("{{}} marks a place for {}", "a value");
PrintLine("{} with nothing to fill it");
```
```text
{} marks a place for a value
{} with nothing to fill it
```
Width, alignment and decimal places inside `{}` — such as `{:>6}` or `{:.2}` — come in the [Format](https://rux-lang.dev/docs/learn/format) lesson.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Console){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Every lesson so far has printed with `PrintLine`. This one looks at it closely, together with its
// partner `Print`. The difference between them is one thing: `PrintLine` ends the line it writes,
// and `Print` leaves the cursor where it stopped, so the next output carries on the same line.
import Io::{ Print, PrintLine };
func Main() -> int {
// Three calls, one line, because only the last call ends it.
Print("one ");
Print("two ");
PrintLine("three");
// A `\n` inside the text is a line break too, so `PrintLine` is `Print` with one added.
Print("four\nfive\n");
// With nothing to print, `PrintLine` writes only the line ending: a blank line.
PrintLine();
// A value can be printed on its own, with no text around it.
PrintLine(42);
PrintLine(2.5);
PrintLine(true);
// More often the value goes inside a message. Each `{}` is filled by the next argument, in
// the order they are written, and any kind of value can fill one.
let name = "Rux";
let major = 0;
let minor = 4;
PrintLine("{} version {}.{}", name, major, minor);
// `Print` takes placeholders as well, so a line can be built from pieces.
Print("{} + {} = ", 2, 3);
PrintLine("{}", 2 + 3);
// To print a brace itself in text that has placeholders, double it. Text passed with no
// arguments at all is printed exactly as written, so there a brace is single.
PrintLine("{{}} marks a place for {}", "a value");
PrintLine("{} with nothing to fill it");
// Watch the count. Once a call has arguments, the compiler matches them against the
// placeholders, and a placeholder with no argument, or an argument with no placeholder, is
// refused:
//
// PrintLine("{} and {}", 1);
// error: format string has 2 placeholders, but 1 argument was provided
// help: pass one argument for each '{}' placeholder
//
// Width, alignment and decimal places inside `{}` wait for the Format lesson.
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Console
rux run
```
```text
one two three
four
five
42
2.5
true
Rux version 0.4
2 + 3 = 5
{} marks a place for a value
{} with nothing to fill it
```
## Common mistakes
::warning
**Placeholders and arguments that do not match.**:br
Once a call has arguments, the compiler counts them against the placeholders. `PrintLine("{} and {}", 1);` fails with `error: format string has 2 placeholders, but 1 argument was provided`. Pass one argument for each `{}`.
::
## Try it yourself
1. Print a small receipt: three items with their prices, each on its own line, and a total on the last.
2. Use `Print` in three calls to build `1, 2, 3` on one line, then end it with `PrintLine()`.
3. Print the text `{name}` literally, followed by a placeholder filled with your name.
## Learn more
- [`Print`](https://rux-lang.dev/docs/api/io/print) in the API Reference
- [Format](https://rux-lang.dev/docs/learn/format) and [Format number](https://rux-lang.dev/docs/learn/format-number) — width, alignment, precision and bases
- [Input](https://rux-lang.dev/docs/learn/input) — the other direction: reading what the user types
# Const
::note
**You'll need**: [Variable](https://rux-lang.dev/docs/learn/variable), [Literal](https://rux-lang.dev/docs/learn/literal), [Console](https://rux-lang.dev/docs/learn/console)
::
`let` names a value while the program **runs**. `const` names one while the program is being **compiled**: the compiler works the value out once, and every use of the name is replaced by the answer. Nothing is left to compute at run time.
## Declaring constants
A constant is declared like a variable, with `const`, and its type is inferred the same way — or written out when the inferred `int` is not the type wanted:
```rux
const Limit = 100;
const Greeting = "Hello from a constant";
const MaxPlayers: uint8 = 4;
const Pi: float32 = 3.1415927f32;
```
Constants are named in **PascalCase**, like types, so they stand out from variables.
## Constants built from constants
A constant may be built from other constants, because all of them are known while compiling. `Doubled` costs nothing when the program runs — the compiler has already stored 200:
```rux
const Doubled = Limit * 2;
const Half = Limit / 2;
```
## Where a constant can live
Constants can be declared **outside any function**, at the top of the file, where the whole file can use them. They can also live inside a function, where only that function sees them:
```rux
const SecondsPerHour = 60 * 60;
```
In an expression, a constant is used like any other value — here beside a run-time `let`:
```rux
let hours = 3;
let seconds = hours * SecondsPerHour;
```
## const or let?
```mermaid
flowchart TD
q{"Is the value known
before the program runs?"}
q -- "yes: a fixed number, a limit,
a name, a conversion factor" --> c["const — computed once by the compiler,
usable anywhere in the file"]
q -- "no: it depends on input,
a calculation, a variable" --> l["let — computed while the program runs"]
```
Because a constant's value is known before the program runs, it can go where the compiler itself needs a value: the length of an array (`bool[Limit]`, in [Part 5](https://rux-lang.dev/docs/learn/array)) and the condition of a compile-time [`when`](https://rux-lang.dev/docs/learn/when).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Const){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `let` names a value while the program runs. `const` names one while the program is being
// compiled: the compiler works the value out once, and every use of the name is replaced by the
// answer. Nothing is left to compute at run time.
//
// That buys two things. A constant can be declared outside any function, at the top of the file,
// where all of the file can use it. And because its value is known before the program runs, it
// can go where the compiler itself needs a value: the length of an array, as in `bool[Limit]`,
// once arrays arrive in Part 5, and the condition of a `when`, which comes later still.
import Io::PrintLine;
// The type is inferred, exactly as with `let`.
const Limit = 100;
const Greeting = "Hello from a constant";
// Or written out, when the inferred `int` is not the type wanted.
const MaxPlayers: uint8 = 4;
const Pi: float32 = 3.1415927f32;
// A constant may be built from other constants, because all of them are known while compiling.
// `Doubled` costs nothing when the program runs: the compiler has already stored 200.
const Doubled = Limit * 2;
const Half = Limit / 2;
func Main() -> int {
PrintLine("Limit {}", Limit);
PrintLine("Greeting {}", Greeting);
PrintLine("MaxPlayers {}", MaxPlayers);
PrintLine("Pi {}", Pi);
PrintLine("Doubled {}", Doubled);
PrintLine("Half {}", Half);
// A constant can also live inside a function, where only that function sees it.
const SecondsPerHour = 60 * 60;
// In an expression a constant is used like any other value, here beside a run-time `let`.
let hours = 3;
let seconds = hours * SecondsPerHour;
PrintLine("3 hours {} seconds", seconds);
// Build a constant only from literals and other constants. A `let` or a `var` gets its
// value while the program runs, which is too late for a `const`:
//
// let hours = 3;
// const Seconds = hours * 3600;
// error: 'hours' is not a compile-time constant
// help: declare 'Seconds' with 'let' to compute it at run time
//
// Constants are named in PascalCase, like types, so they stand out from variables.
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Const
rux run
```
```text
Limit 100
Greeting Hello from a constant
MaxPlayers 4
Pi 3.1415927
Doubled 200
Half 50
3 hours 10800 seconds
```
## Common mistakes
::warning
**Building a constant from a variable.**:br
A `let` or `var` gets its value while the program runs, which is too late for a `const`. `let hours = 3; const Seconds = hours * 3600;` fails with `error: 'hours' is not a compile-time constant`, and the compiler suggests declaring `Seconds` with `let` instead.
::
## Try it yourself
1. Add `const MinutesPerDay = 24 * 60;` at the top of the file and print it.
2. Make a `const TaxRate = 0.2;` and use it with a `let price = 50.0;` to print the tax.
3. Try building a `const` from a `let` and read the error.
## Learn more
- [Constants](https://rux-lang.dev/docs/lang/bindings/constants) in the Rux Reference
- [When](https://rux-lang.dev/docs/learn/when) — choosing code at compile time
# Convert
::note
**You'll need**: [Integer](https://rux-lang.dev/docs/learn/integer), [Float](https://rux-lang.dev/docs/learn/float), [Boolean](https://rux-lang.dev/docs/learn/boolean), [Character](https://rux-lang.dev/docs/learn/character)
::
Rux converts between types on its own only when nothing can be lost. Every other conversion has to be asked for with `as` — because every other conversion can change the value. Writing `as` is how you tell the compiler, and the next reader, that the change is intended.
## Widening happens on its own
Widening within one family and one signedness — `int8` to `int32`, `float32` to `float64` — can never lose anything, so the compiler does it silently:
```rux
let small: int8 = 100;
let widened: int32 = small;
```
## Everything else needs as
```mermaid
flowchart TD
src["A value of type A, needed as type B"] --> w{"Same family and signedness,
and B is wider?"}
w -- "yes" --> auto["Converted automatically"]
w -- "no" --> as["Write value as B"]
as --> n["Narrower integer:
keeps the low bits"]
as --> s["Other signedness:
same bits, read differently"]
as --> f2i["Float to integer:
truncates toward zero, saturates"]
as --> i2f["Integer to float, char to number,
bool to number and back"]
```
### Narrowing keeps the low bits
300 needs nine bits; an `int8` has eight, and what survives is 300 − 256 = 44:
```rux
let big: int32 = 300;
let narrowed = big as int8;
```
### Changing signedness re-reads the bits
−1 has every bit set, and eight set bits read as an unsigned number are 255:
```rux
let negative: int32 = -1;
let reinterpreted = negative as uint8;
```
### Floats and integers
Integers and floats are different families, so crossing between them always needs `as`. Float to integer drops the fraction — it truncates toward zero rather than rounding:
```rux
let ratio = 3.9;
let negativeRatio = -3.9;
```
`ratio as int32` is `3`, and `negativeRatio as int32` is `-3`. A float too large for the integer type does not keep low bits the way narrowing does: it **saturates** to the largest (or smallest) value the type holds, so `1e10 as int32` is `2147483647`.
A narrower float keeps fewer digits, so `float64` to `float32` needs `as` as well: `3.141592653589793` becomes `3.1415927`.
### Characters and booleans
A character is a number underneath — its code point — and `as` shows it: `'A' as uint32` is `65`. A boolean becomes `1` or `0`; in the other direction, zero is `false` and anything else is `true`.
## as never fails
None of these conversions reports a problem. A conversion with `as` always produces a value, and when the original does not fit, that value is simply a different one. To find out first whether a value fits, compare it with the target type's limits — or use the checked conversions of [Checked convert](https://rux-lang.dev/docs/learn/checked-convert).
| Conversion | Example | Result |
| -------------------- | -------------------------- | ---------- |
| Widen | `int8` 100 → `int32` | 100 |
| Narrow | `int32` 300 `as int8` | 44 |
| Signedness | `int32` −1 `as uint8` | 255 |
| Float → int | 3.9 `as int32` | 3 |
| Float → int, too big | 1e10 `as int32` | 2147483647 |
| Int → float | 7 `as float64` | 7.0 |
| Char → int | 'A' `as uint32` | 65 |
| Bool ↔ int | `true as int`, `5 as bool` | 1, true |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Basics/Convert){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A conversion happens on its own only when nothing can be lost: widening within one family and
// one signedness, such as `int8` to `int32` or `float32` to `float64`. Every other conversion is
// asked for with `as`, because every other conversion can change the value. Writing `as` is how
// you tell the compiler, and the next reader, that the change is intended.
import Io::PrintLine;
func Main() -> int {
// Widening needs no `as`. Every `int8` is also an `int32`, so the compiler converts silently.
let small: int8 = 100;
let widened: int32 = small;
PrintLine("widen int8 {} becomes int32 {}", small, widened);
// Narrowing must be asked for, and keeps only the low bits that fit. 300 needs nine bits, an
// `int8` has eight, and what survives is 300 - 256.
let big: int32 = 300;
let narrowed = big as int8;
PrintLine("narrow int32 {} becomes int8 {}", big, narrowed);
// Changing signedness keeps the bits and reads them differently: -1 has every bit set, and
// eight set bits read as an unsigned number are 255.
let negative: int32 = -1;
let reinterpreted = negative as uint8;
PrintLine("sign int32 {} becomes uint8 {}", negative, reinterpreted);
// Integers and floats are different families, so crossing between them always needs `as`.
// Float to integer drops the fraction: it truncates toward zero rather than rounding.
let ratio = 3.9;
let negativeRatio = -3.9;
PrintLine("float->int {} becomes {}", ratio, ratio as int32);
PrintLine("float->int {} becomes {}", negativeRatio, negativeRatio as int32);
// A float too large for the integer type does not keep low bits the way narrowing does. It
// saturates: it becomes the largest value the type holds, or the smallest, below the range.
let huge = 1e10;
PrintLine("float->int {} becomes {}", huge, huge as int32);
let count: int32 = 7;
PrintLine("int->float {} becomes {}", count, count as float64);
// A narrower float keeps fewer digits, so float64 to float32 needs `as` as well.
let pi = 3.141592653589793;
PrintLine("narrow float {} becomes {}", pi, pi as float32);
// A character is a number underneath, its code point, and `as` shows it.
let letter = 'A';
PrintLine("char->int {} becomes {}", letter, letter as uint32);
// A boolean becomes 1 or 0. In the other direction, zero is false and anything else is true.
let yes = true;
let five = 5;
PrintLine("bool->int {} becomes {}", yes, yes as int);
PrintLine("int->bool {} becomes {}", five, five as bool);
// None of the conversions above reported a problem. A conversion with `as` never fails: it
// always produces a value, and when the original does not fit, that value is simply a
// different one. To find out first whether a value fits, compare it with the target type's
// limits — comparisons are the next part, and choosing what to do with the answer the one
// after.
return 0;
}
```
## Run it
```sh
cd Examples/Basics/Convert
rux run
```
```text
widen int8 100 becomes int32 100
narrow int32 300 becomes int8 44
sign int32 -1 becomes uint8 255
float->int 3.9 becomes 3
float->int -3.9 becomes -3
float->int 10000000000.0 becomes 2147483647
int->float 7 becomes 7.0
narrow float 3.141592653589793 becomes 3.1415927
char->int A becomes 65
bool->int true becomes 1
int->bool 5 becomes true
```
## Common mistakes
::warning
**Expecting rounding.**:br`2.99 as int32` is `2`, not `3`: conversion truncates toward zero. Round first if that is what you mean — the [Math](https://rux-lang.dev/docs/learn/math) lesson has `Round`.
::
::warning
**Expecting an error on overflow.**:br`as` never fails. `300 as int8` quietly becomes `44`. When the value might not fit, check it first or use [Checked convert](https://rux-lang.dev/docs/learn/checked-convert).
::
## Try it yourself
1. Convert `-1 as uint16` and `-1 as uint32`. Predict the results before you run.
2. Print the code points of the letters in `R`, `u`, `x` with `as uint32`.
3. Convert `-1e10` to `int32` and check that it saturates to the smallest value.
## Learn more
- [Type casts](https://rux-lang.dev/docs/lang/expressions/casts) in the Rux Reference
- [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) — conversions that report whether the value fits
- [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) — what happens when arithmetic overflows
# Part 2: Operators
Part 1 gave you values and names for them. This part combines them: adding and dividing numbers, comparing them, joining yes-or-no answers into bigger conditions, and updating a variable in place. It is a short part — five lessons — but every program you write from here on is built from these operators, and the comparisons and conditions are exactly what [Part 3](https://rux-lang.dev/docs/learn/control-flow) needs to make decisions.
## What you will learn
- Arithmetic with `+ - * / %`, and why `17 / 5` is 3 for integers but 3.4 for floats.
- Comparing values with `== != < <= > >=`, and why exact `==` between floats is risky.
- Combining conditions with `&&`, `||` and `!`, and how short-circuiting lets one side guard the other.
- Updating a variable with `+=`, `-=` and friends, and stepping it with `++` and `--`.
- Which operator applies first, how same-rank operators group, and when to add parentheses.
## What each kind of operator produces
```mermaid
flowchart LR
n["Numbers"] -- "+ - * / %" --> n2["A number"]
n -- "== != < <= > >=" --> b["A bool"]
b -- "&& || !" --> b2["A bool"]
v["A var"] -- "+= -= ++ --" --> v2["The same var,
updated in place"]
b2 --> cf["Conditions for
Part 3: Control flow"]
b --> cf
```
## Lessons
| | Lesson | What you will learn |
| --- | -------------------------------------------------------- | ------------------------------------------------------------ |
| 2.1 | [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic) | add, subtract, multiply, divide and take the remainder |
| 2.2 | [Comparison](https://rux-lang.dev/docs/learn/comparison) | compare two values with `==`, `!=`, `<`, `<=`, `>` and `>=` |
| 2.3 | [Logical](https://rux-lang.dev/docs/learn/logical) | combine conditions with `&&`, `||` and `!` |
| 2.4 | [Assignment](https://rux-lang.dev/docs/learn/assignment) | update a variable in place with `+=`, `-=`, `++` and friends |
| 2.5 | [Precedence](https://rux-lang.dev/docs/learn/precedence) | which operator binds first, and how parentheses change it |
## Before you start
Finish [Part 1: Basics](https://rux-lang.dev/docs/learn/basics) first — especially [Variable](https://rux-lang.dev/docs/learn/variable), [Mutable](https://rux-lang.dev/docs/learn/mutable), the number types and [Convert](https://rux-lang.dev/docs/learn/convert). Each lesson's package is in the Examples repository's `Operators/` folder:
```sh
cd Examples/Operators/Arithmetic
rux run
```
## After this part
[Part 3: Control flow](https://rux-lang.dev/docs/learn/control-flow) puts the conditions you can now write to work, making programs choose between paths and repeat. After it come the first checkpoint projects, [Thanks](https://rux-lang.dev/docs/learn/thanks) and [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz) — FizzBuzz in particular is built on `%`, `==` and `+=` from this part.
For the full rules, see [Operations](https://rux-lang.dev/docs/lang/expressions/overview) — [arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic), [comparison](https://rux-lang.dev/docs/lang/expressions/comparison) and [logical](https://rux-lang.dev/docs/lang/expressions/logical) — and the [operator list](https://rux-lang.dev/docs/lang/lexical/operators) in the Rux Reference.
# Arithmetic
::note
**You'll need**: [Variable](https://rux-lang.dev/docs/learn/variable), [Integer](https://rux-lang.dev/docs/learn/integer), [Float](https://rux-lang.dev/docs/learn/float), [Console](https://rux-lang.dev/docs/learn/console), [Convert](https://rux-lang.dev/docs/learn/convert)
::
Arithmetic is the first thing most programs do with their values: add up a bill, split it between friends, work out what is left over. Rux has five arithmetic operators, and they look the way they do in maths. The one surprise is division — dividing two whole numbers gives a whole number, and the fraction is quietly dropped.
## The five operators
| Operator | Meaning | `17` and `5` give |
| -------- | -------------- | ----------------- |
| `+` | Addition | `22` |
| `-` | Subtraction | `12` |
| `*` | Multiplication | `85` |
| `/` | Division | `3` |
| `%` | Remainder | `2` |
Each one takes a value on either side and produces a new value. Neither operand changes — `a + b` reads `a` and `b` and hands back their sum:
```rux
let a: int32 = 17;
let b: int32 = 5;
PrintLine("{} + {} = {}", a, b, a + b);
```
## Integer division drops the fraction
On paper, 17 divided by 5 is 3.4. Two integers cannot hold 3.4, so integer division **truncates**: it keeps the whole part and throws the rest away. The `%` operator gives back the part that was dropped, the remainder:
```rux
PrintLine("{} / {} = {}", a, b, a / b);
PrintLine("{} % {} = {}", a, b, a % b);
```
17 is three fives and two left over, so `/` says `3` and `%` says `2`. The pair is useful far beyond maths homework — `%` is how a program asks "is this number even?" (`n % 2 == 0`) or "which minute of the hour is it?" (`seconds / 60 % 60`).
Truncation goes **towards zero**, so −3.4 becomes −3 rather than −4. The remainder keeps the sign of the number being divided:
```rux
PrintLine("{} / {} = {}", -a, b, -a / b);
PrintLine("{} % {} = {}", -a, b, -a % b);
```
## Floats keep the fraction
Divide two floats and the fraction stays. `%` works on floats too, and gives what is left after taking out as many whole fives as fit:
```rux
let x: float64 = 17.0;
let y: float64 = 5.0;
PrintLine("{} / {} = {}", x, y, x / y);
PrintLine("{} % {} = {}", x, y, x % y);
```
What `/` does depends only on the types of its two operands:
```mermaid
flowchart LR
div["left / right"] --> q{"What are the
two operand types?"}
q -- "both integers" --> int["Whole number:
17 / 5 is 3"]
q -- "both floats" --> fl["Keeps the fraction:
17.0 / 5.0 is 3.4"]
q -- "one of each" --> err["error: operator '/' cannot combine
left operand 'int32' with
right operand 'float64'"]
err --> fix["Convert one side with as,
then divide"]
```
## Both sides must have the same type
Rux never mixes an `int32` and a `float64` on its own, because either choice could lose something. To divide two integers and keep the fraction, convert them first with `as` — the operator from [Convert](https://rux-lang.dev/docs/learn/convert):
```rux
PrintLine("{} / {} as floats = {}", a, b, (a as float64) / (b as float64));
```
Converting *after* the division is too late: `(a / b) as float64` is `3.0`, because the fraction was already gone. [Precedence](https://rux-lang.dev/docs/learn/precedence) looks at that case again.
## Overflow wraps around
Every integer type has a largest value. A `uint8` holds 0 to 255, and a sum that goes past the top wraps around and starts again from 0 — with no warning:
```rux
let level: uint8 = 250;
PrintLine("uint8 {} + 10 = {}", level, level + 10);
```
250 + 10 is 260, and 260 − 256 lands on 4. The `10` is a `uint8` here because an unsuffixed literal takes the type of the value beside it, as [Literal](https://rux-lang.dev/docs/learn/literal) showed. Choosing a type wide enough for your numbers is the everyday fix; [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) and [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) cover the tools for when it is not enough.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Operators/Arithmetic){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The five arithmetic operators: `+`, `-`, `*`, `/` and `%`. They work on
// integers and floats alike, but division means something different for each:
// two integers give a whole-number answer and throw the fraction away, while two
// floats keep it.
import Io::PrintLine;
func Main() -> int {
let a: int32 = 17;
let b: int32 = 5;
PrintLine("{} + {} = {}", a, b, a + b);
PrintLine("{} - {} = {}", a, b, a - b);
PrintLine("{} * {} = {}", a, b, a * b);
// Integer division truncates: 17 / 5 is 3, not 3.4. The `%` operator gives
// back the part that division dropped, the remainder.
PrintLine("{} / {} = {}", a, b, a / b);
PrintLine("{} % {} = {}", a, b, a % b);
// Truncation goes towards zero, so -3.4 becomes -3 rather than -4, and the
// remainder keeps the sign of the number being divided.
PrintLine("{} / {} = {}", -a, b, -a / b);
PrintLine("{} % {} = {}", -a, b, -a % b);
// Floats keep the fraction. `%` works on them too.
let x: float64 = 17.0;
let y: float64 = 5.0;
PrintLine("{} / {} = {}", x, y, x / y);
PrintLine("{} % {} = {}", x, y, x % y);
// Both operands must have the same type: `a / y` is rejected, an `int32`
// divided by a `float64`. To divide integers and keep the fraction, convert
// them first with `as`.
PrintLine("{} / {} as floats = {}", a, b, (a as float64) / (b as float64));
// An integer that outgrows its type wraps around without any warning. A
// `uint8` holds 0 to 255, so 250 + 10 lands on 4.
let level: uint8 = 250;
PrintLine("uint8 {} + 10 = {}", level, level + 10);
return 0;
}
```
## Run it
```sh
cd Examples/Operators/Arithmetic
rux run
```
```text
17 + 5 = 22
17 - 5 = 12
17 * 5 = 85
17 / 5 = 3
17 % 5 = 2
-17 / 5 = -3
-17 % 5 = -2
17.0 / 5.0 = 3.4
17.0 % 5.0 = 2.0
17 / 5 as floats = 3.4
uint8 250 + 10 = 4
```
## Common mistakes
::warning
**Mixing an integer and a float.**:br
When `a` is an `int32` and `y` is a `float64`, `a / y` fails with `error: operator '/' cannot combine left operand 'int32' with right operand 'float64'`. Convert one side with `as` so both have the same type.
::
::warning
**Expecting a fraction from two integers.**:br`17 / 5` is `3`, not `3.4`, and `(a / b) as float64` is `3.0`. Convert the operands *before* dividing, not the result after.
::
::warning
**Dividing an integer by zero.**:br
The compiler does not catch it. The program stops while running with `Panic: division by zero` and the line it happened on. When the divisor might be zero, test it first — [Logical](https://rux-lang.dev/docs/learn/logical) shows a neat way to do that.
::
## Try it yourself
1. Turn `1000` seconds into minutes and seconds using `/` and `%`, and print a line like `16 min 40 s`.
2. Change `a` and `b` to `-17` and `-5`. Predict both `/` and `%` before you run.
3. Work out the average of three `int32` scores, `7`, `8` and `10`, once as an integer and once as a `float64`.
4. Change the `uint8` example to `level - 251`. Which number does it wrap around to?
## Learn more
- [Arithmetic operations](https://rux-lang.dev/docs/lang/expressions/arithmetic) in the Rux Reference
- [Precedence](https://rux-lang.dev/docs/learn/precedence) — the order in which mixed operators apply
- [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) and [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) — taking control of overflow
- [Math](https://rux-lang.dev/docs/learn/math) — powers, roots and rounding
# Comparison
::note
**You'll need**: [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic), [Boolean](https://rux-lang.dev/docs/learn/boolean), [Character](https://rux-lang.dev/docs/learn/character)
::
Programs constantly ask questions about their values. Is the user old enough? Is the tank empty? Did the score beat the record? A comparison asks one such question about two values, and the answer is always a `bool` — `true` or `false`. That answer is what the next part, [Control flow](https://rux-lang.dev/docs/learn/control-flow), uses to decide what runs.
## Six questions
| Operator | Asks | `17` and `5` give |
| -------- | ---------------------- | ----------------- |
| `==` | equal? | `false` |
| `!=` | not equal? | `true` |
| `<` | less than? | `false` |
| `<=` | less than or equal? | `false` |
| `>` | greater than? | `true` |
| `>=` | greater than or equal? | `true` |
Equality is written with **two** equals signs. A single `=` is assignment, which changes a variable rather than asking about it.
```rux
let a: int32 = 17;
let b: int32 = 5;
PrintLine("{} == {} is {}", a, b, a == b);
PrintLine("{} != {} is {}", a, b, a != b);
```
## A comparison is a value
The answer is an ordinary `bool`, so it can be printed, passed along, or kept in a variable with a name that says what it means:
```rux
let age: int32 = 18;
let adult = age >= 18;
PrintLine("age {} is adult: {}", age, adult);
```
`adult` is a `bool`, inferred from the comparison. Naming a condition this way often makes the code that later tests it read like a sentence.
## Both sides must agree
Like arithmetic, a comparison needs operands of compatible types. Two integers of the same signedness may differ in width — an `int8` compares with an `int64`, because the narrower one widens without loss. Everything else is refused:
| Left | Right | Result |
| ------- | --------- | ------------------------------------------- |
| `int32` | `int32` | compared |
| `int8` | `int64` | compared — the `int8` widens |
| `int32` | `uint32` | refused — the operands differ in signedness |
| `int32` | `float64` | refused — convert one side with `as` first |
## Characters compare by code point
A character is a number underneath, as [Convert](https://rux-lang.dev/docs/learn/convert) showed, and comparing characters compares those numbers. Every capital letter comes before every lowercase one, so `'Z'` sorts ahead of `'a'`:
```rux
let upper: char32 = 'Z';
let lower: char32 = 'a';
PrintLine("'{}' < '{}' is {}", upper, lower, upper < lower);
```
That is alphabetical order only within one case — worth remembering before sorting names.
## Floats and exact equality
[Float](https://rux-lang.dev/docs/learn/float) showed that `0.1 + 0.2` is not exactly `0.3`. `==` sees the difference, however tiny:
```rux
let sum: float64 = 0.1 + 0.2;
PrintLine("0.1 + 0.2 == 0.3 is {}", sum == 0.3);
PrintLine("0.1 + 0.2 > 0.3 is {}", sum > 0.3);
```
The sum lands just above 0.3, so `==` says `false` and `>` says `true`. Ordering comparisons on floats are fine; be wary of exact `==` between floats that came out of arithmetic. Ask instead whether two values are *close enough* — whether their difference is below some small tolerance you choose.
## Comparisons do not chain
In maths, `1 < age < 30` means "age is between 1 and 30". In Rux, it does not:
```mermaid
flowchart LR
expr["1 < age < 30"] --> first["1 < age
is worked out first"]
first --> b["a bool:
true or false"]
b --> second["that bool < 30"]
second --> err["error: operator '<' cannot compare
left operand 'bool8'
with right operand 'int'"]
```
The first comparison produces a `bool`, and a `bool` cannot be compared with a number. Asking two questions at once takes `&&` — `1 < age && age < 30` — the subject of the [next lesson](https://rux-lang.dev/docs/learn/logical).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Operators/Comparison){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The six comparison operators ask a yes-or-no question about two values:
// `==` equal, `!=` not equal, `<` less, `<=` less or equal, `>` greater and
// `>=` greater or equal. Whatever the operands are, the answer is a `bool`, so
// it can be printed or kept in a variable like any other value.
import Io::PrintLine;
func Main() -> int {
let a: int32 = 17;
let b: int32 = 5;
PrintLine("{} == {} is {}", a, b, a == b);
PrintLine("{} != {} is {}", a, b, a != b);
PrintLine("{} < {} is {}", a, b, a < b);
PrintLine("{} <= {} is {}", a, b, a <= b);
PrintLine("{} > {} is {}", a, b, a > b);
PrintLine("{} >= {} is {}", a, b, a >= b);
// A comparison is an ordinary expression, and its result is a value.
let age: int32 = 18;
let adult = age >= 18;
PrintLine("age {} is adult: {}", age, adult);
// Characters compare by code point. Every capital letter comes before every
// lowercase one, so 'Z' sorts ahead of 'a'.
let upper: char32 = 'Z';
let lower: char32 = 'a';
PrintLine("'{}' < '{}' is {}", upper, lower, upper < lower);
// Floats are stored approximately, so a sum that looks exact on paper can
// miss by a hair, and `==` sees the hair: this sum lands just above 0.3. Be
// wary of exact `==` between floats that came out of arithmetic.
let sum: float64 = 0.1 + 0.2;
PrintLine("0.1 + 0.2 == 0.3 is {}", sum == 0.3);
PrintLine("0.1 + 0.2 > 0.3 is {}", sum > 0.3);
// Comparisons do not chain the way they do in maths. `1 < age < 30` would
// compare `1 < age`, a `bool`, against 30, and the compiler rejects that.
// Asking two questions at once needs `&&`, the subject of the next lesson.
return 0;
}
```
## Run it
```sh
cd Examples/Operators/Comparison
rux run
```
```text
17 == 5 is false
17 != 5 is true
17 < 5 is false
17 <= 5 is false
17 > 5 is true
17 >= 5 is true
age 18 is adult: true
'Z' < 'a' is true
0.1 + 0.2 == 0.3 is false
0.1 + 0.2 > 0.3 is true
```
## Common mistakes
::warning
**Chaining comparisons.**:br`1 < age < 30` fails with `error: operator '<' cannot compare left operand 'bool8' with right operand 'int'`: the left half is already a `bool`. Write `1 < age && age < 30`.
::
::warning
**Comparing an integer with a float.**:br
When `age` is an `int32` and `limit` is a `float64`, `age > limit` fails with `error: operator '>' cannot compare left operand 'int32' with right operand 'float64'`. Convert one side with `as`.
::
::warning
**Comparing signed with unsigned.**:br
An `int32` and a `uint32` are refused too — `note: the operands differ in signedness, so neither converts to the other's type` — and the compiler's help suggests converting one operand with `as`.
::
::warning
**Testing floats for exact equality.**:br`0.1 + 0.2 == 0.3` is `false`. Compare the difference against a small tolerance instead.
::
## Try it yourself
1. Compare `'a'` with `'b'`, and `'9'` with `'A'`. Print the code points with `as uint32` to see why.
2. Declare `let temperature: int32 = 21;` and a `bool` named `comfortable` that is `true` from 18 to 24. You will need `&&` from the next lesson.
3. Print `0.1 + 0.2 - 0.3` to see how far the sum misses.
4. Write `1 < age < 30` and read the error, then fix it.
## Learn more
- [Comparison operations](https://rux-lang.dev/docs/lang/expressions/comparison) in the Rux Reference
- [Logical](https://rux-lang.dev/docs/learn/logical) — combining several comparisons into one condition
- [Equatable](https://rux-lang.dev/docs/learn/equatable) and [Comparable](https://rux-lang.dev/docs/learn/comparable) — making your own types comparable
# Logical
::note
**You'll need**: [Comparison](https://rux-lang.dev/docs/learn/comparison), [Boolean](https://rux-lang.dev/docs/learn/boolean)
::
One comparison answers one question. Real conditions are rarely that simple: "it is raining **and** cold", "the user is an admin **or** the owner", "the file is **not** empty". The logical operators combine `bool` values into new ones, so several small questions become one answer.
## And, or, not
```rux
let raining = true;
let cold = false;
PrintLine("raining && cold is {}", raining && cold);
PrintLine("raining || cold is {}", raining || cold);
PrintLine("!raining is {}", !raining);
```
`&&` (and) is `true` only when both sides are. `||` (or) is `true` when at least one side is. `!` (not) takes a single `bool` and flips it. Every combination fits in one table:
| `a` | `b` | `a && b` | `a || b` | `!a` |
| ------- | ------- | -------- | -------- | ------- |
| `false` | `false` | `false` | `false` | `true` |
| `false` | `true` | `false` | `true` | `true` |
| `true` | `false` | `false` | `true` | `false` |
| `true` | `true` | `true` | `true` | `false` |
The operands must be `bool`s. Rux has no "truthy" numbers, so `items && true` with an `int32` `items` is an error — write the comparison you mean, `items != 0`.
## Short-circuiting
The table has a pattern: once the left side of `&&` is `false`, the answer is `false` whatever the right side is. Once the left side of `||` is `true`, the answer is `true`. So both operators work out the left side first, and **skip the right side** when the left one has already settled the answer:
```mermaid
flowchart LR
and["left && right"] --> l1{"left?"}
l1 -- "false" --> f["false —
right never runs"]
l1 -- "true" --> r1["the answer is right"]
or["left || right"] --> l2{"left?"}
l2 -- "true" --> t["true —
right never runs"]
l2 -- "false" --> r2["the answer is right"]
```
The program makes this visible with a small helper. `Side` is a *function* — a named piece of work, which [Part 4](https://rux-lang.dev/docs/learn/functions) teaches properly. For now, all you need to know is that calling it prints which side is being evaluated, then hands back the answer it was given:
```rux
func Side(name: char8[..], answer: bool) -> bool {
PrintLine(" evaluated {}", name);
return answer;
}
```
```rux
PrintLine("false && true:");
PrintLine(" result {}", Side("left", false) && Side("right", true));
```
For `false && true` the output shows only `evaluated left`: the right side never ran. For `true && false` both sides run, because a `true` on the left does not decide an `&&`.
## A guard on the left
Short-circuiting is not just a speed-up. It lets the left side **protect** the right one. Dividing an integer by zero stops the program, so the average below must not be computed when there are no items:
```rux
let items: int32 = 0;
let total: int32 = 120;
PrintLine("average above 10: {}", items != 0 && total / items > 10);
```
`items != 0` is `false`, so `&&` already knows its answer and `total / items` is never reached. Put the guard first; written the other way round, the division would run before the check.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Operators/Logical){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The logical operators combine bools. `a && b` is true when both are true,
// `a || b` when at least one is, and `!a` flips a single bool.
//
// They also short-circuit: they evaluate the left side first, and skip the right
// side when the left one has already settled the answer. `false && anything` is
// false and `true || anything` is true, so in those cases the right side never
// runs at all.
import Io::PrintLine;
// A preview of functions, which Part 4 teaches: calling `Side` prints which side
// is being evaluated, then hands back the answer it was given unchanged.
func Side(name: char8[..], answer: bool) -> bool {
PrintLine(" evaluated {}", name);
return answer;
}
func Main() -> int {
let raining = true;
let cold = false;
PrintLine("raining && cold is {}", raining && cold);
PrintLine("raining || cold is {}", raining || cold);
PrintLine("!raining is {}", !raining);
// Watch which sides get evaluated. The right side runs only when the left
// one could not decide the answer alone.
PrintLine("false && true:");
PrintLine(" result {}", Side("left", false) && Side("right", true));
PrintLine("true && false:");
PrintLine(" result {}", Side("left", true) && Side("right", false));
PrintLine("true || false:");
PrintLine(" result {}", Side("left", true) || Side("right", false));
PrintLine("false || true:");
PrintLine(" result {}", Side("left", false) || Side("right", true));
// This is what makes short-circuiting useful: the left side can guard the
// right. An integer division by zero stops the program on the spot, with
// "Panic: division by zero" and the line it happened on. With no items, the
// guard settles the answer first and the division is never reached.
let items: int32 = 0;
let total: int32 = 120;
PrintLine("average above 10: {}", items != 0 && total / items > 10);
return 0;
}
```
## Run it
```sh
cd Examples/Operators/Logical
rux run
```
```text
raining && cold is false
raining || cold is true
!raining is false
false && true:
evaluated left
result false
true && false:
evaluated left
evaluated right
result false
true || false:
evaluated left
result true
false || true:
evaluated left
evaluated right
result true
average above 10: false
```
## Common mistakes
::warning
**Using a number as a condition.**:br`items && true` fails with `error: operator '&&' requires a bool left operand, but found 'int32'`, and `!items` with `error: operator '!' requires a bool operand, but found 'int32'`. Compare explicitly: `items != 0`.
::
::warning
**Writing `&` or `|` instead of `&&` or `||`.**:br
The single-character forms compile on `bool`s, but they are the *bitwise* operators of [Bitwise](https://rux-lang.dev/docs/learn/bitwise), and they always evaluate both sides. Swap `&&` for `&` in the guard above and the program stops with `Panic: division by zero`.
::
::warning
**Putting the guard second.**:br`total / items > 10 && items != 0` divides before it checks. The guard only protects what comes after it.
::
## Try it yourself
1. Add `let windy = true;` and print whether it is raining and windy but not cold.
2. Swap the two `Side` calls in each pair and predict which lines of `evaluated` appear.
3. Set `items` to `8` and run again. Which part of the condition decides the answer now?
4. Write a `bool` named `weekend` that is `true` when an `int32` `day` is `6` or `7`.
## Learn more
- [Logical operations](https://rux-lang.dev/docs/lang/expressions/logical) in the Rux Reference
- [Precedence](https://rux-lang.dev/docs/learn/precedence) — how `&&`, `||` and `!` bind next to comparisons
- [Bitwise](https://rux-lang.dev/docs/learn/bitwise) — `&`, `|` and `^` on the bits of integers
# Assignment
::note
**You'll need**: [Mutable](https://rux-lang.dev/docs/learn/mutable), [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic)
::
A running total, a countdown, a score: many variables change by building on their own current value. Writing `score = score + 5` works, but it names `score` twice and makes the reader check that both names really are the same. Rux, like most languages, has a shorter form for "change this variable by this much".
## Compound assignment
`score += 5` means exactly `score = score + 5`. Every arithmetic operator has a compound form:
```rux
var score: int32 = 10;
PrintLine("start {}", score);
score += 5;
PrintLine("+= 5 {}", score);
score -= 3;
PrintLine("-= 3 {}", score);
```
| Short form | Means | `score` goes from 10 to |
| ------------ | ------------------- | ----------------------- |
| `score += 5` | `score = score + 5` | 15 |
| `score -= 3` | `score = score - 3` | 7 |
| `score *= 4` | `score = score * 4` | 40 |
| `score /= 5` | `score = score / 5` | 2 |
| `score %= 5` | `score = score % 5` | 0 |
The program applies them one after another, so each line starts from the result of the one before: 10, 15, 12, 48, 9, 4. The bitwise operators of a later lesson have compound forms too.
## Stepping by one
Adding or taking away exactly one is so common that it is shorter still. `score++` adds one, `score--` takes one away:
```rux
score++;
PrintLine("++ {}", score);
score--;
score--;
PrintLine("-- twice {}", score);
```
These are the steps loops take on every pass, and you will see them throughout [Part 3](https://rux-lang.dev/docs/learn/control-flow).
## Floats too
Compound assignment follows the rules of the operator inside it. On a `float64`, `/=` is float division, so it keeps the fraction, and `++` adds `1.0`:
```rux
var price: float64 = 10.0;
price /= 4.0;
price++;
PrintLine("price {}", price);
```
10.0 / 4.0 is 2.5, plus one is 3.5. The two sides must still agree on type: `score += 1.5` on an `int32` is refused just as `score + 1.5` would be.
## Before or after
On a line of its own, `count++` and `++count` do the same thing. Inside a larger expression, where you write the `++` decides which value the expression hands back:
```rux
var count: int32 = 1;
let before = count++;
PrintLine("count++ gave {}, count is now {}", before, count);
let after = ++count;
PrintLine("++count gave {}, count is now {}", after, count);
```
```mermaid
flowchart LR
post["count++"] --> p1["hands back the old value"] --> p2["then count is one higher"]
pre["++count"] --> q1["count is one higher"] --> q2["hands back the new value"]
```
Either way the variable ends up one higher. Code that depends on the difference is easy to misread, so most Rux code keeps `++` and `--` on lines of their own.
## They all need a var
Every one of these operators *writes* to the variable, so the variable must be declared with `var`, as in [Mutable](https://rux-lang.dev/docs/learn/mutable). On a `let` binding the compiler refuses them, just as it refuses a plain `=`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Operators/Assignment){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Changing a variable based on its own value is so common that it has a short
// form. `score += 5` means `score = score + 5`, and every arithmetic operator
// has one: `+=`, `-=`, `*=`, `/=` and `%=` (the bitwise operators of a later
// lesson have them too). Stepping by exactly one is shorter still: `score++`
// adds one and `score--` takes one away.
//
// All of these write to the variable, so they need a `var`. On a `let` binding
// the compiler refuses them, just as it refuses a plain `=`.
import Io::PrintLine;
func Main() -> int {
var score: int32 = 10;
PrintLine("start {}", score);
score += 5;
PrintLine("+= 5 {}", score);
score -= 3;
PrintLine("-= 3 {}", score);
score *= 4;
PrintLine("*= 4 {}", score);
score /= 5;
PrintLine("/= 5 {}", score);
score %= 5;
PrintLine("%= 5 {}", score);
score++;
PrintLine("++ {}", score);
score--;
score--;
PrintLine("-- twice {}", score);
// They work on floats too. `/=` follows the type's own division, so here it
// keeps the fraction.
var price: float64 = 10.0;
price /= 4.0;
price++;
PrintLine("price {}", price);
// `++` can also stand inside a larger expression, and then where you write
// it matters. After the variable, `count++` steps it and hands back the
// value from before. Before the variable, `++count` steps it and hands back
// the new value. Either way the variable ends up one higher.
var count: int32 = 1;
let before = count++;
PrintLine("count++ gave {}, count is now {}", before, count);
let after = ++count;
PrintLine("++count gave {}, count is now {}", after, count);
return 0;
}
```
## Run it
```sh
cd Examples/Operators/Assignment
rux run
```
```text
start 10
+= 5 15
-= 3 12
*= 4 48
/= 5 9
%= 5 4
++ 5
-- twice 3
price 3.5
count++ gave 1, count is now 2
++count gave 3, count is now 3
```
## Common mistakes
::warning
**Updating a `let`.**:br`let score: int32 = 10;` followed by `score += 5;` or `score++;` fails with `error: cannot modify immutable variable 'score'`, and the compiler's help says to declare `score` with `var`.
::
::warning
**Mixing types.**:br`score += 1.5` on an `int32` fails with `error: operator '+=' cannot combine left operand 'int32' with right operand 'float64'`. Compound assignment follows the same type rules as the operator inside it.
::
::warning
**Writing `=-` instead of `-=`.**:br`score =- 3;` is not a typo the compiler can catch: it reads as `score = -3`, which is valid, and `score` silently becomes `-3`. (`=+` is refused, because Rux has no unary `+`.) The operator always goes before the `=`.
::
## Try it yourself
1. Start a `var balance: int32 = 100;`, apply `-= 30`, `*= 2` and `%= 7`, and predict each step before you run.
2. Halve a `var temperature: float64 = 37.0;` with `/=` and print it.
3. Print `count++` and `++count` directly inside a `PrintLine`, and check the results against the diagram.
4. Change `var score` to `let score` and read every error the compiler reports.
## Learn more
- [Assignment operators](https://rux-lang.dev/docs/lang/lexical/operators) in the Rux Reference
- [`var`](https://rux-lang.dev/docs/lang/bindings/overview#var) and [mutability](https://rux-lang.dev/docs/lang/bindings/overview#mutability)
- [Precedence](https://rux-lang.dev/docs/learn/precedence) — assignment is the loosest operator of all
# Precedence
::note
**You'll need**: [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic), [Comparison](https://rux-lang.dev/docs/learn/comparison), [Logical](https://rux-lang.dev/docs/learn/logical), [Convert](https://rux-lang.dev/docs/learn/convert)
::
`2 + 3 * 4` could mean 20 or 14, depending on which operator goes first. Maths settled this long ago — multiplication before addition — and Rux follows the same idea for every operator it has. The rules that decide the order are called **precedence**, and knowing the few that matter lets you read an expression the way the compiler does.
## The ranking
From tightest to loosest, the operators of this part rank like this:
| Rank | Operators | Kind |
| -------- | --------------------- | ------------------ |
| tightest | `x++` `x--` | step after reading |
| | `-x` `!x` `++x` `--x` | unary |
| | `as` | conversion |
| | `*` `/` `%` | multiplication |
| | `+` `-` | addition |
| | `<` `<=` `>` `>=` | ordering |
| | `==` `!=` | equality |
| | `&&` | and |
| | `||` | or |
| loosest | `=` `+=` `-=` … | assignment |
A tighter operator grabs its operands first. Parentheses override all of it.
```rux
PrintLine("2 + 3 * 4 = {}", 2 + 3 * 4);
PrintLine("(2 + 3) * 4 = {}", (2 + 3) * 4);
```
`*` ranks above `+`, so `3 * 4` happens first and the answer is 14. Wrap `2 + 3` in parentheses and it is 20. `%` ranks with `*`, not with `+`, so `7 + 10 % 4` is `7 + 2`, which is 9.
## Same rank, left to right
When operators share a rank, they group from the left:
```rux
PrintLine("100 / 10 / 5 = {}", 100 / 10 / 5);
PrintLine("100 / (10 / 5) = {}", 100 / (10 / 5));
PrintLine("10 - 3 - 2 = {}", 10 - 3 - 2);
```
`100 / 10 / 5` is `(100 / 10) / 5`, which is 2. Grouping it the other way gives 50. For `+` and `*` the grouping never changes the answer, but for `-` and `/` it does.
## as converts only its neighbour
`as` binds tighter than any arithmetic, so it converts the single operand right next to it — not the whole expression on its left:
```rux
let a: int32 = 17;
let b: int32 = 5;
PrintLine("(a / b) as float64 = {}", (a / b) as float64);
PrintLine("a as float64 / b as float64 = {}", a as float64 / b as float64);
```
The first line divides two integers, losing the fraction, and only then converts: `3.0`. The second converts each integer first and divides two floats: `3.4`. Here is how the compiler groups both:
```mermaid
flowchart LR
e1["(a / b) as float64"] --> d1["a / b = 3
integer division"] --> c1["3 as float64
= 3.0"]
e2["a as float64 / b as float64"] --> c2["17.0 and 5.0
converted first"] --> d2["17.0 / 5.0
= 3.4"]
```
Writing `a / b as float64` would convert only `b`, and then `/` would have an `int32` on one side and a `float64` on the other — which the compiler refuses.
## Conditions read naturally
Arithmetic ranks above comparison, and comparison above `&&` and `||`. That order is chosen so that the usual conditions need no parentheses at all:
```rux
let age: int32 = 25;
PrintLine("age >= 18 && age < 65 is {}", age >= 18 && age < 65);
```
The comparisons are worked out first, then `&&` combines their answers. Between the two logical operators, `&&` binds tighter than `||`, the same way `*` binds tighter than `+`:
```rux
PrintLine("true || false && false is {}", true || false && false);
PrintLine("(true || false) && false is {}", (true || false) && false);
```
The first is `true || (false && false)`, which is `true`. And `!` applies only to the operand right next to it, so `!ready && busy` is `(!ready) && busy` — use parentheses, `!(ready && busy)`, to negate the whole thing.
## When in doubt, add parentheses
Precedence decides what the compiler does; parentheses decide what the reader sees. `a * b + c` is clear to anyone, but `x || y && z` makes most people stop and think. Parentheses that match the default grouping change nothing in the program and save the next reader that pause.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Operators/Precedence){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// When an expression mixes operators, precedence decides which applies first,
// the way multiplication comes before addition in maths. From tightest to
// loosest, the operators seen so far rank like this:
//
// x++ x-- step after reading
// -x !x ++x --x unary
// as conversion
// * / % multiplication
// + - addition
// < <= > >= ordering
// == != equality
// && and
// || or
// = += -= ... assignment, loosest of all
//
// Operators on the same rank group from left to right. Parentheses override
// all of it, and are worth adding wherever a reader would have to stop and think.
import Io::PrintLine;
func Main() -> int {
// Multiplication before addition, unless parentheses say otherwise.
PrintLine("2 + 3 * 4 = {}", 2 + 3 * 4);
PrintLine("(2 + 3) * 4 = {}", (2 + 3) * 4);
// `%` ranks with `*`, not with `+`.
PrintLine("7 + 10 % 4 = {}", 7 + 10 % 4);
// Same rank, left to right: 100 / 10 first, then / 5. Grouping the other
// way round gives a different answer.
PrintLine("100 / 10 / 5 = {}", 100 / 10 / 5);
PrintLine("100 / (10 / 5) = {}", 100 / (10 / 5));
PrintLine("10 - 3 - 2 = {}", 10 - 3 - 2);
// `as` binds tighter than arithmetic, so it converts only its neighbour.
// Converting the result of an integer division is too late: the fraction
// was already gone.
let a: int32 = 17;
let b: int32 = 5;
PrintLine("(a / b) as float64 = {}", (a / b) as float64);
PrintLine("a as float64 / b as float64 = {}", a as float64 / b as float64);
// Arithmetic happens before comparing, and comparing before `&&`, so a
// range check needs no parentheses at all.
let age: int32 = 25;
PrintLine("age >= 18 && age < 65 is {}", age >= 18 && age < 65);
// `&&` binds tighter than `||`, the same way `*` binds tighter than `+`.
PrintLine("true || false && false is {}", true || false && false);
PrintLine("(true || false) && false is {}", (true || false) && false);
// `!` applies only to the operand right next to it.
let ready = true;
let busy = false;
PrintLine("!ready && busy is {}", !ready && busy);
PrintLine("!(ready && busy) is {}", !(ready && busy));
return 0;
}
```
## Run it
```sh
cd Examples/Operators/Precedence
rux run
```
```text
2 + 3 * 4 = 14
(2 + 3) * 4 = 20
7 + 10 % 4 = 9
100 / 10 / 5 = 2
100 / (10 / 5) = 50
10 - 3 - 2 = 5
(a / b) as float64 = 3.0
a as float64 / b as float64 = 3.4
age >= 18 && age < 65 is true
true || false && false is true
(true || false) && false is false
!ready && busy is false
!(ready && busy) is true
```
## Common mistakes
::warning
**Converting too late.**:br`(a / b) as float64` is `3.0`, not `3.4`: the integer division has already dropped the fraction. Convert each operand before dividing.
::
::warning
**Expecting `as` to cover the whole expression.**:br`a / b as float64` converts only `b`, and fails with `error: operator '/' cannot combine left operand 'int32' with right operand 'float64'`.
::
::warning
**Negating more than the next operand.**:br`!count == 0` is `(!count) == 0`, and when `count` is an `int32` it fails with `error: operator '!' requires a bool operand, but found 'int32'`. Write `count != 0`, or wrap what you mean in parentheses.
::
## Try it yourself
1. Predict, then print, `2 * 3 + 4 * 5`, `20 - 4 - 6` and `8 / 2 * 4`.
2. Add parentheses to `true || false && false` that do not change its value, and then some that do.
3. Write the condition "age is under 13 or over 65, and has a ticket" with an `int32` `age` and a `bool` `ticket`. Which parentheses are required?
## Learn more
- [Operator precedence](https://rux-lang.dev/docs/lang/lexical/operators) in the Rux Reference
- [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic), [Comparison](https://rux-lang.dev/docs/learn/comparison) and [Logical](https://rux-lang.dev/docs/learn/logical) — the operators being ranked
- [Ternary](https://rux-lang.dev/docs/learn/ternary) — the conditional operator, which ranks just above assignment
# Part 3: Control flow
So far every program has run each line once, top to bottom. This part makes programs **choose** — run one block or another depending on a condition — and **repeat** — run a block again and again until the job is done. With these two abilities and the operators of Part 2, you can write real little programs: the part ends with the first checkpoint projects.
## What you will learn
- Choosing with `if`, `else` and `else if` chains, and why the order of a chain matters.
- Choosing a value inside an expression with the conditional `? :`.
- Repeating with `while`, `do`-`while` and `loop`, and where each one tests its condition.
- Leaving a loop early with `break` and skipping a pass with `continue`, including from nested loops with labels.
- Describing a run of numbers with ranges `..` and `..=`, and walking one with `for`.
- Selecting a branch by value with `match`, and using `match` as an expression that produces a value.
## Choosing and repeating
```mermaid
flowchart LR
cf(["Control flow"]) --> choose["Choose"]
cf --> repeat["Repeat"]
choose --> by_cond["by condition
if · else if · ? :"]
choose --> by_value["by value
match · match expression"]
repeat --> by_test["while a condition holds
while · do-while · loop"]
repeat --> by_range["over a range
.. · ..= · for"]
by_test --> jump["leave or skip
break · continue · labels"]
by_range --> jump
```
## Lessons
| | Lesson | What you will learn |
| ---- | -------------------------------------------------------------------- | ---------------------------------------------------------------- |
| 3.1 | [If](https://rux-lang.dev/docs/learn/if) | run code only when a condition holds, with `if` and `else` |
| 3.2 | [Else if](https://rux-lang.dev/docs/learn/else-if) | choose one of several branches with an `else if` chain |
| 3.3 | [Ternary](https://rux-lang.dev/docs/learn/ternary) | pick one of two values inside an expression with `? :` |
| 3.4 | [While](https://rux-lang.dev/docs/learn/while) | repeat while a condition holds — possibly zero times |
| 3.5 | [Do-while](https://rux-lang.dev/docs/learn/do-while) | run the body first and test afterwards, so it runs at least once |
| 3.6 | [Loop](https://rux-lang.dev/docs/learn/loop) | repeat forever with `loop` until a `break` leaves |
| 3.7 | [Break](https://rux-lang.dev/docs/learn/break) | leave a loop early when the answer is found |
| 3.8 | [Continue](https://rux-lang.dev/docs/learn/continue) | skip the rest of one iteration and carry on with the next |
| 3.9 | [Range](https://rux-lang.dev/docs/learn/range) | describe a run of numbers with `..` and `..=` |
| 3.10 | [For](https://rux-lang.dev/docs/learn/for) | walk a range with `for` |
| 3.11 | [Label](https://rux-lang.dev/docs/learn/label) | name a loop, so `break` can leave an outer one |
| 3.12 | [Match](https://rux-lang.dev/docs/learn/match) | select a branch by value with `match`, and default with `else` |
| 3.13 | [Match expression](https://rux-lang.dev/docs/learn/match-expression) | use `match` as a value, not only as a statement |
## Before you start
Finish [Part 1: Basics](https://rux-lang.dev/docs/learn/basics) and [Part 2: Operators](https://rux-lang.dev/docs/learn/operators) first — every condition in this part is a comparison or a logical expression from Part 2. Each lesson's package is in the Examples repository's `ControlFlow/` folder:
```sh
cd Examples/ControlFlow/If
rux run
```
## After this part
You are ready for the first checkpoint projects: [Thanks](https://rux-lang.dev/docs/learn/thanks), which prints a banner using loops and `match` expressions, and [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz), the classic test of getting an `else if` chain in the right order. Then [Part 4: Functions](https://rux-lang.dev/docs/learn/functions) gives a piece of work a name, so you can reuse it instead of repeating it.
For the full rules behind this part, see [Statements](https://rux-lang.dev/docs/lang/statements/overview) — [`if`](https://rux-lang.dev/docs/lang/statements/if), [`match`](https://rux-lang.dev/docs/lang/patterns/match), [`while`](https://rux-lang.dev/docs/lang/statements/loops#while), [`for`](https://rux-lang.dev/docs/lang/statements/loops#for), [`loop`](https://rux-lang.dev/docs/lang/statements/loops#loop) and [`break` / `continue`](https://rux-lang.dev/docs/lang/statements/break-continue) — and [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) in the Rux Reference.
# If
::note
**You'll need**: [Comparison](https://rux-lang.dev/docs/learn/comparison), [Logical](https://rux-lang.dev/docs/learn/logical)
::
Until now, every program has run every line, top to bottom, every time. Real programs react: they print a warning only when something is wrong, charge a fee only when it applies, take a different path when the input is different. `if` is the simplest way to make that choice — run a block of code only when a condition holds.
## Run a block, or skip it
An `if` is the keyword, a condition, and a block in braces:
```rux
let temperature: int32 = 18;
if temperature > 15 {
PrintLine("warm enough for a walk");
}
if temperature > 25 {
PrintLine("hot enough for a swim");
}
```
18 is above 15, so the first block runs. It is not above 25, so the second block is skipped. Either way, the program carries on with whatever comes after the closing brace.
## Parentheses optional, braces required
Parentheses around the condition are allowed but never needed:
```rux
if (temperature < 20) {
PrintLine("bring a jacket");
}
```
The braces are the opposite: always needed, even around a single statement. `if ready PrintLine("go");` is an error. Because the braces are always there, there is never any doubt about which lines belong to the `if`.
## else: one of two
Add `else`, and exactly one of two blocks runs — the first when the condition holds, the second when it does not:
```rux
let raining = true;
if raining {
PrintLine("take an umbrella");
} else {
PrintLine("leave the umbrella at home");
}
```
```mermaid
flowchart LR
c{"raining?"} -- "true" --> a["take an umbrella"]
c -- "false" --> b["leave the umbrella at home"]
a --> next["the next statement"]
b --> next
```
The two paths always meet again after the `if`. There is no way for both blocks to run, and no way for neither to.
## The condition is a bool
A condition must be a `bool`: a comparison, a logical expression from [Logical](https://rux-lang.dev/docs/learn/logical), or a `bool` variable. Anything that produces a `bool` will do:
```rux
let windy = true;
if raining && !windy {
PrintLine("the umbrella will do");
} else {
PrintLine("wear a hood instead");
}
```
Rux has no "truthy" numbers, so a number is not a shortcut for "not zero". `if count { }` is refused; write the comparison you mean, `if count != 0 { }`.
## Blocks and the variables around them
A block may read and change variables declared before it:
```rux
var budget: int32 = 12;
let price: int32 = 9;
if price <= budget {
budget -= price;
PrintLine("bought it, {} left", budget);
} else {
PrintLine("cannot afford it");
}
```
`budget` lives outside the `if`, so the change survives after the closing brace. A variable declared *inside* a block is different: it belongs to the block, and it is gone once the block ends. To use a value after the `if`, declare its variable before it.
`if` decides while the program runs. Rux also has `when`, which decides while the program is being compiled — that one comes much later, in [When](https://rux-lang.dev/docs/learn/when).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/If){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `if` runs a block only when its condition is true. Add `else`, and exactly one of two blocks
// runs: the first when the condition holds, the second when it does not.
//
// The condition must be a `bool`: a comparison, a logical expression, or a `bool` binding. Rux has
// no "truthy" numbers, so a number is not a shortcut for "not zero":
//
// if count { }
// error: condition for 'if' must have type 'bool', but found 'int'
//
// Write the comparison you mean instead, `if count != 0 { }`.
//
// `if` decides while the program runs. Rux also has `when`, which decides while the program is
// being compiled; that one comes much later, in the When lesson.
import Io::PrintLine;
func Main() -> int {
let temperature: int32 = 18;
// Without `else`, the block either runs or is skipped, and the program carries on after it.
if temperature > 15 {
PrintLine("warm enough for a walk");
}
if temperature > 25 {
PrintLine("hot enough for a swim");
}
// Parentheses around the condition are allowed but never needed. The braces are always
// needed, even around a single statement: `if ready PrintLine("go");` is an error.
if (temperature < 20) {
PrintLine("bring a jacket");
}
// With `else`, one of the two blocks always runs.
let raining = true;
if raining {
PrintLine("take an umbrella");
} else {
PrintLine("leave the umbrella at home");
}
// Any `bool` expression is a condition, including ones built with `&&`, `||` and `!`.
let windy = true;
if raining && !windy {
PrintLine("the umbrella will do");
} else {
PrintLine("wear a hood instead");
}
// A block may change variables declared before it. A binding declared inside the block,
// though, belongs to the block and is gone after its closing brace.
var budget: int32 = 12;
let price: int32 = 9;
if price <= budget {
budget -= price;
PrintLine("bought it, {} left", budget);
} else {
PrintLine("cannot afford it");
}
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/If
rux run
```
```text
warm enough for a walk
bring a jacket
take an umbrella
wear a hood instead
bought it, 3 left
```
## Common mistakes
::warning
**Using a number as a condition.**:br`if count { }` fails with `error: condition for 'if' must have type 'bool', but found 'int'`. Write the test you mean: `if count != 0 { }`.
::
::warning
**Leaving out the braces.**:br`if ready PrintLine("go");` fails with `error: expected '{' to start the 'if' body before 'PrintLine'`. Every `if` and `else` body is a block in braces.
::
::warning
**Using a block's variable after the block.**:br
A `let change = 10 - price;` declared inside the `if` cannot be printed after it: `error: name 'change' is not defined in this scope`. Declare the variable before the `if` — a `var`, if each branch assigns it.
::
::warning
**Writing `=` for `==`.**:br`if age = 18 { }` is an assignment, not a question, and it is refused with `error: condition for 'if' must have type 'bool', …`. Comparison takes two equals signs.
::
## Try it yourself
1. Change `temperature` to `30`, then to `10`, and predict which lines print each time.
2. Add an `if` that prints `"freezing"` when the temperature is below 0.
3. Make the shop example buy two items in a row, each with its own `if`, and print what is left.
4. Write `if raining { … } else { … }` using `!raining` instead, with the blocks swapped. Does the output change?
## Learn more
- [`if` / `else`](https://rux-lang.dev/docs/lang/statements/if) in the Rux Reference
- [Else if](https://rux-lang.dev/docs/learn/else-if) — choosing among more than two cases
- [Ternary](https://rux-lang.dev/docs/learn/ternary) — choosing between two *values* inside an expression
# Else if
::note
**You'll need**: [If](https://rux-lang.dev/docs/learn/if)
::
`if` and `else` choose between two paths. Many decisions have more than two: a grade is an A, a B, a C or a fail; a temperature is a heat warning, a frost warning or nothing at all. `else if` adds another condition to an `if`, and a chain of them chooses among as many cases as you need.
## A chain of conditions
```rux
let score: int32 = 85;
if score >= 90 {
PrintLine("{} earns an A", score);
} else if score >= 80 {
PrintLine("{} earns a B", score);
} else if score >= 70 {
PrintLine("{} earns a C", score);
} else {
PrintLine("{} needs another try", score);
}
```
The chain tests its conditions from the top, runs the block of the **first** one that holds, and skips everything after it. A final plain `else` catches whatever no condition matched. So at most one block in a chain runs — exactly one, when the chain ends with `else`.
```mermaid
flowchart LR
s(["score = 85"]) --> a{"score >= 90?"}
a -- "yes" --> A["an A"]
a -- "no" --> b{"score >= 80?"}
b -- "yes" --> B["a B"]
b -- "no" --> c{"score >= 70?"}
c -- "yes" --> C["a C"]
c -- "no" --> E["another try"]
```
85 fails the first test and passes the second, so the chain prints `85 earns a B` and never looks at `>= 70`.
## Each test may assume the ones above it failed
By the time the chain reaches `score >= 80`, the score is already known to be below 90 — otherwise the first block would have run. So the chain needs no `score >= 80 && score < 90`. Each condition only has to draw the next line, not repeat the ones above it.
## Order is part of the meaning
Because the first match wins, the order of a chain changes what it does. Here are the same conditions, loosest first:
```rux
if score >= 70 {
PrintLine("reordered chain: a C");
} else if score >= 80 {
PrintLine("reordered chain: a B");
} else if score >= 90 {
PrintLine("reordered chain: an A");
}
```
85 is also at least 70, so the first test already holds and the chain stops there. Worse, the `>= 80` and `>= 90` blocks can now never run for *anyone*: every score that would reach them has already been caught by `>= 70`. The compiler cannot tell this from a deliberate choice, so it is up to you. A good rule for ranges like these: test the narrowest case first.
## A chain may run nothing
Without a final `else`, it is possible for no condition to hold:
```rux
let temperature: int32 = 18;
if temperature > 30 {
PrintLine("heat warning");
} else if temperature < 0 {
PrintLine("frost warning");
}
PrintLine("{} degrees needs no warning", temperature);
```
Leave out the `else` only when "nothing happens" is a real answer, as it is here.
## A chain, or separate ifs?
A chain and a row of separate `if` statements look alike but behave differently:
| Shape | Tests | Blocks that run |
| ------------------------------ | ------------------------------------ | -------------------------- |
| `if … else if … else if …` | until the first condition that holds | at most one |
| `if …` then `if …` then `if …` | every condition, every time | every one whose test holds |
```rux
if temperature > 10 {
PrintLine("above 10");
}
if temperature > 15 {
PrintLine("above 15");
}
```
Both of these print, because they are two separate statements. Written as a chain, only `above 10` would.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/ElseIf){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `else if` adds another condition to an `if`, and a chain of them chooses among many cases. The
// chain tests its conditions from the top, runs the block of the first one that holds, and skips
// everything after it. So at most one block in a chain runs, and a final plain `else` catches
// whatever no condition matched.
//
// Because the first match wins, the order of a chain is part of its meaning. The two chains below
// test the same score with the same conditions, in different orders, and disagree.
import Io::PrintLine;
func Main() -> int {
let score: int32 = 85;
// Each test may assume the ones above it failed: by the `>= 80` line, the score is known to be
// below 90, so the chain needs no `score >= 80 && score < 90`.
if score >= 90 {
PrintLine("{} earns an A", score);
} else if score >= 80 {
PrintLine("{} earns a B", score);
} else if score >= 70 {
PrintLine("{} earns a C", score);
} else {
PrintLine("{} needs another try", score);
}
// The same conditions, loosest first. 85 is also at least 70, so the first test already
// holds, the chain stops there, and the `>= 80` and `>= 90` blocks can never run for anyone.
if score >= 70 {
PrintLine("reordered chain: a C");
} else if score >= 80 {
PrintLine("reordered chain: a B");
} else if score >= 90 {
PrintLine("reordered chain: an A");
}
// Without a final `else`, a chain may run nothing at all.
let temperature: int32 = 18;
if temperature > 30 {
PrintLine("heat warning");
} else if temperature < 0 {
PrintLine("frost warning");
}
PrintLine("{} degrees needs no warning", temperature);
// Compare a chain with separate `if` statements: separate statements test every condition,
// and each one that holds runs. Both of these print, where a chain would print only the first.
if temperature > 10 {
PrintLine("above 10");
}
if temperature > 15 {
PrintLine("above 15");
}
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/ElseIf
rux run
```
```text
85 earns a B
reordered chain: a C
18 degrees needs no warning
above 10
above 15
```
## Common mistakes
::warning
**Testing the loosest case first.**:br
With `score >= 70` at the top, every passing score stops there, and the A and B blocks become unreachable. The compiler does not warn about it — the program simply gives the wrong answer. Put the narrowest condition first.
::
::warning
**Using separate `if`s where only one case should win.**:br
Two separate `if` statements both run when both conditions hold. When the cases are meant to exclude each other, join them with `else if`.
::
## Try it yourself
1. Change `score` to `95`, `72` and `40` and predict each line before you run.
2. Add an `A+` grade for scores of 97 and above. Where in the chain does it have to go?
3. Rewrite the temperature check so it prints `"mild"` when there is no warning, using a final `else`.
4. Turn the two separate `if`s at the end into one chain, and check that only one line prints.
## Learn more
- [`if` / `else`](https://rux-lang.dev/docs/lang/statements/if) in the Rux Reference
- [Match](https://rux-lang.dev/docs/learn/match) — a tidier chain when every test compares one value with `==`
- [If](https://rux-lang.dev/docs/learn/if) — the two-way choice this lesson extends
# Ternary
::note
**You'll need**: [If](https://rux-lang.dev/docs/learn/if)
::
`if` chooses which *statements* run. Sometimes the choice is smaller than that: you only need one of two *values*, right in the middle of an expression — "even" or "odd", the larger of two numbers, an `s` for a plural or nothing. The conditional operator makes that choice without leaving the expression. It is often called the *ternary* operator, because it is the only operator with three operands.
## condition ? whenTrue : whenFalse
The condition comes first, then `?`, the value for `true`, `:`, and the value for `false`:
```rux
let label = n % 2 == 0 ? "even" : "odd";
```
With `n` at 7, `n % 2 == 0` is `false`, so `label` becomes `"odd"`. Only the chosen side is evaluated; the other is skipped, just as an untaken `if` block is.
## Why not just use if?
Here is the same choice written with `if`:
```rux
var labelTheLongWay = "";
if n % 2 == 0 {
labelTheLongWay = "even";
} else {
labelTheLongWay = "odd";
}
```
It works, but the binding has to be declared first with a placeholder value and filled in later, in whichever branch runs — so it must be a `var`. The conditional gives the binding its value where it is declared, so it can be a `let`. That is the real gain, more than the shorter line: a `let` cannot be changed by accident later on.
## A value goes anywhere
Because the whole conditional is a value, it can be passed straight to a call:
```rux
PrintLine("the larger of {} and {} is {}", a, b, a > b ? a : b);
```
```rux
PrintLine("{} item{}", count, count == 1 ? "" : "s");
```
or used inside arithmetic:
```rux
let total = price - (member ? 5 : 0);
```
The parentheses matter in that last line. The conditional ranks below arithmetic, just above assignment, so without them `price - member` would be worked out first — and an `int32` minus a `bool` makes no sense. When a conditional sits inside a larger expression, wrap it.
## One value, one type
The conditional produces one value, so both sides must have the same type. `ready ? "yes" : 0` is refused, because one side is text and the other a number:
```mermaid
flowchart LR
c{"condition"} -- "true" --> t["whenTrue"]
c -- "false" --> f["whenFalse"]
t --> v["one value,
one type"]
f --> v
```
## Chains of conditionals
A conditional may take the place of the false side of another. It reads as a list of cases tested from the left:
```rux
let sign = n < 0 ? "negative" : n == 0 ? "zero" : "positive";
```
`n < 0` is tested first; if it fails, `n == 0`; if that fails too, the answer is `"positive"`. Two or three cases read well like this. Past that, an [`else if` chain](https://rux-lang.dev/docs/learn/else-if) — or a [`match` expression](https://rux-lang.dev/docs/learn/match-expression) — is clearer.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/Ternary){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `if` chooses which statements run. Sometimes the choice is smaller than that: one of two
// *values*, in the middle of an expression. `condition ? whenTrue : whenFalse` makes that choice.
// It is an expression, so it goes anywhere a value goes.
//
// The whole conditional is one value with one type, so both sides must have the same type:
//
// let answer = ready ? "yes" : 0;
// error: conditional branch type mismatch: expected 'char8[..]', found 'int'
// help: make both branches produce the same type
//
// Give both sides text, or both numbers.
import Io::PrintLine;
func Main() -> int {
let n: int32 = 7;
// Written with `if`, choosing a label needs a `var`: the binding is declared first and given
// its value later, in whichever branch runs.
var labelTheLongWay = "";
if n % 2 == 0 {
labelTheLongWay = "even";
} else {
labelTheLongWay = "odd";
}
// Written as a conditional, the binding gets its value where it is declared, so it can be a
// `let`. That is the real gain, more than the shorter line.
let label = n % 2 == 0 ? "even" : "odd";
PrintLine("{} is {} ({} the long way)", n, label, labelTheLongWay);
// Being a value, it can be passed straight to a call or used inside arithmetic.
let a: int32 = 3;
let b: int32 = 5;
PrintLine("the larger of {} and {} is {}", a, b, a > b ? a : b);
let count: int32 = 3;
PrintLine("{} item{}", count, count == 1 ? "" : "s");
let price: int32 = 40;
let member = true;
let total = price - (member ? 5 : 0);
PrintLine("members pay {}", total);
// A conditional may take the place of the false side of another, which reads as a list of
// cases tested from the left. Past two or three cases, an `else if` chain is clearer.
let sign = n < 0 ? "negative" : n == 0 ? "zero" : "positive";
PrintLine("{} is {}", n, sign);
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/Ternary
rux run
```
```text
7 is odd (odd the long way)
the larger of 3 and 5 is 5
3 items
members pay 35
7 is positive
```
## Common mistakes
::warning
**Giving the two sides different types.**:br`let answer = ready ? "yes" : 0;` fails with `error: conditional branch type mismatch: expected 'char8[..]', found 'int'`, and the help says to make both branches produce the same type.
::
::warning
**Forgetting the parentheses inside arithmetic.**:br`price - member ? 5 : 0` is read as `(price - member) ? 5 : 0`, and fails with `error: operator '-' cannot combine left operand 'int32' with right operand 'bool8'`. Write `price - (member ? 5 : 0)`.
::
::warning
**Using a number as the condition.**:br`n % 2 ? "odd" : "even"` fails with `error: condition for '?:' must have type 'bool', but found 'int32'`. As with `if`, write the comparison: `n % 2 != 0`.
::
## Try it yourself
1. Print `"pass"` or `"fail"` for a `score` depending on whether it is at least 50.
2. Use a conditional to print the smaller of `a` and `b`.
3. Set `count` to `1` and check that the plural `s` disappears.
4. Extend `sign` so that numbers above 100 print `"large"`. Is it still easy to read?
## Learn more
- [Precedence](https://rux-lang.dev/docs/learn/precedence) — the order in which operators apply
- [If](https://rux-lang.dev/docs/learn/if) — choosing between blocks of statements
- [Match expression](https://rux-lang.dev/docs/learn/match-expression) — choosing a value from many cases
# While
::note
**You'll need**: [If](https://rux-lang.dev/docs/learn/if), [Assignment](https://rux-lang.dev/docs/learn/assignment)
::
Computers are good at doing the same thing many times without getting bored. A *loop* runs a block of code again and again, and `while` is the most basic one: it repeats its block for as long as a condition holds. Everything else in this part about repeating — `do`-`while`, `loop`, `for` — is a variation on it.
## Test, run, repeat
A `while` looks like an `if`: the keyword, a condition, and a block. The difference is what happens at the closing brace. An `if` carries on; a `while` goes back and tests its condition again.
```rux
var count: int32 = 3;
while count > 0 {
PrintLine("{}...", count);
count -= 1;
}
PrintLine("liftoff");
```
```mermaid
flowchart LR
start(["count = 3"]) --> test{"count > 0?"}
test -- "true" --> body["print count
count -= 1"]
body --> test
test -- "false" --> after["liftoff"]
```
The condition is tested before every pass, the first one included. It holds for 3, 2 and 1; after the third pass `count` is 0, the test fails, and the program carries on after the loop.
## Something must change
A `while` ends only when something inside it makes the condition false. That is usually a `var` the condition reads, changed on every pass — here, `count -= 1`. Leave that line out and `count` stays at 3 forever: the program prints `3...` again and again and never reaches `liftoff`. If that happens to you, press **Ctrl+C** to stop it.
Most loops need two things: a variable that says where the loop has got to, and often another that carries the result. Adding up 1 + 2 + … + 10 uses both:
```rux
var n: int32 = 1;
var sum: int32 = 0;
while n <= 10 {
sum += n;
n += 1;
}
PrintLine("1 + 2 + ... + 10 = {}", sum);
```
## When the number of passes is unknown
`while` suits a loop whose number of passes you cannot know in advance. How many doublings take 1 past 1000? The loop finds out by doing them:
```rux
var value: int32 = 1;
var doublings: int32 = 0;
while value <= 1000 {
value *= 2;
doublings += 1;
}
PrintLine("{} doublings reach {}", doublings, value);
```
The condition describes when to *keep going*, and the loop counts how many passes it took: 10 doublings reach 1024.
## Zero passes is possible
Because the test comes first, a loop whose condition starts out false never runs its body at all:
```rux
var fuel: int32 = 0;
while fuel > 0 {
PrintLine("driving");
fuel -= 1;
}
```
That is usually exactly right — with an empty tank there is nothing to drive. When the body must run at least once, the [next lesson](https://rux-lang.dev/docs/learn/do-while) has the loop for it.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/While){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `while` repeats a block for as long as its condition holds. The condition is tested before
// every pass, the first one included, so a loop whose condition starts out false runs zero times.
//
// The loop ends only when something inside it makes the condition false. That is usually a `var`
// the condition reads, changed on every pass. Forget the change (`count -= 1` below) and the
// condition never becomes false: the program repeats the same pass forever.
import Io::PrintLine;
func Main() -> int {
// A countdown: the condition reads `count`, and the body brings it one step closer to 0.
var count: int32 = 3;
while count > 0 {
PrintLine("{}...", count);
count -= 1;
}
PrintLine("liftoff");
// Adding up 1 + 2 + ... + 10. One variable says where the loop is, another carries the result.
var n: int32 = 1;
var sum: int32 = 0;
while n <= 10 {
sum += n;
n += 1;
}
PrintLine("1 + 2 + ... + 10 = {}", sum);
// `while` suits loops whose number of passes is not known in advance. How many doublings take
// 1 past 1000? The loop finds out by doing them.
var value: int32 = 1;
var doublings: int32 = 0;
while value <= 1000 {
value *= 2;
doublings += 1;
}
PrintLine("{} doublings reach {}", doublings, value);
// Here the condition is false before the first pass, so the body never runs.
var fuel: int32 = 0;
while fuel > 0 {
PrintLine("driving");
fuel -= 1;
}
PrintLine("the tank was empty, so the loop ran zero times");
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/While
rux run
```
```text
3...
2...
1...
liftoff
1 + 2 + ... + 10 = 55
10 doublings reach 1024
the tank was empty, so the loop ran zero times
```
## Common mistakes
::warning
**Forgetting to change the condition's variable.**:br
Without `count -= 1`, the loop's condition never becomes false and the program runs forever. Nothing warns you: it compiles cleanly. Check that every pass moves the loop towards its end.
::
::warning
**Off by one.**:br`while n < 10` stops before adding 10; `while n <= 10` includes it. When a loop gives an answer that is slightly wrong, check the comparison in its condition first.
::
::warning
**Using a number as the condition.**:br`while count { }` fails with `error: condition for 'while' must have type 'bool', but found 'int32'`. Write `while count != 0`.
::
## Try it yourself
1. Make the countdown start from 10 and print `liftoff` at the end.
2. Add up only the even numbers from 2 to 20 by stepping `n` by 2.
3. Find how many times 1,000,000 can be halved with `/= 2` before it reaches 0.
4. Change `while n <= 10` to `while n < 10` and explain the new sum.
## Learn more
- [`while`](https://rux-lang.dev/docs/lang/statements/loops#while) in the Rux Reference
- [Do-while](https://rux-lang.dev/docs/learn/do-while) — test at the bottom, so the body runs at least once
- [For](https://rux-lang.dev/docs/learn/for) — the loop that handles its own counter
# Do-while
::note
**You'll need**: [While](https://rux-lang.dev/docs/learn/while)
::
A `while` loop tests first and runs second, so it may run zero times. Some jobs need the opposite order: do the work once, then decide whether to do it again. Asking a user for input until it is valid is one — you cannot check an answer before you have asked. `do`-`while` puts the test at the bottom, so the body always runs at least once.
## The test at the bottom
```rux
do {
doPasses += 1;
} while doPasses > 5;
```
The body comes first, after `do`. The condition comes last, after `while`, and the whole statement ends with a semicolon. Apart from where the test sits, it behaves like a `while`: after each pass the condition is tested, and the loop repeats while it holds.
```mermaid
flowchart LR
w(["while"]) --> wt{"test"}
wt -- "true" --> wb["body"]
wb --> wt
wt -- "false" --> wa["after the loop"]
d(["do-while"]) --> db["body"]
db --> dt{"test"}
dt -- "true" --> db
dt -- "false" --> da["after the loop"]
```
## The same false condition, both ways
The program runs both loops with a condition that is false from the start:
```rux
var whilePasses: int32 = 0;
while whilePasses > 5 {
whilePasses += 1;
}
var doPasses: int32 = 0;
do {
doPasses += 1;
} while doPasses > 5;
PrintLine("while ran {} times, do-while ran {} time", whilePasses, doPasses);
```
`while` never runs its body, and `do`-`while` runs it once. That guarantee is the only difference between them, and the reason to choose one over the other.
## Where the guarantee matters
Counting the digits of a number is a natural fit: divide by 10 until nothing is left, counting as you go.
```rux
var number: int32 = 4096;
var digits: int32 = 0;
do {
digits += 1;
number /= 10;
} while number != 0;
PrintLine("4096 has {} digits", digits);
```
4096 becomes 409, 40, 4 and 0 — four passes, four digits. Now consider 0. It has one digit, but it is already `0` before the first pass. The `do`-`while` runs once anyway and gets the right answer, `1`. The same loop written with `while` tests `number != 0` first, never runs, and reports that 0 has no digits at all:
```rux
while number != 0 {
digits += 1;
number /= 10;
}
```
| Number | `do`-`while` says | `while` says |
| ------ | ----------------- | ------------ |
| 4096 | 4 digits | 4 digits |
| 0 | 1 digit | 0 digits |
Both are correct for every other number. Edge cases like 0 are where the choice of loop shows.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/DoWhile){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `do { ... } while condition;` runs its body first and tests the condition afterwards. So the
// body always runs at least once, even when the condition is false from the start. That
// guarantee is the only difference from `while`, and the reason to choose one over the other.
//
// The test sits at the end, so the statement ends with a semicolon after the condition. Leave it
// out and the compiler stops at whatever comes next:
//
// error: expected ';' after the 'do while' condition before 'PrintLine'
import Io::PrintLine;
func Main() -> int {
// The same false condition, both ways round: `while` never runs its body, `do` runs it once.
var whilePasses: int32 = 0;
while whilePasses > 5 {
whilePasses += 1;
}
var doPasses: int32 = 0;
do {
doPasses += 1;
} while doPasses > 5;
PrintLine("while ran {} times, do-while ran {} time", whilePasses, doPasses);
// Where the guarantee matters: counting the digits of a number by dividing by 10 until
// nothing is left. Every number has at least one digit, and 0 is the case that shows it.
var number: int32 = 4096;
var digits: int32 = 0;
do {
digits += 1;
number /= 10;
} while number != 0;
PrintLine("4096 has {} digits", digits);
number = 0;
digits = 0;
do {
digits += 1;
number /= 10;
} while number != 0;
PrintLine("0 has {} digit", digits);
// Written with `while`, the same loop never runs for 0 and reports no digits at all.
number = 0;
digits = 0;
while number != 0 {
digits += 1;
number /= 10;
}
PrintLine("the while version says 0 has {} digits", digits);
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/DoWhile
rux run
```
```text
while ran 0 times, do-while ran 1 time
4096 has 4 digits
0 has 1 digit
the while version says 0 has 0 digits
```
## Common mistakes
::warning
**Forgetting the semicolon after the condition.**:br`do { … } while number != 0` must end with `;`. Without it the compiler stops at whatever comes next: `error: expected ';' after the 'do while' condition before 'PrintLine'`.
::
::warning
**Choosing `do`-`while` when zero passes is a valid answer.**:br
If the body must not run for some starting values — an empty list, a balance of 0 — the guarantee of one pass is a bug, not a feature. Use `while` there.
::
## Try it yourself
1. Count the digits of `7`, `10` and `1000000` with the `do`-`while` loop.
2. Change the digit counter to add up the digits instead (`number % 10` is the last digit). What does `4096` give?
3. Write a `do`-`while` that doubles a `var value: int32 = 1;` until it passes 100, and print how many passes it took.
## Learn more
- [`while`](https://rux-lang.dev/docs/lang/statements/loops#while) in the Rux Reference
- [While](https://rux-lang.dev/docs/learn/while) — the loop that tests first
- [Loop](https://rux-lang.dev/docs/learn/loop) — for a loop whose exit belongs in the middle
# Loop
::note
**You'll need**: [While](https://rux-lang.dev/docs/learn/while), [Do-while](https://rux-lang.dev/docs/learn/do-while)
::
`while` tests at the top of each pass, and `do`-`while` at the bottom. Some loops want the test in the **middle**: there is work to do before the question can be asked, and different work after it. `loop` is the keyword for that — a loop with no condition at all, whose way out is a `break` written wherever it belongs.
## A loop with no condition
```rux
loop {
total += odd;
if total > 50 {
break;
}
odd += 2;
}
```
On its own, `loop { … }` would repeat forever. The `break` inside is what ends it: it leaves the loop at once, and the program carries on with the statement after the closing brace. The [next lesson](https://rux-lang.dev/docs/learn/break) looks at `break` in every kind of loop; here it is simply the way out.
## The exit in the middle
The Collatz sequence is a good example of a loop that wants its test in the middle. Start from a number; halve it if it is even, triple it and add one if it is odd; stop on reaching 1. The program prints the whole sequence with arrows between the numbers:
```rux
var n: int32 = 6;
var steps: int32 = 0;
loop {
Print("{}", n);
if n == 1 {
break;
}
Print(" -> ");
if n % 2 == 0 {
n /= 2;
} else {
n = 3 * n + 1;
}
steps += 1;
}
```
```mermaid
flowchart LR
top(["loop"]) --> p["print n"]
p --> t{"n == 1?"}
t -- "yes: break" --> out["after the loop"]
t -- "no" --> arrow["print ' -> '
compute the next n"]
arrow --> p
```
Each number is printed *before* the test, so the final 1 is printed too. The arrow and the next number come *after* the test, so nothing is printed or computed past the end. Move the test to the top of the body or to the bottom, and either way the line ends in a stray arrow with the final 1 missing. `Print`, unlike `PrintLine`, does not end the line, which is how the sequence stays on one row — the `PrintLine()` after the loop finishes it.
## The exit can depend on the work
In the second loop, the test reads a value the body has only just computed:
```rux
var odd: int32 = 1;
var total: int32 = 0;
loop {
total += odd;
if total > 50 {
break;
}
odd += 2;
}
PrintLine("adding odd numbers up to {} passes 50, at {}", odd, total);
```
The total is updated first, then tested, and only if the loop goes on does `odd` move to the next odd number. That way `odd` still holds the number that pushed the total past 50 when the loop ends.
## loop or while true?
`while true { … }` with a `break` inside does the same job and compiles fine. `loop` is the better way to say it: the reader sees at once that the loop's ending is decided inside it, instead of reading a condition that is not really one.
| Loop | Where the test is | Runs at least once? |
| ---------------- | --------------------- | ------------------- |
| `while` | at the top | no |
| `do`-`while` | at the bottom | yes |
| `loop` + `break` | wherever you write it | up to the `break` |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/Loop){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `loop { ... }` repeats its body with no condition at all. On its own it would run forever, so
// somewhere in the body there is a `break`, which leaves the loop at once and carries on with the
// statement after it.
//
// `while` tests at the top of each pass and `do`-`while` at the bottom. `loop` is for the loop
// whose exit belongs in the middle: some work has to happen before the test, and some after it.
// It also says plainly that the loop's ending is decided inside it, which `while true` hides.
//
// The next lesson, Break, looks at `break` in every kind of loop. Here it is just the way out.
import Io::{ Print, PrintLine };
func Main() -> int {
// The Collatz sequence: halve an even number, triple an odd one and add one, and stop on
// reaching 1. Each number is printed before the test, so the final 1 is printed too. The arrow
// and the next number come after the test, so nothing is printed or computed past the end.
var n: int32 = 6;
var steps: int32 = 0;
loop {
Print("{}", n);
if n == 1 {
break;
}
Print(" -> ");
if n % 2 == 0 {
n /= 2;
} else {
n = 3 * n + 1;
}
steps += 1;
}
PrintLine();
PrintLine("6 reaches 1 in {} steps", steps);
// The exit can depend on what the body just computed. Keep adding odd numbers 1 + 3 + 5 + ...
// and stop once the total passes 50.
var odd: int32 = 1;
var total: int32 = 0;
loop {
total += odd;
if total > 50 {
break;
}
odd += 2;
}
PrintLine("adding odd numbers up to {} passes 50, at {}", odd, total);
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/Loop
rux run
```
```text
6 -> 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
6 reaches 1 in 8 steps
adding odd numbers up to 15 passes 50, at 64
```
## Common mistakes
::warning
**A `loop` with no reachable `break`.**:br
The compiler does not insist on a `break`, so a `loop` whose `break` is never reached runs forever. Make sure every path through the body moves towards the exit — press **Ctrl+C** to stop a program that is stuck.
::
::warning
**Putting the test in the wrong place.**:br
Move `if n == 1 { break; }` to the top or the bottom of the Collatz loop and the output ends `4 -> 2 -> `— a stray arrow, and no final `1`. Where the `break` sits decides what happens on the last pass.
::
## Try it yourself
1. Start the Collatz sequence from `27` and count the steps. (It takes a while.)
2. Change the second loop to stop once the total passes 100.
3. Rewrite the second loop with `while`. Which variable needs a different starting value, or which test needs to change?
## Learn more
- [`loop`](https://rux-lang.dev/docs/lang/statements/loops#loop) in the Rux Reference
- [Break](https://rux-lang.dev/docs/learn/break) — leaving any loop early
- [While](https://rux-lang.dev/docs/learn/while) and [Do-while](https://rux-lang.dev/docs/learn/do-while) — loops with a test at one end
# Break
::note
**You'll need**: [Loop](https://rux-lang.dev/docs/learn/loop)
::
A search can stop as soon as it finds what it is looking for. There is no point checking the rest of the haystack once the needle is in your hand. `break` leaves a loop immediately: the rest of the current pass is skipped, the condition is not tested again, and the program carries on with the first statement after the loop. It works in every loop — `while`, `do`-`while`, `loop`, and the `for` loop coming up later in this part.
## Stopping a search early
The program looks for the smallest divisor of 91. It could try every candidate from 2 up to 90, but once one divides evenly there is no reason to keep going:
```rux
let number: int32 = 91;
var divisor: int32 = 2;
while divisor < number {
if number % divisor == 0 {
break;
}
divisor += 1;
}
```
2, 3, 4, 5 and 6 do not divide 91; 7 does, so the loop stops with `divisor` at 7 — after six candidates instead of eighty-nine.
## How did the loop end?
A loop with a `break` can end two ways: its condition became false, or the `break` ran. Code after the loop often needs to know which, and it finds out by looking at the variables the loop left behind:
```rux
if divisor < number {
PrintLine("{} = {} x {}", number, divisor, number / divisor);
} else {
PrintLine("{} is prime", number);
}
```
```mermaid
flowchart LR
test{"divisor < number?"} -- "true" --> check{"number % divisor == 0?"}
check -- "yes: break" --> found["divisor < number:
a divisor was found"]
check -- "no" --> step["divisor += 1"]
step --> test
test -- "false" --> prime["divisor == number:
no divisor, prime"]
```
If the loop ran out of candidates, `divisor` ended equal to `number`; if `break` ran, it is still below. One comparison tells the two endings apart.
## break leaves one loop
With loops inside loops, `break` leaves only the loop it is written in — the innermost one around it. Here it ends one row of a triangle, and the outer loop carries on with the next row as if nothing happened:
```rux
var row: int32 = 1;
while row <= 4 {
var column: int32 = 1;
while column <= 4 {
if column > row {
break;
}
Print("*");
column += 1;
}
PrintLine();
row += 1;
}
```
Row 1 prints one star before `column > row` stops the inner loop, row 2 prints two, and so on. To leave an *outer* loop from inside an inner one, you need a label — the subject of [Label](https://rux-lang.dev/docs/learn/label).
## break in a do-while
In a `do`-`while`, `break` also skips the test at the bottom:
```rux
var attempts: int32 = 0;
do {
attempts += 1;
if attempts == 3 {
break;
}
} while attempts < 10;
PrintLine("gave up after {} attempts", attempts);
```
The condition would allow ten attempts, but the `break` ends the loop at three.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/Break){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `break` leaves a loop immediately. The rest of the current pass is skipped, the condition is
// not tested again, and the program carries on with the first statement after the loop. It works
// in every loop: `while`, `do`-`while` and `loop` alike.
//
// A loop with a `break` can end two ways: its condition became false, or the `break` ran. Code
// after the loop often needs to know which, and it finds out by looking at the variables the
// loop left behind.
//
// `break` belongs inside a loop. Anywhere else it is an error:
//
// error: 'break' can only be used inside 'while', 'for', or 'loop'
import Io::{ Print, PrintLine };
func Main() -> int {
// Searching for the smallest divisor of 91. The loop could try every candidate below 91, but
// once one divides evenly there is no reason to keep going.
let number: int32 = 91;
var divisor: int32 = 2;
while divisor < number {
if number % divisor == 0 {
break;
}
divisor += 1;
}
// Reaching `number` means the condition ended the loop and no divisor was found.
if divisor < number {
PrintLine("{} = {} x {}", number, divisor, number / divisor);
} else {
PrintLine("{} is prime", number);
}
// `break` leaves only the loop it is written in. Here it ends one row of the triangle; the
// outer loop carries on with the next row as if nothing happened.
var row: int32 = 1;
while row <= 4 {
var column: int32 = 1;
while column <= 4 {
if column > row {
break;
}
Print("*");
column += 1;
}
PrintLine();
row += 1;
}
// In a `do`-`while`, `break` also skips the test at the bottom.
var attempts: int32 = 0;
do {
attempts += 1;
if attempts == 3 {
break;
}
} while attempts < 10;
PrintLine("gave up after {} attempts", attempts);
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/Break
rux run
```
```text
91 = 7 x 13
*
**
***
****
gave up after 3 attempts
```
## Common mistakes
::warning
**`break` outside a loop.**:br`break` belongs inside a loop. Anywhere else it fails with `error: 'break' can only be used inside 'while', 'for', or 'loop'`. In particular, it cannot leave an `if` — an `if` block is not a loop.
::
::warning
**Expecting `break` to leave every loop.**:br
In nested loops, a plain `break` ends only the innermost one. The outer loop carries on. Use a [label](https://rux-lang.dev/docs/learn/label) to leave an outer loop.
::
::warning
**Not checking how the loop ended.**:br
After a search loop, the variables may describe "found" or "not found". Test them before using the result — otherwise a prime such as 97 would be reported as `97 = 97 x 1`.
::
## Try it yourself
1. Change `number` to `97` and check that the program reports it as prime.
2. Find the first multiple of 7 above 100 with a `loop` and `break`.
3. Make the triangle print five rows, then make it print the triangle upside down.
## Learn more
- [`break` / `continue`](https://rux-lang.dev/docs/lang/statements/break-continue) in the Rux Reference
- [Continue](https://rux-lang.dev/docs/learn/continue) — skipping one pass instead of leaving the loop
- [Label](https://rux-lang.dev/docs/learn/label) — breaking out of an outer loop
# Continue
::note
**You'll need**: [Break](https://rux-lang.dev/docs/learn/break)
::
`break` gives up on the whole loop. Often you only want to give up on one value — skip the blank line, ignore the negative reading, pass over the multiples of 3 — and carry on with the rest. `continue` abandons the rest of the current pass and starts the next one. It does not leave the loop.
## Skipping a value
```rux
var n: int32 = 0;
while n < 20 {
n += 1;
if n % 3 == 0 {
continue;
}
Print(" {}", n);
}
```
For 3, 6, 9 and the other multiples of 3, `continue` jumps over the `Print`. Every other number reaches it. The shape is worth remembering: the test for "not this one" goes at the top of the body, and the work below it runs only for the values that remain.
## Where continue goes next
Where the next pass begins depends on the loop:
| Loop | After `continue` |
| ------------ | ------------------------------ |
| `while` | tests its condition again |
| `do`-`while` | goes to the test at its bottom |
| `loop` | starts its body again |
| `for` | moves on to the next value |
```mermaid
flowchart LR
test{"n < 20?"} -- "true" --> step["n += 1"]
step --> skip{"n % 3 == 0?"}
skip -- "yes: continue" --> test
skip -- "no" --> work["print n"]
work --> test
test -- "false" --> after["after the loop"]
```
## The step goes first
`continue` skips *everything* after it in the body — including any line that moves the loop on. That is why `n += 1` comes at the top of both loops here. Suppose it were written at the bottom instead:
```rux
while n < 20 {
if n % 3 == 0 {
continue;
}
Print(" {}", n);
n += 1;
}
```
`n` starts at 0, which is already a multiple of 3. `continue` jumps back to the test before `n += 1` runs, so `n` is still 0, the test still holds, `continue` runs again — and the loop tests the same value forever, without printing a thing. Step first, then decide whether to skip.
## Skipped passes still happen
A skipped pass is not a pass that never ran; it just ends early. The second loop counts both:
```rux
var passes: int32 = 0;
var kept: int32 = 0;
var sum: int32 = 0;
n = 0;
while n < 10 {
n += 1;
passes += 1;
if n % 2 == 0 {
continue;
}
kept += 1;
sum += n;
}
```
Ten passes run; five reach the bottom, adding up the odd numbers 1 + 3 + 5 + 7 + 9 = 25.
## continue and break together
The two combine naturally: skip some values, stop at another.
```rux
var k: int32 = 0;
loop {
k += 1;
if k % 2 == 0 {
continue;
}
if k > 7 {
break;
}
Print(" {}", k);
}
```
Even numbers are skipped; the first odd number above 7 — which is 9 — ends the loop. So `k` is 9 afterwards, even though the last number printed was 7.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/Continue){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `continue` abandons the rest of the current pass and starts the next one. Unlike `break`, it
// does not leave the loop: a `while` goes back to testing its condition, a `do`-`while` goes to
// the test at its bottom, and a `loop` starts its body again.
//
// It suits a loop that has to skip some values: the test for "not this one" goes at the top of
// the body, and the work below it runs only for the values that remain.
//
// The trap: `continue` skips *everything* after it, including the step that moves the loop on.
// Write the step below the `continue` and a skipped value is never stepped past, so the loop
// tests the same value forever. That is why `n += 1` comes first in both loops below.
import Io::{ Print, PrintLine };
func Main() -> int {
// Numbers from 1 to 20 that are not multiples of 3.
Print("not multiples of 3:");
var n: int32 = 0;
while n < 20 {
n += 1;
if n % 3 == 0 {
continue;
}
Print(" {}", n);
}
PrintLine();
// Counting and summing only the values that pass a test. The skipped passes still happen;
// they just end early.
var passes: int32 = 0;
var kept: int32 = 0;
var sum: int32 = 0;
n = 0;
while n < 10 {
n += 1;
passes += 1;
if n % 2 == 0 {
continue;
}
kept += 1;
sum += n;
}
PrintLine("{} passes, {} odd numbers kept, summing to {}", passes, kept, sum);
// `continue` and `break` together: skip the even numbers, stop at the first odd one above 7.
Print("odd numbers up to 7:");
var k: int32 = 0;
loop {
k += 1;
if k % 2 == 0 {
continue;
}
if k > 7 {
break;
}
Print(" {}", k);
}
PrintLine();
PrintLine("stopped at {}", k);
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/Continue
rux run
```
```text
not multiples of 3: 1 2 4 5 7 8 10 11 13 14 16 17 19 20
10 passes, 5 odd numbers kept, summing to 25
odd numbers up to 7: 1 3 5 7
stopped at 9
```
## Common mistakes
::warning
**Stepping after the `continue`.**:br
If the line that moves the loop on comes below a `continue`, a skipped value is never stepped past, and the loop runs forever on the same value. The compiler cannot see this — it compiles cleanly. Put the step at the top of the body. ([For](https://rux-lang.dev/docs/learn/for) handles the step itself, so the trap disappears there.)
::
::warning
**`continue` outside a loop.**:br
Like `break`, it belongs in a loop: `error: 'continue' can only be used inside 'while', 'for', or 'loop'`.
::
## Try it yourself
1. Print the numbers from 1 to 30 that are multiples of neither 2 nor 3.
2. Add up only the numbers from 1 to 100 that end in 7 (`n % 10 == 7`).
3. Change the last loop to stop at the first odd number above 15.
## Learn more
- [`break` / `continue`](https://rux-lang.dev/docs/lang/statements/break-continue) in the Rux Reference
- [Break](https://rux-lang.dev/docs/learn/break) — leaving the loop instead of one pass
- [For](https://rux-lang.dev/docs/learn/for) — a loop where `continue` can never skip the step
# Range
::note
**You'll need**: [While](https://rux-lang.dev/docs/learn/while), [Comparison](https://rux-lang.dev/docs/learn/comparison)
::
"The digits 0 to 9." "Faces one to six." "Hours from 9 until 17." A great deal of counting is about a run of consecutive numbers described by where it starts and where it stops. A *range* is a value that holds exactly that: two ends. The main use of a range is walking through it, which the [next lesson](https://rux-lang.dev/docs/learn/for) does in one line; this lesson looks at what a range is, so that the walk holds no surprises.
## Two spellings, two kinds of end
```rux
let digits = 0..10;
let die = 1..=6;
```
`a..b` starts at `a` and stops **just before** `b` — the end is excluded, so `0..10` is the ten digits 0 to 9. `a..=b` **includes** `b`, so `1..=6` is the six faces of a die.
| Range | Type | Values | How many |
| ------- | ----------- | ---------- | --------------------- |
| `0..10` | `int..int` | 0, 1, …, 9 | `end - start` = 10 |
| `1..=6` | `int..=int` | 1, 2, …, 6 | `end - start + 1` = 6 |
| `5..5` | `int..int` | none | 0 |
| `5..=5` | `int..=int` | 5 | 1 |
The two spellings make two different types. That is not a detail to memorise — it means the compiler always knows which kind of end a range has.
## A range holds only its bounds
A range does not list its values. It remembers its two ends, which it offers as `.start` and `.end`:
```rux
PrintLine("digits: start {}, end {} (excluded)", digits.start, digits.end);
PrintLine("die: start {}, end {} (included)", die.start, die.end);
```
Everything else follows from those two numbers. How many values a range holds is a subtraction, plus one for an inclusive end:
```rux
PrintLine("{} digits", digits.end - digits.start);
PrintLine("{} faces", die.end - die.start + 1);
```
Whether a number falls inside is two comparisons — and the one against the end differs by a single character:
```rux
let roll = 7;
let isDigit = roll >= digits.start && roll < digits.end;
let isFace = roll >= die.start && roll <= die.end;
```
## Bounds and their type
The bounds may be any integer expressions, not only literals:
```rux
let first: int32 = 3;
let width: int32 = 4;
let window = first..first + width;
```
`..` ranks below arithmetic, so `first..first + width` is `3..7`. The bounds take the type of the values they are built from — `int32` here. With literal bounds the default is `int`, and an annotation picks another integer type:
```rux
let hours: uint8..uint8 = 0..24;
```
## Walking a range by hand
To visit every value: start at `.start`, step by one, and stop according to the kind of end.
```rux
var face = die.start;
while face <= die.end {
Print(" {}", face);
face += 1;
}
```
```mermaid
flowchart LR
s(["face = start"]) --> t{"exclusive: face < end
inclusive: face <= end"}
t -- "true" --> b["use face
face += 1"]
b --> t
t -- "false" --> done["done"]
```
Every walk over a range has these same three parts — a start, a test, a step — and the only thing that changes is `<` or `<=`. That repetition is exactly what `for` takes off your hands.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/Range){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A range is a value that describes a run of consecutive integers by its two ends. `a..b` starts
// at `a` and stops just before `b`, so `b` is excluded; `a..=b` includes `b`. The two spellings
// make two different types: `0..10` is an `int..int` and `1..=6` an `int..=int`.
//
// A range does not list its values; it only remembers its bounds, which it offers as `.start`
// and `.end`. Everything else (how many values, whether a number is inside) follows from them.
//
// The start may not come after the end. A range written backwards with literal bounds is refused:
//
// let backwards = 5..2;
// error: range start cannot be greater than its end
//
// The main use of a range is walking through it, which the next lesson, For, does in one line.
// Here a `while` loop does it by hand, to show what a range means.
import Io::{ Print, PrintLine };
func Main() -> int {
let digits = 0..10;
let die = 1..=6;
PrintLine("digits: start {}, end {} (excluded)", digits.start, digits.end);
PrintLine("die: start {}, end {} (included)", die.start, die.end);
// How many values each range holds. The inclusive end is one value more.
PrintLine("{} digits", digits.end - digits.start);
PrintLine("{} faces", die.end - die.start + 1);
// Whether a number falls inside: the comparison against the end differs by one character.
let roll = 7;
let isDigit = roll >= digits.start && roll < digits.end;
let isFace = roll >= die.start && roll <= die.end;
PrintLine("{} is a digit: {}, a face: {}", roll, isDigit, isFace);
// Equal bounds: the exclusive range holds nothing at all, the inclusive one holds one value.
let empty = 5..5;
let single = 5..=5;
PrintLine("5..5 holds {} values, 5..=5 holds {}", empty.end - empty.start,
single.end - single.start + 1);
// The bounds may be any integer expressions, not only literals.
let first: int32 = 3;
let width: int32 = 4;
let window = first..first + width;
PrintLine("window: start {}, end {}", window.start, window.end);
// An annotation picks another integer type for the bounds, and literal bounds take it.
let hours: uint8..uint8 = 0..24;
PrintLine("hours: start {}, end {}", hours.start, hours.end);
// Walking a range by hand: start at `.start`, step by one, stop according to the kind of end.
Print("faces:");
var face = die.start;
while face <= die.end {
Print(" {}", face);
face += 1;
}
PrintLine();
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/Range
rux run
```
```text
digits: start 0, end 10 (excluded)
die: start 1, end 6 (included)
10 digits
6 faces
7 is a digit: true, a face: false
5..5 holds 0 values, 5..=5 holds 1
window: start 3, end 7
hours: start 0, end 24
faces: 1 2 3 4 5 6
```
## Common mistakes
::warning
**A range written backwards.**:br
The start may not come after the end. With literal bounds the compiler catches it: `let backwards = 5..2;` fails with `error: range start cannot be greater than its end`. With bounds computed while the program runs, nothing is reported, and a walk over the range runs zero times.
::
::warning
**Off by one at the end.**:br`1..6` stops at 5. When you mean "up to and including", write `..=`. Counting uses `end - start` for `..` but `end - start + 1` for `..=`.
::
## Try it yourself
1. Make a range for the months of the year and print how many values it holds.
2. Test whether `0`, `5` and `10` fall inside `digits`, and whether they fall inside `0..=10`.
3. Walk `digits` by hand with `while`, printing every value. Which comparison does the condition need?
4. Write `let backwards = 5..2;` and read the error.
## Learn more
- [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) and [using ranges](https://rux-lang.dev/docs/lang/ranges/overview#in-a-for-loop) in the Rux Reference
- [For](https://rux-lang.dev/docs/learn/for) — walking a range in one line
- [Range pattern](https://rux-lang.dev/docs/learn/range-pattern) — matching a value against a range
# For
::note
**You'll need**: [Range](https://rux-lang.dev/docs/learn/range), [Continue](https://rux-lang.dev/docs/learn/continue)
::
The [Range](https://rux-lang.dev/docs/learn/range) lesson walked through a range by hand: a counter, a test against the end, a step on every pass. Three separate lines, and getting any one of them wrong — the wrong comparison, a forgotten step — breaks the loop. `for` does the whole walk in one line. It runs its body once for every value in a range, in order, and hands each value to the body by name.
## for name in range
```rux
for i in 1..5 {
Print(" {}", i);
}
```
On each pass, `i` is the next value of the range: 1, 2, 3, 4. The end of `..` is excluded, so 5 is not visited; with `..=` it is:
```rux
for i in 1..=5 {
Print(" {}", i);
}
```
Here is what `for` does for you, compared with the hand-written walk:
| Part | Hand-written `while` | `for i in 1..=5` |
| ---------- | -------------------------- | ----------------------- |
| Start | `var i = 1;` | from the range's start |
| Test | `while i <= 5` | from the kind of end |
| Step | `i += 1;` — easy to forget | done by the loop |
| Loop value | a `var` anyone can change | a fresh `let` each pass |
```mermaid
flowchart LR
r(["for i in 1..=5"]) --> more{"values left?"}
more -- "yes" --> bind["i = next value"]
bind --> body["run the body"]
body --> more
more -- "no" --> after["after the loop"]
```
## Ranges stored in a binding
A range kept in a variable works the same as one written in place. The loop variable takes the type of the bounds, so this `face` is an `int32`, like the `total` it is added to:
```rux
let die: int32..=int32 = 1..=6;
var total: int32 = 0;
for face in die {
total += face;
}
```
## Bounds are evaluated once
The bounds are worked out once, before the first pass. A factorial — 10! is 1 × 2 × … × 10 — multiplies every number up to and *including* `n`, so the inclusive form matches its definition exactly:
```rux
let n = 10;
var factorial = 1;
for i in 2..=n {
factorial *= i;
}
```
Starting at 2 skips a pointless multiplication by 1. An empty range, such as `3..3`, gives the body nothing to run for: the loop runs zero times.
## The loop variable cannot be changed
`i` is a fresh, immutable binding on every pass. The body can read it but cannot assign to it, so it cannot push the loop along or hold it back. When the body really does need to control the stepping — skipping ahead by more than one, or going back — that is a job for `while`.
## break and continue work here too
`break` and `continue` work in `for` as in every other loop. And because the loop does its own stepping, `continue` moves straight on to the next value — the trap from [Continue](https://rux-lang.dev/docs/learn/continue), a step skipped by mistake, cannot happen:
```rux
for i in 0..10 {
if i % 2 == 0 {
continue;
}
Print(" {}", i);
}
```
Whenever a loop visits a known run of numbers, reach for `for` first.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/For){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `for name in range { ... }` runs its body once for every value in the range, in order, binding
// the value to `name` on each pass. It is the walk the Range lesson wrote by hand with `while`,
// with the counter, the test and the step all handled by the loop, so none of them can be
// forgotten or got wrong.
//
// The loop variable is a fresh, immutable binding on every pass, so the body cannot move the loop
// along by assigning to it:
//
// for i in 1..4 { i = 10; }
// error: cannot modify immutable variable 'i'
//
// When the body needs to control the stepping, use `while`.
import Io::{ Print, PrintLine };
func Main() -> int {
// The end of `..` is excluded, the end of `..=` is included.
Print("1..5: ");
for i in 1..5 {
Print(" {}", i);
}
PrintLine();
Print("1..=5:");
for i in 1..=5 {
Print(" {}", i);
}
PrintLine();
// A range stored in a binding works the same as one written in place. The loop variable takes
// the type of the bounds, so this `face` is an `int32`, like the `total` it is added to.
let die: int32..=int32 = 1..=6;
var total: int32 = 0;
for face in die {
total += face;
}
PrintLine("the faces of a die add up to {}", total);
// Bounds are evaluated once, before the first pass. A factorial multiplies every number from
// 1 up to and including n, so the inclusive form matches its definition.
let n = 10;
var factorial = 1;
for i in 2..=n {
factorial *= i;
}
PrintLine("{}! = {}", n, factorial);
// An empty range gives the body nothing to run for.
var passes: int32 = 0;
for i in 3..3 {
passes += 1;
}
PrintLine("3..3 ran the body {} times", passes);
// `break` and `continue` work in `for` as in every other loop. `continue` moves straight on to
// the next value, with no step to forget.
Print("odd numbers below 10:");
for i in 0..10 {
if i % 2 == 0 {
continue;
}
Print(" {}", i);
}
PrintLine();
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/For
rux run
```
```text
1..5: 1 2 3 4
1..=5: 1 2 3 4 5
the faces of a die add up to 21
10! = 3628800
3..3 ran the body 0 times
odd numbers below 10: 1 3 5 7 9
```
## Common mistakes
::warning
**Assigning to the loop variable.**:br`for i in 1..4 { i = 10; }` fails with `error: cannot modify immutable variable 'i'`. The compiler's help suggests declaring `i` with `var`, but a `for` variable cannot be — `for var i in …` does not parse. Use `while` when the body must control the stepping.
::
::warning
**Counting down with a backwards range.**:br
A range only counts up. `for i in 5..0` is refused with `error: range start cannot be greater than its end`, and with bounds computed at run time, such as `high..low`, the body silently runs zero times. Count down with a `while` loop.
::
::warning
**Off by one at the end.**:br`for i in 1..10` stops at 9. If the last value should be included, write `1..=10`.
::
## Try it yourself
1. Print the squares of the numbers from 1 to 10.
2. Add up the numbers from 1 to 100 with `for`, then compare with the `while` version in [While](https://rux-lang.dev/docs/learn/while).
3. Print a multiplication table for 7, from `7 x 1` to `7 x 10`.
4. Compute 12! with the factorial loop. Does the answer still fit in an `int`?
## Learn more
- [`for` / `in`](https://rux-lang.dev/docs/lang/statements/loops#for) in the Rux Reference
- [Label](https://rux-lang.dev/docs/learn/label) — leaving an outer `for` from an inner one
- [Iterator](https://rux-lang.dev/docs/learn/iterator) — making your own types walkable by `for`
# Label
::note
**You'll need**: [For](https://rux-lang.dev/docs/learn/for), [Break](https://rux-lang.dev/docs/learn/break), [Continue](https://rux-lang.dev/docs/learn/continue)
::
A plain `break` or `continue` acts on the innermost loop around it. With one loop inside another, that is sometimes the wrong one. A search through a grid of rows and columns wants to stop the *whole* search once it finds a match — not just the current row, only for the outer loop to start the next one. A **label** gives a loop a name, so `break` and `continue` can say which loop they mean.
## Naming a loop
Write the label before the loop, followed by a colon. Then `break` or `continue` with that name acts on that loop:
```rux
var checks: int32 = 0;
search: for a in 1..10 {
for b in a..10 {
checks += 1;
if a * a + b * b == 100 {
PrintLine("{}^2 + {}^2 = 100", a, b);
break search;
}
}
}
PrintLine("found after {} checks", checks);
```
The program looks for two numbers below 10 whose squares add up to 100. When it finds 6 and 8, `break search;` leaves the loop named `search` — and every loop inside it — and goes straight to the `PrintLine` after it. Without the label, `break` would end only the inner loop over `b`, and the outer loop would go on searching after the answer was found. (The inner range starts at `a`, so each pair is tried only once: 6 and 8, never 8 and 6 as well.)
## continue with a label
`continue search;` abandons the inner loops and starts the next pass of the labelled one. The second example reports, for each row of a times table, the first product above 10:
```rux
rows: for row in 1..=4 {
for column in 1..=9 {
if row * column > 10 {
PrintLine("row {}: {} x {} = {}", row, row, column, row * column);
continue rows;
}
}
PrintLine("row {}: no product above 10", row);
}
```
`continue rows` skips the rest of the row — including the line after the inner loop. So `no product above 10` prints only for a row where the inner loop ran to its end without finding anything, which here is row 1.
## Where each jump goes
```mermaid
flowchart LR
outer(["outer: for …"]) --> inner(["for … (inner)"])
inner --> body["inner body"]
body -- "break" --> afterInner["rest of the outer body"]
body -- "continue" --> inner
body -- "break outer" --> afterOuter["after the outer loop"]
body -- "continue outer" --> outer
```
| Inside the inner loop | Goes to |
| --------------------- | ---------------------------------------- |
| `break;` | the rest of the outer loop's body |
| `continue;` | the next pass of the inner loop |
| `break outer;` | the first statement after the outer loop |
| `continue outer;` | the next pass of the outer loop |
Labels work on `while`, `do`-`while` and `loop` too, not only `for`.
## Labels are checked names
A label is checked like any other name, so a misspelling is caught at compile time instead of jumping somewhere unexpected. And a nested loop may not reuse the label of a loop around it, which would leave `break outer;` naming two loops at once.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/Label){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A plain `break` or `continue` acts on the innermost loop around it. With nested loops, that is
// sometimes the wrong one: a search through a grid wants to stop the *whole* search once it finds
// a match, not just the current row.
//
// A label names a loop. Write it before the loop with a colon, `search: for ...`, and then
// `break search;` leaves that loop and every loop inside it, while `continue search;` abandons
// the inner loops and starts the next pass of the labeled one. Labels work on `while`, `do` and
// `loop` too.
//
// A label is checked like any other name, so a misspelling is caught:
//
// break serach;
// error: 'break' refers to unknown loop label 'serach'
//
// And a nested loop may not reuse the label of a loop around it, which would leave `break outer;`
// naming two loops at once:
//
// outer: for i in 0..3 { outer: for j in 0..3 { break outer; } }
// error: loop label 'outer' shadows an enclosing loop label
// help: give the inner loop a different label
import Io::PrintLine;
func Main() -> int {
// Find two numbers below 10 whose squares add up to 100. Without the label, `break` would
// end only the inner loop, and the outer one would go on searching after the answer was found.
var checks: int32 = 0;
search: for a in 1..10 {
for b in a..10 {
checks += 1;
if a * a + b * b == 100 {
PrintLine("{}^2 + {}^2 = 100", a, b);
break search;
}
}
}
PrintLine("found after {} checks", checks);
// For each row of a times table, report the first product above 10 and move on to the next
// row. `continue rows` skips the rest of the row, including the line after the inner loop,
// which therefore prints only for a row where nothing was found.
rows: for row in 1..=4 {
for column in 1..=9 {
if row * column > 10 {
PrintLine("row {}: {} x {} = {}", row, row, column, row * column);
continue rows;
}
}
PrintLine("row {}: no product above 10", row);
}
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/Label
rux run
```
```text
6^2 + 8^2 = 100
found after 38 checks
row 1: no product above 10
row 2: 2 x 6 = 12
row 3: 3 x 4 = 12
row 4: 4 x 3 = 12
```
## Common mistakes
::warning
**Misspelling the label.**:br`break serach;` fails with `error: 'break' refers to unknown loop label 'serach'`. The label must match the one written before the loop.
::
::warning
**Reusing a label on a nested loop.**:br`outer: for i in 0..3 { outer: for j in 0..3 { break outer; } }` fails with `error: loop label 'outer' shadows an enclosing loop label`, and the help says to give the inner loop a different label.
::
::warning
**Forgetting the label.**:br
A plain `break` in the inner loop leaves only the inner loop. The search then carries on, and `checks` counts far more than it needed to.
::
## Try it yourself
1. Remove `search` from `break search;` and compare the number of checks.
2. Change the search to find two numbers whose squares add up to `65`. How many pairs exist below 10, and which one does the program report?
3. Label a `while` loop and use `break` with its name from inside a nested `loop`.
## Learn more
- [`loop`](https://rux-lang.dev/docs/lang/statements/loops#loop) and [`break` / `continue`](https://rux-lang.dev/docs/lang/statements/break-continue) in the Rux Reference
- [Break](https://rux-lang.dev/docs/learn/break) and [Continue](https://rux-lang.dev/docs/learn/continue) — the unlabelled forms
- [Match](https://rux-lang.dev/docs/learn/match) — the next lesson, choosing a branch by value
# Match
::note
**You'll need**: [Else if](https://rux-lang.dev/docs/learn/else-if)
::
An `else if` chain that compares one value with a list of constants — `if status == 200 … else if status == 404 … else if status == 500 …` — repeats the same name in every condition, and the reader has to check each line to be sure it really is the same value. `match` says it once. It compares one value against a list of **arms** and runs the first arm whose pattern fits.
## Arms and patterns
```rux
let count: int32 = 2;
match count {
0 => PrintLine("none"),
1 => PrintLine("one"),
2 => PrintLine("a pair"),
else => PrintLine("several")
}
```
After `match` comes the value being examined, then the arms in braces. Each arm is `pattern => what to do`, and arms are separated by commas. The patterns here are literals — the arm `2` fits when `count` is 2.
## How an arm is chosen
The arms are tried from the top. The first one that fits runs, and the rest are skipped — exactly like an `else if` chain. Whichever arm runs, or none, the program then carries on after the match:
```mermaid
flowchart LR
v(["match count"]) --> a0{"fits 0?"}
a0 -- "yes" --> r0["none"]
a0 -- "no" --> a1{"fits 1?"}
a1 -- "yes" --> r1["one"]
a1 -- "no" --> a2{"fits 2?"}
a2 -- "yes" --> r2["a pair"]
a2 -- "no" --> e{"is there an
else arm?"}
e -- "yes" --> re["several"]
e -- "no" --> skip["nothing runs"]
```
The last arm may be `else`, which fits any value the arms above it did not name. The default arm is always spelled `else` — the `_` that other languages use is refused.
## An arm with a block
An arm that needs more than one statement takes a block in braces. The comma still follows it:
```rux
match status {
200 => PrintLine("ok"),
404 => {
PrintLine("not found");
PrintLine("check the address");
},
else => PrintLine("status {}", status)
}
```
## Covering every value
A `bool` has only two values, so naming both covers everything, and no `else` is needed:
```rux
match ready {
true => PrintLine("ready"),
false => PrintLine("not ready yet")
}
```
An integer has far too many values to name, and here is the caveat: an integer that fits no arm is **not** an error. Nothing runs, and the program carries on after the match:
```rux
let missed: int32 = 99;
match missed {
1 => PrintLine("one"),
2 => PrintLine("two")
}
```
Write an `else` arm whenever every value ought to be handled. A match that produces a value is held to a stricter rule, as the [next lesson](https://rux-lang.dev/docs/learn/match-expression) shows.
## What match checks for you
`match` is stricter than an `else if` chain in a useful way. A chain will happily run with a condition that can never be reached, but `match` refuses arms that cannot run:
| Mistake | `else if` chain | `match` |
| ------------------------------ | ------------------ | -------------------------------------------------------------------------------- |
| The same case written twice | the second is dead | `error: duplicate pattern in match` |
| A catch-all before other cases | the rest are dead | `error: match arm is unreachable because an earlier pattern matches every value` |
An arm holds exactly one pattern. There is no `6 | 7 =>`: two values that share an outcome are written as two arms. Later parts add richer patterns — [characters](https://rux-lang.dev/docs/learn/character-pattern), [ranges](https://rux-lang.dev/docs/learn/range-pattern) such as `1..=9 =>`, and [guards](https://rux-lang.dev/docs/learn/guard) that add a condition to an arm.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/Match){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `match` compares one value against a list of arms and runs the first arm whose pattern fits.
// It says the same as an `if` / `else if` chain of `==` tests, but names the value once instead
// of repeating it in every condition.
//
// Each arm is `pattern => what to do`, and arms are separated by commas. The last arm may be
// `else`, which fits any value the arms above it did not name. The default arm is always spelled
// `else`; the `_` other languages use is refused:
//
// error: use 'else' for the default match arm
//
// An arm holds exactly one pattern. There is no `1 | 2 =>`: two values that share an outcome are
// written as two arms.
import Io::PrintLine;
func Main() -> int {
// Literal patterns, and `else` for everything else.
let count: int32 = 2;
match count {
0 => PrintLine("none"),
1 => PrintLine("one"),
2 => PrintLine("a pair"),
else => PrintLine("several")
}
// An arm that needs more than one statement takes a block.
let status: int32 = 404;
match status {
200 => PrintLine("ok"),
404 => {
PrintLine("not found");
PrintLine("check the address");
},
else => PrintLine("status {}", status)
}
// A `bool` has only two values, so naming both covers everything and no `else` is needed.
let ready = false;
match ready {
true => PrintLine("ready"),
false => PrintLine("not ready yet")
}
// The caveat: an integer that fits no arm is not an error. Nothing runs, and the program
// carries on after the match. Write an `else` arm whenever every value ought to be handled.
// (A match that produces a value is held to more than this, as the next lesson shows.)
let missed: int32 = 99;
match missed {
1 => PrintLine("one"),
2 => PrintLine("two")
}
PrintLine("99 fit no arm, so that match did nothing");
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/Match
rux run
```
```text
a pair
not found
check the address
not ready yet
99 fit no arm, so that match did nothing
```
## Common mistakes
::warning
**Using `_` for the default arm.**:br`_ => PrintLine("other")` fails with `error: use 'else' for the default match arm`.
::
::warning
**Combining values in one arm.**:br`6 | 7 => …` is not a pattern in Rux; the parser stops with `error: expected '=>' after the match arm pattern before '|'`. Write one arm for each value.
::
::warning
**Forgetting the comma between arms.**:br
Every arm except the last ends with a comma — including an arm whose body is a block. Without it: `error: expected ',' between match arms before 'else'`.
::
::warning
**Putting `else` first.**:br`else` fits everything, so arms after it could never run: `error: match arm is unreachable because an earlier pattern matches every value`. Keep `else` last.
::
::warning
**Leaving out `else` by accident.**:br
An integer match with no `else` silently does nothing for a value no arm names. If every value should be handled, add the `else` arm.
::
## Try it yourself
1. Write a match on an `int32` `day` that prints the name of the day for 1 to 7 and `"no such day"` for anything else.
2. Rewrite the `status` match as an `else if` chain and compare how often `status` is named.
3. Set `missed` to `2` and run again, then add an `else` arm that prints the value.
4. Write the same case twice in one match and read the error.
## Learn more
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
- [Match expression](https://rux-lang.dev/docs/learn/match-expression) — `match` as a value
- [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) — when a match must cover every case
# Match expression
::note
**You'll need**: [Match](https://rux-lang.dev/docs/learn/match), [Ternary](https://rux-lang.dev/docs/learn/ternary)
::
The conditional `? :` chooses between two values; [Ternary](https://rux-lang.dev/docs/learn/ternary) showed why that is better than declaring a `var` and assigning it in an `if`. `match` can do the same with any number of cases. Each arm produces a value instead of running a statement, and the whole match *becomes* the value of the arm that fit.
## The long way: match as a statement
As a statement, choosing a value needs a `var`, declared first with a placeholder and assigned in every arm:
```rux
let day: int32 = 6;
var kindTheLongWay = "";
match day {
6 => {
kindTheLongWay = "weekend";
},
7 => {
kindTheLongWay = "weekend";
},
else => {
kindTheLongWay = "weekday";
}
}
```
It works, but the name is repeated in every arm, nothing stops an arm from forgetting to assign it, and the result is a `var` that anything later could change.
## The short way: match as a value
As an expression, each arm is just the value it produces:
```rux
let kind = match day {
6 => "weekend",
7 => "weekend",
else => "weekday"
};
```
The binding is a `let` that gets its value where it is declared. Note the semicolon after the closing brace: the match is part of the `let` statement, and the statement needs its `;`.
## A value goes anywhere
Like any value, a match expression can be passed straight to a call. Most months have 31 days, so `else` covers the common case and the arms list the exceptions:
```rux
PrintLine("month {} has {} days", month, match month {
2 => 28,
4 => 30,
6 => 30,
9 => 30,
11 => 30,
else => 31
});
```
Or it can stand inside arithmetic — here, a price per cup size, multiplied by the number of cups:
```rux
let total = cups * match size {
1 => 3,
2 => 4,
else => 5
};
```
## One type, every value covered
A match expression has to produce exactly one value, of one type, whatever the input. That gives the compiler two things to check that a match *statement* does not need:
```mermaid
flowchart LR
m(["let x = match value { … }"]) --> t{"Do all arms produce
the same type?"}
t -- "no" --> e1["error: match arm
type mismatch"]
t -- "yes" --> c{"Do the arms cover
every possible value?"}
c -- "no" --> e2["error: match on 'int32'
is not exhaustive"]
c -- "yes" --> ok["x gets the value
of the arm that fits"]
```
A match over numbers cannot name every value, so it needs an `else` arm, and the compiler checks that it is there. The statement-form caveat from [Match](https://rux-lang.dev/docs/learn/match) — an integer that fits no arm silently does nothing — cannot happen here, because "nothing" is not a value.
A `bool` match that names both `true` and `false` already covers every input, so it needs no `else`:
```rux
let sign = match open {
true => "come in",
false => "closed"
};
```
| Form | Arms produce | Must cover every value? | Result |
| ------------------ | ------------------ | --------------------------- | --------- |
| `match` statement | statements | no — unmatched does nothing | nothing |
| `match` expression | values of one type | yes — `else` for numbers | one value |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/ControlFlow/MatchExpression){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `match` is also an expression: each arm can produce a value instead of running a statement,
// and the whole match becomes the value of the arm that fit. So it can initialize a `let`, be
// passed to a call, or appear in arithmetic, just like the `? :` conditional, but with any number
// of cases.
//
// Being one value, it has one type, so every arm must produce the same type:
//
// let label = match day { 6 => "weekend", else => 0 };
// error: match arm type mismatch: expected 'char8[..]', found 'int'
//
// And a value has to come out whatever the input, so the arms must cover every value. A match
// over numbers cannot name them all, so it needs an `else` arm, and the compiler checks:
//
// let kind = match day { 6 => "weekend", 7 => "weekend" };
// error: match on 'int32' is not exhaustive; its arms do not cover every value
// help: add an 'else' arm
//
// A `bool` match that names both `true` and `false` already covers every input.
import Io::PrintLine;
func Main() -> int {
// As a statement, choosing a value needs a `var`, declared first and assigned in every arm.
let day: int32 = 6;
var kindTheLongWay = "";
match day {
6 => {
kindTheLongWay = "weekend";
},
7 => {
kindTheLongWay = "weekend";
},
else => {
kindTheLongWay = "weekday";
}
}
// As an expression, the binding is a `let` that gets its value where it is declared. The
// match ends with a semicolon here, because it is part of the `let` statement.
let kind = match day {
6 => "weekend",
7 => "weekend",
else => "weekday"
};
PrintLine("day {} is a {} ({} the long way)", day, kind, kindTheLongWay);
// Passed straight to a call. Most months have 31 days, so `else` covers the common case.
let month: int32 = 4;
PrintLine("month {} has {} days", month, match month {
2 => 28,
4 => 30,
6 => 30,
9 => 30,
11 => 30,
else => 31
});
// Used inside arithmetic: a price per size, times a quantity.
let size: int32 = 2;
let cups: int32 = 3;
let total = cups * match size {
1 => 3,
2 => 4,
else => 5
};
PrintLine("{} cups of size {} cost {}", cups, size, total);
// A `bool` match that names both values covers every input, so it needs no `else`.
let open = true;
let sign = match open {
true => "come in",
false => "closed"
};
PrintLine("the sign says {}", sign);
return 0;
}
```
## Run it
```sh
cd Examples/ControlFlow/MatchExpression
rux run
```
```text
day 6 is a weekend (weekend the long way)
month 4 has 30 days
3 cups of size 2 cost 12
the sign says come in
```
## Common mistakes
::warning
**Arms of different types.**:br`let label = match day { 6 => "weekend", else => 0 };` fails with `error: match arm type mismatch: expected 'char8[..]', found 'int'`. Every arm must produce the same type.
::
::warning
**Missing the `else` arm.**:br`let kind = match day { 6 => "weekend", 7 => "weekend" };` fails with `error: match on 'int32' is not exhaustive; its arms do not cover every value`, and the help says to add an `else` arm.
::
::warning
**Forgetting the semicolon.**:br
A match that initialises a `let` ends with `};`. Leave out the `;` and the compiler stops at the next statement: `error: expected ';' after the binding declaration before 'PrintLine'`.
::
## Try it yourself
1. Write `let name = match day { … };` that gives the name of each day from 1 to 7, with `"unknown"` for everything else.
2. Make the month match return 29 for February in a leap year, using a `bool` `leap` and the conditional `? :` inside the arm.
3. Turn the cups example into a statement match that assigns a `var`, and compare the two versions.
4. Remove the `else` arm from the month match and read the error.
## Learn more
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
- [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) — how the compiler decides whether a match covers every case
- [Variant match](https://rux-lang.dev/docs/learn/variant-match) — matching the cases of your own types
# Part 4: Functions
A function gives a piece of work a name, so it can be written once and run from anywhere. This part starts with the plain shape — parameters in, a result out — and then shows every way Rux lets one function do more: return early, call itself, share a name, leave arguments out, work for many types, and take another function as an argument. By the end, `Main` stops being the whole program and becomes the place that calls the parts.
## What you will learn
- Declaring functions with typed parameters and a result, and calling them.
- Leaving a function early with `return`, and writing functions with no result.
- Recursion: a function that calls itself, and the base case that stops it.
- Overloading: several functions with one name, chosen by their arguments.
- Default arguments that let a call leave the usual value out.
- Generic functions with a type parameter ``, inferred or given explicitly.
- Passing a function as a value, typed by its shape: `func(int32) -> int32`.
## One idea, many shapes
```mermaid
flowchart LR
f["A function
parameters in, result out"] --> r["Return
leave early, or give nothing back"]
r --> rec["Recursion
call yourself on a smaller problem"]
f --> o["Overload
one name, several parameter lists"]
o --> g["Generic
one body, many types"]
f --> d["Default argument
a parameter the call may skip"]
f --> cb["Callback
a function passed as a value"]
```
## Lessons
| | Lesson | What you will learn |
| --- | -------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| 4.1 | [Function](https://rux-lang.dev/docs/learn/function) | declare functions with parameters and a return value, and call them |
| 4.2 | [Return](https://rux-lang.dev/docs/learn/return) | return early, and write functions that return nothing |
| 4.3 | [Recursion](https://rux-lang.dev/docs/learn/recursion) | a function that calls itself, and the base case that stops it |
| 4.4 | [Overload](https://rux-lang.dev/docs/learn/overload) | give several functions one name, and let the arguments choose between them |
| 4.5 | [Default argument](https://rux-lang.dev/docs/learn/default-argument) | give a parameter a default value so callers may leave it out |
| 4.6 | [Generic](https://rux-lang.dev/docs/learn/generic) | write one function that works for many types |
| 4.7 | [Callback](https://rux-lang.dev/docs/learn/callback) | pass a function to another function as an ordinary value |
## Before you start
Finish [Part 1: Basics](https://rux-lang.dev/docs/learn/basics) and [Part 3: Control flow](https://rux-lang.dev/docs/learn/control-flow) first — the lessons here use `var`, `if` and `for` throughout, and [Part 2: Operators](https://rux-lang.dev/docs/learn/operators) for the arithmetic inside them. Each lesson's package is in the Examples repository's `Functions/` folder:
```sh
cd Examples/Functions/Function
rux run
```
## After this part
[Part 5: Sequences](https://rux-lang.dev/docs/learn/sequences) passes whole arrays, slices and tuples to the functions you can now write — including functions that take any number of arguments. You are also ready for the checkpoint project [Temperature](https://rux-lang.dev/docs/learn/temperature).
For the full rules behind this part, see [Functions](https://rux-lang.dev/docs/lang/functions/declaration), [Function declaration](https://rux-lang.dev/docs/lang/functions/declaration), [Generic functions](https://rux-lang.dev/docs/lang/generics/overview) and [Function type aliases](https://rux-lang.dev/docs/lang/functions/function-types) in the Rux Reference.
# Function
::note
**You'll need**: [Mutable](https://rux-lang.dev/docs/learn/mutable), [Float](https://rux-lang.dev/docs/learn/float), [For](https://rux-lang.dev/docs/learn/for)
::
A function gives a piece of work a name. You write the work once, and then any part of the program can run it by calling that name — with different values each time. `Main` has been a function since the very first lesson; this lesson writes others and calls them from `Main`.
Functions are how a program stays readable as it grows. `CelsiusToFahrenheit(100.0)` says what it does, while `100.0 * 9.0 / 5.0 + 32.0` makes you work it out.
## The shape of a function
Every function has the same parts: `func`, a name, the parameters in parentheses, `->` and the type of the result, then a body in braces that hands the result back with `return`:
```rux
func Add(left: int, right: int) -> int {
return left + right;
}
```
| Part | In `Add` | Meaning |
| ----------- | -------------------------- | ------------------------------------------------- |
| Name | `Add` | How callers refer to it — PascalCase, like `Main` |
| Parameters | `left: int, right: int` | The values the caller must supply, each typed |
| Result type | `-> int` | The type of the value the function gives back |
| Body | `{ return left + right; }` | The work, ending in `return` |
## Calling a function
A call is the name followed by one argument per parameter, in the same order. Each argument becomes the value of its parameter for that one run of the body, and the value after `return` replaces the call:
```mermaid
flowchart LR
call["Add(2, 3)"] --> bind["left = 2
right = 3"]
bind --> body["return left + right;"]
body --> result["the call is
worth 5"]
```
The result type is whatever the work produces. Here two `float64` values go in and one `float64` comes out:
```rux
func Average(first: float64, second: float64) -> float64 {
return (first + second) / 2.0;
}
```
Arguments must match the parameter types exactly — nothing is converted for you. `Square(2.5)` is refused because `Square` takes an `int` and `2.5` is a `float64`.
## Parameters cannot change
Inside the body a parameter behaves like a `let` binding: you can read it, but not assign to it. A function that needs a running value declares a local `var` of its own:
```rux
func SumUpTo(last: int) -> int {
var total = 0;
for i in 1..=last {
total += i;
}
return total;
}
```
Each call gets its own fresh `total`, so calling `SumUpTo` twice never mixes the two sums.
## A call is a value
Anywhere a value of the result type fits, a call fits too. It can be bound to a name, or passed straight into another call:
```rux
let side = Add(4, 5);
```
```rux
PrintLine("Add(Square(3), Square(4)) {}", Add(Square(3), Square(4)));
```
The inner calls run first: `Square(3)` is 9, `Square(4)` is 16, and only then does `Add` run, with 9 and 16 as its arguments.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Functions/Function){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A function gives a piece of work a name, so it can be written once and run
// from as many places as need it. `Main` has been one since the first lesson;
// this lesson writes others and calls them from `Main`.
//
// The shape is always the same: `func`, a name, the parameters in parentheses,
// `->` and the type of the result, then a body that hands the result back with
// `return`.
import Io::PrintLine;
// Every parameter is written `name: type`, separated by commas. A call must
// supply one argument for each, in the same order.
func Add(left: int, right: int) -> int {
return left + right;
}
func Square(value: int) -> int {
return value * value;
}
// The result type is whatever the work produces. Here two `float64` values go
// in and one `float64` comes out.
func Average(first: float64, second: float64) -> float64 {
return (first + second) / 2.0;
}
func CelsiusToFahrenheit(celsius: float64) -> float64 {
return celsius * 9.0 / 5.0 + 32.0;
}
// Parameters are immutable inside the body, exactly like `let` bindings, so a
// running value needs a local `var` of its own.
func SumUpTo(last: int) -> int {
var total = 0;
for i in 1..=last {
total += i;
}
return total;
}
func Main() -> int {
PrintLine("Add(2, 3) {}", Add(2, 3));
PrintLine("Square(7) {}", Square(7));
PrintLine("Average(4.0, 7.0) {}", Average(4.0, 7.0));
PrintLine("CelsiusToFahrenheit(100.0) {}", CelsiusToFahrenheit(100.0));
PrintLine("SumUpTo(10) {}", SumUpTo(10));
// A call is an ordinary value. It can be bound to a name...
let side = Add(4, 5);
PrintLine("side {}", side);
// ...or passed straight into another call. The inner call runs first.
PrintLine("Square(Add(1, 2)) {}", Square(Add(1, 2)));
PrintLine("Add(Square(3), Square(4)) {}", Add(Square(3), Square(4)));
// Arguments must match the parameter types. `Square(2.5)` is rejected,
// because `2.5` is a `float64` and `Square` takes an `int` — the value is
// not converted for you. And assigning to a parameter inside its function
// is an error:
//
// func Bump(n: int) -> int { n = n + 1; return n; }
// error: cannot modify parameter 'n'
return 0;
}
```
## Run it
```sh
cd Examples/Functions/Function
rux run
```
```text
Add(2, 3) 5
Square(7) 49
Average(4.0, 7.0) 5.5
CelsiusToFahrenheit(100.0) 212.0
SumUpTo(10) 55
side 9
Square(Add(1, 2)) 9
Add(Square(3), Square(4)) 25
```
## Common mistakes
::warning
**An argument of the wrong type.**:br`Square(2.5)` fails with `error: argument 1 to 'Square' has type 'float64', but parameter 'value' requires 'int'`. Pass a value of the parameter's type, or convert it with `as` as in [Convert](https://rux-lang.dev/docs/learn/convert).
::
::warning
**Too few or too many arguments.**:br`Add(2)` fails with `error: call to 'Add' expects 2 arguments, but 1 was provided`. Every parameter needs an argument — until [Default argument](https://rux-lang.dev/docs/learn/default-argument) shows how to make one optional.
::
::warning
**Assigning to a parameter.**:br`func Bump(n: int) -> int { n = n + 1; return n; }` fails with `error: cannot modify parameter 'n'`. Copy the parameter into a local `var` and change that instead, as `SumUpTo` does with `total`.
::
::warning
**Getting the name's case wrong.**:br
Names are case-sensitive: `square(2)` fails with `error: name 'square' is not defined in this scope`, and the compiler suggests `did you mean 'Square'?`.
::
## Try it yourself
1. Write `Cube(value: int) -> int` that returns `value * Square(value)`, and print `Cube(3)`.
2. Write `FahrenheitToCelsius` and check that it turns `212.0` back into `100.0`.
3. Write `Product(last: int) -> int` that multiplies the numbers from 1 to `last`, following the shape of `SumUpTo`.
4. Call `Square(2.5)` and `Add(2)` and read both messages.
## Learn more
- [Functions](https://rux-lang.dev/docs/lang/functions/declaration) and [Function declaration](https://rux-lang.dev/docs/lang/functions/declaration) in the Rux Reference
- [The Main entry point](https://rux-lang.dev/docs/lang/functions/main) — the function every program starts in
- [Return](https://rux-lang.dev/docs/learn/return) — leaving a function early, and functions with no result
# Return
::note
**You'll need**: [Function](https://rux-lang.dev/docs/learn/function), [If](https://rux-lang.dev/docs/learn/if), [For](https://rux-lang.dev/docs/learn/for)
::
`return` does two jobs at once: it hands back the result, and it leaves the function on the spot. Nothing after it runs. That makes it a way to answer **early** — the moment the function knows the answer — instead of carrying a result down to the last line. This lesson also meets functions that have no result at all, the ones you call for what they *do*.
## Answering early
Each `if` here answers one case and leaves. By the time the last line runs, the other cases have already been ruled out, so it needs no condition of its own:
```rux
func Sign(value: int) -> int {
if value > 0 {
return 1;
}
if value < 0 {
return -1;
}
return 0;
}
```
The same style turns a set of rules into a straight list. A leap year is decided by three rules, most specific first, and every rule that settles the answer returns at once — no `else` anywhere:
```rux
func IsLeapYear(year: int) -> bool {
if year % 400 == 0 {
return true;
}
if year % 100 == 0 {
return false;
}
return year % 4 == 0;
}
```
```mermaid
flowchart LR
y["year"] --> a{"divisible
by 400?"}
a -- "yes" --> t["return true"]
a -- "no" --> b{"divisible
by 100?"}
b -- "yes" --> f["return false"]
b -- "no" --> c["return year % 4 == 0"]
```
## Returning from inside a loop
`return` inside a loop leaves the loop **and** the function together. The line after the loop runs only when the loop finished without finding anything:
```rux
func SmallestDivisor(number: int) -> int {
for candidate in 2..number {
if number % candidate == 0 {
return candidate;
}
}
return number;
}
```
| Statement | Leaves | Then runs |
| ---------- | -------------------------- | ----------------------------------- |
| `continue` | the rest of this iteration | the next iteration |
| `break` | the loop | the first line after the loop |
| `return` | the loop and the function | the caller, with the returned value |
## Functions with no result
Some functions are called for their effect, such as printing. They leave off the `->` and the result type entirely. A bare `return;` still leaves early, and reaching the closing brace is the ordinary way out:
```rux
func Countdown(from: int) {
if from < 1 {
PrintLine("nothing to count");
return;
}
for i in 0..from {
Print("{} ", from - i);
}
PrintLine("liftoff");
}
```
A function without a result is called as a statement on its own — `Countdown(5);` — because there is no value to bind or print.
## Every path must return
A function with a result type has to return a value of that type on every way through its body. The compiler checks this: if `Sign` lost its final `return 0;`, a value of exactly zero would reach the closing brace with nothing to give back, and the function is refused.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Functions/Return){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `return` does two things at once: it hands back the result, and it leaves
// the function on the spot. Nothing after it runs. That makes it a way to
// answer early — as soon as the function knows the answer — instead of
// carrying a result down to the last line.
//
// And some functions have no result at all. They are called for what they do,
// such as printing, and leave the `->` and the result type off entirely.
import Io::{ Print, PrintLine };
// Each `if` answers one case and leaves. By the time the last line is reached,
// the earlier cases have been ruled out, so it needs no condition of its own.
func Sign(value: int) -> int {
if value > 0 {
return 1;
}
if value < 0 {
return -1;
}
return 0;
}
// A leap year in three rules, most specific first. Every rule that decides the
// answer returns at once, so no `else` is needed anywhere.
func IsLeapYear(year: int) -> bool {
if year % 400 == 0 {
return true;
}
if year % 100 == 0 {
return false;
}
return year % 4 == 0;
}
// `return` inside a loop leaves the loop and the function together — further
// than `break` goes. The line after the loop runs only if nothing was found.
func SmallestDivisor(number: int) -> int {
for candidate in 2..number {
if number % candidate == 0 {
return candidate;
}
}
return number;
}
// No `->`, so no result. A bare `return;` still leaves early; falling off the
// closing brace is the ordinary way out.
func Countdown(from: int) {
if from < 1 {
PrintLine("nothing to count");
return;
}
for i in 0..from {
Print("{} ", from - i);
}
PrintLine("liftoff");
}
func Main() -> int {
PrintLine("Sign(-4) {} Sign(0) {} Sign(9) {}", Sign(-4), Sign(0), Sign(9));
PrintLine("IsLeapYear(2024) {}", IsLeapYear(2024));
PrintLine("IsLeapYear(1900) {}", IsLeapYear(1900));
PrintLine("IsLeapYear(2000) {}", IsLeapYear(2000));
PrintLine("SmallestDivisor(91) {}", SmallestDivisor(91));
PrintLine("SmallestDivisor(97) {}", SmallestDivisor(97));
// A function without a result is called as a statement on its own.
Countdown(5);
Countdown(0);
// The compiler checks that a function with a result returns one on every
// path. Leave out the final `return 0;` in `Sign` and it says:
//
// error: function 'Sign' must return a value of type 'int' on every
// control-flow path
//
// The opposite mistake, `return 5;` in a function with no `->`, is also
// refused: 'return' cannot have a value in a function with no return type.
return 0;
}
```
## Run it
```sh
cd Examples/Functions/Return
rux run
```
```text
Sign(-4) -1 Sign(0) 0 Sign(9) 1
IsLeapYear(2024) true
IsLeapYear(1900) false
IsLeapYear(2000) true
SmallestDivisor(91) 7
SmallestDivisor(97) 97
5 4 3 2 1 liftoff
nothing to count
```
## Common mistakes
::warning
**A path that never returns.**:br
Leave out the final `return 0;` in `Sign` and the compiler says `error: function 'Sign' must return a value of type 'int' on every control-flow path`. Make sure the last line returns something, even when the `if`s above it cover every case you can think of.
::
::warning
**A value in a function with no result.**:br`return 5;` inside `Countdown` fails with `error: 'return' cannot have a value in a function with no return type`. Either drop the value, or give the function a `->` and a result type.
::
::warning
**Printing a function that has no result.**:br`PrintLine("{}", Countdown(3))` is refused with `has type '()', but variadic parameter 'args' requires 'Display'` — `()` is the empty type of "no result", and there is nothing in it to print.
::
## Try it yourself
1. Write `Max(first: int, second: int) -> int` with one `if` and two `return`s.
2. Write `IsPrime(number: int) -> bool` using `SmallestDivisor`. Remember that numbers below 2 are not prime — answer them first and return early.
3. Delete the final `return 0;` from `Sign` and read the error.
4. Give `Countdown` a second early exit that prints `too many` and returns when `from` is greater than 10.
## Learn more
- [Function declaration](https://rux-lang.dev/docs/lang/functions/declaration) in the Rux Reference
- [Break](https://rux-lang.dev/docs/learn/break) and [Continue](https://rux-lang.dev/docs/learn/continue) — leaving a loop without leaving the function
- [Recursion](https://rux-lang.dev/docs/learn/recursion) — a function whose early return is what stops it
# Recursion
::note
**You'll need**: [Function](https://rux-lang.dev/docs/learn/function), [Return](https://rux-lang.dev/docs/learn/return)
::
A function may call itself. That is *recursion*, and it suits any problem that contains a smaller copy of itself: 5! is 5 × 4!, and 4! is 4 × 3!. Instead of describing the steps of a loop, a recursive function describes how a big answer is built from a smaller one — and trusts the smaller call to do the rest.
## Base case and recursive case
Every recursive function has two parts:
- The **base case** is small enough to answer directly. It stops the descent.
- The **recursive case** calls the same function on a smaller problem and builds its answer from the result.
The [For](https://rux-lang.dev/docs/learn/for) lesson built a factorial with a loop. Here is the same definition written recursively — `n <= 1` is the base case, and every other `n` is answered in terms of `n - 1`:
```rux
func Factorial(n: int) -> int {
if n <= 1 {
return 1;
}
return n * Factorial(n - 1);
}
```
`Factorial(4)` cannot finish until `Factorial(3)` has, which waits on `Factorial(2)`, and so on down to the base case. Then the answers travel back up, each call multiplying in its own `n`:
```mermaid
flowchart LR
f4["Factorial(4)
4 × Factorial(3)"] --> f3["Factorial(3)
3 × Factorial(2)"]
f3 --> f2["Factorial(2)
2 × Factorial(1)"]
f2 --> f1["Factorial(1)
base case"]
f1 -. "returns 1" .-> f2
f2 -. "returns 2" .-> f3
f3 -. "returns 6" .-> f4
f4 -. "returns 24" .-> done["the caller"]
```
## Shrinking the problem
The recursive case does not have to subtract one. Euclid's greatest common divisor swaps in a smaller pair on every call:
```rux
func Gcd(first: int, second: int) -> int {
if second == 0 {
return first;
}
return Gcd(second, first % second);
}
```
| Call | `first % second` | Next call |
| ------------- | ---------------- | ------------- |
| `Gcd(48, 18)` | 12 | `Gcd(18, 12)` |
| `Gcd(18, 12)` | 6 | `Gcd(12, 6)` |
| `Gcd(12, 6)` | 0 | `Gcd(6, 0)` |
| `Gcd(6, 0)` | — | base case: 6 |
The remainder is always smaller than `second`, so the pair keeps shrinking and `second == 0` is always reached.
## Each call has its own variables
Every call gets its own `n`, kept safe while the deeper calls run. That is visible when a function does work both before and after its recursive call:
```rux
func Descend(n: int) {
if n == 0 {
Print("| ");
return;
}
Print("{} ", n);
Descend(n - 1);
Print("{} ", n);
}
```
The first `Print` happens on the way down: 4, 3, 2, 1. The second waits until every deeper call has returned, so it happens on the way back up — in reverse order, 1, 2, 3, 4. `Descend(4)` prints `4 3 2 1 | 1 2 3 4`.
## The base case must be reached
Every recursive call must move closer to the base case. `Factorial(-3)` is safe only because the test is `n <= 1`; with `n == 1`, the calls would count down past zero and never stop. Each waiting call takes up a little memory on the *stack*, and when the stack is full the program crashes. The compiler cannot catch this for you — it is the one thing to check every time you write a recursive function.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Functions/Recursion){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A function may call itself. That is recursion, and it suits any problem that
// contains a smaller copy of itself: 5! is 5 times 4!, and 4! is 4 times 3!.
//
// Every recursive function has two parts. The *base case* is small enough to
// answer directly and stops the descent. The *recursive case* calls the same
// function on a smaller problem and builds its answer from the result.
import Io::{ Print, PrintLine };
// The For lesson built a factorial with a loop. This is the same definition
// written recursively: `n <= 1` is the base case, and every other `n` is
// answered in terms of `n - 1`.
func Factorial(n: int) -> int {
if n <= 1 {
return 1;
}
return n * Factorial(n - 1);
}
// Euclid's greatest common divisor. Each call swaps in a smaller pair, and the
// pair always shrinks, so the base case `second == 0` is always reached.
func Gcd(first: int, second: int) -> int {
if second == 0 {
return first;
}
return Gcd(second, first % second);
}
// Each call has its own `n`, kept while the deeper calls run. The first
// `Print` happens on the way down; the second waits until every deeper call
// has returned, so it happens on the way back up, in reverse order.
func Descend(n: int) {
if n == 0 {
Print("| ");
return;
}
Print("{} ", n);
Descend(n - 1);
Print("{} ", n);
}
func Main() -> int {
for i in 1..=6 {
PrintLine("Factorial({}) = {}", i, Factorial(i));
}
PrintLine("Gcd(48, 18) = {}", Gcd(48, 18));
PrintLine("Gcd(17, 5) = {}", Gcd(17, 5));
Descend(4);
PrintLine();
// Watch out for a missing or unreachable base case. `Factorial(-3)` is
// safe only because the test is `n <= 1` rather than `n == 1`; with `==`
// the calls would count down past zero forever, until the program runs
// out of stack space and crashes. Every recursive call must move closer
// to the base case.
return 0;
}
```
## Run it
```sh
cd Examples/Functions/Recursion
rux run
```
```text
Factorial(1) = 1
Factorial(2) = 2
Factorial(3) = 6
Factorial(4) = 24
Factorial(5) = 120
Factorial(6) = 720
Gcd(48, 18) = 6
Gcd(17, 5) = 1
4 3 2 1 | 1 2 3 4
```
## Common mistakes
::warning
**A base case that can be skipped.**:br
A test like `n == 1` is missed by any argument that starts below 1, and the recursion runs until the program crashes. Prefer a test that covers everything at or below the bottom, such as `n <= 1`.
::
::warning
**A recursive call that does not shrink the problem.**:br`return n * Factorial(n);` calls itself with the same argument forever. Each call must pass something strictly closer to the base case.
::
::warning
**Calling yourself but forgetting `return`.**:br
Writing `Gcd(second, first % second);` as a bare statement throws the answer away, and the compiler reports `error: function 'Gcd' must return a value of type 'int' on every control-flow path`. The recursive case has to return what the deeper call gives back.
::
## Try it yourself
1. Write a recursive `Power(base: int, exponent: int) -> int`. What is the base case when `exponent` is 0?
2. Write `SumDigits(number: int) -> int`, so that `SumDigits(1234)` is 10. Use `number % 10` for the last digit and `number / 10` for the rest.
3. Write `Fibonacci(n: int) -> int`, where the first two values are 0 and 1, and print the first ten.
4. Change `n <= 1` to `n == 1` in `Factorial`, call `Factorial(-3)`, and watch what happens.
## Learn more
- [Functions](https://rux-lang.dev/docs/lang/functions/declaration) in the Rux Reference
- [For](https://rux-lang.dev/docs/learn/for) — the loop version of factorial
- [Return](https://rux-lang.dev/docs/learn/return) — the early return that every base case relies on
# Overload
::note
**You'll need**: [Function](https://rux-lang.dev/docs/learn/function), [Return](https://rux-lang.dev/docs/learn/return), [Character](https://rux-lang.dev/docs/learn/character)
::
Several functions may share one name, as long as their parameters differ. At each call the compiler looks at the arguments and picks the version that fits, so the caller writes one name and gets the right behaviour for whatever it passes. A set of functions sharing a name is an *overload set*.
You have been using one since the first lesson. `PrintLine` is not a single function but over twenty: one for `int`, one for `float64`, one for `bool`, one for a single character, one taking nothing at all, one taking a format string and values, and more. Writing `PrintLine(x)` has been choosing among them all along.
## Told apart by type
Four functions, one name, each taking a different type:
```rux
func Describe(value: int) {
PrintLine("an integer: {}", value);
}
func Describe(value: float64) {
PrintLine("a number with a fraction: {}", value);
}
```
…and two more for `bool` and `char`. Nothing at the call says which one to run — each argument's type decides, which is why the same spelling produces four different messages:
```rux
Describe(42);
Describe(2.5);
Describe(true);
Describe('R');
```
## Told apart by count
Overloads may also differ in how many parameters they take:
```rux
func Area(side: int) -> int {
return side * side;
}
func Area(width: int, height: int) -> int {
return width * height;
}
func Area(width: float64, height: float64) -> float64 {
return width * height;
}
```
`Area(4)` can only mean the square, `Area(4, 5)` the integer rectangle and `Area(1.5, 2.0)` the floating-point one. No call can match more than one of them, so the set is unambiguous.
## How a call is resolved
```mermaid
flowchart LR
call["Area(4, 5)"] --> all["Every function
named Area"]
all --> fit{"Which take this many
arguments, of these types?"}
fit -- "exactly one" --> run["That one is called"]
fit -- "none" --> err["error: no matching overload
for 'Area' with argument types …"]
```
The argument types have to fit the parameters as they are. `Area(4, 2.0)` matches nothing: `4` is an `int` and `2.0` a `float64`, and there is no `Area(int, float64)`. Write `Area(4.0, 2.0)` to reach the floating-point version.
## Never by the result
Overloading is decided by the parameters, never by the result type. A call does not say what type it wants back, so two functions that differ only in what they return could not be told apart — and the second is refused:
```rux
func Half(value: int) -> int { ... }
func Half(value: int) -> float64 { ... }
```
When the difference is what comes back, give the functions separate names, such as `Half` and `HalfExact`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Functions/Overload){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Several functions may share one name as long as their parameters differ. The
// compiler picks one by the arguments at each call, so the caller writes one
// name and gets the version that fits.
//
// This is not new: `PrintLine` has been an overload set since the first lesson.
// There is one for `int`, one for `float64`, one for `bool`, one for a single
// character, one taking nothing at all, one taking a format string and values,
// and more — over twenty in total. Writing `PrintLine(x)` has been choosing
// among them all along.
import Io::PrintLine;
// Four functions, one name, told apart by the type of their argument.
func Describe(value: int) {
PrintLine("an integer: {}", value);
}
func Describe(value: float64) {
PrintLine("a number with a fraction: {}", value);
}
func Describe(value: bool) {
PrintLine("a truth value: {}", value);
}
func Describe(value: char) {
PrintLine("a character: {}", value);
}
// Overloads may also differ in how many parameters they take. These are
// unambiguous because no call can match more than one.
func Area(side: int) -> int {
return side * side;
}
func Area(width: int, height: int) -> int {
return width * height;
}
func Area(width: float64, height: float64) -> float64 {
return width * height;
}
func Main() -> int {
// Nothing here says which `Describe` to run. Each argument's type decides,
// which is why the same call spelling produces four different messages.
Describe(42);
Describe(2.5);
Describe(true);
Describe('R');
// The count of arguments chooses just as well as their types.
PrintLine("square {}", Area(4));
PrintLine("rectangle {}", Area(4, 5));
PrintLine("float rectangle {}", Area(1.5, 2.0));
// Overloading is resolved by the parameters, never by the result. Two
// functions differing only in what they return could not be told apart
// at a call, so the second one is refused:
//
// func Half(value: int) -> int { ... }
// func Half(value: int) -> float64 { ... }
// error: function 'Half' has the same parameter signature as an
// earlier overload
//
// Give them separate names when the difference is what comes back.
return 0;
}
```
## Run it
```sh
cd Examples/Functions/Overload
rux run
```
```text
an integer: 42
a number with a fraction: 2.5
a truth value: true
a character: R
square 16
rectangle 20
float rectangle 3.0
```
## Common mistakes
::warning
**Overloads that differ only in their result.**:br
The second `Half` above fails with `error: function 'Half' has the same parameter signature as an earlier overload`. Change the parameters, or use two names.
::
::warning
**Mixing an integer and a float in one call.**:br`Area(4, 2.0)` fails with `error: no matching overload for 'Area' with argument types (int, float64)`. A literal is not converted to suit an overload — write `4.0`, or add the overload you actually need.
::
## Try it yourself
1. Add `Describe(value: char8[..])` that prints `some text: …`, and call `Describe("hello")`.
2. Add a fourth `Area` that takes one `float64` radius and returns the area of a circle (`3.14159 * radius * radius`). Does `Area(2.0)` reach it, and does `Area(2)` still reach the square?
3. Call `Area(4, 2.0)` and read the error, then fix the call.
4. Write the two `Half` functions from this lesson and see the second one refused.
## Learn more
- [Function declaration](https://rux-lang.dev/docs/lang/functions/declaration) in the Rux Reference
- [Console](https://rux-lang.dev/docs/learn/console) — the `PrintLine` overloads you have been calling
- [Generic](https://rux-lang.dev/docs/learn/generic) — one function for many types, instead of one overload per type
# Default argument
::note
**You'll need**: [Function](https://rux-lang.dev/docs/learn/function), [Return](https://rux-lang.dev/docs/learn/return), [Character](https://rux-lang.dev/docs/learn/character)
::
Some arguments are nearly always the same. Squaring is the usual power; 0 to 100 is the usual range. A *default value* lets the common call leave that argument out, while the unusual call can still say exactly what it wants. You write it after the parameter's type: `name: type = value`.
## One optional parameter
`exponent` has a default, so a call may stop after `base`:
```rux
func Power(base: int, exponent: int = 2) -> int {
var result = 1;
for i in 0..exponent {
result *= base;
}
return result;
}
```
`Power(5)` means exactly `Power(5, 2)` — the compiler fills in the missing argument at the call. `Power(2, 10)` passes its own exponent, and the default is not used.
## Arguments fill from the left
Several parameters may have defaults:
```rux
func Clamp(value: int, low: int = 0, high: int = 100) -> int {
```
Arguments still fill the parameters strictly from the left. A call can stop early, but it cannot skip a parameter in the middle:
| Call | `value` | `low` | `high` | Result |
| -------------------- | ------- | ----- | ------ | ------ |
| `Clamp(150)` | 150 | 0 | 100 | 100 |
| `Clamp(-5)` | −5 | 0 | 100 | 0 |
| `Clamp(42, 50)` | 42 | 50 | 100 | 50 |
| `Clamp(150, 0, 255)` | 150 | 0 | 255 | 150 |
The last row is the price of that rule: to change `high`, the call must also give `low`, even when it wants the default there. There is no way to name an argument and skip the ones before it.
## Every parameter optional
A default may be any value of the parameter's type — a character as easily as a number. When every parameter has one, a call may pass nothing at all:
```rux
func Rule(width: int = 12, mark: char = '-') {
for i in 0..width {
Print(mark);
}
PrintLine();
}
```
`Rule()` prints twelve dashes, `Rule(5)` five, and `Rule(5, '=')` five equals signs.
## Defaults come last
A parameter without a default may not follow one with a default. In `Mix(first: int = 1, second: int)` there would be no way to supply `second` while leaving `first` out — the first argument always lands in `first` — so the declaration itself is refused. Put the parameters callers usually change first, and the ones they rarely touch at the end.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Functions/DefaultArgument){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A parameter can carry a default value, written `name: type = value`. A call
// may then leave that argument out, and the default is used in its place.
//
// Defaults suit the argument that is nearly always the same: squaring is the
// usual power, 0 to 100 the usual range. The common call stays short, and the
// unusual one can still say exactly what it wants.
import Io::{ Print, PrintLine };
// `exponent` is optional. `Power(5)` means `Power(5, 2)`.
func Power(base: int, exponent: int = 2) -> int {
var result = 1;
for i in 0..exponent {
result *= base;
}
return result;
}
// Several parameters may have defaults. Arguments still fill the parameters
// from the left, so a call can stop early but cannot skip one in the middle.
func Clamp(value: int, low: int = 0, high: int = 100) -> int {
if value < low {
return low;
}
if value > high {
return high;
}
return value;
}
// A default may be any value of the parameter's type, a character as easily as
// a number. Every parameter here has one, so a call may pass nothing at all.
func Rule(width: int = 12, mark: char = '-') {
for i in 0..width {
Print(mark);
}
PrintLine();
}
func Main() -> int {
PrintLine("Power(5) {}", Power(5));
PrintLine("Power(2, 10) {}", Power(2, 10));
PrintLine("Clamp(150) {}", Clamp(150));
PrintLine("Clamp(-5) {}", Clamp(-5));
PrintLine("Clamp(42, 50) {}", Clamp(42, 50));
// To change `high` the call must also give `low`, even when it wants the
// default there. There is no way to name an argument and skip the rest.
PrintLine("Clamp(150, 0, 255) {}", Clamp(150, 0, 255));
Rule();
Rule(5);
Rule(5, '=');
// A parameter without a default may not come after one with a default,
// because there would be no way to reach it while leaving the earlier one
// out:
//
// func Mix(first: int = 1, second: int) -> int { ... }
// error: parameter 'second' without a default value cannot follow
// a parameter with a default value
return 0;
}
```
## Run it
```sh
cd Examples/Functions/DefaultArgument
rux run
```
```text
Power(5) 25
Power(2, 10) 1024
Clamp(150) 100
Clamp(-5) 0
Clamp(42, 50) 50
Clamp(150, 0, 255) 150
------------
-----
=====
```
## Common mistakes
::warning
**A required parameter after an optional one.**:br`func Mix(first: int = 1, second: int)` fails with `error: parameter 'second' without a default value cannot follow a parameter with a default value`. Move the parameters with defaults to the end.
::
::warning
**Naming an argument to skip ahead.**:br
Rux has no named arguments, so `Clamp(150, high: 255)` is a syntax error: `expected ',' between arguments before ':'`. Pass every argument up to the one you want to change.
::
::warning
**A default of the wrong type.**:br`exponent: int = 2.5` fails with `error: default value type 'float64' does not match parameter type 'int'`. The default is checked against the parameter's type like any other value.
::
## Try it yourself
1. Write `Greet(name: char8[..] = "world")` that prints `Hello, …!`, and call it with and without a name.
2. Predict what `Clamp(150, 255)` returns, then run it. Which parameter did `255` land in?
3. Swap the parameters of `Rule` so that `mark` comes first and only `width` has a default. Which calls in `Main` still compile?
4. Write the `Mix` function from this lesson and read the error.
## Learn more
- [Default arguments](https://rux-lang.dev/docs/lang/functions/declaration) in the Rux Reference
- [Overload](https://rux-lang.dev/docs/learn/overload) — the other way to let one name take different numbers of arguments
- [Variadic](https://rux-lang.dev/docs/learn/variadic) — a function that takes any number of arguments
# Generic
::note
**You'll need**: [Overload](https://rux-lang.dev/docs/learn/overload), [Convert](https://rux-lang.dev/docs/learn/convert)
::
The [Overload](https://rux-lang.dev/docs/learn/overload) lesson wrote one function per type: an `Area` for `int` and another for `float64`, with the same body. That works, but every new type means another copy. A *generic* function writes the body once, with a placeholder where the type goes.
## A type parameter
A name in angle brackets after the function name is a **type parameter**. It stands for a type the caller supplies:
```rux
func Larger(first: T, second: T) -> T {
if first > second {
return first;
}
return second;
}
```
Read it as "for some type `T`, take two `T`s and give back a `T`". Both arguments must be the same type, and the result is that type too — two `int` values give back an `int`, two `float64` values a `float64`.
Nothing is converted at run time. The compiler produces a separate version of the function for each type that is actually used, as if you had written the overloads by hand:
```mermaid
flowchart LR
g["Larger, generic over T"] --> i["Larger for int
Larger(3, 9)"]
g --> f["Larger for float64
Larger(2.5, 1.5)"]
g --> c["Larger for char
Larger('a', 'z')"]
```
A body that does nothing with its values except pass them along accepts any type at all — even text:
```rux
func Pick(useFirst: bool, first: T, second: T) -> T {
```
## Inferred or explicit
Usually `T` is inferred from the arguments, so a call looks like any other:
```rux
PrintLine("Larger(3, 9) {}", Larger(3, 9));
```
You can also give `T` yourself, in angle brackets at the call. That matters when the arguments alone would choose a different type. Bare literals become `int`, but with `` they become `uint8` — and so does the result, which is why adding 100 wraps around past 255 to 44:
```rux
PrintLine("Larger(200, 7) + 100 {}", Larger(200, 7) + 100);
PrintLine("Larger(200, 7) + 100 {}", Larger(200, 7) + 100);
```
## What T is allowed to do
`Larger` uses `>` on a `T` it knows nothing about. That is checked once `T` is known, for each version the program asks for. Numbers, characters and booleans all have `>`, so those versions compile. A string does not, so `Larger("abc", "abd")` is rejected — with a note naming the call that asked for that version.
The same reasoning stops a generic function from printing its value. `PrintLine("{}", first)` inside `Larger` is refused, because `{}` needs a type known to be printable and an unconstrained `T` promises nothing. That is why each function here returns its value for `Main` to print. A *bound* such as `` is how `T` makes that promise; it is taught in [Generic bound](https://rux-lang.dev/docs/learn/generic-bound).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Functions/Generic){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The Overload lesson wrote one function per type: an `Area` for `int` and
// another for `float64`, with the same body. A generic function writes that
// body once.
//
// A name in angle brackets after the function name is a type parameter. It
// stands for a type the caller supplies, and the compiler produces a separate
// version of the function for each type actually used.
import Io::PrintLine;
// Both arguments must be the same type `T`, and the result is that type too,
// so two `int` values give back an `int` and two `float64` values a `float64`.
func Larger(first: T, second: T) -> T {
if first > second {
return first;
}
return second;
}
// Nothing in this body depends on what `T` can do, so it accepts any type.
func Pick(useFirst: bool, first: T, second: T) -> T {
if useFirst {
return first;
}
return second;
}
func Main() -> int {
// Usually `T` is inferred from the arguments. Four types and no conversion
// anywhere: each type used compiles its own version of the function.
PrintLine("Larger(3, 9) {}", Larger(3, 9));
PrintLine("Larger(2.5, 1.5) {}", Larger(2.5, 1.5));
PrintLine("Larger('a', 'z') {}", Larger('a', 'z'));
PrintLine("Pick(true, \"yes\", \"no\") {}", Pick(true, "yes", "no"));
// `T` can also be given explicitly in angle brackets at the call. That
// matters when the arguments alone would choose a different type. Bare
// literals become `int`, but with `` they become `uint8`, and so
// does the result — which is why adding 100 wraps around here.
PrintLine("Larger(200, 7) + 100 {}", Larger(200, 7) + 100);
PrintLine("Larger(200, 7) + 100 {}", Larger(200, 7) + 100);
// `Larger` uses `>` on a `T` it knows nothing about. That is checked once
// `T` is known: calling `Larger` with a type that has no `>` is rejected
// when that version of `Larger` is compiled.
//
// Watch out: a function with an unconstrained `T` cannot print its value.
// `PrintLine("{}", first)` inside `Larger` is refused, because `{}` needs
// a type known to be printable and `T` promises nothing. That is why each
// function here returns its value for `Main` to print. A bound such as
// ``, taught in the Generics part, is how `T` makes that promise.
return 0;
}
```
## Run it
```sh
cd Examples/Functions/Generic
rux run
```
```text
Larger(3, 9) 9
Larger(2.5, 1.5) 2.5
Larger('a', 'z') z
Pick(true, "yes", "no") yes
Larger(200, 7) + 100 300
Larger(200, 7) + 100 44
```
## Common mistakes
::warning
**Arguments that disagree about T.**:br`Larger(3, 2.5)` is refused with `error: argument 1 to 'Larger' has type 'int', but parameter 'first' requires 'T'` — both parameters are the same `T`, and an `int` and a `float64` cannot both be it. Write `Larger(3.0, 2.5)`.
::
::warning
**A type that cannot do what the body needs.**:br`Larger("abc", "abd")` fails with `error: operator '>' is not defined for slice type 'char8[..]'`, followed by `note: in 'Larger' instantiated with T = char8[..]`. The error points into the generic body; the note tells you which call caused it.
::
::warning
**Printing a T.**:br`PrintLine("{}", value)` inside a generic function fails with `has type 'T', but variadic parameter 'args' requires 'Display'`. Return the value and print it where its type is known, or add a bound later in the course.
::
## Try it yourself
1. Write `Smaller` and call it with integers, floats and characters.
2. Write `Clamp(value: T, low: T, high: T) -> T` and call it with `int` and `float64` values.
3. Try `Larger(300, 7)`. Why is `300` refused?
4. Call `Larger("abc", "abd")` and read both the error and its note.
## Learn more
- [Generic functions](https://rux-lang.dev/docs/lang/generics/overview) in the Rux Reference
- [Overload](https://rux-lang.dev/docs/learn/overload) — the hand-written alternative
- [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) — promising what `T` can do, such as being printable
- [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) — why `uint8` 200 + 100 is 44
# Callback
::note
**You'll need**: [Function](https://rux-lang.dev/docs/learn/function), [Return](https://rux-lang.dev/docs/learn/return), [For](https://rux-lang.dev/docs/learn/for)
::
So far every function has been called by name. Functions are also **values**: one can be stored in a binding, passed to another function, and called from there. A function handed over to be called later is a *callback*.
That lets one function be given the part that varies. `ShowTable` below knows how to print a row of results but not what to compute — each caller tells it, by passing a function.
## The type of a function
A function's type is its shape: what it takes and what it gives back. It is written like a declaration with the name and parameter names taken out:
| Function | Its type |
| ------------------------------------ | ---------------------- |
| `func Double(value: int32) -> int32` | `func(int32) -> int32` |
| `func Square(value: int32) -> int32` | `func(int32) -> int32` |
| `func IsEven(value: int32) -> bool` | `func(int32) -> bool` |
`Double` and `Square` have different names and different bodies but the same shape, so either fits wherever a `func(int32) -> int32` is wanted.
## Passing a function
A parameter can have a function type. `ShowTable` accepts any function with the shape `func(int32) -> int32` and calls it once per number:
```rux
func ShowTable(last: int32, operation: func(int32) -> int32) {
for i in 1..=last {
Print("{} ", operation(i));
}
PrintLine();
}
```
At the call, the function is named but **not** called — no parentheses after it:
```rux
ShowTable(5, Double);
ShowTable(5, Square);
```
```mermaid
flowchart LR
main["Main
ShowTable(5, Double)"] --> st["ShowTable
operation = Double"]
st -- "operation(1) … operation(5)" --> d["Double"]
d -- "2, 4, 6, 8, 10" --> st
```
A callback can be called more than once, or have its result fed back into itself:
```rux
func ApplyTwice(operation: func(int32) -> int32, value: int32) -> int32 {
return operation(operation(value));
}
```
And a callback need not compute a number. `CountWhere` takes a *test* — a function returning `bool` — so it can answer any question that can be written as one:
```rux
func CountWhere(last: int32, test: func(int32) -> bool) -> int32 {
```
## A function in a binding
A function value can be held in a binding, with its type written out in full. A `var` binding can be pointed at another function later; either way it is called exactly like the function it holds:
```rux
var chosen: func(int32) -> int32 = Double;
PrintLine("chosen(21) {}", chosen(21));
chosen = Square;
PrintLine("chosen(21) {}", chosen(21));
```
The same call, `chosen(21)`, gives 42 the first time and 441 the second.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Functions/Callback){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Functions have been called by name all along. They are also values: one can
// be stored in a binding, passed to another function, and called from there.
// A function handed over to be called later is a *callback*.
//
// That lets one function be given the part that varies. `ShowTable` below
// knows how to print a row of results but not what to compute; each caller
// tells it, by passing a function.
import Io::{ Print, PrintLine };
func Double(value: int32) -> int32 {
return value * 2;
}
func Square(value: int32) -> int32 {
return value * value;
}
func IsEven(value: int32) -> bool {
return value % 2 == 0;
}
// The parameter's type is the shape of the function: what it takes and what it
// gives back. `func(int32) -> int32` accepts any function with exactly that
// shape, whatever its name.
func ShowTable(last: int32, operation: func(int32) -> int32) {
for i in 1..=last {
Print("{} ", operation(i));
}
PrintLine();
}
func ApplyTwice(operation: func(int32) -> int32, value: int32) -> int32 {
return operation(operation(value));
}
// A function taking a test rather than a value can answer any question that
// can be written as one.
func CountWhere(last: int32, test: func(int32) -> bool) -> int32 {
var found: int32 = 0;
for i in 1..=last {
if test(i) {
found += 1;
}
}
return found;
}
func Main() -> int {
// `Double` is passed, not called — no parentheses after it. `ShowTable`
// does the calling, once per number.
ShowTable(5, Double);
ShowTable(5, Square);
PrintLine("ApplyTwice(Double, 3) {}", ApplyTwice(Double, 3));
PrintLine("ApplyTwice(Square, 3) {}", ApplyTwice(Square, 3));
PrintLine("CountWhere(10, IsEven) {}", CountWhere(10, IsEven));
// A function value can be held in a binding, with its type written out in
// full. A `var` binding can be pointed at another function later; either
// way it is called exactly like the function it holds.
var chosen: func(int32) -> int32 = Double;
PrintLine("chosen(21) {}", chosen(21));
chosen = Square;
PrintLine("chosen(21) {}", chosen(21));
// Watch out: the shape must match exactly. `ShowTable(5, IsEven)` is
// refused, because `IsEven` returns `bool`:
//
// error: argument 2 to 'ShowTable' has type 'func(int32) -> bool8',
// but parameter 'operation' requires 'func(int32) -> int32'
//
// Writing `ShowTable(5, Double(3))` is the other common slip: that calls
// `Double` and passes its `int32` result, not the function.
return 0;
}
```
## Run it
```sh
cd Examples/Functions/Callback
rux run
```
```text
2 4 6 8 10
1 4 9 16 25
ApplyTwice(Double, 3) 12
ApplyTwice(Square, 3) 81
CountWhere(10, IsEven) 5
chosen(21) 42
chosen(21) 441
```
## Common mistakes
::warning
**A function of the wrong shape.**:br`ShowTable(5, IsEven)` fails with `error: argument 2 to 'ShowTable' has type 'func(int32) -> bool8', but parameter 'operation' requires 'func(int32) -> int32'`. The shape must match exactly — `bool8` is the full name of `bool`. The parameter types count too: a `func(int) -> int` does not fit a `func(int32) -> int32` parameter.
::
::warning
**Calling the function instead of passing it.**:br`ShowTable(5, Double(3))` calls `Double` straight away and passes its result, so it fails with `has type 'int32', but parameter 'operation' requires 'func(int32) -> int32'`. Pass the name alone: `ShowTable(5, Double)`.
::
## Try it yourself
1. Write `Triple` and pass it to `ShowTable` and to `ApplyTwice`.
2. Write `IsMultipleOfThree` and count the multiples of three up to 30 with `CountWhere`.
3. Write `ApplyTimes(operation: func(int32) -> int32, value: int32, times: int32) -> int32` that applies `operation` `times` times in a loop.
4. Call `ShowTable(5, IsEven)` and read the error.
## Learn more
- [Function type aliases](https://rux-lang.dev/docs/lang/functions/function-types) in the Rux Reference
- [Generic](https://rux-lang.dev/docs/learn/generic) — the other way to give one function a part that varies
- [Function field](https://rux-lang.dev/docs/learn/function-field) — storing a function inside a struct
# Part 5: Sequences
So far every variable has held one value. This part is about holding many: a fixed row of values of one type in an **array**, a view into part of one in a **slice**, and a small group of values of different types in a **tuple**. Along the way you will write functions that accept arrays of any length, change the caller's elements through a view, take any number of arguments, and hand back more than one result.
## What you will learn
- Arrays: a fixed count of one element type, stored inline, indexed from zero, copied whole.
- Filling an array with one repeated value, `[0; 16]`, and nesting arrays into a grid.
- Slices: read-only views, `int32[..]`, that serve arrays of every length without copying.
- Writable views, `var int32[..]`, and why a view's writability is separate from its binding.
- Variadic functions that collect any number of arguments into a slice, and spreading with `...`.
- Tuples for grouping values of different types, and destructuring them into names.
## Which sequence?
```mermaid
flowchart LR
q{"What are you
holding?"} -- "a fixed number of
values of one type" --> a["Array
int32[4]"]
a -- "rows of rows" --> n["Nested array
int32[4][3]"]
a -- "part of it, or any length,
without copying" --> s["Slice
int32[..]"]
s -- "and change it" --> w["Writable slice
var int32[..]"]
s -- "as arguments" --> v["Variadic
args: int32..."]
q -- "a few values of
different types" --> t["Tuple
(int32, bool)"]
t -- "named in one step" --> d["Destructure
let (x, y) = …"]
```
## Lessons
| | Lesson | What you will learn |
| --- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| 5.1 | [Array](https://rux-lang.dev/docs/learn/array) | an inline array holding a fixed number of values of one type |
| 5.2 | [Array repeat](https://rux-lang.dev/docs/learn/array-repeat) | fill an array with one repeated value: `[0; 16]` |
| 5.3 | [Array nested](https://rux-lang.dev/docs/learn/array-nested) | arrays of arrays: a grid with rows and columns |
| 5.4 | [Slice](https://rux-lang.dev/docs/learn/slice) | view part of an array without copying it, and pass it to a function |
| 5.5 | [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) | change an array through a `var T[..]` view |
| 5.6 | [Variadic](https://rux-lang.dev/docs/learn/variadic) | accept any number of arguments, the way `PrintLine` does |
| 5.7 | [Tuple](https://rux-lang.dev/docs/learn/tuple) | group a few values without declaring a type, and return more than one result |
| 5.8 | [Destructure](https://rux-lang.dev/docs/learn/destructure) | unpack a tuple into separate names with `let (x, y) = ...` |
## Before you start
Finish [Part 3: Control flow](https://rux-lang.dev/docs/learn/control-flow) and [Part 4: Functions](https://rux-lang.dev/docs/learn/functions) first — every lesson here walks a sequence with `for` and passes it to functions of its own. [Convert](https://rux-lang.dev/docs/learn/convert) from Part 1 matters too, because lengths and indices are `uint`. Each lesson's package is in the Examples repository's `Sequences/` folder:
```sh
cd Examples/Sequences/Array
rux run
```
## After this part
[Part 6: Types](https://rux-lang.dev/docs/learn/types) declares types of your own — structs, enums and variants — for the groups of values that deserve names rather than `.0` and `.1`. You are also ready for the checkpoint project [Prime](https://rux-lang.dev/docs/learn/prime).
For the full rules behind this part, see [Arrays](https://rux-lang.dev/docs/lang/arrays/overview), [Slices](https://rux-lang.dev/docs/lang/slices/overview), [Ranges](https://rux-lang.dev/docs/lang/ranges/overview), [Variadic functions](https://rux-lang.dev/docs/lang/functions/parameters#variadic-parameters) and [Tuples](https://rux-lang.dev/docs/lang/tuples/overview) in the Rux Reference.
# Array
::note
**You'll need**: [Mutable](https://rux-lang.dev/docs/learn/mutable), [For](https://rux-lang.dev/docs/learn/for)
::
An *array* holds a fixed number of values of one type, side by side. Four primes, twelve monthly totals, the eight squares of a chess row — whenever you have several values of the same kind and know how many, an array keeps them under one name.
## The type says how many
`int32[4]` means "four `int32` values": the element type, then the count in square brackets. An array literal lists the elements, also in square brackets:
```rux
let primes: int32[4] = [2, 3, 5, 7];
```
The count is part of the type. An `int32[4]` and an `int32[5]` are different types, and an array never grows or shrinks. The elements live **inline**, right inside the variable — exactly like the bytes of a single integer do. Nothing is allocated, and nothing has to be freed.
## Indexing from zero
Elements are numbered from zero, so the last one sits at `length - 1`:
| Index | 0 | 1 | 2 | 3 |
| ------------- | - | - | - | - |
| `primes[...]` | 2 | 3 | 5 | 7 |
```rux
PrintLine("first {}, last {}", primes[0], primes[primes.length - 1]);
PrintLine("length {}", primes.length);
```
## Walking an array
`for` walks an array element by element, just as it walked a range:
```rux
var sum: int32 = 0;
for prime in primes {
sum += prime;
}
```
When the position matters, walk the indices instead. `..` stops before its upper bound, which is exactly the set of valid indices:
```rux
for i in 0..primes.length {
PrintLine("primes[{}] = {}", i, primes[i]);
}
```
## Changing and copying
`let` freezes the elements too. To change one, the array must be `var`:
```rux
var scores: int32[3] = [10, 20, 30];
scores[1] = 25;
```
Assigning an array to another variable copies **all** of its elements. The two variables share nothing, so changing the copy leaves the original alone:
```rux
var copy = scores;
copy[0] = 99;
```
```mermaid
flowchart LR
s["scores
10 · 25 · 30"] -- "var copy = scores;" --> c["copy
10 · 25 · 30"]
c -- "copy[0] = 99;" --> c2["copy
99 · 25 · 30"]
s -. "unchanged" .-> s2["scores
10 · 25 · 30"]
```
Two arrays of the same type are equal when every element is, so `scores == copy` is `false` once one element differs.
## Every index is checked
An index must be below `length`. A constant index past the end does not compile. An index computed while the program runs is checked as it runs, and one past the end stops the program with `Panic: index out of range` — it never reads the memory beyond the array.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/Array){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An array holds a fixed number of values of one type, side by side. `int32[4]` is "four int32
// values": the element type, then the count in square brackets. The count is part of the type,
// so an `int32[4]` and an `int32[5]` are different types, and an array never grows or shrinks.
//
// The elements live inline, right inside the variable, exactly like the bytes of a single
// integer do. Nothing is allocated, and copying an array copies every element.
import Io::PrintLine;
func Main() -> int {
// An array literal lists the elements in square brackets.
let primes: int32[4] = [2, 3, 5, 7];
// Elements are numbered from zero, so the last one sits at `length - 1`.
PrintLine("first {}, last {}", primes[0], primes[primes.length - 1]);
PrintLine("length {}", primes.length);
// `for` walks an array element by element, just as it walked a range.
var sum: int32 = 0;
for prime in primes {
sum += prime;
}
PrintLine("sum {}", sum);
// When the position matters, walk the indices instead. `..` stops before its upper bound,
// which is exactly the set of valid indices.
for i in 0..primes.length {
PrintLine("primes[{}] = {}", i, primes[i]);
}
// `let` freezes the elements too. To change one, the array must be `var`.
var scores: int32[3] = [10, 20, 30];
scores[1] = 25;
// Assigning an array copies all of its elements. Changing the copy leaves the original
// alone, because the two variables share nothing.
var copy = scores;
copy[0] = 99;
PrintLine("scores {} {} {}", scores[0], scores[1], scores[2]);
PrintLine("copy {} {} {}", copy[0], copy[1], copy[2]);
// Two arrays of the same type are equal when every element is.
PrintLine("scores == copy {}", scores == copy);
// Every index must be below `length`, and it is checked. A constant index past the end,
// `primes[7]`, does not compile:
// error: index 7 is out of range for an array of 4 elements
// An index computed while the program runs is checked as it runs, and one past the end stops
// the program with `Panic: index out of range` rather than read memory beyond the array.
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/Array
rux run
```
```text
first 2, last 7
length 4
sum 17
primes[0] = 2
primes[1] = 3
primes[2] = 5
primes[3] = 7
scores 10 25 30
copy 99 25 30
scores == copy false
```
## Common mistakes
::warning
**Changing an element of a `let` array.**:br`primes[0] = 1;` fails with `error: cannot modify immutable variable 'primes'`, and the compiler suggests declaring it with `var`.
::
::warning
**Counting to `length` inclusive.**:br`for i in 0..=primes.length` visits one index too many. The last pass stops the program with `Panic: index out of range`. Use `0..primes.length`. With a constant index the compiler catches it first: `primes[7]` fails with `error: index 7 is out of range for an array of 4 elements` and `help: valid indexes are 0 through 3`.
::
::warning
**A literal with the wrong number of elements.**:br`let three: int32[4] = [1, 2, 3];` fails with `error: cannot assign 'int[3]' to 'int32[4]'`. The count is part of the type, so it has to match.
::
::warning
**Printing a whole array.**:br`PrintLine("{}", primes)` is refused — an array is not printable as one value. Print its elements, as the program does.
::
## Try it yourself
1. Find the largest element of `primes` with a `for` loop and a `var`.
2. Print the elements of `primes` in reverse order by walking the indices backwards.
3. Set `copy[0]` back to `10` and print `scores == copy` again.
4. Change `0..primes.length` to `0..=primes.length` and run the program.
## Learn more
- [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) and [Indexing and iteration](https://rux-lang.dev/docs/lang/arrays/overview#indexing) in the Rux Reference
- [For](https://rux-lang.dev/docs/learn/for) — the loop used to walk arrays
- [Array repeat](https://rux-lang.dev/docs/learn/array-repeat) — an array of many equal values without listing them
- [Copy](https://rux-lang.dev/docs/learn/copy) — what copying a value means, later in the course
# Array repeat
::note
**You'll need**: [Array](https://rux-lang.dev/docs/learn/array), [Function](https://rux-lang.dev/docs/learn/function)
::
Listing every element works for four values, not for sixteen — and not at all for a thousand. `[value; count]` builds an array of `count` copies of one value: `[0; 16]` is sixteen zeros. This lesson shows the form, its one surprise, and the job it is usually given: starting an array from a clean slate.
## Many copies of one value
```rux
let zeros: int32[16] = [0; 16];
```
Read `[0; 16]` as "zero, sixteen times". The element type comes from the variable — here `int32`, because the variable says `int32[16]` — and the count must match the count in the type.
The count must be known **at compile time**, because it becomes part of the array's type. A literal works, and so does a `const`, but a `let` variable does not: its value is only known once the program runs.
## The value is evaluated once
The surprise is in how the value is produced. It is evaluated exactly once, and that one result is copied into every element:
```rux
let rolls = [Roll(); 4];
```
`Roll` prints a line every time it is called, and the output shows `Roll called` only **once**. The four elements are always equal, whatever `Roll` returns.
```mermaid
flowchart LR
r["Roll()
called once"] --> v["4"]
v --> e0["rolls[0] = 4"]
v --> e1["rolls[1] = 4"]
v --> e2["rolls[2] = 4"]
v --> e3["rolls[3] = 4"]
```
If you want each element computed on its own, list them — `[Roll(), Roll(), Roll(), Roll()]` makes four calls — or fill the array in a loop.
## A clean slate to fill in
The usual reason to repeat a value is to start a `var` array from zero and fill it in afterwards. Here, a tally of the last digit of each number:
```rux
var tally: int32[10] = [0; 10];
let numbers: int32[8] = [12, 7, 22, 35, 2, 17, 40, 32];
for number in numbers {
tally[number % 10] += 1;
}
```
`number % 10` is the last digit, 0 to 9, so it is always a valid index into `tally`. Each element counts how many numbers end in that digit: 12, 22, 2 and 32 all land in `tally[2]`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/ArrayRepeat){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Listing every element works for four values, not for sixteen. `[value; count]` builds an array
// of `count` copies of one value: `[0; 16]` is sixteen zeros, of type `int32[16]` when the
// variable says so.
//
// The surprise is in how the value is produced. It is evaluated exactly once, and that one
// result is copied into every element. `[Roll(); 4]` calls `Roll` once, not four times, so the
// four elements are always equal.
import Io::PrintLine;
// Prints a line every time it is called, so the output shows how many calls happened.
func Roll() -> int32 {
PrintLine("Roll called");
return 4;
}
func Main() -> int {
// The count must be known at compile time; the element type comes from the variable.
let zeros: int32[16] = [0; 16];
PrintLine("zeros: length {}, first {}, last {}", zeros.length, zeros[0], zeros[15]);
// One call, four copies of its result.
let rolls = [Roll(); 4];
PrintLine("rolls: {} {} {} {}", rolls[0], rolls[1], rolls[2], rolls[3]);
// The usual reason to repeat a value is to start a `var` array from a clean slate and
// fill it in afterwards. Here, a tally of the last digit of each number.
var tally: int32[10] = [0; 10];
let numbers: int32[8] = [12, 7, 22, 35, 2, 17, 40, 32];
for number in numbers {
tally[number % 10] += 1;
}
for digit in 0..tally.length {
if tally[digit] > 0 {
PrintLine("ending in {}: {}", digit, tally[digit]);
}
}
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/ArrayRepeat
rux run
```
```text
zeros: length 16, first 0, last 0
Roll called
rolls: 4 4 4 4
ending in 0: 1
ending in 2: 4
ending in 5: 1
ending in 7: 2
```
## Common mistakes
::warning
**A count only known at run time.**:br
With `let n = 4;`, the literal `[0; n]` fails with `error: array repeat count must be a non-negative compile-time integer`. Use a literal or a `const`.
::
::warning
**A count that disagrees with the type.**:br`let b: int32[5] = [0; 4];` fails with `error: cannot assign 'int[4]' to 'int32[5]'`. The repeat count and the type's count have to be the same number.
::
::warning
**Expecting a fresh value per element.**:br`[Roll(); 4]` calls `Roll` once. If each element should be different, list the calls or assign the elements in a loop.
::
## Try it yourself
1. Declare `const Size = 5;` and use it in both places: `var squares: int32[Size] = [0; Size];`. Fill the array with the squares of its indices — they are `uint`, so convert with `as int32`.
2. Replace `[Roll(); 4]` with `[Roll(), Roll(), Roll(), Roll()]` and count the `Roll called` lines.
3. Change the tally to count the remainders of each number divided by 3 instead.
4. Try `[0; n]` with a `let n = 4;` and read the error.
## Learn more
- [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) in the Rux Reference
- [Const](https://rux-lang.dev/docs/learn/const) — a value known at compile time
- [Array nested](https://rux-lang.dev/docs/learn/array-nested) — repeating a whole row to build a grid
# Array nested
::note
**You'll need**: [Array](https://rux-lang.dev/docs/learn/array), [Array repeat](https://rux-lang.dev/docs/learn/array-repeat)
::
An array's element type can be another array, and that is all a two-dimensional array is. A grid of three rows of four numbers is an array of three rows, where each row is an `int32[4]`. Boards, tables, images and spreadsheets all start out this way.
## Built inside out, read outside in
The surprise is the order in the type. Each `[n]` wraps everything written before it, so the type is built inside out:
```mermaid
flowchart LR
e["int32
one number"] --> r["int32[4]
one row of 4 numbers"]
r --> g["int32[4][3]
3 of those rows"]
```
Indexing reads the other way, outside in: `grid[row]` picks a row, and `grid[row][column]` picks a number within it.
```rux
let grid: int32[4][3] = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
];
```
| | column 0 | column 1 | column 2 | column 3 |
| --------- | -------- | -------- | -------- | -------- |
| **row 0** | 1 | 2 | 3 | 4 |
| **row 1** | 5 | 6 | 7 | 8 |
| **row 2** | 9 | 10 | 11 | 12 |
So `grid[2][1]` is row 2, column 1: `10`. The outer `length` counts rows and a row's `length` counts columns:
```rux
PrintLine("{} rows of {} columns", grid.length, grid[0].length);
```
## Walking by rows
`for` over the grid yields whole rows, and an inner `for` walks each row:
```rux
for row in grid {
var sum: int32 = 0;
for value in row {
sum += value;
}
PrintLine("row {} {} {} {} adds up to {}", row[0], row[1], row[2], row[3], sum);
}
```
`row` is an ordinary `int32[4]`, so it has a `length`, it can be indexed, and it can be walked like any other array.
## Walking by columns
There is no "column" value to loop over — columns cut across the rows. Going down a column means holding the column fixed and stepping through the rows by index:
```rux
for column in 0..grid[0].length {
var total: int32 = 0;
for row in 0..grid.length {
total += grid[row][column];
}
PrintLine("column {} adds up to {}", column, total);
}
```
## Repeating a row
A repeated array nests too. `[0; 3]` is a row of three zeros, and `[[0; 3]; 3]` is three copies of that row — a blank board to fill in:
```rux
var board: int32[3][3] = [[0; 3]; 3];
for i in 0..board.length {
board[i][i] = 1;
}
```
Setting `board[i][i]` for each `i` puts a 1 on the diagonal. Because each row is a separate copy, changing one row never touches another.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/ArrayNested){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An array's element type can be another array, and that is all a two-dimensional array is. A
// grid of three rows of four numbers is an array of three rows, where each row is an `int32[4]`.
//
// The surprise is the order in the type. Each `[n]` wraps everything written before it, so the
// type is built inside out: `int32[4]` is one row, and `int32[4][3]` is three of those rows.
// Indexing reads the other way, outside in: `grid[row]` picks a row, and `grid[row][column]`
// picks a number within it.
import Io::PrintLine;
func Main() -> int {
// Three rows, each a literal of four numbers.
let grid: int32[4][3] = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
];
// The outer length counts rows; a row's length counts columns.
PrintLine("{} rows of {} columns", grid.length, grid[0].length);
PrintLine("grid[2][1] is {}", grid[2][1]);
// `for` over the grid yields whole rows, and an inner `for` walks each row.
for row in grid {
var sum: int32 = 0;
for value in row {
sum += value;
}
PrintLine("row {} {} {} {} adds up to {}", row[0], row[1], row[2], row[3], sum);
}
// Going down a column means holding the column fixed and stepping through the rows.
for column in 0..grid[0].length {
var total: int32 = 0;
for row in 0..grid.length {
total += grid[row][column];
}
PrintLine("column {} adds up to {}", column, total);
}
// A repeated array nests too: three copies of a row of three zeros, filled in afterwards.
var board: int32[3][3] = [[0; 3]; 3];
for i in 0..board.length {
board[i][i] = 1;
}
for row in board {
PrintLine("{} {} {}", row[0], row[1], row[2]);
}
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/ArrayNested
rux run
```
```text
3 rows of 4 columns
grid[2][1] is 10
row 1 2 3 4 adds up to 10
row 5 6 7 8 adds up to 26
row 9 10 11 12 adds up to 42
column 0 adds up to 15
column 1 adds up to 18
column 2 adds up to 21
column 3 adds up to 24
1 0 0
0 1 0
0 0 1
```
## Common mistakes
::warning
**Writing the dimensions in reading order.**:br
Three rows of four is `int32[4][3]`, not `int32[3][4]`. The wrong order fails with `error: cannot assign 'int[4][3]' to 'int32[3][4]'` — the message shows the shape the literal really has.
::
::warning
**One index with a comma.**:br`grid[1, 2]` is a syntax error: `expected ']' to close the index expression before ','`. Each level takes its own brackets: `grid[1][2]`.
::
::warning
**Rows of different lengths.**:br
Every row has the same type, so a short row is refused: `error: array element 2 has type 'int[3]', but the array's element type is 'int32[4]'`.
::
## Try it yourself
1. Add up every number in `grid` with two nested `for` loops.
2. Print `grid` with its rows and columns swapped, so that the first line reads `1 5 9`.
3. Build a multiplication table, `var table: int32[10][10] = [[0; 10]; 10];`, and fill it with `(row + 1) * (column + 1)`. The indices are `uint`, so the product needs `as int32`.
4. Change the type of `grid` to `int32[3][4]` and read the error.
## Learn more
- [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) and [Indexing and iteration](https://rux-lang.dev/docs/lang/arrays/overview#indexing) in the Rux Reference
- [Array repeat](https://rux-lang.dev/docs/learn/array-repeat) — the `[value; count]` form used for `board`
- [Slice](https://rux-lang.dev/docs/learn/slice) — passing arrays of any length to one function
# Slice
::note
**You'll need**: [Array](https://rux-lang.dev/docs/learn/array), [Function](https://rux-lang.dev/docs/learn/function)
::
An array's length is part of its type, so a function taking an `int32[5]` accepts arrays of exactly five — no more, no fewer. A *slice*, written `int32[..]`, removes that limit. It is a **view** into an array: where the elements start and how many there are, with no elements of its own. One function taking a slice serves arrays of every size, and making a view copies nothing.
## A view, not a copy
A slice is two numbers — a position and a length — pointing into an array that already exists:
```mermaid
flowchart LR
subgraph numbers["numbers: int32[5]"]
direction LR
n0["10"] --- n1["20"] --- n2["30"] --- n3["40"] --- n4["50"]
end
middle["middle = numbers[1..4]
starts at 20, length 3"] -.-> n1
```
The `..` in `int32[..]` reads as "some number of": the element type is fixed, the count is not.
## One function for every length
An array passed where a slice is wanted becomes a view of all of itself:
```rux
func Total(values: int32[..]) -> int32 {
var sum: int32 = 0;
for value in values {
sum += value;
}
return sum;
}
```
```rux
PrintLine("numbers {}", Total(numbers));
PrintLine("more {}", Total(more));
```
`numbers` has five elements and `more` has three, yet both calls reach the same function. Inside it, `values` has a `length`, can be indexed and can be walked with `for`, just like an array.
## Part of an array
Indexing with a [range](https://rux-lang.dev/docs/learn/range) gives a view of part of the array. As everywhere else, `..` stops before its upper bound and `..=` includes it, and either end may be left out:
| Expression | Elements | Meaning |
| ---------------- | -------------- | ----------------------------- |
| `numbers[1..4]` | 20, 30, 40 | indices 1 up to, not incl., 4 |
| `numbers[1..=4]` | 20, 30, 40, 50 | indices 1 to 4 inclusive |
| `numbers[2..]` | 30, 40, 50 | from index 2 to the end |
| `numbers[..2]` | 10, 20 | from the start up to 2 |
| `numbers[..]` | all five | the whole array |
A slice can be sliced again. Its indices count from the start of the **view**, not the array:
```rux
let inner = middle[1..];
```
`middle` starts at 20, so `middle[1..]` starts at 30, and `inner[0]` is 30.
## Strings are slices
A string literal has been a slice all along: `char8[..]`, a view of UTF-8 bytes. That is why `.length` counts bytes:
```rux
let greeting: char8[..] = "hello";
```
## Read-only by default
A plain slice can look at the elements but never change them — `Total` cannot write into `values`. Writing through a view is the subject of the next lesson, [Writable slice](https://rux-lang.dev/docs/learn/writable-slice).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/Slice){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An array's length is part of its type, so a function taking an `int32[5]` accepts arrays of
// exactly five. A slice, written `int32[..]`, removes that limit. It is a view into an array: a
// position and a length, with no elements of its own. One function taking a slice serves arrays
// of every size, and making a view copies nothing.
import Io::PrintLine;
// Every call below reaches this one function, whatever the length of what it is given.
func Total(values: int32[..]) -> int32 {
var sum: int32 = 0;
for value in values {
sum += value;
}
return sum;
}
func Main() -> int {
let numbers: int32[5] = [10, 20, 30, 40, 50];
let more: int32[3] = [1, 2, 3];
// An array passed where a slice is wanted becomes a view of all of itself.
PrintLine("numbers {}", Total(numbers));
PrintLine("more {}", Total(more));
// Indexing with a range gives a view of part of the array. As everywhere else, `..` stops
// before its upper bound, so this is elements 1, 2 and 3; `..=` would include the end.
let middle = numbers[1..4];
PrintLine("numbers[1..4] {} over {} elements", Total(middle), middle.length);
PrintLine("numbers[1..=4] {}", Total(numbers[1..=4]));
// Either end may be left out, meaning "from the start" or "to the end".
PrintLine("numbers[2..] {}", Total(numbers[2..]));
PrintLine("numbers[..2] {}", Total(numbers[..2]));
// A slice can be sliced again. Its indices count from the start of the view, not the array.
let inner = middle[1..];
PrintLine("middle[1..] {}, starting at {}", Total(inner), inner[0]);
// A string literal has been a slice all along: `char8[..]`, a view of UTF-8 bytes.
let greeting: char8[..] = "hello";
PrintLine("\"{}\" is {} bytes", greeting, greeting.length);
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/Slice
rux run
```
```text
numbers 150
more 6
numbers[1..4] 90 over 3 elements
numbers[1..=4] 140
numbers[2..] 120
numbers[..2] 30
middle[1..] 70, starting at 30
"hello" is 5 bytes
```
## Common mistakes
::warning
**Writing through a read-only slice.**:br`values[0] = 1;` inside `Total` fails with `error: cannot modify elements through read-only slice 'int32[..]'`, and the compiler points to `var T[..]` — see [Writable slice](https://rux-lang.dev/docs/learn/writable-slice).
::
::warning
**A range that runs backwards or past the end.**:br`numbers[3..1]` fails with `error: range start cannot be greater than its end`. A range past the end, such as `numbers[1..9]`, is checked when the program runs and stops it with `Panic: index out of range`.
::
::warning
**Comparing two slices with `==`.**:br`a == b` on two `int32[..]` values fails with `error: operator '==' is not defined for slice type 'int32[..]'`. A slice is a view, so comparing views would compare where they point rather than what they contain. Compare the elements one at a time.
::
## Try it yourself
1. Write `Largest(values: int32[..]) -> int32` and call it with `numbers`, `more` and `numbers[1..3]`.
2. Print `Total(numbers[..])` and check that it matches `Total(numbers)`.
3. Write `CountAbove(values: int32[..], limit: int32) -> int32` that counts the elements greater than `limit`, and call it on `numbers[1..]`.
4. Try `numbers[3..1]` and read the error.
## Learn more
- [Slices](https://rux-lang.dev/docs/lang/slices/overview) and [Arrays as slices](https://rux-lang.dev/docs/lang/arrays/overview#arrays-as-slices) in the Rux Reference
- [Using ranges](https://rux-lang.dev/docs/lang/ranges/overview#in-a-for-loop) — ranges as loop bounds and slice indices
- [String literal](https://rux-lang.dev/docs/learn/string-literal) — more on `char8[..]`
# Writable slice
::note
**You'll need**: [Slice](https://rux-lang.dev/docs/learn/slice)
::
A plain slice, `int32[..]`, is read-only: it can look at the elements but never change them. A *writable slice*, `var int32[..]`, may also write through to the array it views. That is how a function changes the elements of an array it was given — fill it, sort it, scale it — without copying it first.
## Two kinds of view
Which kind you get depends on the array you take it from:
| Taken from | Example | Type | Can write elements? |
| ----------------- | -------------------- | --------------- | ------------------- |
| a `var` array | `storage[1..4]` | `var int32[..]` | yes |
| a `let` array | `numbers[1..4]` | `int32[..]` | no |
| any array, passed | `Show("…", storage)` | `int32[..]` | no |
A writable view writes straight into the array behind it:
```rux
var storage: int32[5] = [1, 2, 3, 4, 5];
```
```rux
let view = storage[1..4];
view[0] = 20;
```
`view` starts at index 1 of `storage`, so `view[0] = 20` changes `storage[1]` from 2 to 20.
## A function that changes its argument
A function that writes into its argument's elements asks for a writable view:
```rux
func Fill(values: var int32[..], value: int32) {
for i in 0..values.length {
values[i] = value;
}
}
```
A function that only reads asks for a read-only view — and a writable one is accepted there too, so `Show` takes both.
There is one asymmetry. An array becomes a read-only view on its own, but a writable one has to be **asked for** with a range, so that changing the caller's array is always visible at the call:
```rux
Fill(storage[3..], 0);
```
`storage[..]` would fill all of it.
## The view and its binding
The surprise is that a view's writability has nothing to do with how the view itself is bound. Two separate questions are being answered:
```mermaid
flowchart LR
b["the binding
let or var"] -- "can it be pointed
somewhere else?" --> v["the view
int32[..] or var int32[..]"]
v -- "can it change
the elements?" --> a["the array"]
```
`let view = storage[1..4];` is a writable view in an immutable binding: `view[0] = 20` is fine, because it changes the array, but `view = …` is rejected, because it would change the binding. A `var` binding of a read-only view is the reverse — it may move along the array, but never write an element:
```rux
var cursor: int32[..] = storage[..2];
PrintLine("cursor starts at {}", cursor[0]);
cursor = storage[2..];
```
| Binding | View | Point it elsewhere? | Write elements? |
| ------- | --------------- | ------------------- | --------------- |
| `let` | `int32[..]` | no | no |
| `let` | `var int32[..]` | no | yes |
| `var` | `int32[..]` | yes | no |
| `var` | `var int32[..]` | yes | yes |
## Views see later writes
A writable view may be stored as a read-only one. Both look at the same array, so the read-only view sees what is written through the other afterwards:
```rux
let reader: int32[..] = view;
view[1] = 30;
PrintLine("reader sees {}", reader[1]);
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/WritableSlice){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A plain slice, `int32[..]`, is read-only: it can look at the elements but never change them.
// A writable slice, `var int32[..]`, may also write through to the array it views. Taking a
// range of a `var` array gives a writable view; taking one of a `let` array gives a read-only
// one.
//
// The surprise is that the view's writability has nothing to do with how the view itself is
// bound. `let view = storage[..];` is a writable view held in an immutable binding: `view[0] = 1`
// is fine, because it changes the array, while `view = ...` is rejected, because it would change
// the binding. A `var` binding of a read-only view is the reverse: it can be pointed somewhere
// else, but it can never write an element.
import Io::PrintLine;
// A function that changes its argument's elements asks for a writable view.
func Fill(values: var int32[..], value: int32) {
for i in 0..values.length {
values[i] = value;
}
}
// A function that only reads asks for a read-only view, and a writable one is accepted too.
func Show(label: char8[..], values: int32[..]) {
PrintLine("{} {} {} {} {} {}", label, values[0], values[1], values[2], values[3], values[4]);
}
func Main() -> int {
var storage: int32[5] = [1, 2, 3, 4, 5];
Show("start ", storage);
// A `let` binding, a writable view. Writing through it writes the array.
let view = storage[1..4];
view[0] = 20;
Show("view ", storage);
// The function writes through its view, so the change shows up in `storage`. An array
// becomes a read-only view on its own, but a writable one has to be asked for with a range.
// `Fill(storage[..], 0)` would fill all of it, while `Fill(storage, 0)` stops with
// error: no matching overload for 'Fill' with argument types (int32[5], int)
Fill(storage[3..], 0);
Show("filled ", storage);
// A `var` binding, a read-only view. It may move along the array, but `cursor[0] = 9`
// would be rejected.
var cursor: int32[..] = storage[..2];
PrintLine("cursor starts at {}", cursor[0]);
cursor = storage[2..];
PrintLine("cursor moved to {}", cursor[0]);
// A writable view may be stored as a read-only one, which then sees later writes too.
let reader: int32[..] = view;
view[1] = 30;
PrintLine("reader sees {}", reader[1]);
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/WritableSlice
rux run
```
```text
start 1 2 3 4 5
view 1 20 3 4 5
filled 1 20 3 0 0
cursor starts at 1
cursor moved to 3
reader sees 30
```
## Common mistakes
::warning
**Passing the array itself to a writable parameter.**:br`Fill(storage, 0)` fails with `error: no matching overload for 'Fill' with argument types (int32[5], int)`. An array only becomes a read-only view on its own; write `Fill(storage[..], 0)` to hand over a writable one.
::
::warning
**Taking a writable view of a `let` array.**:br
With `let fixed: int32[3] = [1, 2, 3];`, the call `Fill(fixed[..], 0)` fails with `has type 'int32[..]', but parameter 'values' requires 'var int32[..]'`. A view can never grant more than the array allows.
::
::warning
**Confusing the binding with the view.**:br`cursor[0] = 9;` fails with `error: cannot modify elements through read-only slice 'int32[..]'`, even though `cursor` is `var`. And `view = storage[..2];` fails with `error: cannot modify immutable variable 'view'`, even though `view` can write elements.
::
## Try it yourself
1. Write `Scale(values: var int32[..], factor: int32)` that multiplies every element, and call it on `storage[..]`.
2. Write `Reverse(values: var int32[..])` that swaps elements from both ends towards the middle.
3. Call `Fill(storage, 0)` and read the error, then fix the call.
4. Change `storage` to a `let` array and see which lines stop compiling.
## Learn more
- [Slices](https://rux-lang.dev/docs/lang/slices/overview) and [Indexing and iteration](https://rux-lang.dev/docs/lang/slices/overview#indexing-and-iteration) in the Rux Reference
- [Mutable](https://rux-lang.dev/docs/learn/mutable) — `let` and `var` bindings
- [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — the same "may this change?" question for a single value
# Variadic
::note
**You'll need**: [Slice](https://rux-lang.dev/docs/learn/slice), [Convert](https://rux-lang.dev/docs/learn/convert)
::
Some functions cannot say in advance how many arguments they take. A sum of some numbers, the largest of a few values — the work is the same whether there are two arguments or twenty. A *variadic* function accepts any count, zero included. You have been calling one since the first lesson: `PrintLine` takes a format string and then as many values as it has `{}` placeholders.
## Collecting the rest
A last parameter written `args: int32...` collects every remaining argument into one slice:
```rux
func Sum(args: int32...) -> int32 {
var total: int32 = 0;
for value in args {
total += value;
}
return total;
}
```
Inside the function, `args` is an ordinary `int32[..]` — the [Slice](https://rux-lang.dev/docs/learn/slice) lesson's read-only view. It has a `length`, it can be indexed, and `for` walks it. So one function serves every count:
```mermaid
flowchart LR
c3["Sum(1, 2, 3)"] --> a3["args = view of 1, 2, 3
length 3"]
c1["Sum(42)"] --> a1["args = view of 42
length 1"]
c0["Sum()"] --> a0["args = empty view
length 0"]
```
`Sum()` is perfectly valid: the loop simply runs zero times and the total stays 0.
## Ordinary parameters first
Ordinary parameters may come before the variadic one; only the last parameter can collect the rest:
```rux
func Largest(first: int32, args: int32...) -> int32 {
```
Here `first` is required, so `Largest` needs **at least** one argument — there is always a value to start from — and any more land in `args`.
## Spreading a slice back out
Going the other way, `args...` at a call spreads a slice back out into separate arguments. That is how one variadic function hands everything it received to another:
```rux
func Average(args: int32...) -> int32 {
if args.length == 0 {
return 0;
}
return Sum(args...) / (args.length as int32);
}
```
`args.length` is a `uint`, so it is converted with `as` before dividing an `int32` by it. An array can be spread too: it becomes a view of all its elements, just as it does when passed to a slice parameter:
```rux
let scores: int32[4] = [70, 85, 90, 95];
PrintLine("Average(scores...) {}", Average(scores...));
```
| Call | `args` inside `Average` |
| -------------------- | ----------------------- |
| `Average(2, 4, 9)` | 2, 4, 9 |
| `Average(scores...)` | 70, 85, 90, 95 |
| `Average()` | empty |
## PrintLine is variadic
The `PrintLine` you have been calling with a format string is declared with a variadic parameter of its own — `args: Display...`. `Display` is not one type but a promise, "can be printed", which is why one call can mix an `int`, a `float64` and a string. How such promises work is the subject of [Display](https://rux-lang.dev/docs/learn/display) in the Interfaces part.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/Variadic){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Some functions cannot say in advance how many arguments they take. A last parameter written
// `args: int32...` collects every remaining argument into one slice, so a single function serves
// any count, zero included. Inside the function, `args` is an ordinary `int32[..]`: it has a
// length, it can be indexed, and `for` walks it.
//
// Going the other way, `args...` at a call spreads a slice back out into separate arguments,
// which is how one variadic function hands everything it received to another.
import Io::PrintLine;
func Sum(args: int32...) -> int32 {
var total: int32 = 0;
for value in args {
total += value;
}
return total;
}
// Ordinary parameters may come first; only the last one can collect the rest.
func Largest(first: int32, args: int32...) -> int32 {
var largest = first;
for value in args {
if value > largest {
largest = value;
}
}
return largest;
}
// `args...` passes what this function was given straight on to `Sum`.
func Average(args: int32...) -> int32 {
if args.length == 0 {
return 0;
}
return Sum(args...) / (args.length as int32);
}
func Main() -> int {
// The same function with three arguments, with one, and with none at all.
PrintLine("Sum(1, 2, 3) {}", Sum(1, 2, 3));
PrintLine("Sum(42) {}", Sum(42));
PrintLine("Sum() {}", Sum());
PrintLine("Largest(4, 9, 2) {}", Largest(4, 9, 2));
PrintLine("Largest(7) {}", Largest(7));
PrintLine("Average(2, 4, 9) {}", Average(2, 4, 9));
// An array can be spread too: it becomes a slice of all its elements, just as it does when
// passed to a slice parameter.
let scores: int32[4] = [70, 85, 90, 95];
PrintLine("Average(scores...) {}", Average(scores...));
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/Variadic
rux run
```
```text
Sum(1, 2, 3) 6
Sum(42) 42
Sum() 0
Largest(4, 9, 2) 9
Largest(7) 7
Average(2, 4, 9) 5
Average(scores...) 85
```
## Common mistakes
::warning
**Passing an array without spreading it.**:br`Sum(scores)` fails with `error: argument 1 to 'Sum' has type 'int32[4]', but variadic parameter 'args' requires 'int32'` — each argument must be one `int32`. Write `Sum(scores...)`.
::
::warning
**Mixing a spread with other arguments.**:br`Sum(1, scores...)` fails with `error: spread argument to 'Sum' must be the only argument for variadic parameter 'args'`. Either list the values one by one or spread a single slice.
::
::warning
**Leaving out a required parameter.**:br`Largest()` fails with `error: call to 'Largest' expects at least 1 argument, but 0 were provided`. Only the variadic parameter may receive nothing.
::
## Try it yourself
1. Write `Smallest(first: int32, args: int32...) -> int32`.
2. Write `CountAbove(limit: int32, args: int32...) -> int32` and call it as `CountAbove(80, scores...)`.
3. Print `Sum(scores[1..]...)` — a spread works on any slice, not only a whole array.
4. Call `Sum(scores)` without the `...` and read the error.
## Learn more
- [Variadic functions](https://rux-lang.dev/docs/lang/functions/parameters#variadic-parameters) in the Rux Reference
- [Indexing and iteration](https://rux-lang.dev/docs/lang/slices/overview#indexing-and-iteration) in the Reference — including spreading a slice into arguments
- [Console](https://rux-lang.dev/docs/learn/console) — `PrintLine` and its placeholders
# Tuple
::note
**You'll need**: [Slice](https://rux-lang.dev/docs/learn/slice), [Return](https://rux-lang.dev/docs/learn/return)
::
A *tuple* groups a few values without declaring a type for them first. Its type is the list of its members' types in parentheses — `(int32, bool)` — and a value is written the same way, `(17, true)`. Arrays hold many values of **one** type; a tuple holds a fixed handful of values of **any** types, side by side.
## Writing and reading a tuple
The members are reached by position: `.0` is the first, `.1` the second, and so on:
```rux
let pair: (int32, float64) = (3, 2.5);
PrintLine("pair.0 is {} and pair.1 is {}", pair.0, pair.1);
```
| Collection | Element types | How many | Reached by |
| ---------- | ------------- | ------------------ | -------------- |
| Array | all the same | fixed, any number | `values[i]` |
| Tuple | each its own | fixed, usually few | `pair.0`, `.1` |
Like an array, a tuple is a plain value: assigning it copies every member, and `let` freezes the members too.
## Several results from one function
The everyday use is returning more than one result. A function has a single return type, and a tuple lets that one type carry several values. `Divide` returns the quotient and whether it means anything — there is no number to return for a division by zero, so the second member says whether the first is real:
```rux
func Divide(numerator: int32, denominator: int32) -> (int32, bool) {
if denominator == 0 {
return (0, false);
}
return (numerator / denominator, true);
}
```
```mermaid
flowchart LR
a["Divide(17, 5)"] --> ra["(3, true)
.0 is the answer"]
b["Divide(1, 0)"] --> rb["(0, false)
.0 means nothing"]
```
The caller checks the second member before trusting the first:
```rux
let good = Divide(17, 5);
if good.1 {
PrintLine("17 / 5 is {}", good.0);
}
```
The members need not share a type. `Smallest` returns the smallest value, an `int32`, and its position, a `uint`:
```rux
func Smallest(values: int32[..]) -> (int32, uint) {
```
## Comparing tuples
Tuples of the same type compare member by member: equal when every member is equal. A tuple literal takes its member types from the other side, just as a lone `3` would:
```rux
PrintLine("good == (3, true) {}", good == (3, true));
```
## A tuple inside a tuple
A member may itself be a tuple. The indexes chain, so `outer.0.1` is the second member of the first member:
```rux
let outer: ((int32, int32), char8[..]) = ((3, 4), "point");
PrintLine("{} at {}, {}", outer.1, outer.0.0, outer.0.1);
```
Once a tuple's members need names to be understood — or it has more than two or three of them — it is time for a [struct](https://rux-lang.dev/docs/learn/struct), or at least for the next lesson, [Destructure](https://rux-lang.dev/docs/learn/destructure).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/Tuple){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A tuple groups a few values without declaring a type for them first. Its type is the list of
// its members' types in parentheses — `(int32, bool)` — and a value is written the same way,
// `(17, true)`. The members are reached by position: `.0` is the first, `.1` the second.
//
// The everyday use is returning more than one result from a function. A function has a single
// return type, and a tuple lets that one type carry several values.
import Io::PrintLine;
// Two results from one call: the quotient, and whether it means anything. There is no number to
// return for a division by zero, so the second member says whether the first is real.
func Divide(numerator: int32, denominator: int32) -> (int32, bool) {
if denominator == 0 {
return (0, false);
}
return (numerator / denominator, true);
}
// The members need not share a type.
func Smallest(values: int32[..]) -> (int32, uint) {
var smallest = values[0];
var position: uint = 0;
for i in 1..values.length {
if values[i] < smallest {
smallest = values[i];
position = i;
}
}
return (smallest, position);
}
func Main() -> int {
// Written out in full: the type, then the value.
let pair: (int32, float64) = (3, 2.5);
PrintLine("pair.0 is {} and pair.1 is {}", pair.0, pair.1);
// Check the second member before trusting the first.
let good = Divide(17, 5);
if good.1 {
PrintLine("17 / 5 is {}", good.0);
}
let bad = Divide(1, 0);
if !bad.1 {
PrintLine("1 / 0 has no answer");
}
let temperatures: int32[5] = [12, 9, 4, 7, 11];
let coldest = Smallest(temperatures);
PrintLine("coldest {} at position {}", coldest.0, coldest.1);
// Tuples of the same type compare member by member. The literal `(3, true)` takes its member
// types from the other side, just as a lone `3` would.
PrintLine("good == (3, true) {}", good == (3, true));
PrintLine("bad == (3, true) {}", bad == (3, true));
// A tuple may hold another tuple. The indexes chain, so `outer.0.1` is the second member of
// the first member.
let outer: ((int32, int32), char8[..]) = ((3, 4), "point");
PrintLine("{} at {}, {}", outer.1, outer.0.0, outer.0.1);
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/Tuple
rux run
```
```text
pair.0 is 3 and pair.1 is 2.5
17 / 5 is 3
1 / 0 has no answer
coldest 4 at position 2
good == (3, true) true
bad == (3, true) false
point at 3, 4
```
## Common mistakes
::warning
**A member that does not exist.**:br`pair.2` fails with `error: tuple index 2 is out of range for a tuple with 2 elements`. Members count from `.0`, like array indices.
::
::warning
**Comparing tuples of different types.**:br`good == (1, 2)` fails with `error: operator '==' cannot compare left operand '(int32, bool8)' with right operand '(int, int)'` and the note `two tuples compare as one type`. Both sides must have the same member types in the same order.
::
::warning
**Returning a bare value from a tuple function.**:br`return (numerator / denominator);` in `Divide` fails with `'return' value must have type '(int32, bool8)', but found 'int32'`. Parentheses around one value do not make a tuple — every member must be there.
::
::warning
**Printing a whole tuple.**:br`PrintLine("{}", pair)` is refused, because a tuple is not printable as one value. Print its members.
::
## Try it yourself
1. Write `MinMax(values: int32[..]) -> (int32, int32)` and print both members for `temperatures`.
2. Declare `var point = (1, 2);`, change `point.0`, and print both members.
3. Copy a `var` tuple into a second variable, change the original, and check that the copy kept its old values.
4. Compare `good == (1, 2)` and read the error.
## Learn more
- [Tuples](https://rux-lang.dev/docs/lang/tuples/overview) and [Tuples vs. structs](https://rux-lang.dev/docs/lang/tuples/overview#tuples-or-structs) in the Rux Reference
- [Destructure](https://rux-lang.dev/docs/learn/destructure) — naming a tuple's members in one step
- [Tuple pattern](https://rux-lang.dev/docs/learn/tuple-pattern) — matching on tuples in `match`
# Destructure
::note
**You'll need**: [Tuple](https://rux-lang.dev/docs/learn/tuple)
::
`.0` and `.1` work, but they say nothing about what each member *means*, and a function that uses a tuple's members many times fills up with them. *Destructuring* unpacks a tuple into named bindings in one step: `let (x, y) = pair;` declares `x` from `pair.0` and `y` from `pair.1`.
## One name per member
The pattern on the left of `=` mirrors the tuple's shape, with one name per member:
```rux
let pair = (3, 4);
let (x, y) = pair;
```
It reads best straight off a function that returns several results. Compare `result.0` and `result.1` with names that say what they are:
```rux
let (quotient, remainder) = Divide(17, 5);
PrintLine("17 / 5 is {} remainder {}", quotient, remainder);
```
## Skipping a member with `_`
`_` stands for a member that is not wanted. `Bounds` returns both the lowest and the highest value, but only the high one is needed here:
```rux
let (_, high) = Bounds(readings);
```
The skipped member is not bound to anything, so there is no unused name lying around.
## `var` makes every name mutable
`var` in front of the pattern makes every name it declares mutable, just as it does for a single binding:
```rux
var (low, top) = Bounds(readings);
low -= 1;
top += 1;
```
## Patterns nest
A nested pattern unpacks a tuple inside a tuple, all the way down. The pattern has exactly the shape of the value:
```rux
let segment = ((0, 0), (6, 8));
let ((x1, y1), (x2, y2)) = segment;
```
```mermaid
flowchart LR
v["((0, 0), (6, 8))"] --> p["((x1, y1), (x2, y2))"]
p --> s["x1 = 0, y1 = 0
x2 = 6, y2 = 8"]
```
## It only declares
Destructuring always declares **new** names. It cannot assign to names that already exist, so the swap other languages write as `(a, b) = (b, a);` is rejected: the left side of `=` has to be a single place, such as a variable or an element.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Sequences/Destructure){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `.0` and `.1` work, but they say nothing about what each member means, and a function that
// uses a tuple's members many times fills up with them. Destructuring unpacks a tuple into named
// bindings in one step: `let (x, y) = pair;` declares `x` from `pair.0` and `y` from `pair.1`.
//
// The pattern on the left mirrors the tuple's shape. It needs one name per member, it may nest
// to unpack a tuple inside a tuple, and `_` stands for a member that is not wanted.
import Io::PrintLine;
func Divide(numerator: int32, denominator: int32) -> (int32, int32) {
return (numerator / denominator, numerator % denominator);
}
func Bounds(values: int32[..]) -> (int32, int32) {
var low = values[0];
var high = values[0];
for value in values {
if value < low {
low = value;
}
if value > high {
high = value;
}
}
return (low, high);
}
func Main() -> int {
// The basic form: one name per member.
let pair = (3, 4);
let (x, y) = pair;
PrintLine("x {} y {}", x, y);
// It reads best straight off a function call that returns several results.
let (quotient, remainder) = Divide(17, 5);
PrintLine("17 / 5 is {} remainder {}", quotient, remainder);
// `_` skips a member. Only the high bound is needed here.
let readings: int32[6] = [12, 9, 15, 4, 7, 11];
let (_, high) = Bounds(readings);
PrintLine("highest reading {}", high);
// `var` makes every name mutable, just as it does for a single binding.
var (low, top) = Bounds(readings);
low -= 1;
top += 1;
PrintLine("padded range {}..={}", low, top);
// A nested pattern unpacks a tuple inside a tuple, all the way down.
let segment = ((0, 0), (6, 8));
let ((x1, y1), (x2, y2)) = segment;
PrintLine("from {},{} to {},{}", x1, y1, x2, y2);
// Destructuring only declares new names. Assigning to existing ones, `(a, b) = (b, a);`,
// is rejected: the left side of `=` has to be a single place.
return 0;
}
```
## Run it
```sh
cd Examples/Sequences/Destructure
rux run
```
```text
x 3 y 4
17 / 5 is 3 remainder 2
highest reading 15
padded range 3..=16
from 0,0 to 6,8
```
## Common mistakes
::warning
**A pattern of the wrong size.**:br`let (a, b) = (1, 2, 3);` fails with `error: tuple pattern has 2 elements but type '(int, int, int)' has 3`. Name every member, or skip the ones you do not need with `_`.
::
::warning
**Destructuring something that is not a tuple.**:br`let (x, y) = 5;` fails with `error: cannot destructure non-tuple type 'int'`.
::
::warning
**Assigning to existing names.**:br`(a, b) = (b, a);` fails with `error: operator '=' requires an assignable target, but its left operand has type '(int, int)'`. Assign the names one at a time, through a temporary.
::
::warning
**The same name twice.**:br`let (p, p) = (1, 2);` fails with `error: variable 'p' is already declared in this scope` — each name in a pattern is a separate declaration.
::
## Try it yourself
1. Keep only the remainder from `Divide(23, 4)` using `_`.
2. Write `let (year, month, day) = (2026, 10, 5);` and print the date as `2026-10-5`.
3. Swap two `var` variables `a` and `b` using a temporary, then try `(a, b) = (b, a);` and read the error.
4. Write a function that returns `((int32, int32), int32)` — a point and a radius — and unpack it with one nested pattern.
## Learn more
- [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) in the Rux Reference
- [Tuple](https://rux-lang.dev/docs/learn/tuple) — the values being unpacked
- [Tuple pattern](https://rux-lang.dev/docs/learn/tuple-pattern) — the same patterns inside `match`
# Part 6: Types
Until now every value has had a built-in type — a number, a string, an array, a tuple. This part lets you declare types of your own, shaped to the problem: a `Point` with an `x` and a `y`, a `Direction` that is one of four, a `Command` that carries exactly the data its case needs. By the end you can give those types behaviour of their own and pass them around without copying them.
## What you will learn
- Grouping named fields into a `struct`, and building values with a struct literal.
- Borrowing a value with `&T` to read it, and with `&var T` to change the caller's value in place.
- Giving a type methods in an `extend` block, with a `self` receiver that reads or writes.
- Building a valid value in one place with a constructor, and adding methods to types you did not write.
- Naming a fixed set of cases with `enum`, and choosing the number behind each case.
- Letting each case carry its own data with `variant`, and taking it apart with `match`.
- Giving a type a second name with `type`, and storing a function in a field.
## How the ideas fit together
```mermaid
flowchart LR
s["struct
all fields at once"] --> r["&T and &var T
borrow instead of copy"]
r --> m["extend + self
methods"]
m --> c["constructors"]
m --> x["extending types
you did not write"]
m --> ff["function fields
behaviour as data"]
e["enum
one of a fixed list"] --> ev["enum values
a number per case"]
e --> v["variant
one case, with its data"]
s --> v
v --> vm["match
take the data out"]
a["type alias
a second name"]
```
Structs are the "and" of types — a point has an `x` **and** a `y`. Enums and variants are the "or" — a direction is north **or** east **or** south **or** west. Most programs need both, and the [Patterns](https://rux-lang.dev/docs/learn/patterns) part that follows is all about taking them apart.
## Lessons
| | Lesson | What you will learn |
| ---- | ---------------------------------------------------------------------- | -------------------------------------------------------------------- |
| 6.1 | [Struct](https://rux-lang.dev/docs/learn/struct) | group related values into a struct with named fields |
| 6.2 | [Reference](https://rux-lang.dev/docs/learn/reference) | borrow a value with `&T` instead of copying it |
| 6.3 | [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) | let a function change the caller's value through `&var T` |
| 6.4 | [Method](https://rux-lang.dev/docs/learn/method) | give a struct behaviour with `extend` and a `self` receiver |
| 6.5 | [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) | a method that changes its receiver through `self: &var T` |
| 6.6 | [Constructor](https://rux-lang.dev/docs/learn/constructor) | build a valid value in one place with a constructor |
| 6.7 | [Extension](https://rux-lang.dev/docs/learn/extension) | add methods to a type you did not write |
| 6.8 | [Enum](https://rux-lang.dev/docs/learn/enum) | name a fixed set of cases |
| 6.9 | [Enum value](https://rux-lang.dev/docs/learn/enum-value) | give an enum an explicit underlying type and convert to and from it |
| 6.10 | [Variant](https://rux-lang.dev/docs/learn/variant) | attach data to each case |
| 6.11 | [Variant match](https://rux-lang.dev/docs/learn/variant-match) | take a variant apart in a `match` arm |
| 6.12 | [Type alias](https://rux-lang.dev/docs/learn/type-alias) | give an existing type a second name to make a signature read clearly |
| 6.13 | [Function field](https://rux-lang.dev/docs/learn/function-field) | store a function in a struct field and call it later |
## Before you start
Finish Parts 1–5 first. This part leans most on [Part 4: Functions](https://rux-lang.dev/docs/learn/functions) — overloads and callbacks come back as constructors and function fields — and on [Part 5: Sequences](https://rux-lang.dev/docs/learn/sequences), whose tuples, arrays and slices appear as fields, receivers and extended types. `match` from [Part 3: Control flow](https://rux-lang.dev/docs/learn/control-flow) is how enums and variants are read.
Each lesson's package is in the Examples repository's `Types/` folder:
```sh
cd Examples/Types/Struct
rux run
```
## After this part
[Part 7: Patterns](https://rux-lang.dev/docs/learn/patterns) shows everything a `match` arm can say — guards, ranges, tuples and struct patterns — on top of the enums and variants you can now declare. [Part 8: Optionals](https://rux-lang.dev/docs/learn/optionals) and [Part 9: Errors](https://rux-lang.dev/docs/learn/errors) then build on variants' idea of "one of several cases" for values that may be missing and operations that may fail. After Part 9, the checkpoint project [Calculator](https://rux-lang.dev/docs/learn/calculator) puts a variant to work describing what went wrong.
For the full rules behind this part, see [Structures](https://rux-lang.dev/docs/lang/structs/overview), [Methods](https://rux-lang.dev/docs/lang/structs/methods), [Enumerations](https://rux-lang.dev/docs/lang/enums/overview), [Variants with data](https://rux-lang.dev/docs/lang/variants/overview) and [Type aliases](https://rux-lang.dev/docs/lang/types/aliases) in the Rux Reference.
# Struct
::note
**You'll need**: [Tuple](https://rux-lang.dev/docs/learn/tuple), [Function](https://rux-lang.dev/docs/learn/function)
::
A [tuple](https://rux-lang.dev/docs/learn/tuple) groups values by position. A **struct** groups them by name, and gives the group a type of its own. A `Point` is not just any two numbers: its `x` cannot be mistaken for its `y` the way `.0` can be mistaken for `.1`, and a function that asks for a `Point` cannot be handed a `(int, int)` that happens to mean something else.
This lesson is only about the data — declaring the fields, building a value, reading and writing the fields. Functions that belong to a struct come in [Method](https://rux-lang.dev/docs/learn/method), a few lessons on.
## Declaring a struct
A declaration is `struct`, a name, and a list of fields in braces. Each field has a name, a colon, a type and a closing semicolon:
```rux
struct Point {
x: int;
y: int;
}
```
A field may have any type, including another struct:
```rux
struct Box {
corner: Point;
width: int;
height: int;
}
```
The declarations sit at the top level of the file, beside the functions, and from then on `Point` and `Box` are types like `int` or `bool`.
| A tuple `(int, int)` | A struct `Point` |
| --------------------------------------- | --------------------------------------------- |
| Fields are read by position: `.0`, `.1` | Fields are read by name: `.x`, `.y` |
| Any two `int`s have the same type | A `Point` is its own type, with its name |
| Written on the spot, no declaration | Declared once, then used everywhere |
| Good for a quick pair from a function | Good for data that means something on its own |
## Building a value
A **struct literal** is the type's name and a value for every field, in braces:
```rux
let origin = Point { x: 0, y: 0 };
let corner = Point { y: 2, x: 5 };
```
Because every value is labelled, the fields may come in any order — `corner` lists `y` first. None may be left out, though: a struct value always has all of its fields, so `Point { x: 1 }` is refused.
A field holding a struct takes a struct value, and is read by chaining the dots:
```rux
let box = Box { corner: corner, width: 4, height: 3 };
PrintLine("box at ({}, {}), {} by {}", box.corner.x, box.corner.y, box.width, box.height);
```
## Changing a field
A field is assigned like a variable, and the same rule applies: the binding must be a `var`. Under `let` the whole struct is read-only, every field included.
```rux
var cursor = Point { x: 1, y: 1 };
cursor.x = 10;
cursor.y += 5;
```
## A struct is a value
A struct can be passed to a function and returned from one, like any other value:
```rux
func Shifted(point: Point, dx: int, dy: int) -> Point {
return Point { x: point.x + dx, y: point.y + dy };
}
```
`Shifted` is handed a **copy** of the point it is called with. It builds a new `Point` and returns that, so the caller's `cursor` comes out exactly as it went in:
```mermaid
flowchart LR
cursor["cursor
(10, 6)"] -- "copied in" --> point["point
(10, 6)"]
point -- "builds" --> result["Point (7, 10)"]
result -- "returned" --> moved["moved
(7, 10)"]
cursor -. "unchanged" .-> after["cursor
(10, 6)"]
```
For two numbers the copy costs nothing. For a large struct it does, and for a function that *should* change the caller's value a copy is no use at all — the next two lessons, [Reference](https://rux-lang.dev/docs/learn/reference) and [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), deal with both.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/Struct){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A tuple groups values by position. A struct groups them by name, and gives the group a type of
// its own: a `Point` is not just any two numbers, and its `x` cannot be mistaken for its `y` the
// way `.0` can be mistaken for `.1`.
//
// This lesson is only about the data: declaring the fields, building a value with a struct
// literal, and reading and writing the fields. Functions that belong to a struct come a few
// lessons later.
import Io::PrintLine;
// A declaration lists the fields, each with its type and a closing semicolon.
struct Point {
x: int;
y: int;
}
// A field may have any type, including another struct.
struct Box {
corner: Point;
width: int;
height: int;
}
// A struct is a type like any other, so a function can take one and give one back.
func Shifted(point: Point, dx: int, dy: int) -> Point {
return Point { x: point.x + dx, y: point.y + dy };
}
func Main() -> int {
// A struct literal is the type's name and a value for every field. The fields may come in
// any order, but none may be left out: `Point { x: 1 }` is rejected for missing `y`.
let origin = Point { x: 0, y: 0 };
let corner = Point { y: 2, x: 5 };
PrintLine("origin ({}, {})", origin.x, origin.y);
PrintLine("corner ({}, {})", corner.x, corner.y);
// A field holding a struct is read by chaining the dots.
let box = Box { corner: corner, width: 4, height: 3 };
PrintLine("box at ({}, {}), {} by {}", box.corner.x, box.corner.y, box.width, box.height);
// A field is assigned like a variable, and the same rule applies: the binding must be a
// `var`. Under `let` the whole struct is read-only, every field included.
var cursor = Point { x: 1, y: 1 };
cursor.x = 10;
cursor.y += 5;
PrintLine("cursor ({}, {})", cursor.x, cursor.y);
// `Shifted` was handed a copy of `cursor` and built a new point, so `cursor` is unchanged.
let moved = Shifted(cursor, -3, 4);
PrintLine("moved ({}, {})", moved.x, moved.y);
PrintLine("cursor ({}, {})", cursor.x, cursor.y);
return 0;
}
```
## Run it
```sh
cd Examples/Types/Struct
rux run
```
```text
origin (0, 0)
corner (5, 2)
box at (5, 2), 4 by 3
cursor (10, 6)
moved (7, 10)
cursor (10, 6)
```
## Common mistakes
::warning
**Leaving a field out.**:br`Point { x: 1 }` fails with `error: initializer for 'Point' is missing required field 'y'`. A struct literal names every field; there are no defaults to fall back on. If building a value takes more than listing its fields, give the type a [constructor](https://rux-lang.dev/docs/learn/constructor).
::
::warning
**Misspelling a field.**:br`Point { x: 1, y: 2, z: 3 }` fails with `error: struct 'Point' has no field 'z'`, and a note lists the fields the struct does have.
::
::warning
**Assigning a field of a `let`.**:br
With `let p = Point { x: 1, y: 2 };`, the line `p.x = 5;` fails with `error: cannot modify immutable variable 'p'`, and the compiler suggests declaring `p` with `var`. A field is part of its struct, and a `let` struct is read-only all the way through.
::
::warning
**Printing a struct with `{}`.**:br`PrintLine("{}", origin)` fails with `error: argument 2 to 'PrintLine' has type 'Point', but variadic parameter 'args' requires 'Display'`. `{}` knows how to print numbers, text and booleans, not types you declared. Print the fields one by one — or, once you reach [Display](https://rux-lang.dev/docs/learn/display), teach the type to print itself.
::
## Try it yourself
1. Write `func Mirrored(point: Point) -> Point` that swaps `x` and `y`, and print `Mirrored(corner)`.
2. Write `func Area(box: Box) -> int` and print the area of `box`.
3. Declare a `Line` struct with two `Point` fields, `start` and `end`, build one, and print all four numbers with chained dots.
4. Change `var cursor` to `let cursor` and read the errors on the two assignments.
## Learn more
- [Structures](https://rux-lang.dev/docs/lang/structs/overview) in the Rux Reference
- [Tuples vs structs](https://rux-lang.dev/docs/lang/tuples/overview#tuples-or-structs) — when each one fits
- [Reference](https://rux-lang.dev/docs/learn/reference) — passing a struct without copying it
- [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern) — taking a struct apart in a `match`
# Reference
::note
**You'll need**: [Struct](https://rux-lang.dev/docs/learn/struct)
::
Passing a struct to a function, as the [Struct](https://rux-lang.dev/docs/learn/struct) lesson did, hands the function a copy. For two numbers that costs nothing, but a struct can be large, and copying it only so a function can *read* it is wasted work.
A **reference**, written `&T`, borrows the caller's value instead. The function reads the original through the reference, and nothing is copied. A plain `&` is read-only: the function may look at the value but not change it, so the caller can lend it out without worrying.
## Borrowing a value
The `&` goes in front of the parameter's type:
```rux
func Area(room: &Room) -> float64 {
return room.width * room.length;
}
```
```mermaid
flowchart LR
subgraph copy ["room: Room — by value"]
k1["kitchen"] -- "copied" --> r1["room
(a second Room)"]
end
subgraph borrow ["room: &Room — by reference"]
r2["room"] -- "refers to" --> k2["kitchen
(the only Room)"]
end
```
Two things keep references light to use:
- **The call site writes nothing extra.** `Describe(kitchen)` borrows `kitchen` because the parameter asks for a `&Room`. The function's signature decides, not the caller.
- **A reference is followed automatically.** `room.width` reads the field of the borrowed room — there is no separate operator to "go through" the reference first.
## Read-only, and passed on
A function that borrows a value may lend it on. `Describe` hands the room it borrowed straight to `Area`:
```rux
func Describe(room: &Room) {
PrintLine("{}: {} by {} metres, {} square metres", room.name, room.width, room.length,
Area(room));
}
```
What it may not do is change it. `room.width = 0.0;` inside `Describe` is refused: a `&Room` is a promise to the caller that their room comes back untouched. Lending for writing is a different kind of reference, `&var`, and the subject of the [next lesson](https://rux-lang.dev/docs/learn/mutable-reference).
## Anything can be borrowed
A reference is not only for structs. An array of three floats is worth borrowing too:
```rux
func Widest(widths: &float64[3]) -> float64 {
var widest = widths[0];
for i in 1..3 {
if widths[i] > widest {
widest = widths[i];
}
}
return widest;
}
```
Inside, `widths[i]` indexes the caller's array directly, just as `room.width` read the caller's field.
## A reference as a local
A reference can also be a local variable — a second name for a value that already exists:
```rux
let chosen: &Room = kitchen;
PrintLine("chosen: the {}", chosen.name);
```
`chosen` is not a new room. It is another way to reach `kitchen`.
| Parameter | What the function gets | Copies the value | Can change the caller's value | What can be passed |
| ------------- | --------------------------- | ---------------- | ----------------------------- | ------------------------ |
| `room: Room` | its own copy | yes | no | any `Room` value |
| `room: &Room` | the caller's value, to read | no | no | a named `Room` to borrow |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/Reference){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Passing a struct to a function, as the Struct lesson did, hands the function a copy. For two
// numbers that costs nothing, but a struct can be large, and copying it only so a function can
// read it is wasted work.
//
// A reference, written `&T`, borrows the caller's value instead. The function reads the original
// through the reference and nothing is copied. A plain `&` is read-only: the function may look at
// the value but not change it, so the caller can lend it out without worrying.
//
// Two things keep references light to use. The call site writes nothing extra: `Area(kitchen)`
// borrows `kitchen` because the parameter asks for a `&Room`. And a reference is followed
// automatically, so `room.width` reads the field of the borrowed room.
import Io::PrintLine;
struct Room {
name: char8[..];
width: float64;
length: float64;
}
func Area(room: &Room) -> float64 {
return room.width * room.length;
}
// A reference can be passed on: `Describe` lends the room it borrowed to `Area`. Assigning through
// it is another matter, and `room.width = 0.0;` here would be rejected.
func Describe(room: &Room) {
PrintLine("{}: {} by {} metres, {} square metres", room.name, room.width, room.length,
Area(room));
}
// Any type can be borrowed, an array included.
func Widest(widths: &float64[3]) -> float64 {
var widest = widths[0];
for i in 1..3 {
if widths[i] > widest {
widest = widths[i];
}
}
return widest;
}
func Main() -> int {
let kitchen = Room { name: "kitchen", width: 3.5, length: 4.0 };
let hall = Room { name: "hall", width: 1.5, length: 6.0 };
Describe(kitchen);
Describe(hall);
// A reference can also be a local: a second name for a value that already exists.
let chosen: &Room = kitchen;
PrintLine("chosen: the {}", chosen.name);
let widths: float64[3] = [3.5, 1.5, 2.75];
PrintLine("widest: {} metres", Widest(widths));
// A borrow needs something to borrow from. A struct built on the spot has no name to lend,
// so `Area(Room { ... })` stops with
// error: argument 1 to 'Area' has type 'Room', but parameter 'room' requires '&Room'
// Bind it with `let` first, as above.
return 0;
}
```
## Run it
```sh
cd Examples/Types/Reference
rux run
```
```text
kitchen: 3.5 by 4.0 metres, 14.0 square metres
hall: 1.5 by 6.0 metres, 9.0 square metres
chosen: the kitchen
widest: 3.5 metres
```
## Common mistakes
::warning
**Borrowing something that has no name.**:br
A borrow needs a value to borrow from. `Area(Room { name: "x", width: 1.0, length: 2.0 })` fails with `error: argument 1 to 'Area' has type 'Room', but parameter 'room' requires '&Room'`, and so does passing a plain literal to a `&int` parameter. Bind the value with `let` first, then pass the name.
::
::warning
**Writing through a `&`.**:br`room.width = 0.0;` in a function that takes `room: &Room` fails with `error: cannot modify data through immutable reference '&Room'`. If the function really must change the caller's value, it needs a `&var Room` — see [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference).
::
::warning
**Changing a value while a reference to it is in use.**:br
With `var kitchen` and `let chosen: &Room = kitchen;`, assigning `kitchen.width = 9.0;` and then reading `chosen.width` fails with `error: cannot modify 'kitchen.width' while it is immutably borrowed`. The compiler will not let a value change under a reader that is still looking at it. Finish with `chosen` first; once its last use is behind you, `kitchen` is free to change. [Exclusivity](https://rux-lang.dev/docs/learn/exclusivity) covers the rule in full.
::
## Try it yourself
1. Write `func Perimeter(room: &Room) -> float64` and add the perimeter to what `Describe` prints.
2. Write `func Narrowest(widths: &float64[3]) -> float64` next to `Widest`.
3. Add `room.width = 0.0;` to `Describe` and read the error.
4. Call `Area` with a struct literal, read the error, then fix it with a `let`.
## Learn more
- [Function declaration](https://rux-lang.dev/docs/lang/functions/declaration) — parameters and return types, in the Rux Reference
- [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — lending a value for writing
- [Exclusivity](https://rux-lang.dev/docs/learn/exclusivity) — why a borrowed value cannot change under its reader
- [Pointer](https://rux-lang.dev/docs/learn/pointer) — the lower-level relative of a reference
# Mutable reference
::note
**You'll need**: [Reference](https://rux-lang.dev/docs/learn/reference)
::
A `&T` reference lets a function look at the caller's value. A `&var T` reference lets it **change** that value: the writes land in the caller's own storage, so the caller sees them as soon as the call returns. Nothing is copied in, and nothing needs to be handed back.
This is how a function updates something it was given — a bank balance, a counter, a game's score — without the caller having to write `account = Deposit(account, 50);`.
## Writing through \&var
The parameter's type is `&var` and the type being lent:
```rux
func Deposit(account: &var Account, amount: int) {
account.balance += amount;
account.deposits += 1;
}
```
As with `&`, the call site writes nothing extra, and the changes are visible right after:
```rux
Deposit(alice, 50);
Deposit(alice, 25);
```
After those two calls `alice` itself has a balance of 175 and two deposits.
## Both ends have to agree
The parameter says `&var`, and the value passed in must be a `var`. A binding declared with `let` was promised never to change, and lending it out for writing would break that promise:
```rux
var alice = Account { owner: "Alice", balance: 100, deposits: 0 };
```
Had `alice` been a `let`, every `Deposit(alice, …)` would be refused.
## Replacing the whole value
Assigning to a field changes that field. Assigning to the reference itself replaces the caller's whole value in one step:
```rux
func Close(account: &var Account) {
account = Account { owner: account.owner, balance: 0, deposits: 0 };
}
```
## Any type can be lent
A `&var` is not only for structs. A plain integer can be lent for writing too:
```rux
func Tick(count: &var int) {
count += 1;
}
```
Three calls to `Tick(visits)` leave `visits` at 3.
## One writer at a time
Two *different* accounts may be lent for writing in the same call:
```rux
Transfer(alice, bob, 70);
```
The same account twice, `Transfer(alice, alice, 5)`, is refused. Inside `Transfer`, `from` and `to` would be two names for one value, and every write through one would silently change what the other sees. The rule behind this — while something may write a value, nothing else may touch it — is [Exclusivity](https://rux-lang.dev/docs/learn/exclusivity), in the Ownership part.
## Choosing a parameter type
You now have three ways to take an argument:
| Parameter | What the function gets | Can change the caller's value | The caller passes |
| ----------------- | ----------------------------- | ----------------------------- | ------------------- |
| `a: Account` | its own copy, read-only | no | any `Account` value |
| `a: &Account` | the caller's value, to read | no | a named `Account` |
| `a: &var Account` | the caller's value, to change | yes | a `var` `Account` |
```mermaid
flowchart LR
q1{"Must the function change
the caller's value?"} -- "yes" --> rv["&var T"]
q1 -- "no" --> q2{"Is T large, or is copying it
wasted work?"}
q2 -- "yes" --> r["&T"]
q2 -- "no — a number, a bool" --> v["T"]
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/MutableReference){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `&T` reference lets a function look at the caller's value. A `&var T` reference lets it change
// that value: the writes land in the caller's own storage, so the caller sees them once the call
// returns. Nothing is copied in, and nothing needs to be handed back.
//
// Both ends have to agree. The parameter says `&var`, and the value passed in must be a `var`. A
// binding declared with `let` was promised never to change, and lending it out for writing would
// break that promise, so `Deposit(savings, 10)` with a `let savings` is rejected.
import Io::PrintLine;
struct Account {
owner: char8[..];
balance: int;
deposits: int;
}
// Writing a field through the reference changes that field of the caller's account.
func Deposit(account: &var Account, amount: int) {
account.balance += amount;
account.deposits += 1;
}
// Two different accounts may be borrowed for writing in the same call. The same account twice,
// `Transfer(alice, alice, 5)`, is rejected: two writers to one value at once is the kind of
// conflict the Ownership part is about.
func Transfer(from: &var Account, to: &var Account, amount: int) {
from.balance -= amount;
to.balance += amount;
}
// Assigning to the reference itself replaces the caller's whole value in one step.
func Close(account: &var Account) {
account = Account { owner: account.owner, balance: 0, deposits: 0 };
}
// Any type can be lent this way, a plain integer included.
func Tick(count: &var int) {
count += 1;
}
func Show(account: &Account) {
PrintLine("{}: balance {}, deposits {}", account.owner, account.balance, account.deposits);
}
func Main() -> int {
var alice = Account { owner: "Alice", balance: 100, deposits: 0 };
var bob = Account { owner: "Bob", balance: 20, deposits: 0 };
// As with `&`, the call site writes nothing extra. The changes are visible right after.
Deposit(alice, 50);
Deposit(alice, 25);
Deposit(bob, 5);
Show(alice);
Show(bob);
Transfer(alice, bob, 70);
Show(alice);
Show(bob);
Close(bob);
Show(bob);
var visits = 0;
Tick(visits);
Tick(visits);
Tick(visits);
PrintLine("visits: {}", visits);
return 0;
}
```
## Run it
```sh
cd Examples/Types/MutableReference
rux run
```
```text
Alice: balance 175, deposits 2
Bob: balance 25, deposits 1
Alice: balance 105, deposits 2
Bob: balance 95, deposits 1
Bob: balance 0, deposits 0
visits: 3
```
## Common mistakes
::warning
**Lending a `let` for writing.**:br
With `let savings = Account { … };`, the call `Deposit(savings, 10)` fails with `error: argument 1 to 'Deposit' cannot borrow immutable 'savings' as '&var Account'`, and the compiler suggests declaring `savings` with `var`.
::
::warning
**Changing a by-value parameter.**:br`func Tick(count: int) { count += 1; }` fails with `error: cannot modify parameter 'count'`. A parameter is read-only, and even if it were not, it would be the function's own copy — the caller would never see the change. The compiler's help says it: take `count` as `&var int` to change the caller's value.
::
::warning
**Lending one value twice.**:br`Transfer(alice, alice, 5)` fails with `error: call arguments create overlapping exclusive borrows of 'alice'`. Each `&var` argument must be a different value.
::
::warning
**Passing a literal to `&var`.**:br`Tick(5)` fails with `error: argument 1 to 'Tick' has type 'int', but parameter 'count' requires '&var int'`. There is nowhere for the change to land: a `&var` needs a variable to write into.
::
## Try it yourself
1. Write `func Withdraw(account: &var Account, amount: int) -> bool` that refuses (returns `false`) when the balance is too small, and try it on both accounts.
2. Declare `var counts: int[3] = [0, 0, 0];` and call `Tick(counts[1])` twice, then print the three elements. An array element is a place, so it can be lent for writing too.
3. Change `var bob` to `let bob` and read the errors.
4. Write `func Swap(a: &var int, b: &var int)` and use it to swap two variables. Give the temporary its type, `let saved: int = a;` — written as plain `let saved = a;`, it would be one more reference to `a` rather than a copy of its number, and the compiler would refuse the swap.
## Learn more
- [Reference](https://rux-lang.dev/docs/learn/reference) — the read-only kind
- [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) — a method that changes the value it is called on
- [Exclusivity](https://rux-lang.dev/docs/learn/exclusivity) — why one value cannot be lent for writing twice
- [Out parameter](https://rux-lang.dev/docs/learn/out-parameter) — the same idea with pointers
# Method
::note
**You'll need**: [Struct](https://rux-lang.dev/docs/learn/struct), [Reference](https://rux-lang.dev/docs/learn/reference)
::
So far a function that works on a struct stands on its own and is handed the struct: `Area(card)`. A **method** is a function that belongs to a type and is called on a value of it: `card.Area()`. The value comes first, so the reader sees at once what is being asked about — and the type's behaviour is gathered in one place instead of scattered among free functions.
## The extend block
Methods are declared in an `extend` block, apart from the struct. The struct stays a plain list of fields; the `extend` block holds what the type can do:
```rux
struct Rectangle {
width: float64;
height: float64;
}
extend Rectangle {
func Area(self: &Rectangle) -> float64 {
return self.width * self.height;
}
}
```
## The self receiver
The value a method works on is its first parameter, called the **receiver**. It is always named `self`, and its type is written out like any other parameter's. `self: &Rectangle` is the read-only reference from the [Reference](https://rux-lang.dev/docs/learn/reference) lesson, so these methods may read the rectangle but not change it.
Inside a method, fields are always reached through `self`. `self` is an ordinary parameter, not an implied scope — a bare `width` would be an unknown name.
## Calling a method
The value before the dot becomes `self`, borrowed just as an argument would be. The remaining arguments go in the parentheses:
```rux
PrintLine("card in envelope {}", card.FitsInside(envelope));
```
```mermaid
flowchart LR
call["card.FitsInside(envelope)"] --> self["self = card
(borrowed as &Rectangle)"]
call --> other["other = envelope"]
self --> body["body of Rectangle's FitsInside"]
other --> body
```
That call matches the declaration parameter for parameter — `self` first, then `other`:
```rux
func FitsInside(self: &Rectangle, other: &Rectangle) -> bool {
return self.width <= other.width && self.height <= other.height;
}
```
## Same name, different types
Each type has its own methods. `Circle` can have an `Area` of its own without clashing with the one on `Rectangle`:
```rux
extend Circle {
func Area(self: &Circle) -> float64 {
return 3.14159 * self.radius * self.radius;
}
}
```
Which one runs is decided by the value before the dot: `card.Area()` runs the rectangle's, `coin.Area()` the circle's. Two free functions named `Area` would have to be told apart by their parameter types, as [overloading](https://rux-lang.dev/docs/learn/overload) does; methods simply live with their type.
| Free function | Method |
| --------------------------------------- | ----------------------------------------- |
| `func Area(card: &Rectangle)` | `func Area(self: &Rectangle)` in `extend` |
| Called as `Area(card)` | Called as `card.Area()` |
| Lives among all the package's functions | Lives with its type |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/Method){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// So far a function that works on a struct stands on its own and is handed the struct:
// `Area(card)`. A method is a function that belongs to a type and is called on a value of it:
// `card.Area()`. The value comes first, and the reader sees at once what is being asked about.
//
// Methods are declared in an `extend` block, apart from the struct, which stays a plain list of
// fields. The value a method works on is its first parameter, which is always named `self` and
// has its type written out like any other parameter. `self: &Rectangle` is the read-only
// reference from the Reference lesson, so these methods may read the rectangle but not change it.
import Io::PrintLine;
struct Rectangle {
width: float64;
height: float64;
}
extend Rectangle {
// Inside a method, fields are always reached through `self`. A bare `width` would be an
// unknown name: `self` is an ordinary parameter, not an implied scope.
func Area(self: &Rectangle) -> float64 {
return self.width * self.height;
}
func IsSquare(self: &Rectangle) -> bool {
return self.width == self.height;
}
// Further parameters come after `self`.
func FitsInside(self: &Rectangle, other: &Rectangle) -> bool {
return self.width <= other.width && self.height <= other.height;
}
}
struct Circle {
radius: float64;
}
// Each type has its own methods, so `Circle` can have an `Area` of its own without clashing with
// the one on `Rectangle`. Which one runs is decided by the value before the dot.
extend Circle {
func Area(self: &Circle) -> float64 {
return 3.14159 * self.radius * self.radius;
}
}
func Main() -> int {
let card = Rectangle { width: 9.0, height: 6.0 };
let envelope = Rectangle { width: 11.0, height: 11.0 };
let coin = Circle { radius: 1.25 };
// The value before the dot becomes `self`, borrowed just as an argument would be.
PrintLine("card area {}", card.Area());
PrintLine("envelope area {}", envelope.Area());
PrintLine("coin area {:.2}", coin.Area());
PrintLine("card square {}", card.IsSquare());
PrintLine("envelope square {}", envelope.IsSquare());
// The remaining arguments go in the parentheses, as for any call.
PrintLine("card in envelope {}", card.FitsInside(envelope));
PrintLine("envelope in card {}", envelope.FitsInside(card));
return 0;
}
```
## Run it
```sh
cd Examples/Types/Method
rux run
```
```text
card area 54.0
envelope area 121.0
coin area 4.91
card square false
envelope square true
card in envelope true
envelope in card false
```
## Common mistakes
::warning
**Reaching a field without `self`.**:br`return width * self.height;` fails with `error: name 'width' is not defined in this scope`. Write `self.width`: fields are never in scope by themselves.
::
::warning
**Naming the receiver something else.**:br
A first parameter called `this` is not a receiver, just an ordinary parameter. With `func Area(this: &Rectangle)`, the call `card.Area()` fails with `error: call to 'Area' expects 1 argument, but 0 were provided`. The receiver is always `self`.
::
::warning
**Calling a method like a free function.**:br`Area(card)` fails with `error: name 'Area' is not defined in this scope`. A method belongs to its type, so it is reached through a value: `card.Area()`.
::
::warning
**Leaving out the parentheses.**:br`card.Area` without `()` is read as a field, and fails with `error: struct 'Rectangle' has no field 'Area'`. A method is called, even when it takes no further arguments.
::
## Try it yourself
1. Add `func Perimeter(self: &Rectangle) -> float64` and print the card's perimeter.
2. Give `Circle` an `IsLargerThan(self: &Circle, other: &Circle) -> bool` method.
3. Add `func Scaled(self: &Rectangle, factor: float64) -> Rectangle` that returns a new rectangle, and print the area of `card.Scaled(2.0)`.
4. Remove `self.` from one field in `Area` and read the error.
## Learn more
- [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference
- [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) — a method that changes its receiver
- [Extension](https://rux-lang.dev/docs/learn/extension) — adding methods to types you did not write
- [Interface](https://rux-lang.dev/docs/learn/interface) — a set of methods that many types share
# Mutating method
::note
**You'll need**: [Method](https://rux-lang.dev/docs/learn/method), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference)
::
The methods in the last lesson only looked at their rectangle. A method that **changes** the value it is called on takes `self: &var T` — the writable reference from [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference). Its writes land in the value before the dot.
This is where methods start to pay off. A `Stack` is an array plus a count of how much of it is in use, and the two must change together:
```rux
struct Stack {
items: int[4];
length: uint;
}
```
If every caller updated `items` and `length` by hand, one of them would sooner or later forget the count. With `Push` and `Pop` as the only code that changes them, they stay in step.
## Reading and writing receivers
The receiver's type says what a method may do:
```rux
func IsEmpty(self: &Stack) -> bool {
return self.length == 0;
}
```
```rux
func Push(self: &var Stack, value: int) -> bool {
if self.IsFull() {
return false;
}
self.items[self.length] = value;
self.length += 1;
return true;
}
```
`IsEmpty` takes `&Stack` and only reads. `Push` takes `&var Stack`, so it may assign `self.items[…]` and `self.length`. A writing method may also return a value — here, whether the push happened, since a full stack has no room left.
`Push` calls `self.IsFull()` on its own receiver: a writing method can always use the reading ones.
## How the stack moves
`Push` writes at position `length` and then counts the new item; `Pop` uncounts the top item and then reads it. The last value in is the first one out:
```mermaid
flowchart LR
e["length 0
[ _ _ _ _ ]"] -- "Push(10)" --> a["length 1
[ 10 _ _ _ ]"]
a -- "Push(20)" --> b["length 2
[ 10 20 _ _ ]"]
b -- "Pop() gives 20" --> c["length 1
[ 10 _ _ _ ]"]
```
`Pop` does not erase the old value — it stays in the array, but past `length` it no longer counts.
## The caller needs a var
A writing method needs a `var` to work on, just as assigning a field would:
```rux
var stack = Stack { items: [0; 4], length: 0 };
```
| Method | Receiver | On a `let stack` | On a `var stack` |
| ------------------- | ------------ | ---------------- | ---------------- |
| `IsEmpty`, `IsFull` | `&Stack` | allowed | allowed |
| `Push`, `Pop` | `&var Stack` | refused | allowed |
## A rule the caller keeps
`Pop` has a precondition: it is only to be called on a stack that is not empty. The program keeps it by asking first:
```rux
while !stack.IsEmpty() {
PrintLine("pop {}", stack.Pop());
}
```
Making a method report "there was nothing to pop" in its return type is what [Optionals](https://rux-lang.dev/docs/learn/optionals) are for, a couple of parts on.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/MutatingMethod){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A method that changes the value it is called on takes `self: &var T`, the writable reference
// from the MutableReference lesson. Its writes land in the value before the dot.
//
// This is where methods start to pay off. A `Stack` is an array plus a count of how much of it is
// in use, and the two must change together. If every caller updated them by hand, one would
// sooner or later forget the count. With `Push` and `Pop` as the only code that changes them,
// they stay in step.
import Io::PrintLine;
struct Stack {
items: int[4];
length: uint;
}
extend Stack {
// A reading method still takes `&Stack`, and can be called on any stack.
func IsEmpty(self: &Stack) -> bool {
return self.length == 0;
}
func IsFull(self: &Stack) -> bool {
return self.length == 4;
}
// A writing method takes `&var Stack`. It may also return a value: here, whether the push
// happened, since a full stack has no room left.
func Push(self: &var Stack, value: int) -> bool {
if self.IsFull() {
return false;
}
self.items[self.length] = value;
self.length += 1;
return true;
}
// Only to be called on a stack that is not empty; the caller checks `IsEmpty` first.
func Pop(self: &var Stack) -> int {
self.length -= 1;
return self.items[self.length];
}
}
func Main() -> int {
// A writing method needs a `var` to work on, just as assigning a field would. Had `stack`
// been declared with `let`, every `Push` and `Pop` below would be rejected, while `IsEmpty`
// and `IsFull` would still be allowed.
var stack = Stack { items: [0; 4], length: 0 };
for value in 1..=5 {
let pushed = stack.Push(value * 10);
PrintLine("push {} accepted {}", value * 10, pushed);
}
PrintLine("full {}", stack.IsFull());
while !stack.IsEmpty() {
PrintLine("pop {}", stack.Pop());
}
PrintLine("empty {}", stack.IsEmpty());
return 0;
}
```
## Run it
```sh
cd Examples/Types/MutatingMethod
rux run
```
```text
push 10 accepted true
push 20 accepted true
push 30 accepted true
push 40 accepted true
push 50 accepted false
full true
pop 40
pop 30
pop 20
pop 10
empty true
```
## Common mistakes
::warning
**Calling a writing method on a `let`.**:br
With `let stack = …`, the call `stack.Push(1)` fails with `error: cannot call 'Push' on immutable 'stack'`. A note points out that `Push` declares a writable receiver `&var Stack`, and the help suggests declaring `stack` with `var`. `stack.IsEmpty()` is still fine.
::
::warning
**Writing through a read-only receiver.**:br
A `Clear` method declared as `func Clear(self: &Stack)` that assigns `self.length = 0;` fails with `error: cannot modify data through immutable reference '&Stack'`.
::
::warning
**Taking the receiver by value.**:br`func Clear(self: Stack)` would get a copy, and changing a copy would change nothing. The compiler refuses the write with `error: cannot modify immutable receiver 'self'`, and its help says what to do: take the receiver as `self: &var Stack` to change the caller's value.
::
::warning
**Popping an empty stack.**:br`Pop` does not check. On an empty stack, `self.length -= 1` takes a `uint` below zero, it wraps round to a huge number, and the read stops the program with `Panic: index out of range`. Check `IsEmpty` first.
::
## Try it yourself
1. Add `func Peek(self: &Stack) -> int` that returns the top item without removing it. Which receiver does it need?
2. Add `func Clear(self: &var Stack)` and use it to empty a full stack in one call.
3. Change `var stack` to `let stack` and read which lines the compiler refuses — and which it still accepts.
4. Add a `Size(self: &Stack) -> uint` method and print the size after every push.
## Learn more
- [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference
- [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — the `&var` behind the receiver
- [Constructor](https://rux-lang.dev/docs/learn/constructor) — building a valid value in one place
- [Optional](https://rux-lang.dev/docs/learn/optional) — a `Pop` that can say "nothing here"
# Constructor
::note
**You'll need**: [Method](https://rux-lang.dev/docs/learn/method), [Overload](https://rux-lang.dev/docs/learn/overload)
::
A struct literal names every field at every place a value is built. That is fine for a `Point`, but some types have rules. A time of day has minutes from 0 to 59, and a literal will happily accept `Time { hours: 7, minutes: 95 }`.
A **constructor** is a function that builds the value for you, so the rules are written once instead of at every literal. It is called by the type's name, like a function: `Time(9, 30)`.
## Declaring a constructor
A constructor lives in the type's `extend` block, beside its methods, and differs from them in three ways:
- it has the **same name as the type**;
- it takes **no `self`** — there is no value yet to call it on;
- it **returns the type**.
```rux
extend Time {
func Time(hours: int, minutes: int) -> Time {
return Time(hours * 60 + minutes);
}
// The second constructor and the methods follow.
}
```
| Kind of function | Name | First parameter | Called as |
| ---------------- | -------------- | ---------------------------- | -------------- |
| Method | anything | `self: &Time` or `&var Time` | `start.Show()` |
| Constructor | `Time`, always | none | `Time(9, 30)` |
## Constructors overload
A type can have several constructors, told apart by their parameters — the [overloading](https://rux-lang.dev/docs/learn/overload) you already know from ordinary functions. This one takes a single count of minutes and does the real work:
```rux
func Time(totalMinutes: int) -> Time {
let inDay = totalMinutes % (24 * 60);
return Time { hours: inDay / 60, minutes: inDay % 60 };
}
```
The two-argument constructor turns its hours and minutes into a total and hands it to this one, so the wrapping is written exactly once:
```mermaid
flowchart LR
a["Time(7, 95)"] -- "7 × 60 + 95" --> b["Time(515)"]
b -- "515 % 1440 = 515" --> c["Time { hours: 8, minutes: 35 }"]
```
At the end, the struct literal is still how the value is finally made. A constructor does not replace literals; it decides what goes into one.
## Building new values from old ones
Since a constructor accepts any count of minutes, "135 minutes later" is just another call:
```rux
let later = Time(start.TotalMinutes() + 135);
```
And past midnight the clock wraps, because `% (24 * 60)` keeps the total inside one day.
## A convenience, not a lock
The struct literal is still allowed, rules or no rules: `Time { hours: 7, minutes: 95 }` compiles and makes a time that is not a time. A constructor is a convenience the type offers; it does not lock the door. Hiding the fields so the constructor is the only way in is what [Visibility](https://rux-lang.dev/docs/learn/visibility) is about.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/Constructor){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A struct literal names every field at every place a value is built. That is fine for a `Point`,
// but some types have rules: a time of day has minutes from 0 to 59, and a literal will happily
// accept `Time { hours: 7, minutes: 95 }`.
//
// A constructor is a function that builds the value for you. It is declared inside the type's
// `extend` block, has the same name as the type, takes no `self` (there is no value yet), and
// returns the type. It is called by the type's name, like a function: `Time(9, 30)`.
import Io::PrintLine;
struct Time {
hours: int;
minutes: int;
}
extend Time {
// The plain form: one argument per field, wrapped round the clock so the fields stay valid.
func Time(hours: int, minutes: int) -> Time {
return Time(hours * 60 + minutes);
}
// Constructors overload like any function. This one does real work, splitting a count of
// minutes into hours and minutes, which is what the other constructor relies on. The struct
// literal is still how the value is finally made.
func Time(totalMinutes: int) -> Time {
let inDay = totalMinutes % (24 * 60);
return Time { hours: inDay / 60, minutes: inDay % 60 };
}
func TotalMinutes(self: &Time) -> int {
return self.hours * 60 + self.minutes;
}
func Show(self: &Time) {
PrintLine("{:02}:{:02}", self.hours, self.minutes);
}
}
func Main() -> int {
let start = Time(9, 30);
start.Show();
// Minutes past 59 carry into the hours, which a struct literal would not do.
let odd = Time(7, 95);
odd.Show();
// Building a new value from an old one is a matter of calling a constructor again.
let later = Time(start.TotalMinutes() + 135);
later.Show();
// Past midnight, the clock wraps.
let overnight = Time(later.TotalMinutes() + 14 * 60);
overnight.Show();
// The struct literal is still allowed, rules or no rules. A constructor is a convenience the
// type offers; it does not lock the door. Hiding the fields so the constructor is the only
// way in is what the Visibility lesson is about.
return 0;
}
```
## Run it
```sh
cd Examples/Types/Constructor
rux run
```
```text
09:30
08:35
11:45
01:45
```
## Common mistakes
::warning
**Calling a type that has no constructor.**:br
Without a constructor, `Point(1, 2)` fails with `error: type 'Point' cannot be called because it has no declared constructor`. The help names both ways out: declare a receiverless constructor in its `extend` block, or build the value with a struct literal.
::
::warning
**Declaring the constructor outside `extend`.**:br
A top-level `func Time(…) -> Time` fails with `error: name 'Time' cannot be declared as a function because it is already a type in this scope`. A constructor belongs inside the type's `extend` block.
::
::warning
**Returning something other than the type.**:br`func Time(hours: int, minutes: int) -> int` in `extend Time` fails with `error: constructor 'Time' must return exactly 'Time'`.
::
::warning
**No constructor matches the arguments.**:br
With only the two-argument constructor declared, `Time(9)` fails with `error: call to 'Time' expects 2 arguments, but 1 was provided`. Each set of arguments needs a constructor of its own.
::
## Try it yourself
1. Write `Time { hours: 7, minutes: 95 }` as a literal, call `Show` on it, and compare with `Time(7, 95)`.
2. Add a method `func Later(self: &Time, minutes: int) -> Time` that uses a constructor, and print `start.Later(45)`.
3. Add `func Midnight() -> Time` to the `extend` block — no `self`, and a name that is not the type's. Call it as `Time::Midnight()`.
4. Make the one-argument constructor handle a negative count, so that `Time(-30)` shows `23:30`.
## Learn more
- [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference
- [Overload](https://rux-lang.dev/docs/learn/overload) — several functions with one name
- [Visibility](https://rux-lang.dev/docs/learn/visibility) — making the constructor the only way in
- [Struct](https://rux-lang.dev/docs/learn/struct) — the struct literal a constructor fills in
# Extension
::note
**You'll need**: [Method](https://rux-lang.dev/docs/learn/method), [Slice](https://rux-lang.dev/docs/learn/slice)
::
`extend` is not only for types you declared yourself. It can add methods to a type you did **not** write — the built-in ones included — and from then on, in this package, every value of that type has them. `42.IsEven()` reads better than `IsEven(42)`, and a slice that can `Sum()` itself saves passing it to a helper every time.
## Extending a built-in type
The block looks exactly like the ones in [Method](https://rux-lang.dev/docs/learn/method), with a built-in type's name after `extend`:
```rux
extend int {
func IsEven(self: int) -> bool {
return self % 2 == 0;
}
// Clamped follows in the same block.
}
```
From here on, any `int` can be asked: `answer.IsEven()`, `volume.Clamped(0, 100)`.
## Receivers by value
These receivers are taken **by value** — `self: int`, not `self: &int`. A number or a slice is small, and copying it is as cheap as borrowing it. The three receiver forms now on the table:
| Receiver | The method gets | Typical for |
| -------------- | ----------------------------- | ----------------------------------------- |
| `self: T` | a copy | small values: numbers, slices, enum cases |
| `self: &T` | the caller's value, to read | structs and arrays that only need reading |
| `self: &var T` | the caller's value, to change | methods that update the value |
A slice is a view, so even taken by value it still sees the caller's elements — copying the slice copies the view, not the numbers behind it.
## Extending a slice
Any type can be extended, compound types included:
```rux
extend int[..] {
func Sum(self: int[..]) -> int {
var total = 0;
for value in self {
total += value;
}
return total;
}
// CountEven follows in the same block.
}
```
Inside `CountEven`, the `IsEven` added a few lines earlier is used like any other method: `value.IsEven()`. Extensions build on one another.
## One exact type
The extended type must be one exact type, and methods do not spread to its relatives:
```mermaid
flowchart LR
ext["extend int[..]"] --> yes["int[..]
has Sum and CountEven"]
ext -. "not" .-> no1["uint8[..]"]
ext -. "not" .-> no2["int32[..]"]
ext -. "not" .-> no3["int[5]
(an array, not a slice)"]
```
`int32` is not `int`, `uint8[..]` is not `int[..]`, and an array is not a slice. Each would need an `extend` of its own. An array reaches the slice methods through a view of itself:
```rux
PrintLine("sum of the last 3: {}", scores[2..].Sum());
```
The same view, `scores[..]` or the `let all: int[..] = scores;` in the program, gives the whole array.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/Extension){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `extend` is not only for types you declared yourself. It can add methods to a type you did not
// write, the built-in ones included, and from then on, in this package, every value of that type
// has them.
//
// The receiver here is taken by value: `self: int`, not `self: &int`. A number or a slice is small,
// and copying it is as cheap as borrowing it. A slice is a view, so even by value it still sees
// the caller's elements.
import Io::PrintLine;
extend int {
func IsEven(self: int) -> bool {
return self % 2 == 0;
}
func Clamped(self: int, low: int, high: int) -> int {
if self < low {
return low;
}
if self > high {
return high;
}
return self;
}
}
// The extended type must be one exact type. `int[..]` gains these methods, but a `uint8[..]` or an
// `int32[..]` does not: it is a different type, and would need an `extend` of its own.
extend int[..] {
func Sum(self: int[..]) -> int {
var total = 0;
for value in self {
total += value;
}
return total;
}
func CountEven(self: int[..]) -> uint {
var count: uint = 0;
for value in self {
// A method added above is used like any other.
if value.IsEven() {
count += 1;
}
}
return count;
}
}
func Main() -> int {
let answer = 42;
let volume = 130;
PrintLine("{} is even: {}", answer, answer.IsEven());
PrintLine("{} clamped: {}", volume, volume.Clamped(0, 100));
let scores: int[5] = [7, 12, 9, 20, 4];
let all: int[..] = scores;
PrintLine("sum of all: {}", all.Sum());
PrintLine("even scores: {}", all.CountEven());
// An array is not a slice, so `scores.Sum()` stops with
// error: type 'int[5]' has no field 'Sum'
// The slice methods are reached through a view of the array instead.
PrintLine("sum of the last 3: {}", scores[2..].Sum());
return 0;
}
```
## Run it
```sh
cd Examples/Types/Extension
rux run
```
```text
42 is even: true
130 clamped: 100
sum of all: 52
even scores: 3
sum of the last 3: 33
```
## Common mistakes
::warning
**Calling a slice method on an array.**:br`scores.Sum()` fails with `error: type 'int[5]' has no field 'Sum'`. `int[5]` is an array, a different type from `int[..]`. Take a view first: `scores[..].Sum()`.
::
::warning
**Expecting a method on a related type.**:br`extend int` does not reach `int32`: with `let small: int32 = 4;`, the call `small.Double()` fails with `error: type 'int32' has no field 'Double'`. Likewise a `uint8[..]` view has no `Sum` — the error is `error: slice type 'uint8[..]' has no member 'Sum'`, with a note listing the members a slice does have.
::
## Try it yourself
1. Add `func IsPositive(self: int) -> bool` to `extend int` and use it.
2. Add `func Largest(self: int[..]) -> int` to the slice extension, and print the largest score.
3. Add `func Average(self: int[..]) -> float64` that reuses `Sum`. Convert with `as` before dividing.
4. Add `extend uint8[..]` with its own `Sum`, and call it on a `uint8` array through a view.
## Learn more
- [Method](https://rux-lang.dev/docs/learn/method) — `extend` on your own types
- [Slice](https://rux-lang.dev/docs/learn/slice) — the views these methods work on
- [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference
- [Interface](https://rux-lang.dev/docs/learn/interface) — `extend` that also makes a promise
# Enum
::note
**You'll need**: [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Method](https://rux-lang.dev/docs/learn/method)
::
An **enum** is a type with a fixed list of named values, called its **cases**. A direction is north, east, south or west, and nothing else. Stored as an `int`, a direction could just as well be 47, and nothing would notice; stored as a `Direction`, it cannot — the type has exactly four values, and the compiler knows all of them.
## Declaring an enum
The declaration is `enum`, a name, and the cases separated by commas:
```rux
enum Direction {
North,
East,
South,
West
}
```
The cases carry no data; each one is simply itself. A case that needs to carry something — a number, a name — is a [variant](https://rux-lang.dev/docs/learn/variant), two lessons on.
## Naming a case
Outside a pattern, a case is named through its type, with `::`:
```rux
var heading = Direction::North;
```
Cases compare with `==` and `!=`:
```rux
PrintLine("back to north: {}", heading == Direction::North);
```
## Matching on an enum
A `match` on a `Direction` already knows the type, so its patterns may use the short form `.North`:
```rux
func TurnRight(self: Direction) -> Direction {
return match self {
.North => Direction::East,
.East => Direction::South,
.South => Direction::West,
.West => Direction::North
};
}
```
The short form is for **patterns only**. The value an arm produces is written in full — `Direction::East`, not `.East`.
| Where | Spelling |
| ------------------------------------ | ------------------ |
| A `match` pattern on a `Direction` | `.North` |
| Anywhere else: a value, a comparison | `Direction::North` |
## Every case, no else
Neither `match` in the program has an `else` arm, and neither needs one. Naming all four cases covers every value a `Direction` can hold:
```mermaid
flowchart LR
v(["heading"]) --> m{"match heading"}
m -- ".North" --> n["Direction::East"]
m -- ".East" --> e["Direction::South"]
m -- ".South" --> s["Direction::West"]
m -- ".West" --> w["Direction::North"]
```
This is the best thing about enums. Add a fifth case, `Up`, and every `match` that does not handle it stops compiling, pointing at the case it is missing. An `else` arm would have swallowed `Up` silently — so leave `else` out when you mean "every case".
## Methods on an enum
An enum can be extended with methods like any other type. Taking `self` by value is natural here: a case is as small as a number. The program gives `Direction` a `TurnRight` and a `Name`, because an enum cannot be printed with `{}` directly.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/Enum){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An enum is a type with a fixed list of named values, called its cases. A direction is north,
// east, south or west, and nothing else. Stored as an `int`, a direction could just as well be
// 47, and nothing would notice; stored as a `Direction`, it cannot.
//
// The cases carry no data; each one is simply itself. A case that needs to carry something is a
// `variant`, two lessons on.
import Io::PrintLine;
enum Direction {
North,
East,
South,
West
}
// An enum can be extended with methods like any other type. Taking `self` by value is natural
// here: a case is as small as a number.
extend Direction {
// Inside a `match` on a `Direction`, the type is already known, so a pattern may be written
// `.North` rather than `Direction::North`. A value outside a pattern is written in full:
// `.North => .East` stops with
// error: '.East' must be written in full, as in 'Direction::East'
func TurnRight(self: Direction) -> Direction {
return match self {
.North => Direction::East,
.East => Direction::South,
.South => Direction::West,
.West => Direction::North
};
}
// An enum cannot be printed with `{}` directly, so it is given a name to print.
func Name(self: Direction) -> char8[..] {
return match self {
.North => "north",
.East => "east",
.South => "south",
.West => "west"
};
}
}
func Main() -> int {
// A case is named through its type.
var heading = Direction::North;
PrintLine("start facing {}", heading.Name());
for turn in 1..=4 {
heading = heading.TurnRight();
PrintLine("turn {}: facing {}", turn, heading.Name());
}
// Cases compare with `==` and `!=`.
PrintLine("back to north: {}", heading == Direction::North);
PrintLine("facing east: {}", heading == Direction::East);
// Neither `match` above needs an `else`: naming all four cases covers every value a
// `Direction` can hold. Add a fifth case, and both stop compiling until they handle it.
return 0;
}
```
## Run it
```sh
cd Examples/Types/Enum
rux run
```
```text
start facing north
turn 1: facing east
turn 2: facing south
turn 3: facing west
turn 4: facing north
back to north: true
facing east: false
```
## Common mistakes
::warning
**The short form outside a pattern.**:br`.North => .East` fails with `error: '.East' must be written in full, as in 'Direction::East'`. The same happens to `let d: Direction = .North;`. Only patterns may drop the type name.
::
::warning
**A case without its type.**:br`let d = North;` fails with `error: name 'North' is not defined in this scope`. Cases live inside their enum: `Direction::North`.
::
::warning
**Leaving a case out of a `match`.**:br
A `match` naming only three directions fails with `error: match on 'Direction' is not exhaustive; missing Direction::West`.
::
::warning
**Printing an enum with `{}`.**:br`PrintLine("{}", heading)` fails with `error: argument 2 to 'PrintLine' has type 'Direction', but variadic parameter 'args' requires 'Display'`. Give the enum a `Name` method, as the program does.
::
## Try it yourself
1. Add a `TurnLeft` method, and check that turning left then right comes back to the same heading.
2. Add `func Opposite(self: Direction) -> Direction`.
3. Add a fifth case, `Up`, and read the errors from both `match`es. Then handle it.
4. Declare `enum Suit { Clubs, Diamonds, Hearts, Spades }` with a `Name` method, and print all four.
## Learn more
- [Enumerations](https://rux-lang.dev/docs/lang/enums/overview) in the Rux Reference
- [Match expression](https://rux-lang.dev/docs/learn/match-expression) — the `match` that produces a value
- [Enum value](https://rux-lang.dev/docs/learn/enum-value) — the number behind each case
- [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) — how the compiler checks that every case is handled
# Enum value
::note
**You'll need**: [Enum](https://rux-lang.dev/docs/learn/enum), [Convert](https://rux-lang.dev/docs/learn/convert)
::
Behind every enum case is a number. Usually nobody needs to know it — the [Enum](https://rux-lang.dev/docs/learn/enum) lesson never mentioned it. But sometimes the number is the point: a web server answers 404 for "not found", and a program that reads or writes such codes needs its cases to be exactly those numbers.
An enum can say how its numbers are stored, with an **underlying type** after a colon, and which number each case has. `as` then converts between a case and its number, in both directions.
## Underlying type and explicit numbers
```rux
enum Status: uint16 {
Ok = 200,
Created,
NotFound = 404,
ServerError = 500
}
```
`: uint16` stores each `Status` in a `uint16`. A case may give its number with `=`. A case without a number of its own takes the one after the case before it, so `Created` is 201. With no numbers given at all, the cases count up from zero — which is why `Direction::South as int` would be 2.
```mermaid
flowchart LR
ok["Ok = 200"] -- "+1" --> cr["Created
201"]
cr -. "set explicitly" .-> nf["NotFound = 404"]
nf -. "set explicitly" .-> se["ServerError = 500"]
```
## From a case to its number, and back
```rux
PrintLine("Ok is {}", Status::Ok as uint16);
```
```rux
let received: uint16 = 404;
let status = received as Status;
```
A case is not its number, though. `Status::Ok == 200` is refused: compare a case with a case, or convert first.
## Cases are ordered
Cases order by their numbers, so a range of codes is a pair of comparisons:
```rux
PrintLine("NotFound is an error: {}", status >= Status::NotFound);
```
## as does not check
Here is the surprise. `418 as Status` compiles and runs, and gives a `Status` that is none of its four cases. Nothing goes wrong until something asks which case it is — then a `match` naming all four has no arm for it, and the program stops with `Panic: no match arm matched value of 'Status'`.
So a number from outside — a file, the network, the user — is checked **before** it becomes a case:
```rux
func IsKnown(code: uint16) -> bool {
return match code {
200 => true,
201 => true,
404 => true,
500 => true,
else => false
};
}
```
```mermaid
flowchart LR
n["A uint16 from outside"] --> k{"IsKnown(code)?"}
k -- "yes" --> s["code as Status
a real case"]
k -- "no" --> r["Reject it: report,
or use a fallback"]
```
## Numbers are a promise
Once other programs read these numbers, they are part of your interface. Inserting a case without a number in the middle of the list would quietly renumber every unnumbered case after it. When the numbers matter, write them all out.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/EnumValue){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Behind every enum case is a number. Usually nobody needs to know it, but sometimes the number is
// the point: a web server answers 404 for "not found", and a program that reads or writes such
// codes needs its cases to be exactly those numbers.
//
// An enum can say how its numbers are stored, with an underlying type after a colon, and which
// number each case has. `as` converts between a case and its number, in both directions.
import Io::PrintLine;
// A case without a number of its own takes the one after the case before it, so `Created` is 201.
// With no numbers given at all, the cases count up from zero.
enum Status: uint16 {
Ok = 200,
Created,
NotFound = 404,
ServerError = 500
}
// A code read from outside should be checked before it becomes a `Status`, because `as` trusts it.
func IsKnown(code: uint16) -> bool {
return match code {
200 => true,
201 => true,
404 => true,
500 => true,
else => false
};
}
func Main() -> int {
// From a case to its number.
PrintLine("Ok is {}", Status::Ok as uint16);
PrintLine("Created is {}", Status::Created as uint16);
PrintLine("NotFound is {}", Status::NotFound as uint16);
PrintLine("ServerError is {}", Status::ServerError as uint16);
// From a number to a case.
let received: uint16 = 404;
let status = received as Status;
PrintLine("{} means not found: {}", received, status == Status::NotFound);
// Cases order by their numbers, so a range of codes is a pair of comparisons.
PrintLine("NotFound is an error: {}", status >= Status::NotFound);
PrintLine("Created is an error: {}", Status::Created >= Status::NotFound);
// The surprise: `as` does not check. `418 as Status` compiles and runs, and gives a `Status`
// that is none of its four cases. A `match` naming all four has no arm for it, so the program
// stops there with `Panic: no match arm matched value of 'Status'`. So check first.
let strange: uint16 = 418;
PrintLine("{} is a known status: {}", received, IsKnown(received));
PrintLine("{} is a known status: {}", strange, IsKnown(strange));
// The numbers are now a promise to whoever reads them. Inserting a case without a number in
// the middle of the list would quietly renumber the ones after it.
return 0;
}
```
## Run it
```sh
cd Examples/Types/EnumValue
rux run
```
```text
Ok is 200
Created is 201
NotFound is 404
ServerError is 500
404 means not found: true
NotFound is an error: true
Created is an error: false
404 is a known status: true
418 is a known status: false
```
## Common mistakes
::warning
**Trusting `as` with an unchecked number.**:br`as` never refuses. `418 as Status` makes a value that no `match` arm fits, and the program stops later with `Panic: no match arm matched value of 'Status'` — far from the line that made it. Check the number first, as `IsKnown` does.
::
::warning
**Comparing a case with a number.**:br`Status::Ok == 200` fails with `error: operator '==' cannot compare left operand 'Status' with right operand 'int'`. Write `Status::Ok as uint16 == 200`, or compare with another case.
::
::warning
**Inserting a case in a numbered list.**:br
Add `Accepted` between `Ok` and `Created`, and `Accepted` becomes 201 while `Created` moves to 202 — no error, just different numbers. Give every case whose number matters an explicit `=`.
::
## Try it yourself
1. Add `Accepted = 202` and `NoContent = 204`, update `IsKnown`, and print their numbers.
2. Write `func IsError(self: Status) -> bool` in an `extend Status` block, using a comparison rather than a `match`.
3. Write `func Name(self: Status) -> char8[..]` and use it to print the name of `received as Status`. What happens if you call it on `strange as Status`?
4. Declare `enum Level: uint8 { Low, Medium, High }` and print each case's number.
## Learn more
- [Backing type and explicit values](https://rux-lang.dev/docs/lang/enums/overview#backing-type-and-values) in the Rux Reference
- [Convert](https://rux-lang.dev/docs/learn/convert) — `as` between numeric types
- [Variant](https://rux-lang.dev/docs/learn/variant) — cases that carry data
- [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) — conversions that report whether the value fits
# Variant
::note
**You'll need**: [Enum](https://rux-lang.dev/docs/learn/enum), [Struct](https://rux-lang.dev/docs/learn/struct)
::
An [enum](https://rux-lang.dev/docs/learn/enum) says a value is one of a fixed list of cases. A **variant** says the same, and lets each case carry data of its own. A thermometer reading might be missing, or an exact temperature, or a range between two temperatures. Those are three cases, and two of them hold numbers.
A struct holds *all* of its fields at once. A variant holds *one* case at a time, and only that case's data. This lesson declares and builds variants; reading the data back out is the [next lesson](https://rux-lang.dev/docs/learn/variant-match).
## Three shapes of case
```rux
variant Reading {
Missing,
Exact(float64),
Between { low: float64; high: float64; }
}
```
| Case | Shape | Carries | Built as |
| --------- | -------------------------------- | ---------------- | -------------------------------------------- |
| `Missing` | a bare name, like an enum case | nothing | `Reading::Missing` |
| `Exact` | values by position, like a tuple | one `float64` | `Reading::Exact(21.5)` |
| `Between` | named fields, like a struct | `low` and `high` | `Reading::Between { low: 20.0, high: 24.0 }` |
A positional case may carry several values — `Jump(int, int)` in the next lesson carries two.
## Struct, enum, variant
```mermaid
flowchart LR
s["struct Point
x AND y,
always both"]
e["enum Direction
North OR East OR …,
no data"]
v["variant Reading
Missing OR Exact(t)
OR Between { low, high }"]
s -. "fields" .-> v
e -. "a fixed list of cases" .-> v
```
A variant borrows from both: the "one of a list" of an enum, and the data of a struct or tuple — but only for the case that is held.
## Choosing the case
A variant is a type like any other, so a function can return one, choosing the case as it goes:
```rux
func Measure(low: float64, high: float64) -> Reading {
if low > high {
return Reading::Missing;
}
if low == high {
return Reading::Exact(low);
}
return Reading::Between { low: low, high: high };
}
```
Every `return` gives back a `Reading`; which case it is depends on the numbers.
## Comparing variants
Two variant values are equal when they hold the same case with the same data. Different cases are never equal, whatever they carry:
```rux
PrintLine("same case, same data {}", precise == Reading::Exact(21.5));
PrintLine("different cases {}", precise == absent);
```
## No number behind the case
Unlike an [enum with values](https://rux-lang.dev/docs/learn/enum-value), a variant has no number behind its cases that `as` could reach. Which case is held is the variant's own business. A value that must be written to a file or sent elsewhere as a number is encoded with an enum, whose numbers you choose.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/Variant){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An enum says a value is one of a fixed list of cases. A variant says the same, and lets each
// case carry data of its own. A thermometer reading might be missing, or an exact temperature, or
// a range between two temperatures. Those are three cases, and two of them hold numbers.
//
// A struct holds all of its fields at once. A variant holds one case at a time, and only that
// case's data. This lesson declares and builds variants; reading the data back out is the next.
import Io::PrintLine;
// A case can take one of three shapes. `Missing` carries nothing, like an enum case. `Exact`
// carries one value by position, like a one-element tuple. `Between` carries named fields,
// declared like a struct's.
variant Reading {
Missing,
Exact(float64),
Between { low: float64; high: float64; }
}
// A variant is a type like any other: a function can return one, choosing the case as it goes.
func Measure(low: float64, high: float64) -> Reading {
if low > high {
return Reading::Missing;
}
if low == high {
return Reading::Exact(low);
}
return Reading::Between { low: low, high: high };
}
func Main() -> int {
// Each case is built in its own shape: just its name, its values in parentheses, or its
// fields in braces.
let absent = Reading::Missing;
let precise = Reading::Exact(21.5);
let range = Reading::Between { low: 20.0, high: 24.0 };
// Two variant values are equal when they hold the same case with the same data. Different
// cases are never equal.
PrintLine("same case, same data {}", precise == Reading::Exact(21.5));
PrintLine("same case, different data {}", precise == Reading::Exact(9.0));
PrintLine("different cases {}", precise == absent);
PrintLine("same fields {}",
range == Reading::Between { low: 20.0, high: 24.0 });
PrintLine("Measure(5.0, 5.0) exact {}", Measure(5.0, 5.0) == Reading::Exact(5.0));
PrintLine("Measure(9.0, 1.0) missing {}", Measure(9.0, 1.0) == Reading::Missing);
// Unlike an enum, a variant has no number behind its cases that `as` could reach: which case
// is held is the variant's own business. A value that must be written to a file or sent
// elsewhere is encoded with an enum, whose numbers you choose.
return 0;
}
```
## Run it
```sh
cd Examples/Types/Variant
rux run
```
```text
same case, same data true
same case, different data false
different cases false
same fields true
Measure(5.0, 5.0) exact true
Measure(9.0, 1.0) missing true
```
## Common mistakes
::warning
**Reading a case's data with a dot.**:br`precise.0` fails with an error that the type `Reading` has no field `0`. A variant has no fields of its own to read: the data belongs to one case, and which case is held is only known at run time. Taking it out is a job for `match`, in [Variant match](https://rux-lang.dev/docs/learn/variant-match).
::
::warning
**Leaving out a field of a struct-shaped case.**:br`Reading::Between { low: 1.0 }` fails with `error: initializer for 'Reading::Between' is missing required field 'high'` — the same rule as a struct literal.
::
::warning
**An integer where a float is expected.**:br`Reading::Exact(1)` fails with `error: argument 1 to variant case 'Reading::Exact' has type 'int', but field 1 requires 'float64'`. A whole-number literal never becomes a float on its own; write `1.0`.
::
::warning
**Converting a variant with `as`.**:br`precise as int` fails with an error that the variant cannot be cast to the scalar type `int`. Only an enum's cases have numbers.
::
## Try it yourself
1. Add a case `Faulty(int)` that carries an error code, and build one.
2. Change `Measure` so that a range narrower than `0.5` counts as `Exact` at its midpoint.
3. Declare `variant Shape { Circle(float64), Rectangle { width: float64; height: float64; } }` and build one of each.
4. Check whether `Reading::Between { low: 1.0, high: 2.0 }` equals `Reading::Between { low: 2.0, high: 1.0 }`. Predict first.
## Learn more
- [Variants with data](https://rux-lang.dev/docs/lang/variants/overview) in the Rux Reference
- [Variant match](https://rux-lang.dev/docs/learn/variant-match) — taking the data back out
- [Struct](https://rux-lang.dev/docs/learn/struct) and [Enum](https://rux-lang.dev/docs/learn/enum) — the two ideas a variant combines
- [Sum type](https://rux-lang.dev/docs/learn/sum-type) — a value that is one of several *types*
# Variant match
::note
**You'll need**: [Variant](https://rux-lang.dev/docs/learn/variant), [Match expression](https://rux-lang.dev/docs/learn/match-expression)
::
A variant's data can only be read once it is known which case is held. `match` does both in one step: an arm such as `.Forward(steps)` fits only a `Forward` value, and when it fits, the name in the parentheses is a **new binding** holding that case's data, ready to use in the arm.
That is the guarantee a variant gives. There is no way to read the steps of a `Turn`, because no arm can both fit a `Turn` and bind a `Forward`'s data.
## A list of commands
The program walks a robot through five commands. Each command carries what it needs and nothing more:
```rux
variant Command {
Forward(int),
Turn,
Jump(int, int),
Stop
}
```
## Binding the data
A match that produces a value, as in [Match expression](https://rux-lang.dev/docs/learn/match-expression):
```rux
func Cost(command: Command) -> int {
return match command {
.Forward(steps) => steps,
.Turn => 1,
.Jump(across, _) => across * 2,
.Stop => 0
};
}
```
```mermaid
flowchart LR
c(["Command::Jump(4, 1)"]) --> f{".Forward(steps)?"}
f -- "no — not a Forward" --> t{".Turn?"}
t -- "no" --> j{".Jump(across, _)?"}
j -- "yes: across = 4,
the 1 is ignored" --> r["across * 2 = 8"]
```
The arms are tried in order, and the first whose case fits is the one that runs. Each pattern has one name per value the case carries:
| Pattern | Fits | Binds |
| ------------------ | ------------- | ------------------------------------------------ |
| `.Forward(steps)` | any `Forward` | `steps` — its one `int` |
| `.Turn` | `Turn` | nothing; the case carries nothing |
| `.Jump(across, _)` | any `Jump` | `across` — the first value; `_` skips the second |
| `.Stop` | `Stop` | nothing |
`_` binds nothing. It is the way to say "a value goes here, and this arm does not need it" — the count of names must still match the case.
## A match as a statement
When each case calls for actions rather than a value, the arms are blocks:
```rux
.Jump(across, up) => {
x += across;
y += up;
},
```
The bindings exist only inside their own arm: `steps` is unknown in the `.Jump` arm, and `across` in all the others.
## Every case, named
There is no `else` arm in either match, and none is needed. Every case is named, and a match on a variant must name them all — leave one out, and it does not compile. As with [enums](https://rux-lang.dev/docs/learn/enum), that is a feature: add a fifth command, and the compiler lists every `match` that has yet to handle it.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/VariantMatch){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A variant's data can only be read once it is known which case is held. `match` does both in
// one step: an arm such as `.Forward(steps)` fits only a `Forward` value, and when it fits, the
// name in the parentheses is a new binding holding that case's data, ready to use in the arm.
//
// That is the guarantee a variant gives. There is no way to read the steps of a `Turn`, because
// no arm can both fit a `Turn` and bind a `Forward`'s data.
import Io::PrintLine;
// A walk is a list of commands, and each command carries what it needs and nothing more.
variant Command {
Forward(int),
Turn,
Jump(int, int),
Stop
}
// A match producing a value, as in the MatchExpression lesson. A case with several values binds
// them by position, one name each. `_` binds nothing: it is the way to say "a value goes here,
// and this arm does not need it". A case with no data has nothing to bind.
func Cost(command: Command) -> int {
return match command {
.Forward(steps) => steps,
.Turn => 1,
.Jump(across, _) => across * 2,
.Stop => 0
};
}
func Main() -> int {
let walk: Command[5] = [
Command::Forward(3),
Command::Turn,
Command::Jump(4, 1),
Command::Forward(2),
Command::Stop
];
var x = 0;
var y = 0;
var facingEast = true;
var cost = 0;
for command in walk {
// A match as a statement, with a block for each arm. The bindings exist only inside
// their own arm: `steps` is unknown in the `.Jump` arm, and `across` in the others.
match command {
.Forward(steps) => {
if facingEast {
x += steps;
} else {
y += steps;
}
},
.Turn => {
facingEast = !facingEast;
},
.Jump(across, up) => {
x += across;
y += up;
},
.Stop => {
PrintLine("stopped");
}
}
cost += Cost(command);
PrintLine("at ({}, {}), cost so far {}", x, y, cost);
}
// There is no `else` arm in either match, and none is needed. Every case is named, and a
// match on a variant must name them all: leave one out, and it does not compile.
return 0;
}
```
## Run it
```sh
cd Examples/Types/VariantMatch
rux run
```
```text
at (3, 0), cost so far 3
at (3, 0), cost so far 4
at (7, 1), cost so far 12
at (7, 3), cost so far 14
stopped
at (7, 3), cost so far 14
```
## Common mistakes
::warning
**Leaving a case out.**:br
Drop the `.Stop` arm from `Cost` and it fails with `error: match on 'Command' is not exhaustive; missing Command::Stop`.
::
::warning
**The wrong number of names.**:br`.Jump(across)` fails with `error: pattern for 'Command::Jump' expects 2 fields, but found 1`. Every value the case carries needs a name or an `_`.
::
::warning
**Using a binding from another arm.**:br`.Turn => steps` fails with `error: name 'steps' is not defined in this scope`. `steps` was bound by the `.Forward` arm and exists only there.
::
::warning
**Reaching for `else` too soon.**:br`.Forward(steps) => steps, else => 0` compiles — and a new case added later falls silently into the `else`. When every case matters, name them all.
::
## Try it yourself
1. Add a case `Back(int)` that moves the other way, and handle it in both matches. Let the compiler tell you where.
2. Change `Cost` so that a `Jump` costs `across + up` instead, using both bindings.
3. Add a case with named fields, `Teleport { x: int; y: int; }`. A pattern for it names the fields in braces, and can bind them under new names: `.Teleport { x: toX, y: toY }` — handy here, where `x` and `y` already name the robot's position. Make it move the robot straight to that spot.
4. Count how many `Turn` commands the walk contains, using a `match` with a single interesting arm.
## Learn more
- [Variants with data](https://rux-lang.dev/docs/lang/variants/overview) and [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
- [Patterns](https://rux-lang.dev/docs/learn/patterns) — everything a match arm can say
- [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) — how the compiler checks that every case is handled
- [Error variant](https://rux-lang.dev/docs/learn/error-variant) — a variant used to describe what went wrong
# Type alias
::note
**You'll need**: [Struct](https://rux-lang.dev/docs/learn/struct), [Slice](https://rux-lang.dev/docs/learn/slice)
::
`type` gives an existing type a **second name**. It creates nothing new: a value of the alias and a value of the aliased type are the same type, and can be used in place of each other without a conversion. What an alias buys is a spelling, written once — one that says what a value is for.
## Naming what a value means
A signature saying `Celsius` explains itself where one saying `float64` does not:
```rux
type Celsius = float64;
type Fahrenheit = float64;
func ToFahrenheit(temperature: Celsius) -> Fahrenheit {
return temperature * 9.0 / 5.0 + 32.0;
}
```
Inside the function, `temperature` is a `float64` in every way — the arithmetic, the literals and the result all work as they would without the alias.
## Shortening compound types
An alias is most useful on compound types, where the full spelling is long and repeated:
```rux
type Bytes = uint8[..];
type Row = int32[3];
```
`func Sum(values: Bytes) -> uint` reads as "sum some bytes", and a `uint8[4]` array is passed to it exactly as it would be to a `uint8[..]` parameter.
## Same type, not a new type
```mermaid
flowchart LR
subgraph alias ["type Celsius = float64"]
c1["Celsius"] --- f1["float64"] --- h1["Fahrenheit"]
end
subgraph wrap ["struct Celsius and struct Fahrenheit,
one float64 field each"]
c2["Celsius"]
h2["Fahrenheit"]
end
c2 x--x h2
```
Here is the trap worth knowing before relying on aliases for safety: an alias is only a spelling, so it gives **no protection at all**. `Celsius` and `Fahrenheit` are both `float64`, which makes them the same type, and nothing stops one being passed where the other is meant:
```rux
let mistake: Fahrenheit = 212.0;
PrintLine("this compiles and is wrong: {}F treated as Celsius is {}F",
mistake, ToFahrenheit(mistake));
```
When two things must not be mixed up, they need to be different types. A struct with a single field, `struct Celsius { degrees: float64; }`, is one: two such structs are never the same type, whatever their fields. With structs, the same mistake is refused at compile time.
| You want… | Use |
| -------------------------------------------- | --------------------------- |
| a shorter or clearer spelling of a type | `type Name = …;` |
| a value that cannot be confused with another | `struct Name { value: …; }` |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/TypeAlias){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `type` gives an existing type a second name. It creates nothing new: a value
// of the alias and a value of the aliased type are the same type, and can be
// used in place of each other without a conversion.
import Io::PrintLine;
// The point is a spelling written once. A signature saying `Celsius` explains
// itself where one saying `float64` does not.
type Celsius = float64;
type Fahrenheit = float64;
// It is most useful on compound types, where the full spelling is long and
// repeated. These two are the ones worth reaching for.
type Bytes = uint8[..];
type Row = int32[3];
func ToFahrenheit(temperature: Celsius) -> Fahrenheit {
return temperature * 9.0 / 5.0 + 32.0;
}
func Sum(values: Bytes) -> uint {
var total: uint = 0;
for i in 0..values.length {
total += values[i] as uint;
}
return total;
}
func Main() -> int {
let boiling: Celsius = 100.0;
PrintLine("{}C is {}F", boiling, ToFahrenheit(boiling));
let data: uint8[4] = [ 1, 2, 3, 4 ];
PrintLine("sum of bytes {}", Sum(data));
let row: Row = [ 10, 20, 30 ];
PrintLine("row {} {} {}", row[0], row[1], row[2]);
// The trap worth knowing before relying on aliases for safety: an alias is
// only a spelling, so it gives no protection at all. `Celsius` and
// `Fahrenheit` are both `float64`, which makes them the same type, and
// nothing stops one being passed where the other is meant:
let mistake: Fahrenheit = 212.0;
PrintLine("this compiles and is wrong: {}F treated as Celsius is {}F",
mistake, ToFahrenheit(mistake));
// When two things must not be mixed up, they need to be different types.
// A struct with a single field, `struct Celsius { degrees: float64; }`, is
// one: two such structs are never the same type, whatever their fields.
return 0;
}
```
## Run it
```sh
cd Examples/Types/TypeAlias
rux run
```
```text
100.0C is 212.0F
sum of bytes 10
row 10 20 30
this compiles and is wrong: 212.0F treated as Celsius is 413.6F
```
## Common mistakes
::warning
**Expecting an alias to keep values apart.**:br`ToFahrenheit(mistake)` with a `Fahrenheit` argument compiles and gives a wrong answer. If the two must not mix, make them structs: with `struct Celsius` and `struct Fahrenheit`, the same call fails with `error: argument 1 to 'ToFahrenheit' has type 'Fahrenheit', but parameter 'temperature' requires 'Celsius'`.
::
::warning
**Being surprised by the full type in an error.**:br
Error messages speak of the real type, not the alias. Passing an `int[3]` to `Sum` fails with `error: argument 1 to 'Sum' has type 'int[3]', but parameter 'values' requires 'uint8[..]'` — `Bytes` does not appear.
::
::warning
**Extending an alias.**:br`extend Celsius { … }` adds its methods to `float64` itself, so every `float64` in the package gains them — temperature or not. Methods belong on a type of their own.
::
## Try it yourself
1. Add `type Kelvin = float64;` and a function `ToKelvin(temperature: Celsius) -> Kelvin`.
2. Write `func RowSum(row: Row) -> int32` and print the sum of `row`.
3. Replace the two aliases with single-field structs, `struct Celsius { degrees: float64; }` and its twin, and make the program compile again. Which line now refuses to compile?
4. Write `type Name = char8[..];` and use it in a function that greets someone.
## Learn more
- [Type aliases](https://rux-lang.dev/docs/lang/types/aliases) and [Using type aliases](https://rux-lang.dev/docs/lang/types/aliases) in the Rux Reference
- [Function type aliases](https://rux-lang.dev/docs/lang/functions/function-types) — naming a function type
- [Struct](https://rux-lang.dev/docs/learn/struct) — a new type rather than a new name
- [Function field](https://rux-lang.dev/docs/learn/function-field) — where a long function type is worth naming
# Function field
::note
**You'll need**: [Callback](https://rux-lang.dev/docs/learn/callback), [Method](https://rux-lang.dev/docs/learn/method)
::
The [Callback](https://rux-lang.dev/docs/learn/callback) lesson passed a function as an argument and held one in a local. A struct field can hold one too. A value then carries its **behaviour** with it: an operation knows its symbol and also how to compute, and code that loops over operations needs to know neither in advance.
## A field of function type
The field's type is a function type, exactly as for a parameter — the parameter types in parentheses, then the return type:
```rux
struct Operation {
symbol: char8[..];
apply: func(int, int) -> int;
}
```
Any function that takes two `int`s and returns an `int` fits in `apply`: `Add`, `Subtract`, `Multiply` and `Larger` all do.
## Storing a function
A function is stored by its name, without parentheses — as when passing one to a callback parameter:
```rux
Operation { symbol: "+", apply: Add },
```
`Add` is the function itself; `Add(…)` would call it and store the result, which is an `int`, not a function.
## Calling through the field
Calling a field looks like calling a method, but `apply` is **data**: whatever function was stored in it is what runs.
```rux
func Show(self: &Operation, a: int, b: int) {
PrintLine("{} {} {} = {}", a, self.symbol, b, self.apply(a, b));
}
```
```mermaid
flowchart LR
call["operation.Show(7, 3)"] --> field["self.apply(7, 3)"]
field --> q{"What is stored
in apply?"}
q -- "Add" --> a["10"]
q -- "Multiply" --> m["21"]
q -- "Larger" --> l["7"]
```
The loop that prints all four lines names none of the four functions:
```rux
for operation in operations {
operation.Show(7, 3);
}
```
Adding a fifth operation means adding one line to the array — the loop does not change.
## Method or function field?
| A method `Show` | A function field `apply` |
| ----------------------------------- | ----------------------------------- |
| Declared once in `extend Operation` | Set separately in every value |
| The same code for every `Operation` | Different code in each `Operation` |
| Cannot be changed | Assigned like any field, on a `var` |
A function field is assigned like any other field, which changes what the value does from then on:
```rux
var changing = Operation { symbol: "?", apply: Add };
changing.Show(6, 4);
changing.apply = Multiply;
changing.Show(6, 4);
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Types/FunctionField){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The Callback lesson passed a function as an argument and held one in a local. A struct field
// can hold one too. A value then carries its behaviour with it: an operation knows its symbol and
// also how to compute, and code that loops over operations needs to know neither in advance.
//
// The field's type is a function type, exactly as for a parameter: `func(int, int) -> int`.
import Io::PrintLine;
func Add(a: int, b: int) -> int {
return a + b;
}
func Subtract(a: int, b: int) -> int {
return a - b;
}
func Multiply(a: int, b: int) -> int {
return a * b;
}
func Larger(a: int, b: int) -> int {
return a > b ? a : b;
}
struct Operation {
symbol: char8[..];
apply: func(int, int) -> int;
}
extend Operation {
// Calling a field looks like calling a method, but `apply` is data: whatever function was
// stored in it is what runs.
func Show(self: &Operation, a: int, b: int) {
PrintLine("{} {} {} = {}", a, self.symbol, b, self.apply(a, b));
}
}
func Main() -> int {
// A function is stored by name, without parentheses, as when passing one.
let operations: Operation[4] = [
Operation { symbol: "+", apply: Add },
Operation { symbol: "-", apply: Subtract },
Operation { symbol: "*", apply: Multiply },
Operation { symbol: "max", apply: Larger }
];
// One loop, four behaviours. Nothing in it names any of the four functions.
for operation in operations {
operation.Show(7, 3);
}
// The field is called directly from outside a method too.
let first = operations[0];
PrintLine("through the field: {}", first.apply(20, 22));
// A function field is assigned like any other field, which changes what the value does.
var changing = Operation { symbol: "?", apply: Add };
changing.Show(6, 4);
changing.apply = Multiply;
changing.Show(6, 4);
return 0;
}
```
## Run it
```sh
cd Examples/Types/FunctionField
rux run
```
```text
7 + 3 = 10
7 - 3 = 4
7 * 3 = 21
7 max 3 = 7
through the field: 42
6 ? 4 = 10
6 ? 4 = 24
```
## Common mistakes
::warning
**Calling the function instead of storing it.**:br`apply: Add()` fails with `error: call to 'Add' expects 2 arguments, but 0 were provided`. Write the name alone: `apply: Add`.
::
::warning
**A function of the wrong shape.**:br
A one-argument `Negate` cannot go in `apply`: `apply: Negate` fails with `error: field 'apply' in initializer for 'Operation' has type 'func(int) -> int', but its declaration requires 'func(int, int) -> int'`. The parameter types and the return type must all match.
::
::warning
**Calling the field with the wrong arguments.**:br`first.apply(1)` fails with `error: call to 'function value' expects 2 arguments, but 1 was provided`. The field's type says how it must be called, whatever function is inside.
::
::warning
**Reassigning the field of a `let`.**:br
With `let op = Operation { … };`, the line `op.apply = Add;` fails with `error: cannot modify immutable variable 'op'`. A function field follows the same rule as every other field.
::
## Try it yourself
1. Write `func Smaller(a: int, b: int) -> int` and add a `min` operation to the array. Nothing else should need to change.
2. Write `func Power(a: int, b: int) -> int` with a loop, and add `^` to the array.
3. Name the function type with an alias, `type Binary = func(int, int) -> int;`, and use it in the struct.
4. Give `Operation` a second function field, `check: func(int, int) -> bool`, that says whether the operation is safe for its arguments — for example, a division that refuses a zero divisor.
## Learn more
- [Function type aliases](https://rux-lang.dev/docs/lang/functions/function-types) in the Rux Reference
- [Callback](https://rux-lang.dev/docs/learn/callback) — a function passed as an argument
- [Type alias](https://rux-lang.dev/docs/learn/type-alias) — a short name for a long function type
- [Interface](https://rux-lang.dev/docs/learn/interface) — behaviour shared by many types, chosen by type rather than stored per value
# Part 7: Patterns
[Part 3](https://rux-lang.dev/docs/learn/control-flow) introduced `match` with literal arms, and [Part 6](https://rux-lang.dev/docs/learn/types) used it to take variants apart. This part is about everything else an arm can say. A pattern can test a range of numbers, look at two values at once, pick fields out by name, recognise a character, and carry a condition of its own. By the end you can turn a page of `if` / `else if` tests into a `match` that reads like a table — and you will know why the compiler refuses a match that forgets a case.
## What you will learn
- Adding a condition to an arm with a guard, `pattern if condition =>`, and what happens when it is false.
- Matching a whole run of integers with `1..=9` and `0..10`.
- Deciding on several values at once by matching a tuple: `(0, 0)`, `(x, 0)`, `(_, y)`.
- Taking a variant case apart by field name: `.Circle { radius }`, `{ radius: r }`, `{ radius: 0 }`.
- Matching characters, and using a guard for a whole class of them.
- Why a match on a variant must name every case, and when `else` is needed — or harmful.
## How an arm is chosen
Each lesson adds a new kind of pattern, but the way a `match` uses them never changes:
```mermaid
flowchart LR
v(["The value"]) --> arm["Take the next arm,
top to bottom"]
arm --> p{"Does its pattern fit?
(else always does)"}
p -- "no" --> arm
p -- "yes: its names
are bound" --> g{"Does it have a guard
that is false?"}
g -- "yes" --> arm
g -- "no" --> run["Run this arm
and leave the match"]
arm -- "no arms left" --> skip["Nothing runs —
only a statement
may get here"]
```
The compiler checks the rest before the program ever runs. For a variant or a `bool`, every case must have an unguarded arm; a match that produces a value from a number must end with `else`. That is why a value-producing match can never reach "no arms left".
## Lessons
| | Lesson | What you will learn |
| --- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| 7.1 | [Guard](https://rux-lang.dev/docs/learn/guard) | add an `if` condition to a match arm |
| 7.2 | [Range pattern](https://rux-lang.dev/docs/learn/range-pattern) | match a whole range of numbers in one arm |
| 7.3 | [Tuple pattern](https://rux-lang.dev/docs/learn/tuple-pattern) | match several values at once as a tuple |
| 7.4 | [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern) | take a struct-shaped variant case apart by field name |
| 7.5 | [Character pattern](https://rux-lang.dev/docs/learn/character-pattern) | match single characters |
| 7.6 | [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) | why a match on a variant must cover every case, and when `else` is needed |
## Before you start
Finish [Part 3: Control flow](https://rux-lang.dev/docs/learn/control-flow) — especially [Match](https://rux-lang.dev/docs/learn/match) and [Match expression](https://rux-lang.dev/docs/learn/match-expression) — and [Part 6: Types](https://rux-lang.dev/docs/learn/types), whose [Variant](https://rux-lang.dev/docs/learn/variant) and [Variant match](https://rux-lang.dev/docs/learn/variant-match) lessons most of this part builds on. Tuples come from [Part 5](https://rux-lang.dev/docs/learn/sequences). Each lesson's package is in the Examples repository's `Patterns/` folder:
```sh
cd Examples/Patterns/Guard
rux run
```
## After this part
[Part 8: Optionals](https://rux-lang.dev/docs/learn/optionals) applies these patterns to a value that may be absent, `T?`, where the arm the compiler will never let you forget is `none`. [Part 10: Sum types](https://rux-lang.dev/docs/learn/sum-types) later adds patterns that match by type, such as `n: int32 =>`. The next checkpoint project, [Calculator](https://rux-lang.dev/docs/learn/calculator), comes after [Part 9: Errors](https://rux-lang.dev/docs/learn/errors).
For the full rules, see [`match`](https://rux-lang.dev/docs/lang/patterns/match), [ranges](https://rux-lang.dev/docs/lang/ranges/overview) and [tuple destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) in the Rux Reference.
# Guard
::note
**You'll need**: [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Comparison](https://rux-lang.dev/docs/learn/comparison)
::
A pattern describes the *shape* of a value: "a deposit", "the number zero". Sometimes the shape is not enough, and what you really mean is "a deposit, but only a large one". A **guard** adds that extra condition to an arm. It is the first of the tools in this part for saying more in a `match` arm than a plain literal can.
## A condition after the pattern
Write `if` and a `bool` expression after the pattern, just before the `=>`:
```rux
.Deposit(amount) if amount >= 10000 => "deposit, held for a check",
```
The arm is taken only when the pattern matches **and** the guard is true. The guard runs after the pattern, so it can use the names the pattern has just bound — here `amount`, the data the `.Deposit` case carries.
## A false guard moves on
When the guard turns out false, nothing is lost: the match simply tries the next arm, exactly as if this one had not matched at all. That is what makes the pair of `.Deposit` arms in `Review` work:
```rux
func Review(entry: Transaction) -> char8[..] {
return match entry {
.Deposit(amount) if amount >= 10000 => "deposit, held for a check",
.Deposit(_) => "deposit",
.Withdrawal(amount) if amount > 500 => "withdrawal, over the daily limit",
.Withdrawal(_) => "withdrawal",
.Fee => "fee"
};
}
```
Follow two deposits through it:
```mermaid
flowchart LR
a["Deposit(12000)"] --> g1{"Arm 1: .Deposit(amount)
if amount >= 10000"}
b["Deposit(250)"] --> g1
g1 -- "pattern fits,
guard true" --> held["deposit, held for a check"]
g1 -- "pattern fits,
guard false" --> p2{"Arm 2: .Deposit(_)"}
p2 -- "pattern fits" --> plain["deposit"]
```
Arms are tried from top to bottom, so each guarded arm sits **above** the plain arm that catches the rest of the same case. The plain arm is the safety net for every deposit the guard turned away.
## A guard on a plain name
A plain name is a pattern too: it matches any value and binds it. Add a guard, and it becomes a condition on the whole value — something a literal pattern cannot express:
```rux
func Parity(number: int32) -> char8[..] {
return match number {
0 => "zero",
value if value % 2 == 0 => "even",
else => "odd"
};
}
```
`0` is checked first and claims zero. Every other number reaches the second arm, is bound to `value`, and is accepted only if it is even. The odd ones fall through to `else`.
| Arm | Matches when |
| ---------------------------- | ------------------------------------------------- |
| `0 =>` | the value is exactly 0 |
| `value if value % 2 == 0 =>` | any value, bound to `value`, for which `% 2` is 0 |
| `.Deposit(amount) if … =>` | a `Deposit`, and the guard holds for its `amount` |
| `else =>` | anything that no arm above it took |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Patterns/Guard){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A pattern describes the shape of a value: "a deposit", "the number zero". Sometimes the shape is
// not enough, and you want "a deposit, but only a large one". A guard adds that condition: write
// `if` and a `bool` expression after the pattern, just before the `=>`.
//
// The arm is taken only when the pattern matches *and* the guard is true. The guard runs after the
// pattern, so it can use the names the pattern has just bound. When the guard turns out false,
// nothing is lost: the match moves on and tries the next arm, exactly as if this one had not
// matched at all.
import Io::PrintLine;
variant Transaction {
Deposit(int32),
Withdrawal(int32),
Fee
}
// Arms are tried from top to bottom, so each guarded arm sits above the plain arm that catches the
// rest of the same case. Swap them and the guarded arm could never be reached.
func Review(entry: Transaction) -> char8[..] {
return match entry {
.Deposit(amount) if amount >= 10000 => "deposit, held for a check",
.Deposit(_) => "deposit",
.Withdrawal(amount) if amount > 500 => "withdrawal, over the daily limit",
.Withdrawal(_) => "withdrawal",
.Fee => "fee"
};
}
// A plain name is a pattern too: it matches any value and binds it. Add a guard and it becomes a
// condition on the whole value, something a literal pattern cannot express.
func Parity(number: int32) -> char8[..] {
return match number {
0 => "zero",
value if value % 2 == 0 => "even",
else => "odd"
};
}
func Main() -> int {
let entries: Transaction[5] = [
Transaction::Deposit(250),
Transaction::Deposit(12000),
Transaction::Withdrawal(80),
Transaction::Withdrawal(900),
Transaction::Fee
];
for index in 0..5 {
PrintLine("{}", Review(entries[index]));
}
PrintLine("0 is {}, 14 is {}, -7 is {}", Parity(0), Parity(14), Parity(-7));
return 0;
}
```
## Run it
```sh
cd Examples/Patterns/Guard
rux run
```
```text
deposit
deposit, held for a check
withdrawal
withdrawal, over the daily limit
fee
0 is zero, 14 is even, -7 is odd
```
## Common mistakes
::warning
**A guarded arm below its plain arm.**:br
Put `.Deposit(_)` above `.Deposit(amount) if amount >= 10000` and the program still compiles — but every deposit is taken by the plain arm first, so the guarded one can never run, and `Deposit(12000)` prints just `deposit`. The compiler does not warn about it. Specific arms go first.
::
::warning
**Counting a guarded arm as covering its case.**:br
A guard might be false, so a guarded arm never counts towards covering a case. Delete `.Deposit(_) => "deposit",` and the match fails with `error: match on 'Transaction' is not exhaustive; missing Transaction::Deposit`, even though a `.Deposit` arm is still there.
::
::warning
**A guarded number match with no `else`.**:br
Guards cannot prove that every integer is covered. Remove `else => "odd"` from `Parity` and the compiler stops with `error: match on 'int32' is not exhaustive; its arms do not cover every value`.
::
## Try it yourself
1. Swap the two `.Deposit` arms and predict what `Deposit(12000)` prints before you run it.
2. Add an arm to `Review` that labels a withdrawal of more than 5000 as `"withdrawal, blocked"`. Where must it go so that it can be reached?
3. Extend `Parity` so that even numbers above 100 print `"large even"`.
## Learn more
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference — its Guards section
- [Range pattern](https://rux-lang.dev/docs/learn/range-pattern) — matching a whole run of numbers, with or without a guard
- [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) — which arms count when the compiler checks that every case is covered
# Range pattern
::note
**You'll need**: [Match](https://rux-lang.dev/docs/learn/match), [Range](https://rux-lang.dev/docs/learn/range), [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Guard](https://rux-lang.dev/docs/learn/guard)
::
A literal pattern matches one value. A **range pattern** matches a whole run of them. It turns the question "is the value between these two numbers?" into a single arm instead of a pair of comparisons, and a ladder of ranges reads like the table it came from.
## Two kinds of range
A range pattern is written with the same two operators as a range in a [`for` loop](https://rux-lang.dev/docs/learn/range):
| Pattern | Matches | Suits |
| ----------- | ---------------------------------------------- | ----------------------------------------- |
| `200..=299` | 200 up to **and including** 299 | tables whose rows name first and last |
| `0..10` | 0 up to, but **not including**, 10 (so 0 to 9) | boundaries where one range starts another |
## Inclusive ranges: a table
HTTP status codes come in blocks of a hundred, and each block is described by its first and last code. `..=` writes those rows exactly as a reference table would:
```rux
func Status(code: int32) -> char8[..] {
return match code {
100..=199 => "informational",
200..=299 => "success",
300..=399 => "redirection",
400..=499 => "client error",
500..=599 => "server error",
else => "not a status code"
};
}
```
An `int32` has billions of values the arms do not reach, so the match ends with `else`, which decides what all of them mean.
## Exclusive ranges: boundaries
`..` stops just before its end, so when each range ends where the next one starts, no value falls between two arms and none is claimed twice:
```rux
func Size(count: int32) -> char8[..] {
return match count {
0 => "none",
1..10 => "a few",
10..100 => "dozens",
100..1000 => "hundreds",
-1000..0 => "a debt",
else => "a lot"
};
}
```
Two more things show here. Ranges mix freely with literal arms — `0` has an arm of its own — and a negative end is written with its minus sign, as in `-1000..0`.
```mermaid
flowchart LR
n["count"] --> z["0
none"]
n --> a["1..10
a few"]
n --> b["10..100
dozens"]
n --> c["100..1000
hundreds"]
n --> d["-1000..0
a debt"]
n --> e["else
a lot"]
```
Ten is the end of `1..10` but not part of it, so `Size(10)` is `"dozens"`, as the output shows.
## Ranges take guards
A range is a pattern like any other, so it can carry a [guard](https://rux-lang.dev/docs/learn/guard). The arm is chosen only when the value is in the range **and** the condition holds; otherwise the match moves on:
```rux
func Fare(age: int32, student: bool) -> int32 {
return match age {
0..=5 => 0,
6..=17 => 5,
18..=25 if student => 5,
65..=120 => 6,
else => 10
};
}
```
A student of 20 takes the third arm and pays 5. A non-student of 20 fails the guard, matches no other range, and reaches `else`: full fare.
## Only literal numbers
Both ends must be literal integers. A named constant is not accepted as an end, because a bare name in a pattern means "bind a new variable" — that is how `value if …` worked in the previous lesson. Ranges also work on integers only; characters are matched one at a time, as [Character pattern](https://rux-lang.dev/docs/learn/character-pattern) shows.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Patterns/RangePattern){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A literal pattern matches one value. A range pattern matches a whole run of them, written with
// the same two operators as a range in a `for` loop:
//
// 200..=299 from 200 up to and including 299
// 0..10 from 0 up to, but not including, 10
//
// That turns "is the value between these two numbers?" into a single arm instead of a pair of
// comparisons, and a ladder of ranges reads like the table it came from.
//
// Both ends must be literal numbers. A named constant is not accepted as an end, because a bare
// name in a pattern means "bind a new variable". With `const Low = 100;`, the arm `Low..=199 =>`
// stops with
// error: pattern 'Low' cannot bind a new variable because 'Low' already names a constant
// Ranges work on integers only; characters are matched one at a time (see the CharacterPattern
// lesson).
import Io::PrintLine;
// Inclusive ranges, `..=`, suit a table whose rows name their first and last value.
func Status(code: int32) -> char8[..] {
return match code {
100..=199 => "informational",
200..=299 => "success",
300..=399 => "redirection",
400..=499 => "client error",
500..=599 => "server error",
else => "not a status code"
};
}
// Exclusive ranges, `..`, suit boundaries: each range stops where the next one starts, so no value
// falls between two arms and none is claimed twice. Ranges mix freely with literal arms, and a
// negative end is written with its minus sign.
func Size(count: int32) -> char8[..] {
return match count {
0 => "none",
1..10 => "a few",
10..100 => "dozens",
100..1000 => "hundreds",
-1000..0 => "a debt",
else => "a lot"
};
}
// A range takes a guard like any other pattern. The arm is chosen only when the age is in the range
// *and* the condition holds; otherwise the match moves on, so a non-student of 20 pays full fare.
func Fare(age: int32, student: bool) -> int32 {
return match age {
0..=5 => 0,
6..=17 => 5,
18..=25 if student => 5,
65..=120 => 6,
else => 10
};
}
func Main() -> int {
let codes: int32[4] = [204, 302, 404, 99];
for index in 0..4 {
PrintLine("{} {}", codes[index], Status(codes[index]));
}
let counts: int32[5] = [0, 9, 10, 250, -3];
for index in 0..5 {
PrintLine("{} is {}", counts[index], Size(counts[index]));
}
PrintLine("fares: age 4 pays {}, 12 pays {}, 70 pays {}", Fare(4, false), Fare(12, false),
Fare(70, false));
PrintLine("age 20 pays {} as a student, {} otherwise", Fare(20, true), Fare(20, false));
return 0;
}
```
## Run it
```sh
cd Examples/Patterns/RangePattern
rux run
```
```text
204 success
302 redirection
404 client error
99 not a status code
0 is none
9 is a few
10 is dozens
250 is hundreds
-3 is a debt
fares: age 4 pays 0, 12 pays 5, 70 pays 6
age 20 pays 5 as a student, 10 otherwise
```
## Common mistakes
::warning
**A named constant as a range end.**:br
With `const Low = 100;`, the arm `Low..=199 =>` stops with `error: pattern 'Low' cannot bind a new variable because 'Low' already names a constant`. Write the number itself.
::
::warning
**Inclusive ranges that share an end.**:br`0..=10 => "low", 10..=20 => "mid"` compiles, but both arms claim 10 and the first one wins, so `10` is `"low"`. Use `..` for ranges that meet, or make sure each inclusive range starts one past the previous end.
::
::warning
**Ends written the wrong way round.**:br`10..1 =>` is accepted, but no number is at least 10 and below 1, so the arm never matches and every value goes on to the next arm. The smaller end comes first.
::
::warning
**A range of characters.**:br`'a'..='z' =>` stops with `error: range pattern cannot match value of type 'char32'`. Bind the character and compare it in a guard instead — [Character pattern](https://rux-lang.dev/docs/learn/character-pattern) shows how.
::
## Try it yourself
1. Add a `1000..10000 => "thousands"` arm to `Size` and print `Size(4096)`.
2. Rewrite `Status` with exclusive ranges, `100..200` and so on. Does the output change?
3. Give `Fare` a guarded arm that lets anyone of 60 to 64 travel for 6 when they hold a pass, adding a `pass: bool` parameter.
## Learn more
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) and [ranges](https://rux-lang.dev/docs/lang/ranges/overview) in the Rux Reference
- [Range](https://rux-lang.dev/docs/learn/range) — the same `..` and `..=` in a `for` loop
- [Tuple pattern](https://rux-lang.dev/docs/learn/tuple-pattern) — ranges as parts of a bigger pattern
# Tuple pattern
::note
**You'll need**: [Tuple](https://rux-lang.dev/docs/learn/tuple), [Guard](https://rux-lang.dev/docs/learn/guard), [Range pattern](https://rux-lang.dev/docs/learn/range-pattern)
::
A `match` looks at one value. Some decisions depend on two: where a point lies depends on both of its coordinates, and FizzBuzz on two remainders at once. Put the values in a [tuple](https://rux-lang.dev/docs/learn/tuple) and match the tuple, and every arm can talk about all of them together.
## One part per member
Each arm is then a **tuple pattern** with one part per member, and the arm is taken only when every part matches its member:
| Pattern | Matches when |
| -------- | ----------------------------------------------------- |
| `(0, 0)` | both members are 0 |
| `(x, 0)` | the second member is 0; the first is bound to `x` |
| `(_, y)` | any first member, ignored; the second is bound to `y` |
A part can be any pattern you have met so far: a literal, a name, `_` or a [range](https://rux-lang.dev/docs/learn/range-pattern). Literal parts test a member, names bind one, and `_` ignores one.
## Where a point lies
```rux
func Locate(point: (int32, int32)) {
match point {
(0, 0) => PrintLine("the origin"),
(x, 0) => PrintLine("on the x axis, {} across", x),
(0, y) => PrintLine("on the y axis, {} up", y),
(x, y) if x == y => PrintLine("on the diagonal, {} each way", x),
(_, y) if y > 0 => PrintLine("above the x axis, {} up", y),
else => PrintLine("below the x axis")
}
}
```
Arms are tried from top to bottom, so the most specific ones go first. `(0, 0)` would also fit `(x, 0)` and `(0, y)`, which is why it comes before both. A [guard](https://rux-lang.dev/docs/learn/guard) follows the pattern as usual and can compare the members with each other: `(x, y) if x == y` is something no literal pattern can say.
A pair of `int32`s has far more combinations than any list of arms, so this match ends with `else`, which decides what all the others mean.
## Building the tuple in the match
The tuple need not exist beforehand. Build it right in the `match` from the values the decision depends on — here, FizzBuzz decided by both remainders at once:
```rux
match (n % 3, n % 5) {
(0, 0) => Print("FizzBuzz"),
(0, _) => Print("Fizz"),
(_, 0) => Print("Buzz"),
else => Print("{}", n)
}
```
Compare it with the `if` / `else if` version from [the FizzBuzz project](https://rux-lang.dev/docs/learn/fizz-buzz): the four outcomes are now four lines that read like a truth table.
## Covering every combination
Two `bool`s make only four combinations, so the arms can list them all and need no `else`:
```rux
let advice = match (raining, windy) {
(true, true) => "a raincoat",
(true, false) => "an umbrella",
(false, true) => "a jacket",
(false, false) => "nothing extra"
};
```
The compiler checks the list. Leave one combination out and it names the missing one exactly.
## Structs work by name
A struct can be taken apart the same way, by field name instead of position: `Point { x: 0, y } =>` tests `x` and binds `y`. A field written alone, like `y`, is short for `y: y`. The next lesson, [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern), uses this form on variant cases.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Patterns/TuplePattern){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `match` looks at one value. To decide on two values together, put them in a tuple and match
// the tuple. Each arm is then a tuple pattern with one part per member, and the arm is taken only
// when every part matches its member:
//
// (0, 0) both members are 0
// (x, 0) the second member is 0, and the first is bound to `x`
// (_, y) any first member, ignored, and the second bound to `y`
//
// A part can be any pattern you have met so far: a literal, a name, `_` or a range. Arms are tried
// from top to bottom, so the most specific ones go first, and a guard can follow the pattern as
// usual. A pair of numbers has far more combinations than any list of arms, so a match on one ends
// with `else`, which decides what all the others mean. A pair of `bool`s has only four, so its
// arms can list them all, and the compiler checks that none is missing.
import Io::{ Print, PrintLine };
// Where a point lies. Literal parts test a member, names bind one, and `_` ignores one.
func Locate(point: (int32, int32)) {
match point {
(0, 0) => PrintLine("the origin"),
(x, 0) => PrintLine("on the x axis, {} across", x),
(0, y) => PrintLine("on the y axis, {} up", y),
(x, y) if x == y => PrintLine("on the diagonal, {} each way", x),
(_, y) if y > 0 => PrintLine("above the x axis, {} up", y),
else => PrintLine("below the x axis")
}
}
func Main() -> int {
let points: (int32, int32)[6] = [(0, 0), (4, 0), (0, 6), (3, 3), (-1, 5), (2, -7)];
for point in points {
Print("({}, {}) is ", point.0, point.1);
Locate(point);
}
// The tuple need not exist beforehand: build it right in the `match` from the values the
// decision depends on. This is FizzBuzz, decided by both remainders at once.
for n in 1..=15 {
match (n % 3, n % 5) {
(0, 0) => Print("FizzBuzz"),
(0, _) => Print("Fizz"),
(_, 0) => Print("Buzz"),
else => Print("{}", n)
}
if n < 15 {
Print(" ");
}
}
PrintLine("");
// Two `bool`s make four combinations, and these arms list them all, so no `else` is needed.
// Leave one out and the compiler names it:
// error: match on '(bool8, bool8)' is not exhaustive; missing (true, false)
let raining = true;
let windy = false;
let advice = match (raining, windy) {
(true, true) => "a raincoat",
(true, false) => "an umbrella",
(false, true) => "a jacket",
(false, false) => "nothing extra"
};
PrintLine("rain without wind: take {}", advice);
// A struct can be taken apart the same way, by field name instead of position:
// `Point { x: 0, y } =>` tests `x` and binds `y`. A field written alone, like `y`, is short
// for `y: y`.
return 0;
}
```
## Run it
```sh
cd Examples/Patterns/TuplePattern
rux run
```
```text
(0, 0) is the origin
(4, 0) is on the x axis, 4 across
(0, 6) is on the y axis, 6 up
(3, 3) is on the diagonal, 3 each way
(-1, 5) is above the x axis, 5 up
(2, -7) is below the x axis
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
rain without wind: take an umbrella
```
## Common mistakes
::warning
**A missing combination.**:br
Leave `(true, false)` out of the weather match and the compiler stops with `error: match on '(bool8, bool8)' is not exhaustive; missing (true, false)`. Either add the arm or, if the remaining combinations really share an answer, an `else`.
::
::warning
**The wrong number of parts.**:br
A pattern needs exactly one part per member. Matching a pair with `(0, 0, 0) =>` fails with `error: tuple pattern has 3 elements, but matched tuple has 2`.
::
::warning
**A general arm above a specific one.**:br`(x, 0)` also fits `(0, 0)`. Put it first and the origin is reported as "on the x axis, 0 across" — the compiler accepts the order, so the arms must be arranged from most to least specific by you.
::
## Try it yourself
1. Add an arm to `Locate` for points on the other diagonal, where `x == -y`. Where does it have to go to be reached by `(-2, 2)`?
2. Rewrite FizzBuzz to also print `"Bazz"` for multiples of 7, matching a three-member tuple `(n % 3, n % 5, n % 7)`.
3. Match a `(bool, int32)` pair — say, `(member, age)` — using a range for the age, such as `(true, 0..=17) =>`.
## Learn more
- [Tuples](https://rux-lang.dev/docs/lang/tuples/overview) and [destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) in the Rux Reference
- [Tuple](https://rux-lang.dev/docs/learn/tuple) and [Destructure](https://rux-lang.dev/docs/learn/destructure) — building and taking apart tuples outside a `match`
- [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern) — the same idea by field name
# Struct pattern
::note
**You'll need**: [Variant](https://rux-lang.dev/docs/learn/variant), [Variant match](https://rux-lang.dev/docs/learn/variant-match)
::
[Variant](https://rux-lang.dev/docs/learn/variant) showed that a case can carry **named fields**, declared like a struct's, as in `Between { low: float64; high: float64; }`. [Variant match](https://rux-lang.dev/docs/learn/variant-match) then took cases apart by position, as in `.Jump(across, _)`. Named fields deserve a pattern that uses their names: with two or three values of the same type, a name says far more than a position — is the second number the height or the base? This lesson shows that pattern.
## Cases with named fields
```rux
variant Shape {
Circle { radius: int32; },
Rectangle { width: int32; height: int32; },
Triangle { base: int32; height: int32; }
}
```
A value is built with the same braces, naming each field: `Shape::Circle { radius: 5 }`. The pattern that matches it looks just like that literal, with names where the values were.
## Binding fields by name
The shorthand writes each field alone, and each one arrives as a variable of the same name:
```rux
func Area(shape: Shape) -> int32 {
return match shape {
.Circle { radius } => 3 * radius * radius,
.Rectangle { width, height } => width * height,
.Triangle { base, height } => base * height / 2
};
}
```
A field written alone, `radius`, is short for `radius: radius`: "read the `radius` field into a variable called `radius`".
## Renaming and leaving out
Fields are found by **name**, so their order in the pattern does not matter, and you are free to skip the ones you do not need:
```rux
func Width(shape: Shape) -> int32 {
return match shape {
.Circle { radius: r } => 2 * r,
.Rectangle { width } => width,
.Triangle { height: _, base } => base
};
}
```
`radius: r` reads the radius into `r`. The rectangle leaves its `height` out, and a field left out matches anything, as if it were written `height: _`. The triangle writes `height: _` anyway — that says "I know about this field and I do not need it" — and lists its fields in the opposite order from the declaration.
## Testing a field
A literal in a field's place picks out the cases holding that value:
```rux
func Kind(shape: Shape) -> char8[..] {
return match shape {
.Circle { radius: 0 } => "dot",
.Circle {} => "circle",
.Rectangle {} => "rectangle",
.Triangle {} => "triangle"
};
}
```
Arms are tried in order, so the circle with radius `0` comes first and the plain circle arm below it catches every other radius. The other arms test nothing, and `{}` leaves every field out.
## Four spellings
| Field in the pattern | Means |
| ----------------------- | --------------------------------------------- |
| `radius` | bind the field to a variable of the same name |
| `radius: r` | bind the field to a name of your choosing |
| `height: _` or left out | ignore the field |
| `radius: 0` | match only when the field holds this value |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Patterns/StructPattern){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A variant case can carry named fields, declared like a struct's: `Rectangle { width: int32;
// height: int32; }`. The pattern for such a case looks like the literal that builds it, with
// names where the values were: `.Rectangle { width, height } =>`.
//
// Each field in the pattern binds a variable or tests a value. Four spellings cover what you
// usually need:
//
// .Circle { radius } bind the field to a variable of the same name
// .Circle { radius: r } bind the field to a name of your choosing
// .Rectangle { width } leave out a field you do not need
// .Circle { radius: 0 } match only when the field holds this value
//
// Fields are found by name, so their order in the pattern does not matter. A field left out
// matches anything, as if it were written `height: _`, and `.Rectangle {}` leaves out every field.
// Writing `height: _` anyway says "I know about this field and I do not need it".
import Io::PrintLine;
variant Shape {
Circle { radius: int32; },
Rectangle { width: int32; height: int32; },
Triangle { base: int32; height: int32; }
}
// The shorthand: each field arrives under its own name.
func Area(shape: Shape) -> int32 {
return match shape {
.Circle { radius } => 3 * radius * radius,
.Rectangle { width, height } => width * height,
.Triangle { base, height } => base * height / 2
};
}
// Renaming and ignoring. `radius: r` reads the radius into `r`; the rectangle leaves out the
// height this question does not need; and the triangle skips it with `height: _`, listing its
// fields in the opposite order.
func Width(shape: Shape) -> int32 {
return match shape {
.Circle { radius: r } => 2 * r,
.Rectangle { width } => width,
.Triangle { height: _, base } => base
};
}
// Testing. A literal in a field picks out the cases holding that value. Arms are tried in order, so
// the circle with a `0` comes first and the plain circle arm below it catches every other radius.
// The other arms test no field, so `{}` leaves every field out.
func Kind(shape: Shape) -> char8[..] {
return match shape {
.Circle { radius: 0 } => "dot",
.Circle {} => "circle",
.Rectangle {} => "rectangle",
.Triangle {} => "triangle"
};
}
func Main() -> int {
let shapes: Shape[4] = [
Shape::Circle { radius: 5 },
Shape::Rectangle { width: 4, height: 6 },
Shape::Triangle { base: 10, height: 3 },
Shape::Circle { radius: 0 }
];
for index in 0..4 {
PrintLine("{:9} width {:2}, area {}", Kind(shapes[index]), Width(shapes[index]),
Area(shapes[index]));
}
return 0;
}
```
## Run it
```sh
cd Examples/Patterns/StructPattern
rux run
```
```text
circle width 10, area 75
rectangle width 4, area 24
triangle width 10, area 15
dot width 0, area 0
```
## Common mistakes
::warning
**A misspelt field name.**:br
Fields are found by name, so a name the case does not have is an error: `.Circle { radiuss } =>` stops with `error: unknown field 'radiuss' in variant pattern`.
::
::warning
**Leaving out the braces.**:br`.Circle =>` names a case with no data. For a case with fields it fails with `error: pattern for 'Shape::Circle' expects 1 field, but found 0`. Write `.Circle {}` to match any circle without reading its fields.
::
::warning
**Testing a field without a catch-all arm.**:br`.Circle { radius: 0 }` covers only the circles of radius 0. If no plain `.Circle {}` (or `.Circle { radius }`) arm follows, the match fails with `error: match on 'Shape' is not exhaustive; missing Shape::Circle`.
::
## Try it yourself
1. Add a `Square { side: int32; }` case to `Shape`. Run `rux check` first and read which matches the compiler sends you to.
2. Add an arm to `Kind` that names a rectangle whose `width` and `height` are equal a `"square"`. You will need a [guard](https://rux-lang.dev/docs/learn/guard).
3. Write a function `Perimeter` that renames every field it reads, as in `.Rectangle { width: w, height: h }`.
## Learn more
- [Variants with data](https://rux-lang.dev/docs/lang/variants/overview) and [structs](https://rux-lang.dev/docs/lang/structs/overview) in the Rux Reference
- [Variant](https://rux-lang.dev/docs/learn/variant) and [Variant match](https://rux-lang.dev/docs/learn/variant-match) — building and matching cases
- [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) — why every case of `Shape` needs an arm
# Character pattern
::note
**You'll need**: [Character](https://rux-lang.dev/docs/learn/character), [Match](https://rux-lang.dev/docs/learn/match), [Guard](https://rux-lang.dev/docs/learn/guard)
::
A character literal is a pattern, just as a number is. That makes `match` the natural way to react to a key press, a command letter or a separator in some text: one arm per character, and `else` for the rest. This lesson shows the arms, the one rule about their type, and what to do when you need a whole class of characters such as "any digit".
## One arm per character
`'w' =>` matches the character w. Escapes work the same way they do in a [literal](https://rux-lang.dev/docs/learn/character): `' '` is a space and `'\n'` a line break, and any Unicode character can have an arm, `'λ'` included.
```rux
func Command(key: char32) -> char8[..] {
return match key {
'w' => "move up",
'a' => "move left",
's' => "move down",
'd' => "move right",
' ' => "jump",
'λ' => "cast a spell",
else => "ignored"
};
}
```
Uppercase and lowercase are different characters, so `'W'` does not reach the first arm. Anything without an arm of its own falls through to `else`. A match that produces a value from a character should always end with one: a `char32` has far too many values to list, and `else` is where you decide what all the others mean.
## The pattern and the value share a width
A pattern must have the same character width as the value it is matched against:
| Value type | Pattern | Example |
| ---------------------------------------- | ------- | ---------- |
| `char32` — an unprefixed literal's type | `'w'` | `'w' =>` |
| `char8` — such as a byte out of a string | `c8'w'` | `c8'w' =>` |
`Command` takes a `char32`, so its plain `'w'` patterns fit. A `char8` value is matched with the `c8` prefix you met in [Character](https://rux-lang.dev/docs/learn/character) — `c8'w'`. Every byte of a string literal is a `char8`, as [String literal](https://rux-lang.dev/docs/learn/string-literal) shows later in the course.
## A class of characters needs a guard
There is no character range pattern: `'a'..='z'` is refused. When you need a whole class, bind the character to a name and test it in a [guard](https://rux-lang.dev/docs/learn/guard):
```rux
func Kind(key: char32) -> char8[..] {
return match key {
'\n' => "line break",
c if c >= '0' && c <= '9' => "digit",
c if c >= 'a' && c <= 'z' => "lowercase letter",
else => "something else"
};
}
```
This works because characters compare by their code, and the digits, like the letters `a` to `z`, have consecutive codes. `'7'` lies between `'0'` and `'9'`; `'K'` is an uppercase letter, whose codes come before the lowercase ones, so it is "something else".
```mermaid
flowchart LR
k["key"] --> lf{"a line break?"}
lf -- "yes" --> l1["line break"]
lf -- "no" --> d{"'0' ≤ c ≤ '9'?"}
d -- "yes" --> l2["digit"]
d -- "no" --> a{"'a' ≤ c ≤ 'z'?"}
a -- "yes" --> l3["lowercase letter"]
a -- "no" --> l4["something else"]
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Patterns/CharacterPattern){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A character literal is a pattern, just as a number is. `'w' =>` matches the character w, and
// escapes work the same way they do in a literal: `' '` is a space, `'\n'` a line break.
//
// The pattern must have the same character width as the value. An unprefixed `'w'` is a `char32`,
// so it matches a `char32` value; a `char8` value, such as a byte read out of a text literal, is
// matched with `c8'w'` instead. Mix the two and the compiler stops with
// error: pattern has type 'char32', but the matched value has type 'char8'
//
// There is no character range pattern. `'a'..='z' =>` stops with
// error: range pattern cannot match value of type 'char32'
// When you need a whole class of characters, bind the character and test it in a guard.
import Io::PrintLine;
// One arm per key. Uppercase and lowercase are different characters, so `'W'` would not reach the
// first arm, and anything without an arm of its own falls through to `else`.
func Command(key: char32) -> char8[..] {
return match key {
'w' => "move up",
'a' => "move left",
's' => "move down",
'd' => "move right",
' ' => "jump",
'λ' => "cast a spell",
else => "ignored"
};
}
// A guard stands in for the missing range pattern. Characters compare by their code, and the
// digits, like the letters, have consecutive codes.
func Kind(key: char32) -> char8[..] {
return match key {
'\n' => "line break",
c if c >= '0' && c <= '9' => "digit",
c if c >= 'a' && c <= 'z' => "lowercase letter",
else => "something else"
};
}
func Main() -> int {
let keys: char32[6] = ['w', 'd', ' ', 'λ', 'W', 'q'];
for index in 0..6 {
PrintLine("'{}' -> {}", keys[index], Command(keys[index]));
}
PrintLine("'7' is a {}, 'k' is a {}, 'K' is {}", Kind('7'), Kind('k'), Kind('K'));
PrintLine("'\\n' is a {}", Kind('\n'));
return 0;
}
```
## Run it
```sh
cd Examples/Patterns/CharacterPattern
rux run
```
```text
'w' -> move up
'd' -> move right
' ' -> jump
'λ' -> cast a spell
'W' -> ignored
'q' -> ignored
'7' is a digit, 'k' is a lowercase letter, 'K' is something else
'\n' is a line break
```
## Common mistakes
::warning
**A pattern of the wrong width.**:br
Each character of a string literal is a `char8`, while an unprefixed `'a'` is a `char32`. Matching one with the other stops with `error: pattern has type 'char32', but the matched value has type 'char8'`. Write `c8'a'` for a `char8` value.
::
::warning
**A character range.**:br`'a'..='z' =>` stops with `error: range pattern cannot match value of type 'char32'` — ranges work on integers only. Bind the character and test it in a guard, as `Kind` does.
::
::warning
**Forgetting the other case.**:br`'w'` and `'W'` are different characters, so an arm for one never matches the other. Give each its own arm; an arm holds exactly one pattern, so there is no `'w' | 'W'`.
::
## Try it yourself
1. Make `Command` accept the uppercase keys `W`, `A`, `S` and `D` as well.
2. Extend `Kind` so that `'K'` is reported as an `"uppercase letter"`.
3. Loop over the text `"pattern matching"` with `for c in …` — each `c` is a `char8` — and count its vowels with arms such as `c8'a' =>`.
## Learn more
- [`char32`](https://rux-lang.dev/docs/lang/types/characters), [`char8`](https://rux-lang.dev/docs/lang/types/characters) and [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
- [Character](https://rux-lang.dev/docs/learn/character) — character literals, escapes and widths
- [Word count](https://rux-lang.dev/docs/learn/word-count) — a checkpoint project that sorts characters into letters and the rest
# Exhaustive
::note
**You'll need**: [Enum](https://rux-lang.dev/docs/learn/enum), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Guard](https://rux-lang.dev/docs/learn/guard), [Range pattern](https://rux-lang.dev/docs/learn/range-pattern)
::
A `match` is **exhaustive** when every value it could meet has an arm. You have already seen the compiler insist on it — a missing case in [Variant match](https://rux-lang.dev/docs/learn/variant-match), a missing `else` in [Match expression](https://rux-lang.dev/docs/learn/match-expression). This lesson collects the rules in one place and, more importantly, explains why the check is a feature you want: it is what lets a program grow without silently breaking.
## A closed list of cases
A variant or an enum has a closed list of cases, so the compiler knows every value a match on it could meet, and it insists that every one has an arm:
```rux
variant Player {
Stopped,
Playing(int32),
Paused(int32)
}
```
```rux
func Describe(player: Player) -> char8[..] {
return match player {
.Stopped => "stopped",
.Playing(second) if second < 5 => "just started",
.Playing(_) => "playing",
.Paused(_) => "paused"
};
}
```
Every case has an arm, so no `else` is needed — and none should be written. Notice the two `.Playing` arms. A **guarded arm does not count** towards covering its case, because the guard might be false; `.Playing(second)` still needs the unguarded `.Playing(_)` below it.
## Why the check helps
That check is what makes a variant safe to grow. Suppose you add a `Buffering` case to `Player` next year:
```mermaid
flowchart LR
add["Add a Buffering case"] --> q{"How does each
match end?"}
q -- "names every case" --> err["Fails to build: missing
Player::Buffering,
pointing at the spot to fix"]
q -- "ends with else" --> quiet["Builds silently:
Buffering takes the
else arm's answer"]
```
Every match that names its cases one by one fails to compile, and the error points at the exact place to decide what buffering means. A match with `else` keeps compiling — and quietly gives `Buffering` whatever answer `else` gives.
## else on a variant is a promise
That is the trade-off in `IsPlaying`:
```rux
func IsPlaying(player: Player) -> bool {
return match player {
.Playing(_) => true,
else => false
};
}
```
An `else` on a variant is allowed, but it is a promise you make on behalf of cases that do not exist yet: a future `Buffering` would count as "not playing" with no error to warn you. Use it only when that is truly the right answer for any new case.
## else is for the values you cannot list
An integer has billions of values, so a match that produces a value from an integer must end with `else`, and deciding what the leftovers mean is up to you:
```rux
func Loudness(volume: int32) -> char8[..] {
return match volume {
0 => "muted",
1..=3 => "quiet",
10 => "maximum",
else => "normal"
};
}
```
Even ranges that happened to reach every value would not do instead: an integer match producing a value needs its `else`.
| Matched type | Covered by | `else` |
| -------------------- | ------------------------------ | ---------------------------- |
| A variant or an enum | one unguarded arm per case | allowed, but hides new cases |
| `bool` | a `true` arm and a `false` arm | not needed |
| A tuple of `bool`s | every combination | not needed |
| An integer | nothing short of `else` | required to produce a value |
A match used as a **statement**, producing no value, is more relaxed about integers and `bool`s: a value with no arm simply skips the match, as in [Match](https://rux-lang.dev/docs/learn/match). A match on a variant must still name every case.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Patterns/Exhaustive){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A variant or an enum has a closed list of cases, so the compiler knows every value a match on it
// could meet, and it insists that every one has an arm. Leave a case out and the program does not
// build. Delete the `.Paused` arm below and the compiler names what is missing:
// error: match on 'Player' is not exhaustive; missing Player::Paused
//
// That check is what makes a variant safe to grow. Add a case such as `Buffering` to `Player` next
// year, and every match that forgot it fails to compile, pointing at the exact spot to fix. A match
// that names every case therefore has no `else`, and should not have one.
//
// `else` is for the values you cannot list. An integer has billions of them, so a match that
// produces a value from an integer must end with `else`, and deciding what the leftovers mean is
// up to you. Delete the `else` arm in `Loudness` and the compiler stops with
// error: match on 'int32' is not exhaustive; its arms do not cover every value
// A `bool` has just two values, so `true` and `false` arms cover it, and a missing one is named:
// error: match on 'bool8' is not exhaustive; missing false
// A match used as a statement, producing no value, may leave integers and bools out: a value with
// no arm simply skips it.
import Io::PrintLine;
variant Player {
Stopped,
Playing(int32),
Paused(int32)
}
// Every case has an arm, so no `else` is needed. A guarded arm does not count towards that: the
// guard might be false, so `.Playing(second)` still needs an unguarded arm of its own. Without it
// the error would read `missing Player::Playing`.
func Describe(player: Player) -> char8[..] {
return match player {
.Stopped => "stopped",
.Playing(second) if second < 5 => "just started",
.Playing(_) => "playing",
.Paused(_) => "paused"
};
}
// An `else` on a variant is allowed, but it is a promise you make on behalf of cases that do not
// exist yet: a future `Buffering` would quietly count as "not playing" here, with no error to
// warn you. Use it only when that is truly the right answer for any new case.
func IsPlaying(player: Player) -> bool {
return match player {
.Playing(_) => true,
else => false
};
}
// The arms name a few volume levels, and `else` covers the rest. Even ranges that happened to reach
// every value would not do instead: an integer match producing a value needs its `else`.
func Loudness(volume: int32) -> char8[..] {
return match volume {
0 => "muted",
1..=3 => "quiet",
10 => "maximum",
else => "normal"
};
}
func Main() -> int {
let states: Player[4] = [
Player::Stopped,
Player::Playing(2),
Player::Playing(95),
Player::Paused(95)
];
for index in 0..4 {
PrintLine("{:12} playing: {}", Describe(states[index]), IsPlaying(states[index]));
}
PrintLine("0 {}, 2 {}, 7 {}, 10 {}", Loudness(0), Loudness(2), Loudness(7), Loudness(10));
return 0;
}
```
## Run it
```sh
cd Examples/Patterns/Exhaustive
rux run
```
```text
stopped playing: false
just started playing: true
playing playing: true
paused playing: false
0 muted, 2 quiet, 7 normal, 10 maximum
```
## Common mistakes
::warning
**A case with no arm.**:br
Delete the `.Paused` arm and the program does not build: `error: match on 'Player' is not exhaustive; missing Player::Paused`. The message names the case, so the fix is to add its arm — not an `else`.
::
::warning
**Only a guarded arm for a case.**:br
Delete `.Playing(_) => "playing",` and the guarded `.Playing(second) if second < 5` arm is left alone. It might not match, so the error reads `missing Player::Playing`.
::
::warning
**An integer match with no `else`.**:br
Delete the `else` arm in `Loudness` and the compiler stops with `error: match on 'int32' is not exhaustive; its arms do not cover every value`. A `bool` match is held to the same rule, but there two arms are enough; leave one out and it is named: `error: match on 'bool8' is not exhaustive; missing false`.
::
## Try it yourself
1. Add a `Buffering` case to `Player` and run `rux check`. Which function fails, and which one keeps compiling?
2. Rewrite `IsPlaying` without `else`, so that a future case is reported instead of quietly counted as "not playing".
3. Give `Loudness` arms that between them cover `0..=10`, then delete the `else`. Read why it still does not compile.
## Learn more
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) and [enumerations](https://rux-lang.dev/docs/lang/enums/overview) in the Rux Reference
- [Enum](https://rux-lang.dev/docs/learn/enum) and [Variant match](https://rux-lang.dev/docs/learn/variant-match) — the closed lists that make the check possible
- [Presence](https://rux-lang.dev/docs/learn/presence) — the same rule for optionals, where the case you cannot forget is `none`
# Part 8: Optionals
Some questions have no answer: the first even number in a list of odd ones, the score of a player nobody signed. Rux writes "a value that may be absent" as `T?` — an `int?` holds an `int` or `none` — and the compiler will not let you use it as an `int` until you have said what happens when it is missing. This part covers every tool for saying so, from a full `match` to a single character.
## What you will learn
- What `int?` is, how a plain value becomes a present optional, and how to write `none`.
- Opening an optional with `match` and the presence patterns `value?` and `none`.
- Replacing absence with a fallback using `??`, which runs the fallback only when it is needed.
- Leaving a function or loop with `?? return`, `?? continue` or `?? break`.
- Passing absence on to the caller with a postfix `?`.
- Telling "nothing found" from "found nothing" with `int??`.
## What to do with an optional
Every lesson after the first answers one question — what should happen when the value is `none`?
```mermaid
flowchart LR
o(["An int?"]) --> m["match
value? => …
none => …"]
o --> c["?? fallback"]
o --> x["?? return / continue / break"]
o --> p["? (postfix)"]
m --> m2["Each case gets
its own code"]
c --> c2["A plain int:
the value, or the fallback"]
x --> x2["A plain int,
or control leaves here"]
p --> p2["A plain int,
or the function
returns none"]
```
| You want to… | Use | Lesson |
| -------------------------------- | -------------------------------------- | ------------------------------------------------------------------------ |
| run different code for each case | `match` with `value?` / `none` | [Presence](https://rux-lang.dev/docs/learn/presence) |
| carry on with a stand-in value | `?? fallback` | [Coalesce](https://rux-lang.dev/docs/learn/coalesce) |
| stop this function or loop | `?? return`, `?? continue`, `?? break` | [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) |
| let the caller decide | postfix `?` | [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) |
## Lessons
| | Lesson | What you will learn |
| --- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
| 8.1 | [Optional](https://rux-lang.dev/docs/learn/optional) | a value that may be missing: `int?` and `none` |
| 8.2 | [Presence](https://rux-lang.dev/docs/learn/presence) | match an optional with `value?` and `none` arms |
| 8.3 | [Coalesce](https://rux-lang.dev/docs/learn/coalesce) | supply a fallback for a missing value with `??` |
| 8.4 | [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) | leave with `?? return`, `?? continue` or `?? break` when a value is missing |
| 8.5 | [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) | pass absence to the caller with `?` |
| 8.6 | [Nested optional](https://rux-lang.dev/docs/learn/nested-optional) | `int??`: telling "nothing found" from "found nothing" |
## Before you start
Finish [Part 7: Patterns](https://rux-lang.dev/docs/learn/patterns): an optional is opened with the same `match`, guards and exhaustiveness rules, and the first lesson assumes [Variant](https://rux-lang.dev/docs/learn/variant) and [Variant match](https://rux-lang.dev/docs/learn/variant-match) from Part 6. Each lesson's package is in the Examples repository's `Optionals/` folder:
```sh
cd Examples/Optionals/Optional
rux run
```
## After this part
An optional says only *that* a value is missing. [Part 9: Errors](https://rux-lang.dev/docs/learn/errors) adds the fallible `T ! E`, which also says *why* — with its own `?`, its own fallbacks, and [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) to turn a `none` into a failure. After Part 9 you are ready for the checkpoint project [Calculator](https://rux-lang.dev/docs/learn/calculator).
For the patterns used here, see [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference.
# Optional
::note
**You'll need**: [Variant](https://rux-lang.dev/docs/learn/variant), [Variant match](https://rux-lang.dev/docs/learn/variant-match)
::
Some questions have no answer. "Which is the first even number here?" has none when every number is odd. A function returning `int` cannot say so: every value an `int` holds is a perfectly good answer, so any "special" number you pick — `0`, `-1` — could also be a real result. Rux gives absence a type of its own, and this part is about using it.
## int? — an int, or nothing
`int?` is an **optional** `int`. It either holds an `int`, and is then called *present*, or it holds nothing, and is *absent*. Absence has its own spelling, `none`.
```mermaid
flowchart LR
opt["int?"] --> some["present:
holds an int, such as 42"]
opt --> none["absent:
none"]
```
Any type can be made optional by adding `?`: `float64?`, `bool?`, `char8[..]?`. You can write an optional down directly, as a plain value or as `none`:
```rux
let known: int? = 42;
let unknown: int? = none;
```
## A plain value becomes present on its own
A plain `int` turns into a present `int?` by itself wherever an `int?` is expected, so there is nothing to wrap. `FirstEven` returns `value`, an ordinary `int`, and it arrives as a present answer:
```rux
func FirstEven(values: int[..]) -> int? {
for value in values {
if value % 2 == 0 {
// A plain `int`, returned where an `int?` is expected: it becomes present.
return value;
}
}
return none;
}
```
The return type says, right in the signature, that this function may find nothing. A caller cannot miss it.
## Looking inside
An optional cannot be printed or added up as it is. First you have to find out whether there is a value inside, and the smallest way to do that is a `match` with two arms:
```rux
match answer {
value? => PrintLine("{}: {}", label, value),
none => PrintLine("{}: none", label)
}
```
`value?` means "present, and call what it holds `value`"; `none` means absent. The [next lesson](https://rux-lang.dev/docs/learn/presence) explains these patterns fully.
## The type keeps the two apart
An `int?` is not an `int` until a match has shown that a value is there. Arithmetic on it is refused, because the optional might be `none`:
| You have | You can | You cannot |
| -------- | ------------------------------------- | ------------------------------- |
| `int` | add, compare, print it | store `none` in it |
| `int?` | store an `int` or `none`, match on it | add to it, or print it directly |
That refusal is the point. In languages where any value may secretly be "null", forgetting the check is a crash at run time; here it is a compile error, at the line that forgot.
## Absent, but not why
An optional says only *that* a value is missing, never *why*. "No even number" needs no explanation, but "could not open the file" does. When the reason matters, the fallible `int ! E` of [Part 9: Errors](https://rux-lang.dev/docs/learn/errors) carries it.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Optionals/Optional){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Some questions have no answer. "Which is the first even number here?" has none when every
// number is odd, and no `int` can mean "there wasn't one" — every value an `int` holds is a
// perfectly good answer.
//
// `int?` is the type for that: an optional `int`. It either holds an `int`, and is then called
// present, or it holds nothing, and is absent. Absence has its own spelling, `none`. A plain
// `int` turns into a present `int?` by itself wherever an `int?` is expected, so there is nothing
// to wrap.
import Io::PrintLine;
// The return type says, right in the signature, that this may find nothing.
func FirstEven(values: int[..]) -> int? {
for value in values {
if value % 2 == 0 {
// A plain `int`, returned where an `int?` is expected: it becomes present.
return value;
}
}
return none;
}
// An optional cannot be printed or added up as it is; first you have to find out whether there
// is a value inside. This is the smallest `match` that does it. `value?` means "present, and call
// what it holds `value`", and `none` means absent. The next lesson, Presence, explains it fully.
func Show(label: char8[..], answer: int?) {
match answer {
value? => PrintLine("{}: {}", label, value),
none => PrintLine("{}: none", label)
}
}
func Main() -> int {
// Optionals can be written down directly, too: a plain value or `none`.
let known: int? = 42;
let unknown: int? = none;
Show("known", known);
Show("unknown", unknown);
Show("first even of 3, 7, 8, 9", FirstEven([ 3, 7, 8, 9 ]));
Show("first even of 1, 3, 5, 7", FirstEven([ 1, 3, 5, 7 ]));
// The type keeps the two apart. `let doubled = known * 2;` is rejected — "operator '*'
// cannot combine left operand 'int?' with right operand 'int'" — because `known` might be
// `none`. An `int?` is not an `int` until a match has shown that a value is there.
//
// An optional says only that a value is missing, never why. When the reason matters, the
// fallible `int ! E` of the Errors part carries it.
return 0;
}
```
## Run it
```sh
cd Examples/Optionals/Optional
rux run
```
```text
known: 42
unknown: none
first even of 3, 7, 8, 9: 8
first even of 1, 3, 5, 7: none
```
## Common mistakes
::warning
**Doing arithmetic on an optional.**:br`let doubled = known * 2;` is rejected with `error: operator '*' cannot combine left operand 'int?' with right operand 'int'`. `known` might be `none`; open it with a `match` first, or give it a fallback with [`??`](https://rux-lang.dev/docs/learn/coalesce).
::
::warning
**Printing an optional directly.**:br`PrintLine("{}", known);` fails with `error: argument 2 to 'PrintLine' has type 'int?', but variadic parameter 'args' requires 'Display'`. Only the value inside can be printed, once a match has found it.
::
::warning
**Inventing a "no value" number.**:br
Returning `-1` or `0` for "not found" from a function declared `-> int` compiles, but every caller has to know the convention, and nothing stops one from adding the `-1` to a total. Return `int?` and the compiler makes every caller deal with absence.
::
## Try it yourself
1. Write `FirstNegative(values: int[..]) -> int?` and show its answer for `[3, -2, 5]` and `[1, 2, 3]`.
2. Write `Divide(a: int, b: int) -> int?` that returns `none` when `b` is 0.
3. Try `let doubled = known * 2;` and the direct `PrintLine` of an optional, and read both errors.
## Learn more
- [Presence](https://rux-lang.dev/docs/learn/presence) — opening an optional with `match`
- [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — replacing absence with a fallback in one line
- [Fallible](https://rux-lang.dev/docs/learn/fallible) — when the caller needs to know *why* there is no value
# Presence
::note
**You'll need**: [Optional](https://rux-lang.dev/docs/learn/optional), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Guard](https://rux-lang.dev/docs/learn/guard)
::
An `int?` holds either an `int` or nothing, and the program cannot do sums with it until it knows which. `match` is how you find out. It opens the optional and hands you what is inside — but only in the arm where something is actually there. Everything you learnt about patterns in [Part 7](https://rux-lang.dev/docs/learn/patterns) carries over.
## Two arms
The lesson's `ScoreOf` looks up the player wearing a number and returns their score, or `none` when nobody wears it. Opening its answer takes two arms:
```rux
match ScoreOf(players, number) {
score? => PrintLine("#{} scored {}", number, score),
none => PrintLine("#{} is not on the team", number)
}
```
| Arm | Taken when | Inside the arm |
| ----------- | ----------------------- | ----------------------------------- |
| `score? =>` | the optional is present | `score` is the plain `int` it holds |
| `none =>` | the optional is absent | there is nothing to bind |
`score?` is a pattern, not a question. Read the `?` as "a present …": "a present value, called `score`". The name before it becomes an ordinary `int`, but only inside its own arm — there is no way to reach a `score` in the `none` arm, because there is none to reach.
## The long spelling
An optional is a two-case type, much like a [variant](https://rux-lang.dev/docs/learn/variant-match), and its present case has a name: `.Some`. These two arms mean exactly the same thing:
```rux
.Some(score) => PrintLine("#{} scored {}", number, score),
```
```rux
score? => PrintLine("#{} scored {}", number, score),
```
The short form is the usual one. The long one is worth recognising, because the compiler uses it in its messages, and [Nested optional](https://rux-lang.dev/docs/learn/nested-optional) needs it to explain two levels of absence.
## Any pattern can be present
The `?` wraps any pattern, so a literal and a [guard](https://rux-lang.dev/docs/learn/guard) work as usual:
```rux
func Rating(players: Player[..], number: int) -> char8[..] {
return match ScoreOf(players, number) {
0? => "has not scored yet",
score? if score >= 20 => "is the top scorer",
score? => "is a regular",
none => "is not on the team"
};
}
```
`0?` is "a present 0". The arms are tried from the top, so the general `score?` arm comes after the specific ones, exactly as in [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive).
```mermaid
flowchart LR
r["ScoreOf(…)"] --> q{"Present?"}
q -- "no" --> n["none
is not on the team"]
q -- "yes" --> z{"Is it 0?"}
z -- "yes" --> a["0?
has not scored yet"]
z -- "no" --> t{"score >= 20?"}
t -- "yes" --> b["score? if …
is the top scorer"]
t -- "no" --> c["score?
is a regular"]
```
## none cannot be forgotten
A match on an optional must be exhaustive, like a match on a variant — even when it is a statement. The `none` arm is required, because absence is the case the optional exists to make you handle.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Optionals/Presence){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An `int?` holds either an `int` or nothing, and the program cannot do sums
// with it until it knows which. `match` is how you find out. It opens the
// optional and hands you what is inside, but only in the arm where something
// is actually there.
//
// There are two arms to write:
//
// score? => ... present: `score` is the `int` inside
// none => ... absent: there is nothing to bind
//
// `score?` is a pattern, not a question. Read the `?` as "a present ...". The
// name before it becomes an ordinary `int`, but only inside its arm.
import Io::PrintLine;
struct Player {
number: int;
score: int;
}
// The score of the player wearing `number`, or `none` when nobody does.
func ScoreOf(players: Player[..], number: int) -> int? {
for player in players {
if player.number == number {
return player.score;
}
}
return none;
}
func Report(players: Player[..], number: int) {
match ScoreOf(players, number) {
score? => PrintLine("#{} scored {}", number, score),
none => PrintLine("#{} is not on the team", number)
}
}
// The same match with the long spelling. `.Some(score)` and `score?` mean the
// same thing, much as a variant case is matched by name. The short form is the
// usual one.
func ReportLong(players: Player[..], number: int) {
match ScoreOf(players, number) {
.Some(score) => PrintLine("#{} scored {}", number, score),
none => PrintLine("#{} is not on the team", number)
}
}
// The `?` wraps any pattern, so a literal and a guard work as usual. The arms
// are tried from the top, so the general `score?` arm comes after the
// specific ones.
func Rating(players: Player[..], number: int) -> char8[..] {
return match ScoreOf(players, number) {
0? => "has not scored yet",
score? if score >= 20 => "is the top scorer",
score? => "is a regular",
none => "is not on the team"
};
}
func Main() -> int {
let players = [
Player { number: 4, score: 0 },
Player { number: 7, score: 12 },
Player { number: 10, score: 25 }
];
Report(players, 7);
Report(players, 9);
ReportLong(players, 10);
for number in [ 4, 7, 10, 9 ] {
let rating = Rating(players, number);
PrintLine("#{} {}", number, rating);
}
// Watch out: the `none` arm is required. If you leave it out, the
// compiler stops with "match on 'int?' is not exhaustive; missing none",
// because that is the case the optional exists to make you handle.
return 0;
}
```
## Run it
```sh
cd Examples/Optionals/Presence
rux run
```
```text
#7 scored 12
#9 is not on the team
#10 scored 25
#4 has not scored yet
#7 is a regular
#10 is the top scorer
#9 is not on the team
```
## Common mistakes
::warning
**Leaving out the `none` arm.**:br
Without it the compiler stops with `error: match on 'int?' is not exhaustive; missing none` — even for a match used as a statement.
::
::warning
**Leaving out the present arm.**:br
The other half is required too. A match with only `none =>` fails with `error: match on 'int?' is not exhaustive; missing .Some(_)` — the long spelling at work.
::
::warning
**Only specific present arms.**:br`0?` and a guarded `score? if …` do not cover every present value. Without a plain `score?` arm the match is still missing `.Some(_)`.
::
## Try it yourself
1. Delete the `none` arm from `Report` and read the error.
2. Give `Rating` an arm for players who scored exactly 1, reading `"has scored once"`. Where does it go?
3. Write `Bench(players: Player[..], number: int) -> bool` that is `true` for a player with no goals and `false` otherwise — including for numbers nobody wears.
## Learn more
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
- [Optional](https://rux-lang.dev/docs/learn/optional) — what `int?` is
- [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — when all you need is a fallback, `??` is shorter than a match
# Coalesce
::note
**You'll need**: [Optional](https://rux-lang.dev/docs/learn/optional), [Function](https://rux-lang.dev/docs/learn/function)
::
Often absence has an obvious stand-in. With no frost reading there is no frost, so report zero; with no nickname, use the real name. A whole `match` for that is a lot of code for a small idea. The **coalescing operator** `??` says it in one line.
## optional ?? fallback
```rux
PrintLine("monday: {}", FirstFrost(monday) ?? 0);
PrintLine("tuesday: {}", FirstFrost(tuesday) ?? 0);
```
When the optional is present you get what is inside it. When it is `none` you get the fallback. Either way the result is a plain `int` — not an `int?` — so the rest of the program never has to think about absence again. Monday has a frost and prints `-4`; Tuesday has none and prints `0`.
It is a shorter way to write a match you already know:
| With `??` | Means the same as |
| ---------------------- | ---------------------------------------------- |
| `FirstFrost(day) ?? 0` | `match FirstFrost(day) { t? => t, none => 0 }` |
The fallback must have the type of the value inside: an `int?` takes an `int` fallback.
## The fallback is lazy
The fallback is evaluated only when it is needed. `Forecast` prints a line whenever it runs, so you can see when that is:
```rux
PrintLine(" {}", FirstFrost(monday) ?? Forecast());
PrintLine(" {}", FirstFrost(tuesday) ?? Forecast());
```
```mermaid
flowchart LR
o["FirstFrost(day)"] --> q{"Present?"}
q -- "yes" --> v["its value
(Forecast never runs)"]
q -- "no" --> f["run Forecast()
and use its result"]
```
Monday has a frost, so the forecast is never asked. Tuesday has none, so it is — and `(asking the forecast)` appears in the output only once. That matters when the fallback is slow, or does something you can see, such as printing or reading a file.
## Chaining
`??` chains. The optionals are tried from left to right, the first present one wins, and the last fallback covers the case where every one is absent:
```rux
let either = FirstFrost(tuesday) ?? FirstFrost(monday) ?? 0;
```
Tuesday is `none`, Monday is `-4`, so `either` is `-4`, and the final `0` is never needed.
## Mind the precedence
`==` binds more tightly than `??`, so a comparison next to a fallback needs parentheses:
```rux
let mild = (FirstFrost(tuesday) ?? 0) == 0;
```
Without them, `FirstFrost(tuesday) ?? 0 == 0` would mean `FirstFrost(tuesday) ?? (0 == 0)` — an `int` optional with a `bool` fallback.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Optionals/Coalesce){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Often absence has an obvious stand-in. With no reading there is no frost,
// so report zero. A whole `match` for that is a lot of code, and `??` says it
// in one line:
//
// optional ?? fallback
//
// When the optional is present you get what is inside it. When it is `none`
// you get the fallback. Either way the result is a plain `int`, so the rest of
// the program never has to think about absence again.
import Io::PrintLine;
// The first temperature below zero, or `none` on a mild day.
func FirstFrost(readings: int[..]) -> int? {
for reading in readings {
if reading < 0 {
return reading;
}
}
return none;
}
// A slow fallback, which prints so that you can see when it runs.
func Forecast() -> int {
PrintLine(" (asking the forecast)");
return -2;
}
func Main() -> int {
let monday = [ 3, 1, -4, -1 ];
let tuesday = [ 5, 6, 4, 2 ];
PrintLine("monday: {}", FirstFrost(monday) ?? 0);
PrintLine("tuesday: {}", FirstFrost(tuesday) ?? 0);
// The fallback is lazy: it is evaluated only when it is needed. Monday has
// a frost, so the forecast is never asked. Tuesday has none, so it is.
PrintLine("monday or forecast:");
PrintLine(" {}", FirstFrost(monday) ?? Forecast());
PrintLine("tuesday or forecast:");
PrintLine(" {}", FirstFrost(tuesday) ?? Forecast());
// `??` chains. The optionals are tried from left to right, the first
// present one wins, and the last fallback covers the case where every one
// is absent.
let either = FirstFrost(tuesday) ?? FirstFrost(monday) ?? 0;
PrintLine("first frost this week: {}", either);
// Watch out: `==` binds more tightly than `??`. Without the parentheses,
// `FirstFrost(tuesday) ?? 0 == 0` would mean `FirstFrost(tuesday) ?? (0 == 0)`,
// which mixes an `int` with a `bool` and does not compile.
let mild = (FirstFrost(tuesday) ?? 0) == 0;
PrintLine("tuesday was mild: {}", mild);
return 0;
}
```
## Run it
```sh
cd Examples/Optionals/Coalesce
rux run
```
```text
monday: -4
tuesday: 0
monday or forecast:
-4
tuesday or forecast:
(asking the forecast)
-2
first frost this week: -4
tuesday was mild: true
```
## Common mistakes
::warning
**A comparison without parentheses.**:br`let mild = FirstFrost(tuesday) ?? 0 == 0;` reads as `FirstFrost(tuesday) ?? (0 == 0)` and stops with `error: coalescing fallback has type 'bool8', but the optional payload is 'int'`. Wrap the coalescing part: `(FirstFrost(tuesday) ?? 0) == 0`.
::
::warning
**`??` on a value that is not optional.**:br
A plain `int` is never absent, so there is nothing to fall back from: `count ?? 0` fails with `error: operator '??' requires an optional left operand, but found 'int'`. Remove the `??`.
::
::warning
**A fallback that always runs — in your head.**:br
The fallback runs only for `none`. If it has an effect you rely on, such as a line of output, that effect happens only on the absent path, as the forecast shows.
::
## Try it yourself
1. Print the first frost of a week of three days, `FirstFrost(a) ?? FirstFrost(b) ?? FirstFrost(c) ?? 0`, with only the last day frosty.
2. Make `Forecast` return `-9` and check that Monday's line still prints `-4`.
3. Declare `let nickname: char8[..]? = none;` and print `nickname ?? "anonymous"`.
## Learn more
- [Presence](https://rux-lang.dev/docs/learn/presence) — the `match` that `??` abbreviates
- [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) — a fallback that leaves the function or loop instead of producing a value
- [Precedence](https://rux-lang.dev/docs/learn/precedence) — how Rux decides which operator binds first
# Coalesce exit
::note
**You'll need**: [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Return](https://rux-lang.dev/docs/learn/return), [Break](https://rux-lang.dev/docs/learn/break), [Continue](https://rux-lang.dev/docs/learn/continue)
::
Sometimes there is no sensible stand-in for a missing value. A bonus for a player who does not exist is not 0 — there is simply nothing more to compute. The fallback after `??` does not have to be a value: it can also be a **way out**. That turns the most common shape of optional code — "if it is missing, stop here" — into a single line.
## Three ways out
Any of these three can follow `??`:
```rux
let score = ScoreOf(players, number) ?? return 0; // leave the function
let score = ScoreOf(players, number) ?? continue; // skip to the next turn
let score = ScoreOf(players, number) ?? break; // leave the loop
```
If the optional is present, `score` is the `int` inside it and the code below runs as normal. If it is `none`, control leaves right there, and the line never produces a value at all. That is why `score` is a plain `int` with no `?`: by the next line, absence has already been dealt with.
| Written | When the optional is `none` | Allowed in |
| ------------- | ---------------------------------- | ------------------------------------------------------ |
| `?? return x` | the function returns `x` | any function — plain `?? return` in one with no result |
| `?? continue` | the loop moves on to its next turn | a `for`, `while` or `loop` |
| `?? break` | the loop ends; code after it runs | a `for`, `while` or `loop` |
## ?? return: a guard at the top
`?? return` deals with the missing case first, so the rest of the function can be written as if the value were always there:
```rux
func Bonus(players: Player[..], number: int) -> int {
let score = ScoreOf(players, number) ?? return 0;
if score >= 20 {
return 5;
}
return 1;
}
```
```mermaid
flowchart LR
s["ScoreOf(players, number)"] --> q{"Present?"}
q -- "yes" --> b["score is a plain int;
the function goes on"]
q -- "no" --> r["return 0
— nothing below runs"]
```
\#3 is not on the team, so `Bonus(players, 3)` returns 0 on its first line. #10 is, with 25, and earns 5.
## ?? continue and ?? break in a loop
Inside a loop, the same idea skips one turn or ends the loop. `requested` holds `7, 3, 10, 4`, and #3 is unknown:
```rux
var total = 0;
for number in requested {
let score = ScoreOf(players, number) ?? continue;
total += score;
}
```
`?? continue` skips #3 and the loop goes on, so the total is 12 + 25 + 8 = 45.
```rux
var counted = 0;
for number in requested {
let score = ScoreOf(players, number) ?? break;
PrintLine(" #{} scored {}", number, score);
counted++;
}
```
`?? break` stops at the first unknown number instead: #7 is printed, #3 ends the loop, and #10 and #4 are never looked at.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Optionals/CoalesceExit){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The fallback after `??` does not have to be a value. It can also be a way
// out:
//
// let score = ScoreOf(players, number) ?? return 0; leave the function
// let score = ScoreOf(players, number) ?? continue; skip to the next turn
// let score = ScoreOf(players, number) ?? break; leave the loop
//
// If the optional is present, `score` is the `int` inside it and the code
// below runs as normal. If it is `none`, control leaves right there, and the
// line never produces a value at all. That is why `score` is a plain `int`
// with no `?`: by the next line, absence has already been dealt with.
import Io::PrintLine;
struct Player {
number: int;
score: int;
}
func ScoreOf(players: Player[..], number: int) -> int? {
for player in players {
if player.number == number {
return player.score;
}
}
return none;
}
// `?? return` is a guard at the top of a function: deal with the missing case
// first, then write the rest as if the value were always there.
func Bonus(players: Player[..], number: int) -> int {
let score = ScoreOf(players, number) ?? return 0;
if score >= 20 {
return 5;
}
return 1;
}
func Main() -> int {
let players = [
Player { number: 4, score: 8 },
Player { number: 7, score: 12 },
Player { number: 10, score: 25 }
];
let requested = [ 7, 3, 10, 4 ];
PrintLine("bonus for #10: {}", Bonus(players, 10));
PrintLine("bonus for #3: {}", Bonus(players, 3));
// `?? continue` skips a number nobody wears, and the loop goes on.
var total = 0;
for number in requested {
let score = ScoreOf(players, number) ?? continue;
total += score;
}
PrintLine("total, skipping unknown numbers: {}", total);
// `?? break` stops at the first unknown number instead.
var counted = 0;
for number in requested {
let score = ScoreOf(players, number) ?? break;
PrintLine(" #{} scored {}", number, score);
counted++;
}
PrintLine("counted {} before the first unknown number", counted);
// Watch out: these exits only make sense where they are allowed. Outside
// a loop, `?? continue` and `?? break` are errors, just as a bare
// `continue;` would be.
return 0;
}
```
## Run it
```sh
cd Examples/Optionals/CoalesceExit
rux run
```
```text
bonus for #10: 5
bonus for #3: 0
total, skipping unknown numbers: 45
#7 scored 12
counted 1 before the first unknown number
```
## Common mistakes
::warning
**Leaving a loop that is not there.**:br
Outside a loop, `?? continue` and `?? break` are errors, just as a bare `continue;` would be: `error: 'continue' can only be used inside 'while', 'for', or 'loop'`.
::
::warning
**Returning `none` from a function that does not return an optional.**:br
In a function declared `-> int`, `?? return none` fails with `error: 'none' needs an expected optional type, but found 'int'`. Return a value of the function's own type, or make the function return `int?` — and then [`?`](https://rux-lang.dev/docs/learn/optional-propagate) says the same thing more briefly.
::
::warning
**Expecting `?? break` to skip.**:br`break` ends the whole loop at the first `none`; it does not move on to the next item. Reach for `?? continue` when the other items should still be processed.
::
## Try it yourself
1. Change `requested` so that the unknown number comes last. What do the two loops print now?
2. Write `Describe(players: Player[..], number: int) -> char8[..]` that begins with `?? return "unknown"` and then labels the score.
3. Make the `?? continue` loop also report how many numbers it skipped. Hint: count every turn before the `??` line and every success after it.
## Learn more
- [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — a fallback that is a value
- [Return](https://rux-lang.dev/docs/learn/return), [Break](https://rux-lang.dev/docs/learn/break) and [Continue](https://rux-lang.dev/docs/learn/continue) — the exits themselves
- [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) — a fourth way out, `?? fail`, from the Errors part
# Optional propagate
::note
**You'll need**: [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce)
::
Sometimes a function cannot do anything useful with a missing value except report that it is missing too. How far ahead of #7 is #10? If either player is unknown there is no answer, and the honest result is `none`. A postfix `?` writes exactly that: "if this is absent, so is my answer".
## Two matches, or one ?
With `match`, `Lead` would need two nested matches and two `none` arms that say the same thing. A `?` after an optional says it for you:
```rux
func Lead(players: Player[..], a: int, b: int) -> int? {
return ScoreOf(players, a)? - ScoreOf(players, b)?;
}
```
If the optional is present, `ScoreOf(players, a)?` is the `int` inside it. If it is `none`, the function returns `none` straight away, and nothing after it runs. The subtraction can therefore be written as if both scores were plain numbers.
```mermaid
flowchart LR
e["ScoreOf(players, a)?"] --> q{"Present?"}
q -- "yes" --> v["the int inside;
the expression goes on"]
q -- "no" --> r["Lead returns none
at once"]
r --> c["the caller decides:
match or ??"]
```
## Each line produces its value or ends the function
`?` works anywhere in the function, including in a `let` inside a loop:
```rux
func TeamTotal(players: Player[..], numbers: int[..]) -> int? {
var total = 0;
for number in numbers {
let score = ScoreOf(players, number)?;
total += score;
}
return total;
}
```
For the starters `4, 7, 10` every lookup succeeds and the total is 45. For `4, 9, 10`, #9 is unknown: the `?` returns `none` from `TeamTotal` in the middle of the loop, and the partial total is never seen.
## The caller handles it
`?` only moves absence up one level. Somewhere a caller has to deal with it, with a [match](https://rux-lang.dev/docs/learn/presence) or a [fallback](https://rux-lang.dev/docs/learn/coalesce):
```rux
PrintLine("starters total: {}", TeamTotal(players, starters) ?? -1);
PrintLine("mixed total: {}", TeamTotal(players, mixed) ?? -1);
```
## ? compared with ?? return
The two look alike, and the difference is what the function returns:
| Written | When the value is `none` | The function must return |
| ------------------------ | -------------------------- | --------------------------- |
| `ScoreOf(…)?` | returns `none` | an optional, such as `int?` |
| `ScoreOf(…) ?? return 0` | returns the value you name | anything that value fits |
So `?` can only appear in a function whose own result is optional: it needs a `none` it can return. `Main` returns a plain `int`, which is why `Main` opens the results with `match` and `??` instead.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Optionals/OptionalPropagate){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Sometimes a function cannot do anything useful with a missing value except
// report that it is missing too. How far ahead is #7 of #10? If either player
// is unknown, there is no answer, and the honest result is `none`.
//
// Writing that with `match` takes two nested matches and two `none` arms that
// say the same thing. A `?` after an optional says it for you:
//
// ScoreOf(players, a)?
//
// If the optional is present, this is the `int` inside it. If it is `none`, the
// function returns `none` straight away, and nothing after it runs. So `?` can
// only appear in a function whose own result is optional: it needs a `none` it
// can return.
import Io::PrintLine;
struct Player {
number: int;
score: int;
}
func ScoreOf(players: Player[..], number: int) -> int? {
for player in players {
if player.number == number {
return player.score;
}
}
return none;
}
// Both scores are needed, and either may be missing. The `?`s let the sum be
// written as if they were plain numbers.
func Lead(players: Player[..], a: int, b: int) -> int? {
return ScoreOf(players, a)? - ScoreOf(players, b)?;
}
// `?` works anywhere in the function, including in a `let`. Each line either
// produces its value or ends the function.
func TeamTotal(players: Player[..], numbers: int[..]) -> int? {
var total = 0;
for number in numbers {
let score = ScoreOf(players, number)?;
total += score;
}
return total;
}
func Main() -> int {
let players = [
Player { number: 4, score: 8 },
Player { number: 7, score: 12 },
Player { number: 10, score: 25 }
];
// The caller is where absence is finally handled, with `match` or `??`.
match Lead(players, 10, 7) {
lead? => PrintLine("#10 leads #7 by {}", lead),
none => PrintLine("no answer")
}
match Lead(players, 10, 3) {
lead? => PrintLine("#10 leads #3 by {}", lead),
none => PrintLine("#10 against #3: no answer, #3 is not on the team")
}
let starters = [ 4, 7, 10 ];
let mixed = [ 4, 9, 10 ];
PrintLine("starters total: {}", TeamTotal(players, starters) ?? -1);
PrintLine("mixed total: {}", TeamTotal(players, mixed) ?? -1);
// Watch out: `?` is not available in `Main`, or in any function that
// returns a plain `int`. There is no `none` to hand back, so the compiler
// rejects it. Use `match` or `??` there instead.
return 0;
}
```
## Run it
```sh
cd Examples/Optionals/OptionalPropagate
rux run
```
```text
#10 leads #7 by 13
#10 against #3: no answer, #3 is not on the team
starters total: 45
mixed total: -1
```
## Common mistakes
::warning
**`?` in a function that cannot return `none`.**:br
In `Main`, or any function returning a plain `int`, `let v = ScoreOf(players, 7)?;` stops with `error: '?' propagates the absence of 'int?', but the enclosing function returns 'int'`. The compiler's help says what to do: declare an optional result, as in `-> T?`, or supply a fallback with `??`.
::
::warning
**Propagating when a fallback is meant.**:br`?` makes the whole function's answer `none`. If one missing score should simply count as zero, write `ScoreOf(players, number) ?? 0` and keep the rest of the total.
::
## Try it yourself
1. Write `Average(players: Player[..], a: int, b: int) -> int?` with two `?`s, and show it for a known and an unknown pair.
2. Rewrite `Lead` with nested `match`es and compare the length.
3. Change `TeamTotal` so that an unknown number is skipped rather than ending the total. Which operator did you use instead of `?`?
## Learn more
- [Presence](https://rux-lang.dev/docs/learn/presence) and [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — the two ways a caller finally handles absence
- [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) — `?? return` for functions that do not return an optional
- [Propagate](https://rux-lang.dev/docs/learn/propagate) — the same `?` passing an error up, in the Errors part
# Nested optional
::note
**You'll need**: [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce)
::
A player who has not played yet has no score, so this lesson's team sheet stores each score as an `int?`. Now look a player up by number. The answer can be missing for two quite different reasons — nobody wears that number, or the player exists but has no score yet. An optional of an optional, `int??`, keeps the two apart.
## Three answers, one type
```rux
struct Player {
number: int;
score: int?;
}
```
A lookup that returns the player's `score` must also be able to say "not found", so its result wraps the `int?` in one more optional:
```mermaid
flowchart TD
r["int??"] --> n["none
nobody wears that number"]
r --> s[".Some(…) — the player exists"]
s --> sn[".Some(none)
no score yet"]
s --> ss[".Some(.Some(score))
has scored"]
```
Rux never squashes the two levels into one. In some languages "a missing missing value" collapses into plain "missing", and the difference is lost; here the two kinds of "nothing" stay different, and you can tell them apart.
## Building one
`player.score` is an `int?`. Returned where an `int??` is expected it becomes a present `int??` on its own, even when the score inside is `none` — the same rule that turned an `int` into a present `int?` in [Optional](https://rux-lang.dev/docs/learn/optional). Only the `none` at the bottom means "not found":
```rux
func ScoreOf(players: Player[..], number: int) -> int?? {
for player in players {
if player.number == number {
return player.score;
}
}
return none;
}
```
## Opening one, level by level
Each `?` in a pattern opens one level:
```rux
match ScoreOf(players, number) {
score?? => PrintLine("#{} scored {}", number, score),
none? => PrintLine("#{} has not played yet", number),
none => PrintLine("#{} is not on the team", number)
}
```
| Short | Long | Meaning |
| --------- | --------------------- | ----------------------------------------- |
| `score??` | `.Some(.Some(score))` | a present, present score |
| `none?` | `.Some(none)` | a present `none`: found, but no score yet |
| `none` | `none` | the outer absence: not on the team |
The long spelling, used in `ReportLong`, shows the nesting plainly; the short one is the usual one.
## ?? opens one level too
`??` removes exactly one level, so on an `int??` its result is still an `int?`:
```rux
let once = ScoreOf(players, 9) ?? 0;
let twice = once ?? 0;
```
For #9 the outer level is present, so the fallback is not used, and `once` is the inner `none`. A second `??` deals with that one, and `twice` is a plain `int`, `0`.
That first `?? 0` looks as if it gives an `int`, and it does compile — but only because the `0` is made into a present `int?` to match the inner level. `once` is an `int?`, which is why the program compares it with `none` instead of printing it.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Optionals/NestedOptional){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A player who has not played yet has no score, so the team sheet stores each
// score as an `int?`. Now look a player up by number. The answer can be
// missing for two different reasons:
//
// none nobody wears that number
// .Some(none) the player exists, but has no score yet
// .Some(.Some(score)) the player exists and has scored
//
// The type that holds all three is `int??`, an optional of an optional. Rux
// never squashes the two levels into one, so the two kinds of "nothing" stay
// different and you can tell them apart.
import Io::PrintLine;
struct Player {
number: int;
score: int?;
}
// `player.score` is an `int?`. Returned from here it becomes a present `int??`
// on its own, even when the score inside is `none`. Only the `none` at the
// bottom means "not found".
func ScoreOf(players: Player[..], number: int) -> int?? {
for player in players {
if player.number == number {
return player.score;
}
}
return none;
}
// Each `?` in a pattern opens one level. `score??` is "a present, present
// score". `none?` is "a present `none`": the player was found but has no
// score. Plain `none` is the outer absence.
func Report(players: Player[..], number: int) {
match ScoreOf(players, number) {
score?? => PrintLine("#{} scored {}", number, score),
none? => PrintLine("#{} has not played yet", number),
none => PrintLine("#{} is not on the team", number)
}
}
// The same three cases in the long spelling.
func ReportLong(players: Player[..], number: int) {
match ScoreOf(players, number) {
.Some(.Some(score)) => PrintLine("#{} scored {}", number, score),
.Some(none) => PrintLine("#{} has not played yet", number),
none => PrintLine("#{} is not on the team", number)
}
}
func Main() -> int {
let players = [
Player { number: 7, score: 12 },
Player { number: 9, score: none }
];
Report(players, 7);
Report(players, 9);
Report(players, 3);
ReportLong(players, 9);
// `??` opens just one level too. For #9 the outer level is present, so
// the fallback is not used and `once` is the inner `none`, still an
// `int?`. A second `??` deals with that one.
let once = ScoreOf(players, 9) ?? 0;
let twice = once ?? 0;
PrintLine("#9 opened once is still none: {}", once == none);
PrintLine("#9 opened twice: {}", twice);
// Watch out: that first `?? 0` looks as if it gives an `int`, and it does
// compile, but only because the `0` is made into a present `int?` to
// match the inner level. Try printing `once` directly: the compiler
// refuses, because an `int?` is not something `PrintLine` can show.
return 0;
}
```
## Run it
```sh
cd Examples/Optionals/NestedOptional
rux run
```
```text
#7 scored 12
#9 has not played yet
#3 is not on the team
#9 has not played yet
#9 opened once is still none: true
#9 opened twice: 0
```
## Common mistakes
::warning
**Treating the result of one `??` as plain.**:br`PrintLine("{}", once);` fails with `error: argument 2 to 'PrintLine' has type 'int?', but variadic parameter 'args' requires 'Display'`. One `??` opened only the outer level; open the inner one too.
::
::warning
**Chaining the two fallbacks without parentheses.**:br`??` groups from the right, so `ScoreOf(players, 9) ?? 0 ?? 0` means `ScoreOf(players, 9) ?? (0 ?? 0)` and fails with `error: operator '??' requires an optional left operand, but found 'int'`. Write `(ScoreOf(players, 9) ?? 0) ?? 0`, or use two `let`s as the lesson does.
::
::warning
**Forgetting the middle case.**:br
A match with only `score??` and `none` arms is not exhaustive: `error: match on 'int??' is not exhaustive; missing .Some(none)`. The player who exists but has no score needs its own arm.
::
## Try it yourself
1. Add a player `Player { number: 11, score: 0 }` and check that #11 is reported as having scored 0, not as "has not played yet".
2. Write `Played(players: Player[..], number: int) -> bool?` that is `none` for an unknown number, and `true` or `false` otherwise.
3. Replace the three-arm match with `??`: print `-1` for "not on the team" and `0` for "has not played yet".
## Learn more
- [Presence](https://rux-lang.dev/docs/learn/presence) — the one-level patterns `score?` and `none`
- [Optional pointer](https://rux-lang.dev/docs/learn/optional-pointer) — the same care with `(*T)?` and `*T?`, in the Memory part
- [Nested fallible](https://rux-lang.dev/docs/learn/nested-fallible) — an optional inside a fallible, `int? ! E`, in the Errors part
# Part 9: Errors
An [optional](https://rux-lang.dev/docs/learn/optional) can say that a value is missing, but never why. This part gives a failure a voice. A *fallible* `T ! E` is an answer of type `T` or an error of type `E` saying what went wrong, and the compiler makes sure no failure is ever dropped without the code saying so. By the end of the part you can write functions that fail precisely, decide at each step whether to handle a failure or pass it on, and tell a failure the caller can fix from a bug that should stop the program.
## What you will learn
- Declaring a fallible with `T ! E`, or `! E` when there is no answer, and failing with `fail`.
- Opening an outcome with `.Success` and `.Failure`, and why a fallible result cannot be ignored.
- Recovering with `catch` — one arm per case, or one `else` fallback for all.
- Passing a failure to the caller with `?`, and adding context on the way with `? else (e => ...)`.
- Designing error types: a variant of cases, or a sum of unrelated errors `A | B`.
- A fallible `Main`, turning absence into an error with `?? fail`, and nested fallibles.
- `Panic`, `Assert` and `DebugAssert` for the situations only a bug can reach.
## Which error tool?
Most of this part is choosing the right tool for one moment in the code. The questions run in this order:
```mermaid
flowchart TD
start(["Something can go wrong"]) --> bug{"Can it happen in a
program with no bugs?"}
bug -- "no, only a bug" --> stop["Panic, Assert, DebugAssert
9.15–9.16"]
bug -- "yes" --> why{"Does the caller need
to know why?"}
why -- "no" --> opt["An optional T?
Part 8"]
why -- "yes" --> fal["A fallible T ! E, or ! E
9.1–9.3"]
fal --> who{"Who decides what
the failure means?"}
who -- "this function" --> reason{"Does the reason
matter here?"}
reason -- "yes" --> arms["match, or catch with
one arm per case
9.4, 9.6"]
reason -- "no" --> fallback["catch { else => fallback }
or a deliberate discard
9.5, 9.7"]
who -- "the caller" --> same{"Does the error already
fit the caller's type?"}
same -- "yes" --> q["? passes it on
9.8, 9.11"]
same -- "no, or context to add" --> mapped["? else (e => ...)
9.10"]
```
## Lessons
| | Lesson | What you will learn |
| ---- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| 9.1 | [Fallible](https://rux-lang.dev/docs/learn/fallible) | return either a value or an error with `T ! E` |
| 9.2 | [Fail](https://rux-lang.dev/docs/learn/fail) | report a failure with `fail` |
| 9.3 | [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible) | a function that returns nothing but can still fail: `! E` |
| 9.4 | [Outcome](https://rux-lang.dev/docs/learn/outcome) | match a result as `.Success` or `.Failure` |
| 9.5 | [Discard](https://rux-lang.dev/docs/learn/discard) | why the compiler refuses to let a result be ignored, and how to discard one on purpose |
| 9.6 | [Catch](https://rux-lang.dev/docs/learn/catch) | handle the ways an operation can fail with `catch` |
| 9.7 | [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) | turn any failure into a default value with `catch { else => ... }` |
| 9.8 | [Propagate](https://rux-lang.dev/docs/learn/propagate) | hand a failure straight to the caller with `?` instead of matching it |
| 9.9 | [Error variant](https://rux-lang.dev/docs/learn/error-variant) | describe the ways an operation can fail with a variant |
| 9.10 | [Error mapping](https://rux-lang.dev/docs/learn/error-mapping) | add context to an error as it passes through with `? else (e => ...)` |
| 9.11 | [Error sum](https://rux-lang.dev/docs/learn/error-sum) | fail in more than one way with an error sum `A | B` |
| 9.12 | [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) | let `Main` itself fail, and see the exit status |
| 9.13 | [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) | turn a missing value into a failure with `?? fail` |
| 9.14 | [Nested fallible](https://rux-lang.dev/docs/learn/nested-fallible) | results inside results: `T? ! E` and `(T ! E1) ! E2` |
| 9.15 | [Panic](https://rux-lang.dev/docs/learn/panic) | stop the program when something impossible happens |
| 9.16 | [Assert](https://rux-lang.dev/docs/learn/assert) | check an assumption while the program runs |
## Before you start
Finish [Part 8: Optionals](https://rux-lang.dev/docs/learn/optionals) first — fallibles are built on the same ideas, and several lessons compare the two side by side. You also need [variants](https://rux-lang.dev/docs/learn/variant) from Part 6 and the patterns of [Part 7](https://rux-lang.dev/docs/learn/patterns). Each lesson's package is in the Examples repository's `Errors/` folder:
```sh
cd Examples/Errors/Fallible
rux run
```
## After this part
[Part 10: Sum types](https://rux-lang.dev/docs/learn/sum-types) takes the `A | B` you met in [Error sum](https://rux-lang.dev/docs/learn/error-sum) and makes it a type of its own, usable anywhere. Before moving on, try the checkpoint project [Calculator](https://rux-lang.dev/docs/learn/calculator): it evaluates sums and key-press tapes where every way a calculation can fail is a case of an error variant.
For the rules behind this part, see [Error handling](https://rux-lang.dev/docs/lang/errors/overview) and [Fatal errors](https://rux-lang.dev/docs/lang/errors/panics) in the Rux Reference, and [Panic](https://rux-lang.dev/docs/api/core/panic) and [Assert](https://rux-lang.dev/docs/api/core/assert) in the API reference.
# Fallible
::note
**You'll need**: [Optional](https://rux-lang.dev/docs/learn/optional), [Variant](https://rux-lang.dev/docs/learn/variant)
::
An [optional](https://rux-lang.dev/docs/learn/optional) says that a value is missing, but never *why*. Often the why is exactly what the caller needs. Dividing 12 by 0 and dividing 13 by 4 both fail to give an exact answer — for different reasons that deserve different replies.
A *fallible* type carries the reason with it. `int ! DivideError` reads "an `int`, or else a `DivideError`". This lesson introduces the type and shows how a caller finds out which of the two it got; the rest of Part 9 is about the many ways of handling the second one.
## Two channels
A fallible has two channels. The **success** channel holds the answer. The **failure** channel holds an *error* — an ordinary value describing what went wrong. The error type is whatever suits, and a [variant](https://rux-lang.dev/docs/learn/variant) is a natural fit: one case for each way the operation can go wrong.
```rux
variant DivideError {
ByZero,
NotExact(int)
}
```
`NotExact` carries the remainder with it, so the caller can say how far off the division was. The signature puts the two channels side by side, success type first, error type after the `!`:
```rux
func ExactDivide(numerator: int, denominator: int) -> int ! DivideError {
```
| Type | Holds |
| ------------------- | --------------------------------------------------- |
| `int` | always an `int` |
| `int?` | an `int`, or nothing — with no reason given |
| `int ! DivideError` | an `int`, or a `DivideError` saying what went wrong |
## The success needs no ceremony
Inside a fallible function, `return value;` is the success, just as a plain value returned from an `int?` function is a present one. `fail`, the subject of the [next lesson](https://rux-lang.dev/docs/learn/fail), leaves through the failure channel instead:
```rux
if denominator == 0 {
fail DivideError::ByZero;
}
if numerator % denominator != 0 {
fail DivideError::NotExact(numerator % denominator);
}
return numerator / denominator;
```
```mermaid
flowchart LR
call["ExactDivide(12, 0)"] --> body{"Which way does
the function leave?"}
body -- "return value;" --> s[".Success(value)
the success channel"]
body -- "fail error;" --> f[".Failure(error)
the failure channel"]
s --> caller["The caller opens it:
match, catch or ?"]
f --> caller
```
## Opening the result
What comes back is not yet an `int`. To use the answer, find out which channel it came through. `.Success(value)` matches the answer and binds it; `.Failure(error)` matches an error and binds that:
```rux
match ExactDivide(numerator, denominator) {
.Success(value) => PrintLine("{} / {} = {}", numerator, denominator, value),
.Failure(error) => Explain(numerator, denominator, error)
}
```
The error is an ordinary `DivideError`, so `Explain` uses a second `match` to pick out its case — the same [variant match](https://rux-lang.dev/docs/learn/variant-match) you already know:
```rux
match error {
DivideError::ByZero => PrintLine("{} / {} fails: no dividing by zero", numerator,
denominator),
DivideError::NotExact(remainder) => PrintLine("{} / {} fails: {} left over", numerator,
denominator, remainder)
}
```
The match must cover both channels. A failure can never slip past unnoticed: the compiler refuses a match that forgets `.Failure`, and — as you will see in [Discard](https://rux-lang.dev/docs/learn/discard) — a call whose result nobody looks at.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Fallible){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An optional says that a value is missing, but never why. Often the why is exactly what the
// caller needs: dividing 12 by 0 and dividing 13 by 4 both fail to give an exact answer, for
// different reasons that deserve different replies.
//
// `int ! DivideError` reads "an `int`, or else a `DivideError`". A fallible like this has two
// channels. The success channel holds the answer. The failure channel holds an error, a value
// describing what went wrong. The error type is whatever suits, and a variant is a natural fit:
// one case for each way the operation can go wrong.
//
// The success needs no ceremony. `return value;` in a fallible function is the success, just as a
// plain value returned from an `int?` function is a present one.
import Io::PrintLine;
// The ways an exact division can go wrong. `NotExact` carries the remainder with it.
variant DivideError {
ByZero,
NotExact(int)
}
// The signature says it all: this produces an `int`, or a `DivideError`. `fail`, the subject of
// the next lesson, leaves through the failure channel; an ordinary `return` is the success.
func ExactDivide(numerator: int, denominator: int) -> int ! DivideError {
if denominator == 0 {
fail DivideError::ByZero;
}
if numerator % denominator != 0 {
fail DivideError::NotExact(numerator % denominator);
}
return numerator / denominator;
}
// To use the answer, find out which channel it came through. `.Success(value)` matches the answer
// and binds it; `.Failure(error)` matches an error and binds that. The error is an ordinary
// `DivideError`, so a second match picks out its case.
func Report(numerator: int, denominator: int) {
match ExactDivide(numerator, denominator) {
.Success(value) => PrintLine("{} / {} = {}", numerator, denominator, value),
.Failure(error) => Explain(numerator, denominator, error)
}
}
func Explain(numerator: int, denominator: int, error: DivideError) {
match error {
DivideError::ByZero => PrintLine("{} / {} fails: no dividing by zero", numerator,
denominator),
DivideError::NotExact(remainder) => PrintLine("{} / {} fails: {} left over", numerator,
denominator, remainder)
}
}
func Main() -> int {
Report(12, 4);
Report(12, 0);
Report(13, 4);
// A fallible is not an `int` until it has been opened. `let half = ExactDivide(12, 2) / 2;`
// is rejected, and so is calling `ExactDivide(12, 4);` and ignoring what comes back: a
// failure can never be dropped without the code saying so.
return 0;
}
```
## Run it
```sh
cd Examples/Errors/Fallible
rux run
```
```text
12 / 4 = 3
12 / 0 fails: no dividing by zero
13 / 4 fails: 1 left over
```
## Common mistakes
::warning
**Using a fallible as if it were the answer.**:br`let half = ExactDivide(12, 2) / 2;` fails with `error: operator '/' cannot combine left operand 'int ! DivideError' with right operand 'int'`. The `int` is inside the success channel and has to be taken out first — with `match` here, and later with `catch` or `?`.
::
::warning
**Calling it and ignoring what comes back.**:br`ExactDivide(12, 4);` on its own is `error: fallible result of type 'int ! DivideError' is discarded`, with the note `a failure that nothing handles is lost`. [Discard](https://rux-lang.dev/docs/learn/discard) shows how to ignore a result on purpose.
::
::warning
**Returning the error.**:br`return DivideError::ByZero;` is refused with `error: 'return' value must have type 'int ! DivideError', but found 'DivideError'`. `return` is the success channel; an error leaves with `fail`.
::
## Try it yourself
1. Add `Report(0, 5);` to `Main`. Predict the line it prints before you run it.
2. Delete the `.Failure` arm from `Report`'s match and read the error. Which pattern does the compiler say is missing?
3. Add a case `Negative` to `DivideError` and fail with it when the denominator is below zero. Where does the compiler send you next, and why?
## Learn more
- [Fail](https://rux-lang.dev/docs/learn/fail) — the failure channel's `return`
- [Outcome](https://rux-lang.dev/docs/learn/outcome) — storing, passing and building a fallible's result
- [Optional](https://rux-lang.dev/docs/learn/optional) — the simpler form, for a value that may be missing with no reason given
- [Error handling](https://rux-lang.dev/docs/lang/errors/overview) in the Rux Reference
# Fail
::note
**You'll need**: [Fallible](https://rux-lang.dev/docs/learn/fallible), [Struct](https://rux-lang.dev/docs/learn/struct)
::
A fallible function has two ways out. `return value;` is the success, as the [previous lesson](https://rux-lang.dev/docs/learn/fallible) showed. `fail` is the other one: it leaves the function at once through the failure channel, carrying an error value for the caller.
This lesson is a cash machine. A withdrawal either answers with the new balance or refuses — and when it refuses, it says exactly how much money was missing.
## The error is plain data
An error can be any type you like. Here it is a [struct](https://rux-lang.dev/docs/learn/struct), so it can report exactly what went wrong — how much the account held and how much was asked for:
```rux
struct ShortOfFunds {
balance: int;
requested: int;
}
```
There is nothing special about the type itself. It becomes an error only because a signature names it after the `!`:
```rux
func Withdraw(balance: int, amount: int) -> int ! ShortOfFunds {
```
## fail leaves at once
`fail` builds the error and hands it over in one statement. Nothing after a `fail` runs, just as nothing after a `return` does:
```rux
if amount > balance {
fail ShortOfFunds { balance: balance, requested: amount };
}
// Only reached when the money is there.
return balance - amount;
```
| Statement | Leaves through | The caller sees |
| ---------------------------- | ------------------- | ----------------- |
| `return balance - amount;` | the success channel | `.Success(left)` |
| `fail ShortOfFunds { ... };` | the failure channel | `.Failure(error)` |
## Reading the details
Because the error is a struct, the caller reads its fields like any other. The refusal message works out the shortfall from the two numbers the error carried:
```rux
.Failure(error) => PrintLine("take {} from {}: refused, {} short", amount, balance,
error.requested - error.balance)
```
Taking exactly the whole balance is fine — `amount > balance` is false for 100 and 100, so `Report(100, 100)` leaves 0. An empty account refuses even 5.
## Where fail is allowed
`fail` has two rules, and the compiler enforces both. Its value must have the error type named after the `!` — a `ShortOfFunds` here, not a number. And it belongs only in a function that can fail: `Main` returns a plain `int`, so a `fail` there has nowhere to go.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Fail){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A fallible function has two ways out. `return value;` is the success, as the previous lesson
// showed. `fail` is the other one: it leaves the function at once through the failure channel,
// carrying an error value for the caller.
//
// The error can be any type you like. Here it is a struct, so it can report exactly what went
// wrong — how much money the account held and how much was asked for. `fail ShortOfFunds { ... };`
// builds that struct and hands it over in one statement. Nothing after a `fail` runs, just as
// nothing after a `return` does.
import Io::PrintLine;
// The error: plain data describing the problem, nothing special about the type itself.
struct ShortOfFunds {
balance: int;
requested: int;
}
// Answers with the new balance, or fails with the details of why it could not.
func Withdraw(balance: int, amount: int) -> int ! ShortOfFunds {
if amount > balance {
fail ShortOfFunds { balance: balance, requested: amount };
}
// Only reached when the money is there.
return balance - amount;
}
func Report(balance: int, amount: int) {
match Withdraw(balance, amount) {
.Success(left) => PrintLine("take {} from {}: {} left", amount, balance, left),
.Failure(error) => PrintLine("take {} from {}: refused, {} short", amount, balance,
error.requested - error.balance)
}
}
func Main() -> int {
Report(100, 30);
Report(100, 100);
Report(100, 130);
Report(0, 5);
// `fail` must carry a value of the error type named after the `!`. `fail 30;` inside
// `Withdraw` is rejected — "'fail' value must have type 'ShortOfFunds', but found 'int'".
// And `fail` belongs only in a function that can fail: here in `Main`, which returns a plain
// `int`, it is an error too — "'fail' needs an enclosing fallible function, but this
// function returns 'int'".
return 0;
}
```
## Run it
```sh
cd Examples/Errors/Fail
rux run
```
```text
take 30 from 100: 70 left
take 100 from 100: 0 left
take 130 from 100: refused, 30 short
take 5 from 0: refused, 5 short
```
## Common mistakes
::warning
**Failing with the wrong type.**:br`fail 30;` inside `Withdraw` is rejected: `error: 'fail' value must have type 'ShortOfFunds', but found 'int'`. The error type is part of the signature, and every `fail` must match it.
::
::warning
**fail in a function that cannot fail.**:br
In `Main`, which returns `int`, a `fail` is `error: 'fail' needs an enclosing fallible function, but this function returns 'int'`. The help line says what to do: `declare the function's error channel, as in '-> T ! E' or '-> ! E'`.
::
## Try it yourself
1. Add `Report(100, 101);` and predict the shortfall it prints.
2. Write `Deposit(balance: int, amount: int) -> int ! TooMuch` that refuses when the new balance would pass 1000, with a `TooMuch` struct that says by how much.
3. Make `Withdraw` refuse a negative amount too. Is `ShortOfFunds` the right error for that? [Error variant](https://rux-lang.dev/docs/learn/error-variant) shows the better shape.
## Learn more
- [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible) — a fallible function with no answer to give back
- [Error variant](https://rux-lang.dev/docs/learn/error-variant) — one error type for several ways to fail
- [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) — `fail` as the fallback of `??`
- [Struct](https://rux-lang.dev/docs/learn/struct) — the type the error is built from
# Unit fallible
::note
**You'll need**: [Fail](https://rux-lang.dev/docs/learn/fail), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference)
::
Some operations have no answer to give back. When a withdrawal works, the money is gone from the account and there is nothing more to say. But it can still fail, and the caller still needs to know.
Such a function is written `-> ! E`: an error type after the `!`, and nothing before it.
## ! E is () ! E
`! E` is short for `() ! E`. The `()` is the *unit* — the empty [tuple](https://rux-lang.dev/docs/learn/tuple), whose only value is also written `()`. It is a success that carries no information beyond "it worked". Both spellings compile and mean the same type; the short one is what you will see everywhere:
```rux
func Withdraw(account: &var Account, amount: int) -> ! ShortOfFunds {
```
The account is a [mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), so the function changes the caller's account in place. The only thing left to report is whether that happened.
## Three ways out
Because there is no value to return, the function succeeds simply by reaching its end. A bare `return;` succeeds early, and only `fail` makes it fail:
```rux
if amount == 0 {
// Nothing to do, and that counts as success.
return;
}
if amount > account.balance {
fail ShortOfFunds { missing: amount - account.balance };
}
account.balance -= amount;
// Falling off the end: success.
```
| The function… | The outcome |
| ------------------------- | ------------------------- |
| reaches its closing brace | success |
| runs `return;` | success, early |
| runs `fail error;` | failure, carrying `error` |
## Matching a unit success
The success channel still holds a value — the unit — so its pattern has parentheses with `()` inside:
```rux
match Withdraw(account, amount) {
.Success(()) => PrintLine("take {}: done, {} left", amount, account.balance),
.Failure(error) => PrintLine("take {}: refused, {} short", amount, error.missing)
}
```
`.Success(_)` works too, since `_` matches any value, the unit included. What does not work is leaving the parentheses out: `.Success` alone is a pattern with no payload, and the success channel always has one.
The output follows the balance as it changes. Taking 0 succeeds without touching anything, 80 is refused while 60 is left, and the next 60 empties the account — so even 1 is refused after that.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/UnitFallible){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Some operations have no answer to give back. When a withdrawal works, the money is gone from
// the account and there is nothing more to say. Such a function is written `-> ! E`: an error
// type after the `!`, and nothing before it.
//
// `! E` is short for `() ! E`. The `()` is the unit, the empty tuple, whose only value is also
// `()` — a success that carries no information beyond "it worked". Because there is no value to
// return, the function succeeds simply by reaching its end. A bare `return;` succeeds early, and
// only `fail` makes it fail.
import Io::PrintLine;
struct Account {
balance: int;
}
struct ShortOfFunds {
missing: int;
}
// Changes the account in place; the only thing to report is whether that happened.
func Withdraw(account: &var Account, amount: int) -> ! ShortOfFunds {
if amount == 0 {
// Nothing to do, and that counts as success.
return;
}
if amount > account.balance {
fail ShortOfFunds { missing: amount - account.balance };
}
account.balance -= amount;
// Falling off the end: success.
}
func Attempt(account: &var Account, amount: int) {
// The success pattern holds the unit, so it is written `.Success(())`.
match Withdraw(account, amount) {
.Success(()) => PrintLine("take {}: done, {} left", amount, account.balance),
.Failure(error) => PrintLine("take {}: refused, {} short", amount, error.missing)
}
}
func Main() -> int {
var account = Account { balance: 100 };
Attempt(account, 40);
Attempt(account, 0);
Attempt(account, 80);
Attempt(account, 60);
Attempt(account, 1);
return 0;
}
```
## Run it
```sh
cd Examples/Errors/UnitFallible
rux run
```
```text
take 40: done, 60 left
take 0: done, 60 left
take 80: refused, 20 short
take 60: done, 0 left
take 1: refused, 1 short
```
## Common mistakes
::warning
**Returning a value from a ! E function.**:br`return 0;` inside `Withdraw` is `error: 'return' value must have type '! ShortOfFunds', but found 'int'`. There is no success value to give; write `return;` or let the function reach its end.
::
::warning
**Writing .Success without the unit.**:br`.Success => ...` is refused with `error: pattern '.Success' expects 1 field, but found 0`. Write `.Success(())` or `.Success(_)`.
::
::warning
**Calling it as a plain statement.**:br`Withdraw(account, 40);` on its own is `error: fallible result of type '! ShortOfFunds' is discarded`. No value comes back, but the failure still does. [Discard](https://rux-lang.dev/docs/learn/discard) explains how to ignore it on purpose.
::
## Try it yourself
1. Change the signature to the long spelling `-> () ! ShortOfFunds` and check that the program still compiles and prints the same.
2. Add `Deposit(account: &var Account, amount: int) -> ! TooMuch` that refuses when the balance would pass 1000.
3. Replace `.Success(())` with `.Success(_)`. Does anything change?
## Learn more
- [Fail](https://rux-lang.dev/docs/learn/fail) — the failure channel's `return`
- [Discard](https://rux-lang.dev/docs/learn/discard) — the deliberate way to ignore a `! E` result
- [Tuple](https://rux-lang.dev/docs/learn/tuple) — the empty tuple `()` is the unit
- [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — how `Withdraw` changes the caller's account
# Outcome
::note
**You'll need**: [Fail](https://rux-lang.dev/docs/learn/fail), [Match expression](https://rux-lang.dev/docs/learn/match-expression)
::
What a fallible call hands back is an *outcome*: one value that is either a success or a failure. So far every outcome was matched the moment it arrived, but nothing requires that. An outcome is an ordinary value. It can sit in a variable, be passed to a function, and be looked at later — and you can even build one by hand.
## Store first, look later
Each call's outcome is stored first and examined afterwards:
```rux
let party = Share(12, 4);
let empty = Share(12, 0);
Describe(party);
Describe(empty);
```
`Describe` takes the outcome as a parameter like any other value. Its type is written exactly as a return type would be:
```rux
func Describe(outcome: int ! NobodyToShare) {
```
## Patterns look inside
`.Success(...)` and `.Failure(...)` are the two shapes an outcome can have. In a `match` they are patterns, and whatever is written inside the parentheses is matched against the payload — a name binds it, a literal compares it, `_` ignores it:
```rux
match outcome {
.Success(0) => PrintLine("not even one slice each"),
.Success(each) => PrintLine("{} slices each", each),
.Failure(error) => PrintLine("{} slices and nobody to eat them", error.slices)
}
```
| Pattern | Matches | Binds |
| ----------------- | -------------------------- | --------- |
| `.Success(0)` | a success whose value is 0 | nothing |
| `.Success(each)` | any success | the value |
| `.Failure(error)` | any failure | the error |
| `.Failure(_)` | any failure | nothing |
Order matters, as in every [match](https://rux-lang.dev/docs/learn/match-expression): `.Success(0)` comes before `.Success(each)`, because the general arm would otherwise take the zero too. The compiler catches that order and calls the specific arm unreachable.
## Building an outcome by hand
Outside a match, the same two shapes are constructors. No function needs to be called:
```rux
let promised: int ! NobodyToShare = .Success(2);
let refused: int ! NobodyToShare = .Failure(NobodyToShare { slices: 8 });
```
The type annotation is required. `.Success(2)` says which channel the value goes in, but not what the other channel would have held — and an outcome's type needs both.
## A match that produces a value
A `match` on an outcome can be an expression, with one value per shape:
```rux
let eaten = match party {
.Success(each) => each * 4,
.Failure(_) => 0
};
```
Twelve slices shared among four is three each, so four people eat 12. A match on an outcome must cover both shapes — a failure can never slip through unnoticed.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Outcome){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// What a fallible call hands back is an outcome: one value that is either a success or a
// failure. It is an ordinary value. It can sit in a variable, be passed to a function, and be
// looked at later.
//
// `.Success(...)` and `.Failure(...)` are the two shapes an outcome can have. In a `match` they
// are patterns, and whatever is written inside the parentheses is matched against the payload —
// a name binds it, a literal compares it. Outside a match they are constructors, so an outcome
// can also be built by hand, without calling anything.
import Io::PrintLine;
struct NobodyToShare {
slices: int;
}
// Shares out pizza slices evenly. It fails when there is nobody to share them with.
func Share(slices: int, people: int) -> int ! NobodyToShare {
if people == 0 {
fail NobodyToShare { slices: slices };
}
return slices / people;
}
// An outcome arrives as a parameter like any other value.
func Describe(outcome: int ! NobodyToShare) {
match outcome {
.Success(0) => PrintLine("not even one slice each"),
.Success(each) => PrintLine("{} slices each", each),
.Failure(error) => PrintLine("{} slices and nobody to eat them", error.slices)
}
}
func Main() -> int {
// Each call's outcome is stored first and examined afterwards.
let party = Share(12, 4);
let empty = Share(12, 0);
Describe(party);
Describe(empty);
Describe(Share(3, 4));
// The same two shapes as constructors. The type annotation says which fallible is meant.
let promised: int ! NobodyToShare = .Success(2);
let refused: int ! NobodyToShare = .Failure(NobodyToShare { slices: 8 });
Describe(promised);
Describe(refused);
// A match can also produce a value, one per shape.
let eaten = match party {
.Success(each) => each * 4,
.Failure(_) => 0
};
PrintLine("eaten at the party: {}", eaten);
// A match on an outcome must cover both shapes. Drop the `.Failure` arm above and the
// compiler refuses: "match on 'int ! NobodyToShare' is not exhaustive; missing .Failure(_)".
// A failure can never slip through unnoticed.
return 0;
}
```
## Run it
```sh
cd Examples/Errors/Outcome
rux run
```
```text
3 slices each
12 slices and nobody to eat them
not even one slice each
2 slices each
8 slices and nobody to eat them
eaten at the party: 12
```
## Common mistakes
::warning
**Forgetting the failure arm.**:br
Drop the `.Failure` arm and the compiler refuses: `error: match on 'int ! NobodyToShare' is not exhaustive; missing .Failure(_)`.
::
::warning
**A constructor with nothing to say what it builds.**:br`let promised = .Success(2);` is `error: cannot infer the type of 'promised' from a native constructor with an unknown channel`. Annotate the variable, as the help line shows: `let value: int32 ! ParseError = .Success(1i32);`.
::
::warning
**The general arm first.**:br
Swap the two success arms and `.Success(0)` is rejected: `error: match arm is unreachable because earlier arms already match every value it matches`. Put specific patterns before general ones.
::
## Try it yourself
1. Add an arm that prints `"exactly one slice each"`. Where in the match must it go?
2. Build an outcome by hand with `.Failure(NobodyToShare { slices: 0 })` and pass it to `Describe`.
3. Store `Share(7, 2)` in a variable, then use one `match` expression to work out how many slices are eaten and another to print it.
## Learn more
- [Match expression](https://rux-lang.dev/docs/learn/match-expression) — a `match` that produces a value
- [Catch](https://rux-lang.dev/docs/learn/catch) — a shorter way to deal with only the failure
- [Generic outcome](https://rux-lang.dev/docs/learn/generic-outcome) — functions that accept any `T ! E`
# Discard
::note
**You'll need**: [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible), [Outcome](https://rux-lang.dev/docs/learn/outcome)
::
A fallible result cannot be ignored. Calling a fallible function as a bare statement, with nothing looking at what it returns, is a compile error — otherwise a failure could vanish without anyone noticing.
Sometimes ignoring it is exactly right, though. A cash machine whose receipt printer has run out of paper should still hand over the money. This lesson shows why the compiler is strict and how to tell it, in the code, that you mean it.
## A bare call is refused
`PrintReceipt` is a [unit fallible](https://rux-lang.dev/docs/learn/unit-fallible): it either printed the receipt or it did not. Call it the way you call `PrintLine` and the compiler stops you:
```rux
PrintReceipt(printer, amount);
```
```text
error: fallible result of type '! OutOfPaper' is discarded
note: a failure that nothing handles is lost
help: propagate it with '?', recover with 'catch', or match both '.Success' and '.Failure'
```
Binding the result to `_` does not help: `let _ = PrintReceipt(printer, amount);` is refused the same way, with the note `binding a fallible to '_' does not handle its failure`. A `_` only throws the value away; it does nothing about the failure inside it.
## Discarding on purpose
The deliberate discard is one phrase:
```rux
PrintReceipt(printer, amount) catch { else => {} };
```
It says "I know this can fail, and it does not matter here". `catch` is the subject of the [next lesson](https://rux-lang.dev/docs/learn/catch); for now, read the line as that one phrase. The third withdrawal in the output finds the paper gone, hands over the money anyway, and prints no receipt.
## Only for a unit success
The empty block `{}` stands in for the missing success, and an empty block has no value — it completes with `()`. So this phrase works only for a `! E` function. One that succeeds with a value, such as `ExactDivide` from [Fallible](https://rux-lang.dev/docs/learn/fallible), is discarded with a match that names both channels and does nothing in either:
```rux
match ExactDivide(12, 4) {
.Success(_) => {},
.Failure(_) => {}
}
```
| What you write | Result |
| ---------------------------------------------------------- | ------------------------------------- |
| `PrintReceipt(printer, amount);` | error: the result is discarded |
| `let _ = PrintReceipt(printer, amount);` | error: binding to `_` handles nothing |
| `let r = PrintReceipt(printer, amount);` | a warning if `r` is never read |
| `PrintReceipt(printer, amount) catch { else => {} };` | a deliberate discard of a `! E` |
| a `match` with `.Success(_) => {}` and `.Failure(_) => {}` | a deliberate discard of any fallible |
## Why PrintLine was never a problem
`PrintLine` is not fallible. It returns an optional, `IoError?`, saying whether the console write had trouble — and an optional may be ignored. That is why every lesson so far could call it as a bare statement.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Discard){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A fallible result cannot be ignored. Calling a fallible function as a bare statement, with
// nothing looking at what it returns, is a compile error — otherwise a failure could vanish
// without anyone noticing.
//
// Sometimes ignoring it is exactly right, though. A cash machine whose receipt printer has run
// out of paper should still hand over the money. For that there is a deliberate discard:
// `F() catch { else => {} };`. It says, in the code, "I know this can fail and it does not
// matter here". `catch` is the subject of a later lesson; for now, read it as that one phrase.
import Io::PrintLine;
struct Printer {
paper: int;
}
struct OutOfPaper {
}
// A unit fallible: it either printed the receipt or it did not.
func PrintReceipt(printer: &var Printer, amount: int) -> ! OutOfPaper {
if printer.paper == 0 {
fail OutOfPaper {};
}
printer.paper -= 1;
PrintLine(" receipt: {} taken", amount);
}
func Withdraw(printer: &var Printer, amount: int) {
PrintLine("hand over {}", amount);
// The line below does not compile:
//
// PrintReceipt(printer, amount);
//
// error: fallible result of type '! OutOfPaper' is discarded
// note: a failure that nothing handles is lost
// help: propagate it with '?', recover with 'catch', or match both '.Success' and '.Failure'
//
// Writing `let _ = PrintReceipt(printer, amount);` is refused the same way: binding a
// fallible to `_` does not handle its failure either.
// The deliberate discard: a missing receipt is not worth stopping for. The empty block `{}`
// stands in for the missing success, so this works only for a `! E` function. One that
// succeeds with a value, such as an `int`, is discarded with a match whose arms are
// `.Success(_) => {}` and `.Failure(_) => {}`.
PrintReceipt(printer, amount) catch { else => {} };
}
func Main() -> int {
var printer = Printer { paper: 2 };
Withdraw(printer, 20);
Withdraw(printer, 50);
Withdraw(printer, 10);
// `PrintLine` itself is not fallible. It returns an optional, `IoError?`, saying whether
// the console write had trouble, and an optional may be ignored — which is why every lesson
// so far could call it as a bare statement.
return 0;
}
```
## Run it
```sh
cd Examples/Errors/Discard
rux run
```
```text
hand over 20
receipt: 20 taken
hand over 50
receipt: 50 taken
hand over 10
```
## Common mistakes
::warning
**Hiding the result behind `_`.**:br`let _ = PrintReceipt(printer, amount);` is `error: fallible result of type '! OutOfPaper' is discarded`, with the note `binding a fallible to '_' does not handle its failure`. Write the `catch { else => {} }` phrase instead.
::
::warning
**The unit phrase on a fallible with a value.**:br
On an `int ! E` result, `catch { else => {} }` is `error: a block arm completes with '()', but 'catch' must recover a value of type 'int'`. Use the two-arm `match` above.
::
::warning
**Storing the result and forgetting it.**:br`let r = ExactDivide(12, 4);` with `r` never read compiles, but warns: `fallible local 'r' is never read; its failure is never handled`. Take the warning seriously — the failure is just as lost.
::
## Try it yourself
1. Start the printer with no paper at all. What does the output look like?
2. Replace the discard with a `match` that prints `" no receipt: out of paper"` on failure.
3. Write the bare call `PrintReceipt(printer, amount);` back in and read all three lines of the error.
## Learn more
- [Catch](https://rux-lang.dev/docs/learn/catch) — the full form of `catch`, with one arm per error
- [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) — `catch { else => ... }` with a real fallback value
- [Optional](https://rux-lang.dev/docs/learn/optional) — the type `PrintLine` returns, which may be ignored
# Catch
::note
**You'll need**: [Outcome](https://rux-lang.dev/docs/learn/outcome), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Guard](https://rux-lang.dev/docs/learn/guard)
::
`catch` turns a fallible back into a plain value. `outcome catch { arms }` lets a success straight through, unchanged. A failure goes to the arms, which see only the error and must produce a value of the success type in its place.
The example reads a digit someone typed. A few typing slips are easy to forgive: a capital O was surely meant as 0, and a lower-case l as 1. Anything else becomes -1, meaning unreadable.
## Success passes, failure goes to the arms
```rux
func ForgivingDigit(c: char) -> int {
return ReadDigit(c) catch {
DigitError::Letter(letter) if letter == 'O' => 0,
DigitError::Letter(letter) if letter == 'l' => 1,
DigitError::Letter(_) => -1,
DigitError::Symbol(_) => -1,
DigitError::Blank => -1
};
}
```
`ForgivingDigit` returns a plain `int`. After the `catch` there is no failure left to handle, so the function itself cannot fail.
```mermaid
flowchart LR
call["ReadDigit(c)"] --> q{"Success or failure?"}
q -- "success" --> pass["the digit, unchanged"]
q -- "failure" --> arms["the arms see only
the DigitError"]
arms --> value["each arm gives
an int in its place"]
pass --> result["a plain int"]
value --> result
```
## The arms are match arms
When the error is a [variant](https://rux-lang.dev/docs/learn/variant), each arm names a case, just as in a [variant match](https://rux-lang.dev/docs/learn/variant-match), and an arm may add a [guard](https://rux-lang.dev/docs/learn/guard) with `if`. Together the arms must cover every error, so no failure is left over.
The order is doing real work here. The two guarded `Letter` arms come first; `DigitError::Letter(_)` catches every other letter after them. Put `Letter(_)` first and it takes every letter, the O and the l included — and the compiler does not point it out. Every letter would quietly read as -1.
| | `match` | `catch` |
| ------------ | -------------------------------- | ------------------------ |
| Its arms see | the whole outcome | only the error |
| A success | needs an arm of its own | passes through unchanged |
| Patterns | `.Success(...)`, `.Failure(...)` | the error's own patterns |
| The result | whatever the arms produce | the success type |
## An arm may leave instead
Every arm has to give back an `int`, because that is what a success would have been. An arm that has no sensible value may leave the function instead, with `return`:
```rux
func DigitOrStop(c: char) -> int {
let digit = ReadDigit(c) catch {
DigitError::Blank => return 0,
else => -1
};
return digit * 2;
}
```
A blank makes `DigitOrStop` return 0 at once; any other failure carries on with -1. The `else` arm is the default arm, as in `match`, and covers whatever cases are left.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Catch){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `catch` turns a fallible back into a plain value. `outcome catch { arms }` lets a success
// straight through, unchanged. A failure goes to the arms, which see only the error and must
// produce a value of the success type in its place.
//
// When the error is a variant, each arm names a case, just as in a `match`, and an arm may add a
// guard with `if`. Together the arms must cover every error, so no failure is left over.
//
// The example reads a digit someone typed. A few typing slips are easy to forgive: a capital O
// was surely meant as 0, and a lower-case l as 1. Anything else becomes -1, meaning unreadable.
import Io::PrintLine;
variant DigitError {
Blank,
Letter(char),
Symbol(char)
}
func ReadDigit(c: char) -> int ! DigitError {
if c == ' ' {
fail DigitError::Blank;
}
if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') {
fail DigitError::Letter(c);
}
if c < '0' || c > '9' {
fail DigitError::Symbol(c);
}
return (c as int) - ('0' as int);
}
// The result is a plain `int`: after the `catch`, there is no failure left to handle.
func ForgivingDigit(c: char) -> int {
return ReadDigit(c) catch {
DigitError::Letter(letter) if letter == 'O' => 0,
DigitError::Letter(letter) if letter == 'l' => 1,
DigitError::Letter(_) => -1,
DigitError::Symbol(_) => -1,
DigitError::Blank => -1
};
}
func Show(c: char) {
PrintLine("'{}' reads as {}", c, ForgivingDigit(c));
}
func Main() -> int {
Show('7');
Show('O');
Show('l');
Show('x');
Show('#');
Show(' ');
// Every arm has to give back an `int`, because that is what a success would have been; an
// arm that has no sensible value may leave the function instead, with `return`. And the
// arms must cover every case: drop the `Blank` arm and the compiler says "match on
// 'DigitError' is not exhaustive; missing DigitError::Blank".
return 0;
}
```
## Run it
```sh
cd Examples/Errors/Catch
rux run
```
```text
'7' reads as 7
'O' reads as 0
'l' reads as 1
'x' reads as -1
'#' reads as -1
' ' reads as -1
```
## Common mistakes
::warning
**An arm of the wrong type.**:br`DigitError::Blank => "blank"` is `error: 'catch' arm produces 'char8[..]', but the recovered value has type 'int'`. A `catch` arm replaces the success, so it must have the success type.
::
::warning
**Missing a case.**:br
Drop the `Blank` arm and the compiler says `error: match on 'DigitError' is not exhaustive; missing DigitError::Blank`. Add the arm, or an `else` arm for everything left.
::
::warning
**A catch-all before the specific arms.**:br`DigitError::Letter(_)` above the guarded `Letter` arms compiles, but swallows them: `'O'` then reads as -1. Specific arms first, general ones after.
::
## Try it yourself
1. Forgive a capital `S` as 5 and a capital `B` as 8.
2. Make a blank read as 0 instead of -1. Predict the last line of the output before you run it.
3. Replace the last three arms with a single `else => -1`. Does the output change?
## Learn more
- [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) — one `else` arm for every error
- [Propagate](https://rux-lang.dev/docs/learn/propagate) — when the failure is the caller's to handle
- [Guard](https://rux-lang.dev/docs/learn/guard) — the `if` that refines an arm
- [Variant match](https://rux-lang.dev/docs/learn/variant-match) — the patterns `catch` arms use
# Catch fallback
::note
**You'll need**: [Catch](https://rux-lang.dev/docs/learn/catch), [Coalesce](https://rux-lang.dev/docs/learn/coalesce)
::
Often the reason for a failure does not matter, only that there is a value to carry on with. `outcome catch { else => fallback }` says exactly that: the success if there is one, otherwise the fallback. The single `else` arm takes every error alike, so nothing needs listing.
## One arm for every error
A settings form where every field is one typed digit, each with a sensible default:
```rux
let volume = ReadDigit('7') catch { else => 5 };
let brightness = ReadDigit('x') catch { else => 8 };
let contrast = ReadDigit(' ') catch { else => 5 };
```
`'7'` reads fine, so `volume` is 7. `'x'` is not a digit and `' '` is blank — two different errors, and both get the field's default. The fallback must have the success type, an `int` here.
## The twin of ??
If you have done [Coalesce](https://rux-lang.dev/docs/learn/coalesce), the shape is familiar. `catch { else => ... }` is the fallible twin of `option ?? fallback`:
| You have | Value or default | The fallback runs |
| -------- | ------------------------------------ | ----------------- |
| `T?` | `option ?? fallback` | only when absent |
| `T ! E` | `outcome catch { else => fallback }` | only on a failure |
Like `??`, the fallback is worked out only when it is needed. A fallback that calls a function which prints something prints nothing at all when the read succeeds.
Why not just `??` on a fallible, then? Because `??` would drop the error without a word, and the compiler will not do that silently. On a fallible it says `operator '??' cannot take 'int ! DigitError'`, and the note explains: `coalescing tests one optional level and would silently discard an error`. Writing `catch { else => ... }` is how you say that dropping it is intended.
## catch binds tightly
`catch` binds tightly to the call right before it, so several of them can sit in one expression without parentheses. Adding up a code, where a digit that cannot be read counts as nothing:
```rux
let total = ReadDigit('4') catch { else => 0 } + ReadDigit('?') catch { else => 0 }
+ ReadDigit('9') catch { else => 0 };
```
Each `catch` belongs to the `ReadDigit` just before it, so the sum is 4 + 0 + 9 = 13.
## The price
The convenience has a price: the error is thrown away unseen. `'x'` and `' '` both became defaults, and nothing in the program can tell which went wrong or why. When the reason should change what happens next, give `catch` one arm per case instead, as [Catch](https://rux-lang.dev/docs/learn/catch) does.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/CatchFallback){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Often the reason for a failure does not matter, only that there is a value to carry on with.
// `outcome catch { else => fallback }` says exactly that: the success if there is one, otherwise
// the fallback. The single `else` arm takes every error alike, so nothing needs listing.
//
// It is the fallible twin of `option ?? fallback`. The fallback is only worked out when it is
// needed, and it must have the success type — an `int` here.
//
// `catch` binds tightly to the call right before it, so several of them can sit in one
// expression without parentheses.
import Io::PrintLine;
variant DigitError {
Blank,
NotADigit(char)
}
func ReadDigit(c: char) -> int ! DigitError {
if c == ' ' {
fail DigitError::Blank;
}
if c < '0' || c > '9' {
fail DigitError::NotADigit(c);
}
return (c as int) - ('0' as int);
}
func Main() -> int {
// A settings form where every field is one typed digit, each with a sensible default.
let volume = ReadDigit('7') catch { else => 5 };
let brightness = ReadDigit('x') catch { else => 8 };
let contrast = ReadDigit(' ') catch { else => 5 };
PrintLine("volume {}, brightness {}, contrast {}", volume, brightness, contrast);
// Adding up a code, where a digit that cannot be read counts as nothing.
let total = ReadDigit('4') catch { else => 0 } + ReadDigit('?') catch { else => 0 }
+ ReadDigit('9') catch { else => 0 };
PrintLine("sum of 4?9 is {}", total);
// The convenience has a price: the error is thrown away unseen. When the reason should
// change what happens next, give `catch` one arm per case instead.
return 0;
}
```
## Run it
```sh
cd Examples/Errors/CatchFallback
rux run
```
```text
volume 7, brightness 8, contrast 5
sum of 4?9 is 13
```
## Common mistakes
::warning
**Using ?? on a fallible.**:br`ReadDigit('7') ?? 5` is refused: `error: operator '??' cannot take 'int ! DigitError'`. The help line names the two ways out: `recover the error with 'catch', or propagate it with '?'`.
::
::warning
**A fallback of the wrong type.**:br`catch { else => 8.5 }` is `error: 'catch' arm produces 'float64', but the recovered value has type 'int'`. The fallback stands in for the success, so it must be an `int`.
::
## Try it yourself
1. Add up the digits `'7'`, `'x'` and `'7'` with a fallback of 0, then with a fallback of 5. Predict both sums.
2. Write `func Default() -> int` that prints `"(using the default)"` and returns 5, and use it as the fallback for `volume`. When does the line appear?
3. Rewrite `brightness` with one arm per case — `Blank` gives 8, `NotADigit(_)` gives 0.
## Learn more
- [Catch](https://rux-lang.dev/docs/learn/catch) — one arm per error case
- [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — the same idea for optionals
- [Propagate](https://rux-lang.dev/docs/learn/propagate) — when the caller should decide instead
# Propagate
::note
**You'll need**: [Outcome](https://rux-lang.dev/docs/learn/outcome), [Catch](https://rux-lang.dev/docs/learn/catch)
::
Handling a fallible with `match` or `catch` is right when this function is the one that should decide what a failure means. Often it is not. The function is a step in the middle, and a failure should simply become the caller's problem.
Postfix `?` does exactly that. On success, `step?` is the value inside. On failure, the enclosing function fails at once with that same error, and nothing after the `?` runs.
## The long way
Written by hand, passing a failure on is the same few lines every time: match, keep the success, fail with the error unchanged.
```rux
var high = 0;
match ReadDigit(tens) {
.Success(value) => high = value,
.Failure(error) => fail error
}
```
`TwoDigitsByHand` writes that block twice, once per digit. The two blocks are near-identical and say nothing new.
## The same with ?
`?` is those lines written once. The success values are used straight away:
```rux
func TwoDigits(tens: char, units: char) -> int ! DigitError {
let high = ReadDigit(tens)?;
let low = ReadDigit(units)?;
return high * 10 + low;
}
```
```mermaid
flowchart LR
step["ReadDigit(tens)?"] --> q{"Success or failure?"}
q -- ".Success(value)" --> go["the expression is value;
TwoDigits carries on"]
q -- ".Failure(error)" --> out["TwoDigits fails at once
with the same error"]
out --> caller["the caller's match
sees the original error"]
```
Both versions behave identically: `?` is shorthand, not a different rule. When both digits are wrong, as in `TwoDigits('x', '?')`, only the first one is reported — the first `?` leaves, and the second read never happens.
## The error travels intact
Neither `TwoDigits` nor `?` looked at the error, yet the caller still learns which character was wrong:
```rux
.Failure(DigitError::NotADigit(c)) => PrintLine("'{}' is not a digit", c)
```
`Show` matches the nested pattern `.Failure(DigitError::NotADigit(c))` directly, reaching through the failure channel into the variant case in one step.
## Two requirements
`?` asks two things of the function it is used in:
1. **It must be fallible**, since that is where the failure goes. `Main` returns a plain `int`, so `ReadDigit('4')?` there is rejected.
2. **The error must fit its error type.** Here both are `DigitError`, so it passes through as is. A different error type needs [Error mapping](https://rux-lang.dev/docs/learn/error-mapping), or an [error sum](https://rux-lang.dev/docs/learn/error-sum) that includes it.
| Tool | Who decides what a failure means | The function stays |
| ---------------- | -------------------------------- | ---------------------------------- |
| `match`, `catch` | this function | as fallible as you choose |
| `?` | the caller | fallible, with the same error type |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Propagate){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Handling a fallible with `match` or `catch` is right when this function is the one that should
// decide what a failure means. Often it is not: the function is a step in the middle, and a
// failure should simply become the caller's problem.
//
// Written by hand that is the same few lines every time: match, keep the success, fail with the
// error unchanged. Postfix `?` is those lines written once. On success, `step?` is the value
// inside. On failure, the enclosing function fails at once with that same error, and nothing after
// the `?` runs.
import Io::PrintLine;
variant DigitError {
Blank,
NotADigit(char)
}
// One small step that can fail.
func ReadDigit(c: char) -> int ! DigitError {
if c == ' ' {
fail DigitError::Blank;
}
if c < '0' || c > '9' {
fail DigitError::NotADigit(c);
}
return (c as int) - ('0' as int);
}
// The long way. Each failure is matched only to be handed straight back to the caller: two
// near-identical blocks that say nothing new.
func TwoDigitsByHand(tens: char, units: char) -> int ! DigitError {
var high = 0;
match ReadDigit(tens) {
.Success(value) => high = value,
.Failure(error) => fail error
}
var low = 0;
match ReadDigit(units) {
.Success(value) => low = value,
.Failure(error) => fail error
}
return high * 10 + low;
}
// The same function with `?`. The success values are used straight away.
func TwoDigits(tens: char, units: char) -> int ! DigitError {
let high = ReadDigit(tens)?;
let low = ReadDigit(units)?;
return high * 10 + low;
}
func Show(outcome: int ! DigitError) {
match outcome {
.Success(value) => PrintLine("read {}", value),
.Failure(DigitError::Blank) => PrintLine("a digit is missing"),
.Failure(DigitError::NotADigit(c)) => PrintLine("'{}' is not a digit", c)
}
}
func Main() -> int {
Show(TwoDigits('4', '2'));
Show(TwoDigits('4', 'x'));
Show(TwoDigits('?', '2'));
Show(TwoDigits(' ', '7'));
// Both versions behave identically: `?` is shorthand, not a different rule.
Show(TwoDigitsByHand('4', 'x'));
// The error travels intact. Neither `TwoDigits` nor `?` looked at it, yet the caller still
// learns which character was wrong.
//
// `?` has two requirements. The function using it must itself be fallible, since that is
// where the failure goes. In `Main`, which returns a plain `int`, `ReadDigit('4')?` is
// rejected: "'?' propagates native fallible 'int ! DigitError', but the enclosing function
// returns 'int'". And the error must fit that function's error type. Here both are
// `DigitError`, so it passes through as is.
return 0;
}
```
## Run it
```sh
cd Examples/Errors/Propagate
rux run
```
```text
read 42
'x' is not a digit
'?' is not a digit
a digit is missing
'x' is not a digit
```
## Common mistakes
::warning
**? in a function that cannot fail.**:br
In `Main`, `ReadDigit('4')?` is `error: '?' propagates native fallible 'int ! DigitError', but the enclosing function returns 'int'`. Handle the failure with `match` or `catch` there — or make `Main` fallible, as [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) shows.
::
::warning
**Forgetting the ?.**:br`let high = ReadDigit(tens);` makes `high` the whole fallible, and the next line fails: `error: operator '*' cannot combine left operand 'int ! DigitError' with right operand 'int'`.
::
::warning
**Passing on an error of another type.**:br`?` never converts an error. In a function that fails with a `SettingError`, `?` on a `DigitError` is `error: '?' propagates error type 'DigitError', but the enclosing function fails with 'SettingError'`. [Error mapping](https://rux-lang.dev/docs/learn/error-mapping) is the fix.
::
## Try it yourself
1. Add `Show(TwoDigits(' ', 'x'));` and predict which of the two messages it prints.
2. Write `ThreeDigits(hundreds: char, tens: char, units: char) -> int ! DigitError` with three `?`.
3. Rewrite `ThreeDigits` to call `TwoDigits` for the last two digits. Does the `?` still need anything special?
## Learn more
- [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) — the same `?` for absence
- [Error mapping](https://rux-lang.dev/docs/learn/error-mapping) — `? else` adds context on the way through
- [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) — `?` at the top level of a program
- [Error variant](https://rux-lang.dev/docs/learn/error-variant) — one error type for a whole chain of steps
# Error variant
::note
**You'll need**: [Propagate](https://rux-lang.dev/docs/learn/propagate), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern)
::
An operation that can go wrong in several ways deserves an error type that lists those ways. A [variant](https://rux-lang.dev/docs/learn/variant) does that: one case per kind of failure, and each case carries only the details that make sense for it.
The caller can then match on the case to decide what to do, and read the details to say precisely what went wrong — far more than a single error code could tell it.
## One case per way to fail
A transfer between three bank accounts can fail in three ways:
```rux
variant TransferError {
SameAccount,
UnknownAccount(int),
ShortOfFunds { balance: int; requested: int; }
}
```
| Case | Shape | Carries |
| ---------------- | ------------------------- | ------------------------------------ |
| `SameAccount` | bare | nothing — the case says it all |
| `UnknownAccount` | one fact, in parentheses | the account number that was wrong |
| `ShortOfFunds` | several facts, with names | the balance and the amount asked for |
A case with several facts gives them names, like a small [struct](https://rux-lang.dev/docs/learn/struct), so nobody has to remember which number came first.
## Failing with each case
Each `fail` builds the case that fits, with its details:
```rux
if from == to {
fail TransferError::SameAccount;
}
```
```rux
if balances[source] < amount {
fail TransferError::ShortOfFunds { balance: balances[source], requested: amount };
}
```
`UnknownAccount` comes from a helper, `Find`, which turns an account number into a position in the list of balances. It fails with the same error type, so `Transfer` passes its failures on with [`?`](https://rux-lang.dev/docs/learn/propagate), unchanged:
```rux
let source = Find(from)?;
let target = Find(to)?;
```
The checks run in order, and the first one that fails decides the error:
```mermaid
flowchart TD
start["Transfer(from, to, amount)"] --> same{"from == to?"}
same -- "yes" --> e1["SameAccount"]
same -- "no" --> find{"Do both accounts exist?
Find(from)? and Find(to)?"}
find -- "no" --> e2["UnknownAccount(number)"]
find -- "yes" --> funds{"Is there enough money?"}
funds -- "no" --> e3["ShortOfFunds { balance, requested }"]
funds -- "yes" --> done["Success: balances updated"]
```
## Reading the details
`Explain` gives one message per case, each built from the details that case carries. The patterns are the [struct patterns](https://rux-lang.dev/docs/learn/struct-pattern) you already know:
```rux
match error {
.SameAccount => PrintLine(" refused: an account cannot pay itself"),
.UnknownAccount(number) => PrintLine(" refused: there is no account {}", number),
.ShortOfFunds { balance, requested } => PrintLine(
" refused: asked for {} but only {} is there", requested, balance)
}
```
`.ShortOfFunds { balance, requested }` binds each field to a variable of the same name. A pattern written with field names may also leave fields out — `.ShortOfFunds { requested }` binds only the one it needs.
Because the match must cover every case, adding a fourth way to fail later makes the compiler point at every `match` that does not handle it yet. That is the main payoff of a variant error: no new failure goes unexplained.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/ErrorVariant){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An operation that can go wrong in several ways deserves an error type that lists those ways.
// A variant does that: one case per kind of failure, and each case carries only the details that
// make sense for it.
//
// A case with nothing to add is bare. A case with one fact carries it in parentheses. A case with
// several facts gives them names, like a small struct. The caller can then match on the case to
// decide what to do, and read the details to say precisely what went wrong — far more than a
// single error code could tell it.
import Io::PrintLine;
variant TransferError {
SameAccount,
UnknownAccount(int),
ShortOfFunds { balance: int; requested: int; }
}
// Turns an account number into a position in the list of balances.
func Find(number: int) -> int ! TransferError {
if number < 1 || number > 3 {
fail TransferError::UnknownAccount(number);
}
return number - 1;
}
func Transfer(balances: &var int[3], from: int, to: int, amount: int) -> ! TransferError {
if from == to {
fail TransferError::SameAccount;
}
// The same error type all the way through, so `?` passes each failure on unchanged.
let source = Find(from)?;
let target = Find(to)?;
if balances[source] < amount {
fail TransferError::ShortOfFunds { balance: balances[source], requested: amount };
}
balances[source] -= amount;
balances[target] += amount;
}
// One message per case, each built from the details that case carries.
func Explain(error: TransferError) {
match error {
.SameAccount => PrintLine(" refused: an account cannot pay itself"),
.UnknownAccount(number) => PrintLine(" refused: there is no account {}", number),
.ShortOfFunds { balance, requested } => PrintLine(
" refused: asked for {} but only {} is there", requested, balance)
}
}
func Attempt(balances: &var int[3], from: int, to: int, amount: int) {
PrintLine("move {} from account {} to account {}", amount, from, to);
match Transfer(balances, from, to, amount) {
.Success(()) => PrintLine(" done: balances {}, {}, {}", balances[0], balances[1],
balances[2]),
.Failure(error) => Explain(error)
}
}
func Main() -> int {
var balances: int[3] = [100, 50, 0];
Attempt(balances, 1, 3, 30);
Attempt(balances, 2, 2, 10);
Attempt(balances, 1, 7, 10);
Attempt(balances, 3, 1, 45);
return 0;
}
```
## Run it
```sh
cd Examples/Errors/ErrorVariant
rux run
```
```text
move 30 from account 1 to account 3
done: balances 70, 50, 30
move 10 from account 2 to account 2
refused: an account cannot pay itself
move 10 from account 1 to account 7
refused: there is no account 7
move 45 from account 3 to account 1
refused: asked for 45 but only 30 is there
```
## Common mistakes
::warning
**Forgetting a case.**:br
Drop the `SameAccount` arm from `Explain` and the compiler says `error: match on 'TransferError' is not exhaustive; missing TransferError::SameAccount`.
::
::warning
**Giving a bare case a payload.**:br`fail TransferError::SameAccount(from);` is `error: call to 'TransferError::SameAccount' expects 0 arguments, but 1 was provided`. If the detail matters, declare the case with it.
::
## Try it yourself
1. Add a case `ZeroAmount` and refuse a transfer of 0 before any other check. Follow the compiler to every match that needs a new arm.
2. Make `ShortOfFunds` print how much is missing as well, using only the two fields it already carries.
3. Add `Attempt(balances, 2, 1, 50);` at the end and predict the balances it prints.
## Learn more
- [Variant](https://rux-lang.dev/docs/learn/variant) and [Variant match](https://rux-lang.dev/docs/learn/variant-match) — the type and its patterns
- [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern) — binding named fields
- [Error mapping](https://rux-lang.dev/docs/learn/error-mapping) — turning a lower-level error into one of these cases
- [Error sum](https://rux-lang.dev/docs/learn/error-sum) — the alternative when the errors are unrelated types
# Error mapping
::note
**You'll need**: [Fail](https://rux-lang.dev/docs/learn/fail), [Propagate](https://rux-lang.dev/docs/learn/propagate)
::
A low-level function knows *what* went wrong but not *where*. `ParseNumber` can say "the character at position 2 is not a digit", but it has no idea that its text came from line 3 of a settings file. The function that does know is the one calling it.
This lesson shows how that caller adds what it knows to the error as it passes through, with `? else (e => ...)`.
## Plain ? cannot add context
Two error types meet here. `ParseNumber` fails with a `DigitError`, which knows a position; the settings reader fails with a `SettingError`, which needs a line and a column:
```rux
struct DigitError {
position: uint;
}
struct SettingError {
line: uint;
column: uint;
}
```
Plain [`?`](https://rux-lang.dev/docs/learn/propagate) passes an error on unchanged, so it cannot add the line number — and it is rejected here anyway, because a `DigitError` is not a `SettingError`. The compiler's note is worth reading: `'?' moves an error into the outer failure only by identity, sum member injection, or subset widening; it never converts an error`.
## ? else maps the error
The mapped form fixes both problems:
```rux
let w = ParseNumber(width)? else (e => SettingError { line: 1, column: e.position + 1 });
let h = ParseNumber(height)? else (e => SettingError { line: 2, column: e.position + 1 });
```
| Piece | Meaning |
| ---------------------- | ------------------------------------------------------------------------------------ |
| `ParseNumber(width)?` | on success, the number; the mapper never runs |
| `else (e => ...)` | on failure, `e` is the whole original `DigitError` |
| `SettingError { ... }` | the new error, built from `e` and anything else in scope; the function fails with it |
```mermaid
flowchart LR
call["ParseNumber(width)"] --> q{"Success or failure?"}
q -- "success" --> n["the number;
the mapper never runs"]
q -- "failure, as e" --> m["the mapper builds
SettingError { line: 1, column: e.position + 1 }"]
m --> f["WindowArea fails
with the new error"]
```
## The mapper uses what is in scope
The mapper is a single expression, and it can read anything the function can. Here it adds the line number, which only `WindowArea` knows, and turns the 0-based position into the 1-based column a person would look for. In `"8O"` the letter O sits at position 1, so the message says column 2.
The parentheses around `e => ...` hold exactly one name and one expression. The mapper must produce the enclosing function's error type — the compiler checks that, but not whether the arithmetic inside is right.
## Where to map
Map an error where the context is. `ParseNumber` stays small and reusable because it knows nothing about files; `WindowArea` is the first function that knows about lines, so it is the one that maps. Its own caller then deals with `SettingError` alone and never needs to know that a `DigitError` existed.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/ErrorMapping){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A low-level function knows what went wrong but not where. `ParseNumber` below
// can say "the character at position 2 is not a digit", but it has no idea that
// its text came from line 3 of a settings file. The function that *does* know
// is the one calling it.
//
// Plain `?` passes an error on unchanged, so it cannot add that context — and
// it is rejected here anyway, because a `DigitError` is not a `SettingError`.
// The mapped form fixes both:
//
// ParseNumber(text)? else (e => SettingError { ... })
//
// On success the expression is the number and the mapper never runs. On
// failure `e` is the whole original error, the mapper builds the new one from
// `e` plus anything else in scope, and the function fails with it.
import Io::PrintLine;
struct DigitError {
position: uint;
}
struct SettingError {
line: uint;
column: uint;
}
// Knows about characters, nothing about files or lines.
func ParseNumber(text: char8[..]) -> int ! DigitError {
var total = 0;
for i in 0..text.length {
let byte = text[i];
if byte < c8'0' || byte > c8'9' {
fail DigitError { position: i };
}
total = total * 10 + ((byte as int) - 48);
}
return total;
}
// Reads a window size from two settings lines. Each `? else` adds the line
// number, which only this function knows, and turns the 0-based position into
// the 1-based column a person would look for.
func WindowArea(width: char8[..], height: char8[..]) -> int ! SettingError {
let w = ParseNumber(width)? else (e => SettingError { line: 1, column: e.position + 1 });
let h = ParseNumber(height)? else (e => SettingError { line: 2, column: e.position + 1 });
return w * h;
}
func Show(width: char8[..], height: char8[..]) {
match WindowArea(width, height) {
.Success(area) => PrintLine("{} x {}: area {}", width, height, area),
.Failure(e) => PrintLine("{} x {}: line {}, column {} is not a digit",
width, height, e.line, e.column)
}
}
func Main() -> int {
Show("80", "24");
Show("8O", "24");
Show("80", "2-4");
return 0;
}
```
## Run it
```sh
cd Examples/Errors/ErrorMapping
rux run
```
```text
80 x 24: area 1920
8O x 24: line 1, column 2 is not a digit
80 x 2-4: line 2, column 2 is not a digit
```
## Common mistakes
::warning
**Plain ? across error types.**:br`let w = ParseNumber(width)?;` is `error: '?' propagates error type 'DigitError', but the enclosing function fails with 'SettingError'`, with the help `map the error to 'SettingError' with '? else (e => ...)', or match the value`.
::
::warning
**A mapper that builds the wrong type.**:br`ParseNumber(width)? else (e => 5)` is `error: the mapped error has type 'int', but the enclosing function fails with 'SettingError'`.
::
::warning
**Off by one in the mapper.**:br
Write `column: e.position` and the program still compiles — it just reports column 1 for `"8O"`. The compiler checks the mapper's type, not its meaning.
::
## Try it yourself
1. Add a `found: char8` field to `SettingError` and fill it in the mapper with the bad character, `width[e.position]`. Print it in `Show`.
2. Call `Show("", "24")`. Predict the output before you run it — is an empty line an error?
3. Read a third setting, a depth on line 3, and print the volume instead of the area.
## Learn more
- [Propagate](https://rux-lang.dev/docs/learn/propagate) — plain `?`, which passes an error on unchanged
- [Error variant](https://rux-lang.dev/docs/learn/error-variant) — a richer target type for a mapper
- [Error sum](https://rux-lang.dev/docs/learn/error-sum) — passing on two error types without mapping either
# Error sum
::note
**You'll need**: [Outcome](https://rux-lang.dev/docs/learn/outcome), [Propagate](https://rux-lang.dev/docs/learn/propagate)
::
Some functions can fail in more than one unrelated way. Reading a percentage can find a character that is not a digit, or a perfectly good number that is bigger than 100. Those are two different error types, and the error channel can hold either one.
## Two errors in one channel
The two errors are separate structs, each with its own details:
```rux
struct DigitError {
position: uint;
}
struct RangeError {
value: int;
limit: int;
}
```
`ParsePercent` names both after the `!`:
```rux
func ParsePercent(text: char8[..]) -> int ! (DigitError | RangeError) {
```
`A | B` is a *sum*: one value that is an `A` or a `B`, and knows which. Sums have a part of their own, [Sum types](https://rux-lang.dev/docs/learn/sum-types), straight after this one; here you need only what it means in an error channel. The order does not matter — `(RangeError | DigitError)` is the same type. The parentheses are for the reader: in a type, `|` binds tighter than `!`, so they could be left out, but with them nobody has to remember that.
## Widening needs no code
The surprise is how little the function body has to do:
```rux
let value = ParseNumber(text)?;
if value > 100 {
fail RangeError { value: value, limit: 100 };
}
return value;
```
`ParseNumber` fails with a plain `DigitError`, yet `?` passes it on unchanged — a `DigitError` fits into the wider sum on its own. `fail RangeError { ... }` needs no wrapping either. This is called *widening*: a narrower error always fits into a sum that includes it.
```mermaid
flowchart LR
d["ParseNumber fails
with a DigitError"] -- "?" --> sum["ParsePercent fails with
DigitError | RangeError"]
r["fail RangeError { ... }"] --> sum
sum --> m{"The caller's match"}
m -- "e: DigitError" --> a["no digit at position …"]
m -- "e: RangeError" --> b["… is over 100"]
```
## Telling the members apart
The caller tells the members apart with a *typed pattern*. `e: DigitError` matches only that member and binds it as a plain `DigitError`, so its fields are right there:
```rux
match ParsePercent(text) {
.Success(percent) => PrintLine("{:5} -> {}%", text, percent),
.Failure(e: DigitError) => PrintLine("{:5} -> no digit at position {}", text, e.position),
.Failure(e: RangeError) => PrintLine("{:5} -> {} is over {}", text, e.value, e.limit)
}
```
The match must cover every member of the sum, or the compiler names the missing one.
## A sum or a variant?
[Error variant](https://rux-lang.dev/docs/learn/error-variant) solved a similar problem with one type and several cases. Both work; they suit different situations.
| | Error sum `A | B` | Error variant |
| -------------------------- | ------------------------------------------ | ------------------------------- |
| The errors are | separate types that already exist | cases you declare together |
| Passing one on with `?` | widens on its own | needs a mapping into a case |
| Adding a new kind of error | changes every signature that lists the sum | one new case in one declaration |
| Best for | combining errors from different places | a stable, named set of failures |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/ErrorSum){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Some functions can fail in more than one unrelated way. Reading a percentage
// can find a character that is not a digit, or a perfectly good number that
// is bigger than 100. Those are two different error types, and the error
// channel can hold either one:
//
// func ParsePercent(text: char8[..]) -> int ! (DigitError | RangeError)
//
// `A | B` is a sum: one value that is an `A` or a `B`, and knows which. The
// order does not matter — `(RangeError | DigitError)` is the same type.
//
// The surprise is how little the function body has to do. A `DigitError`
// fits into the wider sum on its own, so `ParseNumber(text)?` passes it on
// unchanged and `fail RangeError { ... }` needs no wrapping either. This is
// called widening: a narrower error always fits into a sum that includes it.
import Io::PrintLine;
struct DigitError {
position: uint;
}
struct RangeError {
value: int;
limit: int;
}
func ParseNumber(text: char8[..]) -> int ! DigitError {
var total = 0;
for i in 0..text.length {
let byte = text[i];
if byte < c8'0' || byte > c8'9' {
fail DigitError { position: i };
}
total = total * 10 + ((byte as int) - 48);
}
return total;
}
func ParsePercent(text: char8[..]) -> int ! (DigitError | RangeError) {
// A `DigitError` widens into the sum as it passes through `?`.
let value = ParseNumber(text)?;
if value > 100 {
fail RangeError { value: value, limit: 100 };
}
return value;
}
// The caller tells the members apart with a typed pattern: `e: DigitError`
// matches only that member and binds it as a plain `DigitError`. The match
// must cover every member of the sum, or the compiler names the missing one.
// Drop the `RangeError` arm and it says "match on 'int ! (DigitError |
// RangeError)' is not exhaustive; missing .Failure(_: RangeError)".
func Show(text: char8[..]) {
match ParsePercent(text) {
.Success(percent) => PrintLine("{:5} -> {}%", text, percent),
.Failure(e: DigitError) => PrintLine("{:5} -> no digit at position {}", text, e.position),
.Failure(e: RangeError) => PrintLine("{:5} -> {} is over {}", text, e.value, e.limit)
}
}
func Main() -> int {
Show("75");
Show("7%");
Show("250");
return 0;
}
```
## Run it
```sh
cd Examples/Errors/ErrorSum
rux run
```
```text
75 -> 75%
7% -> no digit at position 1
250 -> 250 is over 100
```
## Common mistakes
::warning
**Leaving out a member.**:br
Drop the `RangeError` arm and the compiler says `error: match on 'int ! (DigitError | RangeError)' is not exhaustive; missing .Failure(_: RangeError)`.
::
::warning
**Failing with a type that is not in the sum.**:br`fail 5;` in `ParsePercent` is `error: 'fail' value must have type 'DigitError | RangeError', but found 'int'`. Widening only accepts a member of the sum.
::
::warning
**An untyped failure arm first.**:br`.Failure(e) =>` matches every member, so a later `.Failure(e: RangeError)` arm is `error: match arm is unreachable because earlier arms already match every value it matches`.
::
## Try it yourself
1. Add `Show("100");` and `Show("101");` and predict both lines.
2. `Show("")` prints `0%`. Add an `EmptyError` struct, put it in the sum, and fail with it on empty text. What else does the compiler ask you to change?
3. Swap the order of the members in the signature to `(RangeError | DigitError)` and check that nothing else has to change.
## Learn more
- [Sum types](https://rux-lang.dev/docs/learn/sum-types) — the next part, all about `A | B`
- [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) — `e: DigitError` in detail
- [Sum widening](https://rux-lang.dev/docs/learn/sum-widening) — when a narrower type fits a wider sum
- [Error variant](https://rux-lang.dev/docs/learn/error-variant) — the alternative with named cases
# Fallible main
::note
**You'll need**: [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible), [Propagate](https://rux-lang.dev/docs/learn/propagate)
::
Every program so far ended with `return 0;` from `func Main() -> int`. That made [`?`](https://rux-lang.dev/docs/learn/propagate) useless at the top level: `Main` could not fail, so a failure had nowhere to go, and every step had to be matched by hand.
`Main` may also be fallible. Then `?` works at the top level too, and a failure ends the program.
## Main may fail
The installer in this lesson reserves disk space for two programs. `Reserve` fails with a `SpaceError` when there is not enough room, and `Main` declares the same error:
```rux
func Main() -> ! SpaceError {
var free = 100;
PrintLine("Installing the editor (40 MB of {} MB free)", free);
free = Reserve(40, free)?;
```
`Main` is now a [unit fallible](https://rux-lang.dev/docs/learn/unit-fallible), so it follows the same rules: falling off its end is success, and it has no `return 0;`. The second reservation asks for 90 MB with only 60 free, so its `?` ends the program — the last `PrintLine` never runs.
## How the program ends
The operating system receives a number when a program ends, its *exit status*. 0 means success; anything else means something went wrong.
| `Main` is declared | and ends by | Exit status |
| ------------------------ | ---------------------- | ----------- |
| `-> int` | `return n;` | `n` |
| `-> ! E` | reaching its end | 0 |
| `-> int ! E` | `return n;` | `n` |
| `-> ! E` or `-> int ! E` | `fail` or a failed `?` | 1 |
A failure still runs the program's ordinary cleanup on the way out, just as a `return` would — unlike a [panic](https://rux-lang.dev/docs/learn/panic), which stops at once.
## A failure prints nothing
The surprise is what a failure does *not* do: it prints nothing. The `SpaceError` value is not shown anywhere, so a user only sees the program stop after the second line.
The exit status is for whoever started the program — a script, a build tool or a shell. In PowerShell, `echo $LASTEXITCODE` shows it right after the program ends; in a POSIX shell such as bash, it is `echo $?`. That `1` is the last line of the output below.
If a person should learn *why* the program stopped, print a message yourself before failing. A `catch` arm can do both — print, then `fail` with the same error:
```rux
free = Reserve(90, free) catch {
e => {
PrintLine("Not enough space: {} MB needed, {} MB free", e.needed, e.free);
fail e;
}
};
```
The arm `e => ...` binds the whole error, and its block ends by leaving, so it never has to produce a value.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/FallibleMain){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Every program so far ended with `return 0;` from `func Main() -> int`. `Main`
// may also be fallible:
//
// func Main() -> ! SpaceError
//
// Now `?` works at the top level too. Falling off the end of `Main` is
// success, and the program exits with status 0. A failure — from `fail` or
// from a `?` — ends the program with exit status 1.
//
// The surprise is what a failure does *not* do: it prints nothing. The error
// value is not shown anywhere, so a user only sees the program stop. If they
// should learn why, print a message yourself before failing. The exit status
// is for whoever started the program: a script, a build tool or a shell. In
// PowerShell, `echo $LASTEXITCODE` shows it right after the program ends.
//
// This program fails on purpose: the second install does not fit.
import Io::PrintLine;
struct SpaceError {
needed: int;
free: int;
}
func Reserve(needed: int, free: int) -> int ! SpaceError {
if needed > free {
fail SpaceError { needed: needed, free: free };
}
return free - needed;
}
func Main() -> ! SpaceError {
var free = 100;
PrintLine("Installing the editor (40 MB of {} MB free)", free);
free = Reserve(40, free)?;
PrintLine("Installing the compiler (90 MB of {} MB free)", free);
free = Reserve(90, free)?;
// Never printed: the `?` above ended the program with status 1.
PrintLine("Everything is installed, {} MB left", free);
}
```
## Run it
```sh
cd Examples/Errors/FallibleMain
rux run
echo $?
```
The program fails on purpose, so it stops after the second line, and `echo $?` prints its exit status, 1. In PowerShell, use `echo $LASTEXITCODE` instead.
```text
Installing the editor (40 MB of 100 MB free)
Installing the compiler (90 MB of 60 MB free)
1
```
## Common mistakes
::warning
**Keeping return 0 in a fallible Main.**:br
In `func Main() -> ! SpaceError`, `return 0;` is `error: 'return' value must have type '! SpaceError', but found 'int'`. Let `Main` reach its end — or declare `-> int ! SpaceError` if you want to choose the status yourself.
::
::warning
**Expecting the error to be shown.**:br
A failed `Main` exits with status 1 and prints nothing about the error. Print what the user needs to know before the failure, as the `catch` above does.
::
## Try it yourself
1. Change the compiler's 90 MB to 50 and run again. What does `echo $LASTEXITCODE` show now?
2. Replace the second `?` with the `catch` above, so the user learns why the install stopped.
3. Declare `func Main() -> int ! SpaceError`, end it with `return 2;`, and check the exit status when everything fits.
## Learn more
- [The Main entry point](https://rux-lang.dev/docs/lang/functions/main) in the Rux Reference
- [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible) — the rules a `-> ! E` function follows
- [Propagate](https://rux-lang.dev/docs/learn/propagate) — the `?` that `Main` can now use
- [Panic](https://rux-lang.dev/docs/learn/panic) — stopping a program that has hit a bug
# Absence to error
::note
**You'll need**: [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Fail](https://rux-lang.dev/docs/learn/fail)
::
An [optional](https://rux-lang.dev/docs/learn/optional) says "nothing here" and stops there. `PriceOf(7)` returns `none`, and `none` cannot say which code was missing or why that matters. When absence is a real problem for the caller, turn it into an error that does say so.
## ?? fail
A small price list where an unknown code is simply absent:
```rux
func PriceOf(code: int) -> int? {
return match code {
1 => 250,
2 => 120,
3 => 75,
else => none
};
}
```
`Total` needs a price, and a missing one is a failure worth reporting:
```rux
func Total(code: int, quantity: int) -> int ! UnknownProduct {
let price = PriceOf(code) ?? fail UnknownProduct { code: code };
return price * quantity;
}
```
This is the `??` from [Coalesce](https://rux-lang.dev/docs/learn/coalesce), with `fail` as its fallback — just as [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) used `return`. When the price is present, `price` is that number. When it is absent, the function fails right there, and the error can carry whatever context is at hand — here, the code that was not found.
```mermaid
flowchart LR
look["PriceOf(code)"] --> q{"Present or none?"}
q -- "present" --> price["price is the number;
Total carries on"]
q -- "none" --> fail["fail UnknownProduct { code }"]
fail --> caller["the caller learns
which code was missing"]
```
## The error is built only when needed
Like every `??` fallback, the right side runs only when it is needed. For a product that exists, the `UnknownProduct` value is never even built.
## Why not ?
On an optional, [`?`](https://rux-lang.dev/docs/learn/optional-propagate) passes the absence on — but only as absence. It works in a function that returns an optional, and `Total` returns a fallible. `?` never invents an error for a missing value; `?? fail` is how you supply one yourself.
| You write | When the price is missing, `Total`… |
| --------------------------- | -------------------------------------------------------- |
| `PriceOf(code) ?? 0` | carries on with 0, so the order costs nothing |
| `PriceOf(code) ?? return …` | succeeds at once with the value given to `return` |
| `PriceOf(code)?` | is rejected: `Total` returns a fallible, not an optional |
| `PriceOf(code) ?? fail …` | fails, saying which code it was |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/AbsenceToError){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An optional says "nothing here" and stops there. `PriceOf(7)` returns
// `none`, and `none` cannot say which code was missing or why that matters.
// When absence is a real problem for the caller, turn it into an error that
// does say so:
//
// let price = PriceOf(code) ?? fail UnknownProduct { code: code };
//
// This is the `??` from the optionals part with `fail` as its fallback. When
// the price is present, `price` is that number. When it is absent, the
// function fails right there, and the error can carry whatever context is at
// hand — here, the code that was not found.
//
// Like every `??` fallback, the right side runs only when it is needed, so
// the error is not even built for a product that exists.
import Io::PrintLine;
struct UnknownProduct {
code: int;
}
// A small price list. An unknown code is simply absent.
func PriceOf(code: int) -> int? {
return match code {
1 => 250,
2 => 120,
3 => 75,
else => none
};
}
func Total(code: int, quantity: int) -> int ! UnknownProduct {
let price = PriceOf(code) ?? fail UnknownProduct { code: code };
return price * quantity;
}
func Order(code: int, quantity: int) {
match Total(code, quantity) {
.Success(total) => PrintLine("{} x product {}: {} cents", quantity, code, total),
.Failure(e) => PrintLine("{} x product {}: no product has code {}", quantity, code, e.code)
}
}
func Main() -> int {
Order(1, 2);
Order(3, 4);
Order(7, 1);
return 0;
}
```
## Run it
```sh
cd Examples/Errors/AbsenceToError
rux run
```
```text
2 x product 1: 500 cents
4 x product 3: 300 cents
1 x product 7: no product has code 7
```
## Common mistakes
::warning
**Using ? to turn absence into an error.**:br`let price = PriceOf(code)?;` in `Total` is `error: '?' propagates the absence of 'int?', but the enclosing function returns 'int ! UnknownProduct'`, with the note `'?' never invents an error for it`. Write `?? fail` with the error you mean.
::
::warning
**?? fail in a function that cannot fail.**:br
In `Main`, `PriceOf(1) ?? fail UnknownProduct { code: 1 }` is `error: 'fail' needs an enclosing fallible function, but this function returns 'int'`.
::
::warning
**Comparing without parentheses.**:br`==` binds tighter than `??`, so `PriceOf(1) ?? 0 == 250` means `PriceOf(1) ?? (0 == 250)` and fails with `error: coalescing fallback has type 'bool8', but the optional payload is 'int'`. Write `(PriceOf(1) ?? 0) == 250`.
::
## Try it yourself
1. Add product 4 at 990 cents and order three of them.
2. Refuse an order of zero items as well. One error struct no longer fits — try a variant with an `UnknownCode(int)` case and a `NoItems` case.
3. Change `Order(7, 1)` to use `PriceOf(7) ?? 0` directly in `Main` instead of `Total`. What does the customer pay for an unknown product?
## Learn more
- [Coalesce](https://rux-lang.dev/docs/learn/coalesce) and [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) — `??` with a value and with `return`
- [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) — `?` on an optional
- [Fail](https://rux-lang.dev/docs/learn/fail) — the statement used as the fallback here
# Nested fallible
::note
**You'll need**: [Nested optional](https://rux-lang.dev/docs/learn/nested-optional), [Outcome](https://rux-lang.dev/docs/learn/outcome)
::
Optionals and fallibles nest, and each level keeps its own meaning. You saw that with optionals in [Nested optional](https://rux-lang.dev/docs/learn/nested-optional); this lesson does the same with fallibles. Two shapes come up all the time, and both are about keeping two different answers apart.
## The next value, the end, or a failure
`int? ! SensorError` is the shape of "read the next value". Three answers are possible: a value, no more values, or the read itself broke:
```rux
func Reading(sensor: int, index: int) -> int? ! SensorError {
```
Running out is not an error, and a broken sensor is not "no more" — so both levels are needed. Patterns nest to reach each answer:
```rux
match Reading(sensor, index) {
.Success(value?) => Print(" {}", value),
.Success(none) => {
PrintLine(" (end)");
break;
},
.Failure(e) => {
PrintLine(" (sensor {} broke)", e.sensor);
break;
}
}
```
| Pattern | Means |
| ------------------ | -------------------------------------- |
| `.Success(value?)` | the read worked and found a value |
| `.Success(none)` | the read worked and found nothing more |
| `.Failure(e)` | the read itself broke |
Walking a folder in the `FileSystem` package has exactly this shape, as you will see in [Directory](https://rux-lang.dev/docs/learn/directory).
## An answer that can itself fail
`(int ! RangeError) ! SensorError` is the shape of "ask for a result that can itself fail". The outer failure means the question never got an answer. A success holds the answer — and that answer may be a failure of its own: the sensor replied, and what it replied was "out of range".
```rux
if sensor == 2 {
return .Success(.Failure(RangeError { value: 999 }));
}
return .Success(.Success(21));
```
The parentheses are required. A type holds at most one `!` without them, so `int ! RangeError ! SensorError` is refused, and the help line suggests the two groupings — `(T ! E) ! F` or `T ! (E ! F)` — which mean different things.
```mermaid
flowchart LR
test["SelfTest(sensor)"] --> outer{"Outer level:
did the sensor answer?"}
outer -- ".Failure(e)" --> none["no answer
(a SensorError)"]
outer -- ".Success(answer)" --> inner{"Inner level:
what did it answer?"}
inner -- ".Success(value)" --> ok["a reading"]
inner -- ".Failure(e)" --> range["out of range
(a RangeError)"]
```
## Levels never merge
An inner failure is ordinary data inside a success, so `?` or `catch` on the outer level never touches it. Here `catch` handles only the outer failure, and the inner one comes through intact:
```rux
let inner = SelfTest(2) catch { else => .Success(0) };
match inner {
.Success(v) => PrintLine("ok {}", v),
.Failure(e) => PrintLine("inner failure {}", e.value)
}
```
Sensor 2 did answer, so the `catch` has nothing to do, and the program prints `inner failure 999`. The `else` arm's `.Success(0)` would only stand in for an outer failure — and since it replaces an answer, it has to be one, an `int ! RangeError`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/NestedFallible){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Native forms nest, and each level keeps its own meaning. Two shapes come up
// all the time.
//
// `int? ! SensorError` — "read the next value". Three answers are possible:
// a value, no more values (`none`), or the read itself broke (a failure).
// Running out is not an error and a broken sensor is not "no more", so both
// levels are needed. Walking a folder in the `FileSystem` package has exactly
// this shape.
//
// `(int ! RangeError) ! SensorError` — "ask for a result that can itself
// fail". The outer failure means the question never got an answer. A success
// holds the answer, and that answer may be a failure of its own: the sensor
// replied, and what it replied was "out of range".
//
// Levels never merge. An inner failure is ordinary data inside a success, so
// `?` or `catch` on the outer level never touches it. Patterns nest to reach
// every case: `.Success(value?)`, `.Success(none)`, `.Success(.Failure(e))`.
import Io::{ Print, PrintLine };
struct SensorError {
sensor: int;
}
struct RangeError {
value: int;
}
// Sensor 1 logged three readings. Sensor 2 broke while reading its third.
func Reading(sensor: int, index: int) -> int? ! SensorError {
if sensor == 2 && index == 2 {
fail SensorError { sensor: sensor };
}
if index >= 3 {
return none;
}
return 20 + index;
}
// Sensor 1 answers 21, sensor 2 answers with a reading it rejects, and
// sensor 3 does not answer at all.
func SelfTest(sensor: int) -> (int ! RangeError) ! SensorError {
if sensor == 3 {
fail SensorError { sensor: sensor };
}
if sensor == 2 {
return .Success(.Failure(RangeError { value: 999 }));
}
return .Success(.Success(21));
}
func Main() -> int {
for sensor in 1..=2 {
Print("sensor {} log:", sensor);
for index in 0..10 {
match Reading(sensor, index) {
.Success(value?) => Print(" {}", value),
.Success(none) => {
PrintLine(" (end)");
break;
},
.Failure(e) => {
PrintLine(" (sensor {} broke)", e.sensor);
break;
}
}
}
}
for sensor in 1..=3 {
match SelfTest(sensor) {
.Success(.Success(value)) => PrintLine("sensor {} test: reads {}", sensor, value),
.Success(.Failure(e)) => PrintLine("sensor {} test: {} is out of range",
sensor, e.value),
.Failure(e) => PrintLine("sensor {} test: no answer", e.sensor)
}
}
return 0;
}
```
## Run it
```sh
cd Examples/Errors/NestedFallible
rux run
```
```text
sensor 1 log: 20 21 22 (end)
sensor 2 log: 20 21 (sensor 2 broke)
sensor 1 test: reads 21
sensor 2 test: 999 is out of range
sensor 3 test: no answer
```
## Common mistakes
::warning
**Forgetting the end of the data.**:br
Drop the `.Success(none)` arm and the compiler says `error: match on 'int? ! SensorError' is not exhaustive; missing .Success(none)`.
::
::warning
**Treating the outer success as the reading.**:br
In `SelfTest`'s match, `.Success(value)` binds the inner `int ! RangeError`, not an `int`. `value + 1` there is `error: operator '+' cannot combine left operand 'int ! RangeError' with right operand 'int'`. Match one level deeper with `.Success(.Success(value))`.
::
::warning
**Leaving out the parentheses.**:br`int ! RangeError ! SensorError` is `error: a type contains at most one unparenthesized '!'`. Group the level you mean.
::
## Try it yourself
1. Extend the first loop to sensor 3 with `1..=3`. Predict its log line before you run.
2. Make sensor 1's self-test answer 150 and treat anything over 100 as out of range.
3. Write `func FirstReading(sensor: int) -> int? ! SensorError` that returns the reading at index 0 using `?` on the outer level. What type does `Reading(sensor, 0)?` have?
## Learn more
- [Nested optional](https://rux-lang.dev/docs/learn/nested-optional) — the same idea with `T??`
- [Outcome](https://rux-lang.dev/docs/learn/outcome) — `.Success` and `.Failure` as patterns and constructors
- [Iterator](https://rux-lang.dev/docs/learn/iterator) — `T?` as "the next item, or the end"
- [Directory](https://rux-lang.dev/docs/learn/directory) — a real `T? ! E` from the `FileSystem` package
# Panic
::note
**You'll need**: [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Range pattern](https://rux-lang.dev/docs/learn/range-pattern), [Fallible](https://rux-lang.dev/docs/learn/fallible)
::
A fallible `T ! E` is for failures a caller can do something about: a bad number typed by a user, a file that is missing. Some situations are not like that. They cannot happen unless the program itself is wrong, and no caller could sensibly recover. For those there is `Core::Panic(message)`.
## Failures and bugs
The question to ask is: *could this happen in a program with no bugs?*
| | A fallible `T ! E` | `Panic` |
| ----------- | ----------------------------------------------------- | --------------------------- |
| Is for | something the world can do: bad input, a missing file | something only a bug can do |
| The caller | must handle it, and can recover | never sees it |
| The program | carries on | stops at once |
| Cleanup | runs, as on any `return` | does not run |
A month number of 13 typed by a user is a failure: tell them and ask again. A month number of 13 computed by `% 12 + 1` is a bug: the arithmetic is wrong, and nothing the caller could do would fix it.
## What Panic does
`Panic` prints its message and where it was called, on the error stream, then stops the program at once. Nothing unwinds: no cleanup runs, no caller gets a chance to catch it, and the program exits with a failure status. That is on purpose — once something "impossible" has happened, the program's assumptions are broken, and running more of its code would only spread the damage.
This program never panics, but change the month calculation to `start + offset as uint` and the third month is 13. The output then ends like this, and nothing after it runs:
```text
month 11: 30 days
month 12: 31 days
month 13 does not exist
Panic: a month is always 1 to 12
at BadMonth (Src/Main.rux:30:5)
```
Rux panics the same way on its own when an array index is out of range: the program stops with `Panic: index out of range` and the place of the bad subscript.
## Panic never returns
Because `Panic` never returns, it may stand where a value is expected. Every arm of `DaysIn`'s match must produce an `int`, and the `else` arm produces none — it never finishes:
```rux
return match month {
2 => 28,
4 => 30,
6 => 30,
9 => 30,
11 => 30,
1..=12 => 31,
else => BadMonth(month)
};
```
The `1..=12` [range pattern](https://rux-lang.dev/docs/learn/range-pattern) gives every other real month 31 days, so the `else` arm is reached only by a month that does not exist.
## #NoReturn for your own functions
A function of your own can promise the same with the `#NoReturn()` attribute. `BadMonth` uses that to print the offending value, which a plain `Panic` message cannot include, before it panics:
```rux
#NoReturn()
func BadMonth(month: uint) {
PrintLine("month {} does not exist", month);
Panic("a month is always 1 to 12");
}
```
Without the attribute, `BadMonth` looks like an ordinary function that returns nothing, and the `else` arm no longer produces an `int`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Panic){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A fallible `T ! E` is for failures a caller can do something about: a bad
// number typed by a user, a file that is missing. Some situations are not like
// that. They cannot happen unless the program itself is wrong, and no caller
// could sensibly recover. For those there is `Core::Panic(message)`.
//
// `Panic` prints its message and where it was called, then stops the program
// at once. Nothing unwinds: no cleanup runs, no caller gets a chance to catch
// it, and the program exits with a failure status. That is on purpose — once
// something "impossible" has happened, the program's assumptions are broken,
// and running more of its code would only spread the damage.
//
// `Panic` never returns, so it may stand where a value is expected, like the
// `else` arm below. A function of your own can promise the same with the
// `#NoReturn()` attribute. `BadMonth` uses that to print the offending value,
// which a plain `Panic` message cannot include, before it panics.
//
// This program never panics: `% 12 + 1` keeps every month between 1 and 12.
// If `BadMonth` were reached, the output would end like this, and nothing
// after it would run:
//
// month 13 does not exist
// Panic: a month is always 1 to 12
// at BadMonth (Src/Main.rux:30:5)
import Core::Panic;
import Io::PrintLine;
#NoReturn()
func BadMonth(month: uint) {
PrintLine("month {} does not exist", month);
Panic("a month is always 1 to 12");
}
func DaysIn(month: uint) -> int {
return match month {
2 => 28,
4 => 30,
6 => 30,
9 => 30,
11 => 30,
1..=12 => 31,
else => BadMonth(month)
};
}
func Main() -> int {
// A loan that starts in November, counted month by month.
let start: uint = 11;
var total = 0;
for offset in 0..4 {
let month = (start - 1 + offset as uint) % 12 + 1;
let days = DaysIn(month);
total += days;
PrintLine("month {:2}: {} days", month, days);
}
PrintLine("total: {} days", total);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Errors/Panic
rux run
```
```text
month 11: 30 days
month 12: 31 days
month 1: 31 days
month 2: 28 days
total: 120 days
```
## Common mistakes
::warning
**Forgetting #NoReturn.**:br
Remove the attribute and `DaysIn` stops compiling: `error: match arm type mismatch: expected 'int', found '()'`. The compiler no longer knows that `BadMonth` never comes back.
::
::warning
**Panicking on bad input.**:br
Input from a user, a file or the network can be wrong in a working program. Report that with a fallible so the caller can recover; keep `Panic` for the cases only a bug can reach.
::
::warning
**Expecting cleanup to run.**:br
A panic stops the program at once. A `defer` that would run on a `return` or a failed `?` does not run on a panic.
::
## Try it yourself
1. Change the month calculation to `start + offset as uint` and run the program. Compare the output with the listing above.
2. Make `DaysIn` panic for month 0 with a message of its own, before the `else` arm.
3. Write a `#NoReturn()` function `Unreachable(where: char8[..])` that prints `where` and panics, and use it in another `match`.
## Learn more
- [Panic](https://rux-lang.dev/docs/api/core/panic) in the API reference
- [Fatal errors](https://rux-lang.dev/docs/lang/errors/panics) and [NoReturn](https://rux-lang.dev/docs/lang/attributes/noreturn) in the Rux Reference
- [Assert](https://rux-lang.dev/docs/learn/assert) — a panic with a condition attached
- [Fallible](https://rux-lang.dev/docs/learn/fallible) — for failures a caller can handle
# Assert
::note
**You'll need**: [Panic](https://rux-lang.dev/docs/learn/panic), [Slice](https://rux-lang.dev/docs/learn/slice)
::
An *assertion* states something the program relies on, and checks it while the program runs. It is a [panic](https://rux-lang.dev/docs/learn/panic) with a condition attached: when the condition holds, nothing happens; when it does not, the program stops.
Assertions are for the same kind of problem as `Panic` — a bug, not bad input. They make an assumption visible in the code, and make sure that if it is ever wrong, the program says so at once instead of carrying on with nonsense.
## Assert
The median of a list is its middle score, and an empty list has no middle:
```rux
Assert(scores.length > 0, "a median needs at least one score");
```
Call `Median` with an empty slice and the program stops exactly like a `Panic`, reporting the message and where the assertion is:
```text
Assertion failed: a median needs at least one score
at Median (Src/Main.rux:43:5)
```
Write the message as what was *expected*, since the failing value is not printed. "a median needs at least one score" tells the reader the rule that was broken; "empty list" would only describe the symptom.
## DebugAssert
`Core` has two assertions, and the difference is the build:
```rux
DebugAssert(InOrder(scores), "the scores are sorted");
```
| | `Assert` | `DebugAssert` |
| ----------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------- |
| Debug build (`rux run`) | checked | checked |
| Release build (`rux run --release`) | checked | removed — the condition is not evaluated |
| Use it for | a promise that must hold in the program you ship | a check worth making while developing, too slow to pay for when shipped |
`InOrder` is a whole pass over the scores. That is worth doing while developing, but it would turn a cheap lookup into a slow one on every call in a shipped program — so it sits in a `DebugAssert`.
## Removed means not evaluated
`InOrder` prints a line so you can see the difference. Run the program both ways and the line appears only in the debug build — in a release build the call never happens at all.
That has two consequences. An expensive check costs nothing when shipped. And a condition with a side effect loses that effect: anything a `DebugAssert`'s condition *does* is gone from the release build.
It also means a release build no longer catches the mistake the check was there for. Give `Median` the unsorted scores `[3, 8, 5, 13, 21]`, and the debug build stops with `Assertion failed: the scores are sorted`, while the release build quietly prints `median: 5`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Errors/Assert){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An assertion states something the program relies on, and checks it while
// the program runs:
//
// Assert(scores.length > 0, "a median needs at least one score");
//
// When the condition holds, nothing happens. When it does not, the program
// stops exactly like a `Panic`, reporting the message and where it was:
//
// Assertion failed: a median needs at least one score
// at Median (Src/Main.rux:43:5)
//
// Write the message as what was expected, since the failing value is not
// printed.
//
// `Core` has two of them, and the difference is the build:
//
// - `Assert` is checked in every build. Use it for a promise that must hold
// in the program you ship.
// - `DebugAssert` is checked in a debug build (`rux run`) and removed from a
// release build (`rux run --release`). Removed means its condition is not
// even evaluated, so an expensive check costs nothing when shipped — and a
// condition with a side effect loses that effect.
//
// `InOrder` below prints a line so you can see that difference: run the
// program both ways and the line appears only in the debug build.
import Core::{ Assert, DebugAssert };
import Io::PrintLine;
// A whole pass over the scores: worth checking while developing, too slow to
// pay for on every call in a shipped program.
func InOrder(scores: int[..]) -> bool {
PrintLine("(checking that the scores are in order)");
for i in 1..scores.length {
if scores[i - 1] > scores[i] {
return false;
}
}
return true;
}
// The middle score of a sorted list.
func Median(scores: int[..]) -> int {
Assert(scores.length > 0, "a median needs at least one score");
DebugAssert(InOrder(scores), "the scores are sorted");
return scores[scores.length / 2];
}
func Main() -> int {
let scores = [3, 5, 8, 13, 21];
PrintLine("median: {}", Median(scores));
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Errors/Assert
rux run
```
```text
(checking that the scores are in order)
median: 8
```
A release build drops the `DebugAssert`, so its check never runs:
```sh
rux run --release
```
```text
median: 8
```
## Common mistakes
::warning
**Work inside a DebugAssert.**:br
A condition such as `DebugAssert(Load(settings), "...")` does its loading in a debug build and not at all in a release build. Do the work first, store the result, and assert on the stored value.
::
::warning
**A condition that is not a bool.**:br`Assert(scores.length, "...")` is `error: argument 1 to 'Assert' has type 'uint64', but parameter 'condition' requires 'bool8'`. Write the comparison out: `scores.length > 0`.
::
::warning
**Asserting on user input.**:br
Like `Panic`, an assertion is for bugs. Input that can be wrong in a working program deserves a fallible and a helpful message, not a stopped program.
::
## Try it yourself
1. Call `Median(scores[0..0])` — an empty slice of the scores — and read the assertion message.
2. Change the scores to `[3, 8, 5, 13, 21]` and run with `rux run` and `rux run --release`. Compare the two.
3. Add an `Assert` that every score is between 0 and 100, and decide whether it should be an `Assert` or a `DebugAssert`.
## Learn more
- [Assert](https://rux-lang.dev/docs/api/core/assert) and [#build](https://rux-lang.dev/docs/api/core/build) in the API reference
- [Build mode](https://rux-lang.dev/docs/learn/build-mode) — debug and release builds, and how a program can tell them apart
- [rux run](https://rux-lang.dev/docs/cli/run) — the `--release` flag
- [Panic](https://rux-lang.dev/docs/learn/panic) — the same stop, without a condition
# Part 10: Sum types
A setting in a configuration file is a number, a switch or a word. A token from a tokenizer is a number, a name or an operator. Rux writes "one of these types" directly in the type: `int32 | bool | char8[..]`. The value always knows which member it holds, and the compiler will not let you treat it as any one of them until you have asked. This part shows how to build such a value, how to ask, and how sums of different sizes fit together.
## What you will learn
- What `A | B` means, and why it is a *set* of types: order and duplicates make no difference, and `int32 | int32` is plain `int32`.
- How a value goes into a sum, and when a literal is ambiguous.
- Matching a sum by the type it holds with a typed pattern, `n: int32 =>`, and covering every member.
- Handling several members in one arm with a subset pattern, `box: Square | Rectangle =>`, and passing the smaller sum on.
- Asking which member is active with `is`, which answers with a `bool` and never unpacks anything.
- Widening: a smaller sum fits wherever a larger one is expected, but never the other way round.
## Into a sum and back out
```mermaid
flowchart LR
m["A member value
Square { side: 2.0 }"] -- "goes in" --> s(["Circle | Square | Rectangle"])
small["A smaller sum
Square | Rectangle"] -- "widening" --> s
s --> t["typed pattern
s: Square =>"]
s --> sub["subset pattern
box: Square | Rectangle =>"]
s --> is["is
shape is Circle"]
t --> t2["one member,
as its own type"]
sub --> sub2["a smaller sum"]
is --> is2["a bool, nothing unpacked"]
```
| You want to… | Use | Lesson |
| ---------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------- |
| use the value inside | `match` with `n: int32 =>` | [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) |
| handle several members alike | `match` with `box: Square | Rectangle =>` | [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern) |
| only know which member it is | `value is T`, `value is (A | B)` | [The is operator](https://rux-lang.dev/docs/learn/is) |
| pass a smaller sum to a larger parameter | nothing — it widens by itself | [Sum widening](https://rux-lang.dev/docs/learn/sum-widening) |
## Lessons
| | Lesson | What you will learn |
| ---- | ---------------------------------------------------------------- | ---------------------------------------------------------- |
| 10.1 | [Sum type](https://rux-lang.dev/docs/learn/sum-type) | a value that can be an `int32` or a `bool`: `int32 | bool` |
| 10.2 | [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) | match a sum by the type it holds |
| 10.3 | [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern) | match several members of a sum in one arm |
| 10.4 | [The is operator](https://rux-lang.dev/docs/learn/is) | ask which type a sum holds with `is` |
| 10.5 | [Sum widening](https://rux-lang.dev/docs/learn/sum-widening) | pass a smaller sum where a larger one is expected |
## Before you start
Finish Parts 1–9 first. This part leans on [Type alias](https://rux-lang.dev/docs/learn/type-alias), [Variant](https://rux-lang.dev/docs/learn/variant) and [Variant match](https://rux-lang.dev/docs/learn/variant-match) from Part 6 — a sum is matched much as a variant is — and on [Error sum](https://rux-lang.dev/docs/learn/error-sum) from Part 9, where you first wrote `A | B` as a function's error type. Each lesson's package is in the Examples repository's `SumTypes/` folder:
```sh
cd Examples/SumTypes/SumType
rux run
```
## After this part
[Part 11: Ownership](https://rux-lang.dev/docs/learn/ownership) turns from what a value *is* to who *owns* it — copies, moves, destructors and `defer`. Sums come back in [Part 13: Generics](https://rux-lang.dev/docs/learn/generics), where [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) builds `T | U` from type parameters and the "a sum is a set" rule decides what `T | U` becomes when `T` and `U` are the same type. The next checkpoint projects, [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic), come after Part 16.
For the rules behind this part, see [`match`](https://rux-lang.dev/docs/lang/patterns/match), [Type tests](https://rux-lang.dev/docs/lang/sums/type-tests) and [Type aliases](https://rux-lang.dev/docs/lang/types/aliases) in the Rux Reference.
# Sum type
::note
**You'll need**: [Type alias](https://rux-lang.dev/docs/learn/type-alias), [Variant](https://rux-lang.dev/docs/learn/variant), [Error sum](https://rux-lang.dev/docs/learn/error-sum)
::
A *sum type* holds one value whose type is one of a short list. `int32 | bool` reads "an `int32` or a `bool`": a variable of that type can hold `8080` now and `true` later, and it always knows which of the two it is holding.
You have met the idea twice already. A [variant](https://rux-lang.dev/docs/learn/variant) holds one of several cases, and an [error sum](https://rux-lang.dev/docs/learn/error-sum) let a function fail in two unrelated ways. A sum type is the general form: any types at all, joined with `|`. This lesson builds sums, stores them and compares them. Getting a member back out takes a pattern, and that is the [next lesson](https://rux-lang.dev/docs/learn/typed-pattern).
## A value of one of several types
A setting in a configuration file might be a number, a switch or a word. A [type alias](https://rux-lang.dev/docs/learn/type-alias) gives that sum a short name — it is still exactly `int32 | bool | char8[..]`, just easier to write:
```rux
type Setting = int32 | bool | char8[..];
```
A value of any member type goes straight into the sum, with nothing to wrap it in:
```rux
let port: Setting = 8080;
let verbose: Setting = true;
let name: Setting = "server";
```
The value picks the member. `8080` is an integer, and `Setting` has exactly one integer member, so `port` holds an `int32`. `true` can only be the `bool`, and `"server"` only the text.
```mermaid
flowchart LR
v["A value meets
a sum type"] --> q{"Is its type
a member?"}
q -- "yes" --> ok["That member
becomes active"]
q -- "an unsuffixed literal" --> lit{"How many members
of its kind?"}
lit -- "exactly one" --> ok
lit -- "several" --> amb["error: integer literal
is ambiguous"]
q -- "no" --> err["error: cannot assign
'float64' to …"]
```
## Switching members
A `var` sum can change its member, not only its value. Each assignment replaces both the value and the record of which member is active:
```rux
var limit: int32 | bool = 100;
limit = false;
limit = 250;
```
`limit` starts as the number 100, becomes the switch `false`, and ends as the number 250. A plain `int32` variable could never hold `false`, and a `bool` could never hold 250; the sum can hold either.
## A set of types
A variant names its cases. A sum names only types, so its list is a **set**: the order the types are written in does not matter, and a type written twice counts once. These are three spellings of one type:
```rux
let reordered: bool | int32 = limit;
let repeated: int32 | bool | int32 = reordered;
```
No conversion happens on either line, because there is nothing to convert — `limit`, `reordered` and `repeated` all have the same type. The compiler even writes every sum in its own sorted order in error messages, so `int32 | bool` appears there as `bool8 | int32` (`bool8` is `bool`'s full name).
The same rule makes a sum of one type just that type. `int32 | int32` collapses to `int32`, so it takes part in arithmetic like any other integer:
```rux
let single: int32 | int32 = 20;
PrintLine("single + 1 {}", single + 1);
```
| Written | Is the type |
| ---------------------- | -------------- |
| `int32 | bool` | `int32 | bool` |
| `bool | int32` | the same type |
| `int32 | bool | int32` | the same type |
| `int32 | int32` | plain `int32` |
That collapse looks like a curiosity now. It matters in [Generic sum](https://rux-lang.dev/docs/learn/generic-sum), where `T | U` is written before anyone knows whether `T` and `U` are the same type.
## Comparing sums
Two sums are equal when the same member is active and the values agree:
```rux
let off: int32 | bool = false;
let zero: int32 | bool = 0;
PrintLine("limit == 250 {}", limit == 250);
PrintLine("repeated == limit {}", repeated == limit);
PrintLine("off == zero {}", off == zero);
```
`off == zero` is `false`. A switch and a number are never equal, whatever their bits look like — `false` and `0` may well be the same byte in memory, but the sum remembers that one is a `bool` and the other an `int32`.
## Sum or variant?
Both hold one of several things. The difference is what the alternatives are called:
| | Variant | Sum |
| -------------------- | ------------------------------- | -------------------------------- |
| Declared as | `variant Reading { … }` | `int32 | bool`, no declaration |
| Alternatives are | named cases, such as `.Exact` | types, such as `int32` |
| Two of the same type | allowed, as two different cases | impossible — duplicates collapse |
| Matched with | `.Exact(value) =>` | `value: int32 =>` |
Reach for a variant when the cases *mean* different things even if they carry the same type — a temperature in Celsius and one in Fahrenheit are both `float64`. Reach for a sum when the types themselves are the difference, as with a setting that is a number, a switch or a word.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/SumTypes/SumType){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A sum type holds one value whose type is one of a short list. `int32 | bool` is "an int32 or
// a bool": a variable of that type can hold 8080 now and `true` later, and it always knows which
// of the two it is holding.
//
// You have met this shape before. A variant also holds one of several things, but it names its
// cases. A sum names only types, so its list is a *set* of types: the order they are written in
// does not matter, and a type written twice counts once. `int32 | bool`, `bool | int32` and
// `int32 | bool | int32` are three spellings of one type.
//
// Getting a member back out takes a pattern, and that is the next lesson. Here a sum is only
// built, stored, reassigned and compared.
import Io::PrintLine;
// A setting in a configuration file is a number, a switch or a word. A type alias gives the sum
// a short name; it is still exactly `int32 | bool | char8[..]`.
type Setting = int32 | bool | char8[..];
func Main() -> int {
// A value of any member type goes straight into the sum. The value picks the member: 8080
// is an integer, and the sum has exactly one integer member to put it in.
let port: Setting = 8080;
let verbose: Setting = true;
let name: Setting = "server";
// A `var` sum can switch members. Each assignment replaces both the value and the record of
// which member is active.
var limit: int32 | bool = 100;
limit = false;
limit = 250;
// Order and duplicates do not make a new type, so these copies need no conversion at all.
let reordered: bool | int32 = limit;
let repeated: int32 | bool | int32 = reordered;
// Two sums are equal when the same member is active and the values agree. A number and a
// switch are never equal, whatever their bits look like.
let off: int32 | bool = false;
let zero: int32 | bool = 0;
PrintLine("limit == 250 {}", limit == 250);
PrintLine("repeated == limit {}", repeated == limit);
PrintLine("off == zero {}", off == zero);
// A sum of one type is just that type: `int32 | int32` collapses to `int32`, so it takes
// part in arithmetic like any other integer.
let single: int32 | int32 = 20;
PrintLine("single + 1 {}", single + 1);
// The other settings are stored and ready, but printing one needs its member first.
// `PrintLine("{}", port)` is rejected: a sum is not a number, even when it holds one.
return 0;
}
```
## Run it
```sh
cd Examples/SumTypes/SumType
rux run
```
```text
limit == 250 true
repeated == limit true
off == zero false
single + 1 21
```
## Common mistakes
::warning
**Using a sum as if it were its member.**:br
A sum is not a number, even while it holds one. `PrintLine("{}", port)` fails with `error: argument 2 to 'PrintLine' has type 'bool8 | char8[..] | int32', but variadic parameter 'args' requires 'Display'`, and with `let x: int32 | bool = 3;` the sum `x + 1` fails with `error: operator '+' cannot combine left operand 'bool8 | int32' with right operand 'int'`. Take the member out with a [typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) first.
::
::warning
**A literal that fits more than one member.**:br`let s: int32 | int64 = 5;` fails with `error: integer literal is ambiguous for 'int32 | int64', which has several integer members; add a suffix or a cast`. The compiler will not guess which integer you meant. Write `5i64`, or `5 as int32`.
::
::warning
**A value whose type is not a member.**:br`let s: int32 | bool = 2.5;` fails with `error: cannot assign 'float64' to 'bool8 | int32'`. A sum holds its members and nothing else — no conversion is tried.
::
::warning
**Comparing a sum with a plain value.**:br`limit == false` fails with `error: operator '==' cannot compare 'bool8 | int32' with 'bool8'`, and the compiler's note explains why: a comparison never injects, widens, or wraps an operand. Give the value the sum's type first, as the program does with `let off: int32 | bool = false;`, and compare two sums.
::
## Try it yourself
1. Add `float64` to `Setting` and store `let ratio: Setting = 0.75;`. Why is `0.75` not ambiguous, when an integer literal would be for `int32 | int64`?
2. Declare `let wide: int32 | int64 = 5;`, read the error, then fix it with a suffix.
3. Declare `let alsoOff: bool | int32 = false;` and print `off == alsoOff`. The two types are spelled differently — can they be compared?
4. Try `PrintLine("{}", port);` and read which parameter type the sum fails to satisfy.
## Learn more
- [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) — getting a member back out of a sum
- [Variant](https://rux-lang.dev/docs/learn/variant) and [Error sum](https://rux-lang.dev/docs/learn/error-sum) — the two forms you met before this one
- [Type aliases](https://rux-lang.dev/docs/lang/types/aliases) in the Rux Reference
# Typed pattern
::note
**You'll need**: [Sum type](https://rux-lang.dev/docs/learn/sum-type), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Match expression](https://rux-lang.dev/docs/learn/match-expression)
::
A sum knows which member it holds, and a *typed pattern* asks it. The arm `n: int32 =>` matches only when the active member is `int32`, and binds that value to `n` **as an `int32`** — so inside the arm it is an ordinary number again. It prints, it adds, it compares.
In the [last lesson](https://rux-lang.dev/docs/learn/sum-type) a sum could be built, stored and compared, but not printed or used in arithmetic. This lesson is the way back out.
## One arm per member
A [variant match](https://rux-lang.dev/docs/learn/variant-match) arm names a case: `.Exact(value) =>`. A sum has no case names, so its arm names a **type** instead — the binding, a colon, the member type:
```rux
type Setting = int32 | bool | char8[..];
func Show(key: char8[..], value: Setting) {
match value {
seconds: int32 => PrintLine("{:8} = {} ms", key, seconds * 1000),
flag: bool => PrintLine("{:8} = {}", key, flag ? "on" : "off"),
word: char8[..] => PrintLine("{:8} = \"{}\"", key, word)
}
}
```
Each binding has its member's own type. `seconds` is an `int32`, so `seconds * 1000` is integer arithmetic. `flag` is a `bool`, so it can drive the [ternary](https://rux-lang.dev/docs/learn/ternary). `word` is a text slice, ready for a placeholder.
```mermaid
flowchart LR
v["value: Setting"] --> q{"Which member
is active?"}
q -- "int32" --> a1["seconds: int32 =>
seconds * 1000"]
q -- "bool" --> a2["flag: bool =>
on or off"]
q -- "char8[..]" --> a3["word: char8[..] =>
print the word"]
```
Exactly one arm runs, the one whose type is the active member. The order of the arms does not change which one that is — a member can only ever match its own arm.
## A match that produces a value
As an expression, a match on a sum produces one value from whichever arm ran, just as a [match expression](https://rux-lang.dev/docs/learn/match-expression) on a number does:
```rux
func Weight(value: Setting) -> int32 {
return match value {
n: int32 => n,
flag: bool => flag ? 1 : 0,
_: char8[..] => -1
};
}
```
All three arms produce an `int32`, so the whole `match` is an `int32`, whichever member came in.
## Matching without a binding
The text arm in `Weight` needs no value from its member — only the fact that the setting is text. `_: char8[..]` matches the type without naming it. Use it whenever an arm would otherwise bind a name it never reads.
| Pattern | Matches when the active member is | Binds |
| -------------- | --------------------------------- | --------------- |
| `n: int32` | `int32` | `n`, an `int32` |
| `_: char8[..]` | `char8[..]` | nothing |
| `else` | anything earlier arms left | nothing |
## Every member must be covered
A match on a sum must handle every member, just as a variant match handles every case. Leave out an arm and the compiler names the pattern you still need. Without the `int32` arm in `Show`:
```text
error: match on 'bool8 | char8[..] | int32' is not exhaustive; missing _: int32
```
The compiler spells the sum in its own sorted order, and `bool` by its full name `bool8`, so the members may not appear in the order you wrote them.
When several members are handled alike, an `else` arm covers whatever the earlier arms left:
```rux
match value {
n: int32 => PrintLine("number {}", n),
else => PrintLine("something else")
}
```
That works, but it gives something up: add a fourth member to `Setting` later and this match quietly sends it to `else`. With one arm per member, the compiler stops you and points at every match that needs the new arm. The [next lesson](https://rux-lang.dev/docs/learn/subset-pattern) shows a middle way — one arm for a chosen group of members.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/SumTypes/TypedPattern){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A sum knows which member it holds, and a typed pattern asks it. The arm `n: int32 =>` matches
// only when the active member is `int32`, and binds that value to `n` *as an int32*, so inside
// the arm it is an ordinary number again: it prints, it adds, it compares.
//
// A variant arm names a case, `.Exact(value) =>`. A sum has no case names, so its arm names a
// type instead: the binding, a colon, the member type. `_: char8[..] =>` matches the member
// without binding it, for when only the fact matters.
//
// The match must cover every member, just as a variant match covers every case. Leave out the
// `int32` arm below and the compiler names the pattern you still need:
// error: match on 'bool8 | char8[..] | int32' is not exhaustive; missing _: int32
// (The compiler spells the sum in its own sorted order, and `bool` by its full name `bool8`.)
import Io::PrintLine;
type Setting = int32 | bool | char8[..];
// One arm per member. Each binding has the member's own type, so `seconds * 1000` is integer
// arithmetic and `word` is a text slice ready for a placeholder.
func Show(key: char8[..], value: Setting) {
match value {
seconds: int32 => PrintLine("{:8} = {} ms", key, seconds * 1000),
flag: bool => PrintLine("{:8} = {}", key, flag ? "on" : "off"),
word: char8[..] => PrintLine("{:8} = \"{}\"", key, word)
}
}
// As an expression, a match on a sum produces one value from whichever arm ran. Here the text
// arm needs no value from its member, so `_` matches the type without naming it.
func Weight(value: Setting) -> int32 {
return match value {
n: int32 => n,
flag: bool => flag ? 1 : 0,
_: char8[..] => -1
};
}
func Main() -> int {
let timeout: Setting = 30;
let verbose: Setting = true;
let mode: Setting = "fast";
Show("timeout", timeout);
Show("verbose", verbose);
Show("mode", mode);
PrintLine("weights {} {} {}", Weight(timeout), Weight(verbose), Weight(mode));
return 0;
}
```
## Run it
```sh
cd Examples/SumTypes/TypedPattern
rux run
```
```text
timeout = 30000 ms
verbose = on
mode = "fast"
weights 30 1 -1
```
## Common mistakes
::warning
**Leaving out a member.**:br
A match on a sum must be exhaustive. Drop the `int32` arm from `Show` and it fails with `error: match on 'bool8 | char8[..] | int32' is not exhaustive; missing _: int32`.
::
::warning
**Naming a type that is not a member.**:br`n: int64 =>` on a `Setting` fails with `error: type 'int64' is not a member or subset of sum 'bool8 | char8[..] | int32'`. The pattern must name the member exactly — an `int32` member is not matched by a wider integer type.
::
::warning
**Writing only the type.**:br
An arm `int32 => "number"` fails with `error: pattern 'int32' cannot bind a new variable because 'int32' already names a type`. A bare name in a pattern is a new binding, and a type name cannot be one. Write `_: int32 =>`, or `n: int32 =>` if you need the value.
::
## Try it yourself
1. Add `float64` to `Setting` and run the program. Read the error, then add a `float64` arm to both `Show` and `Weight`.
2. Write `func Kind(value: Setting) -> char8[..]` that returns `"number"`, `"switch"` or `"text"`. Which patterns need no binding at all?
3. Rewrite `Show` with one `int32` arm and an `else` arm. Then add a fourth member to `Setting` and compare: which version of `Show` makes the compiler complain?
## Learn more
- [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern) — one arm for several members
- [Variant match](https://rux-lang.dev/docs/learn/variant-match) — the same idea for named cases
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
# Subset pattern
::note
**You'll need**: [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern), [Struct](https://rux-lang.dev/docs/learn/struct)
::
A typed pattern can name more than one member. The arm `box: Square | Rectangle =>` matches when the active member is either of the two, and `box` is then a **smaller sum**: still one of several types, but only the ones the arm let through.
That is useful when some members are handled the same way. A circle has no corners, while a square and a rectangle both have four — so counting corners needs two arms, not three. And when the shared members still need work of their own, the smaller sum can be handed on whole to a function that takes exactly those members.
## The shapes
Each shape is a [struct](https://rux-lang.dev/docs/learn/struct) of its own, and `Shape` is the sum of all three:
```rux
struct Circle {
radius: float64;
}
struct Square {
side: float64;
}
struct Rectangle {
width: float64;
height: float64;
}
type Shape = Circle | Square | Rectangle;
```
## Several members in one arm
Write the members after the colon, joined by `|` just as in the type itself:
```rux
func Corners(shape: Shape) -> int32 {
return match shape {
_: Circle => 0,
_: Square | Rectangle => 4
};
}
```
A subset needs no binding either: `_: Square | Rectangle` only asks the question. The match is still exhaustive — between them, the two arms cover all three members.
## The binding is a smaller sum
With a name instead of `_`, the arm binds what it matched. Here `box` has the type `Square | Rectangle`:
```rux
func Describe(shape: Shape) {
match shape {
c: Circle => PrintLine("circle, radius {}, {} corners", c.radius, Corners(shape)),
box: Square | Rectangle => PrintLine("box, area {}, {} corners", BoxArea(box),
Corners(shape))
}
}
```
`box` is not a square, and it is not a rectangle — it is whichever of the two `shape` held, and it remembers which. That is exactly what `BoxArea` takes, so `box` is passed on whole:
```rux
func BoxArea(box: Square | Rectangle) -> float64 {
return match box {
s: Square => s.side * s.side,
r: Rectangle => r.width * r.height
};
}
```
`BoxArea` only ever sees boxes, so it matches two members and no more. It never has to say what to do with a circle, because a circle cannot reach it.
```mermaid
flowchart LR
s["shape: Shape"] --> d{"Describe"}
d -- "c: Circle" --> c["radius, 0 corners"]
d -- "box: Square | Rectangle" --> b{"BoxArea(box)"}
b -- "s: Square" --> sq["side * side"]
b -- "r: Rectangle" --> re["width * height"]
```
| Pattern | Matches when the active member is | The binding's type |
| ------------------------- | --------------------------------- | -------------------- |
| `c: Circle` | `Circle` | `Circle` |
| `box: Square | Rectangle` | `Square` or `Rectangle` | `Square | Rectangle` |
| `_: Square | Rectangle` | `Square` or `Rectangle` | nothing is bound |
## The first matching arm wins
Arms are tried from the top, and the first one whose types include the active member runs. Two subsets may share a member — with `round: Circle | Square =>` above `box: Square | Rectangle =>`, a square goes to the first arm and a rectangle to the second.
What the compiler refuses is an arm that can *never* run. Once `box: Square | Rectangle` has taken every square, a later `s: Square =>` would be dead code, and it is an error rather than a silent leftover.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/SumTypes/SubsetPattern){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A typed pattern can name more than one member. The arm `box: Square | Rectangle =>` matches
// when the active member is either of the two, and `box` is then a *smaller sum*: it is still
// one of several types, but only the ones the arm let through.
//
// That is useful when some members are handled the same way. A circle has no corners and a
// square and a rectangle both have four, so the corner count needs two arms, not three. And when
// the shared members need their own work, the smaller sum can be handed on whole to a function
// that takes exactly those members, and matched again there.
//
// An arm after a subset may not repeat a member the subset already took. Add `s: Square =>`
// after the `box` arm in `Describe` and the compiler refuses the arm that can never run:
// error: match arm is unreachable because earlier arms already match every value it matches
import Io::PrintLine;
struct Circle {
radius: float64;
}
struct Square {
side: float64;
}
struct Rectangle {
width: float64;
height: float64;
}
type Shape = Circle | Square | Rectangle;
// A subset needs no binding either: `_: Square | Rectangle` only asks the question.
func Corners(shape: Shape) -> int32 {
return match shape {
_: Circle => 0,
_: Square | Rectangle => 4
};
}
// This function only ever sees boxes, so it matches two members and no more.
func BoxArea(box: Square | Rectangle) -> float64 {
return match box {
s: Square => s.side * s.side,
r: Rectangle => r.width * r.height
};
}
func Describe(shape: Shape) {
match shape {
c: Circle => PrintLine("circle, radius {}, {} corners", c.radius, Corners(shape)),
box: Square | Rectangle => PrintLine("box, area {}, {} corners", BoxArea(box),
Corners(shape))
}
}
func Main() -> int {
let wheel: Shape = Circle { radius: 1.5 };
let tile: Shape = Square { side: 2.0 };
let door: Shape = Rectangle { width: 1.0, height: 2.5 };
Describe(wheel);
Describe(tile);
Describe(door);
return 0;
}
```
## Run it
```sh
cd Examples/SumTypes/SubsetPattern
rux run
```
```text
circle, radius 1.5, 0 corners
box, area 4.0, 4 corners
box, area 2.5, 4 corners
```
## Common mistakes
::warning
**An arm that an earlier subset already covers.**:br
Add `s: Square =>` after the `box` arm in `Describe` and it fails with `error: match arm is unreachable because earlier arms already match every value it matches`. Every square has already gone to `box`. Either delete the arm, or move it above the subset so squares are handled on their own first.
::
::warning
**Reading a field through a smaller sum.**:br
Inside the `box` arm, `box.side` fails with `error: type 'Rectangle | Square' has no field 'side'`. A rectangle has no `side`, and `box` might be one. Match `box` again, as `BoxArea` does, to reach the fields of each member.
::
## Try it yourself
1. Add `struct Triangle { base: float64; height: float64; }` to `Shape`. Which matches does the compiler now reject, and what does each one need?
2. Write `func IsRound(shape: Shape) -> bool` with exactly two arms.
3. Move `s: Square =>` above the `box` arm in `Describe`, so squares print differently from rectangles. Which members can still reach `box` now?
## Learn more
- [The is operator](https://rux-lang.dev/docs/learn/is) — ask which member a sum holds without unpacking it
- [Sum widening](https://rux-lang.dev/docs/learn/sum-widening) — passing a smaller sum where a larger one is expected
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
# The is operator
::note
**You'll need**: [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern), [For](https://rux-lang.dev/docs/learn/for), [Array](https://rux-lang.dev/docs/learn/array)
::
Sometimes a program only needs to know *which* member a sum holds, not what is inside it. Is this token a number? Is it an operator? `value is T` answers with a plain `bool`: `true` when the active member is `T`.
A [typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) can answer the same question, but it takes a whole `match` with an arm for every member. `is` is the one-line version for when a yes or no is all you want.
## The tokens
A tokenizer turns the text `price + 2 - discount + 1` into pieces, and each piece is one of four types:
```rux
struct Number {
value: int32;
}
struct Name {
text: char8[..];
}
struct Plus {}
struct Minus {}
type Token = Number | Name | Plus | Minus;
```
`Plus` and `Minus` carry no fields at all — for them, the type *is* the whole message.
The [array](https://rux-lang.dev/docs/learn/array) is annotated `Token[7]`, which makes every element a `Token`, so one array holds all four member types side by side:
```rux
let tokens: Token[7] = [
Name { text: "price" },
Plus {},
Number { value: 2 },
Minus {},
Name { text: "discount" },
Plus {},
Number { value: 1 }
];
```
## Asking which member
Inside the [loop](https://rux-lang.dev/docs/learn/for), `is` sorts the tokens without unpacking any of them:
```rux
for token in tokens {
if token is Number {
numbers++;
}
if token is (Plus | Minus) {
operators++;
}
}
```
`token is Number` is `true` for the two numbers. Put a smaller sum in parentheses, `token is (Plus | Minus)`, and the test is `true` when the active member is any one of them — the three operators here. It is the `is` form of a [subset pattern](https://rux-lang.dev/docs/learn/subset-pattern).
The parentheses are required. Without them, `token is Plus | Minus` is refused, and the compiler's help shows the grouped form to write instead.
## An ordinary bool
The result is a `bool` like any other, so it can be stored, printed or combined with `&&` and `||`:
```rux
let startsWithName = tokens[0] is Name;
PrintLine("starts with a name: {}", startsWithName);
PrintLine("ends with a number: {}", tokens[6] is Number);
```
## What is does not do
`is` only asks. Two things follow from that.
**It does not narrow.** Inside `if token is Number { … }`, `token` is still the whole `Token`, not a `Number`. Reading `token.value` there is rejected, because a `Name`, a `Plus` or a `Minus` has no `value`. To use what is inside, bind it with a typed pattern in a `match`.
**It does not quietly answer `false`.** A test that could never be true is a mistake, and the compiler says so. `token is bool` is refused because `bool` is not a member of `Token`. On a value that is not a sum at all, `is` compares exact types, and a mismatch is an error too.
```mermaid
flowchart LR
q{"Do you need the
value inside?"} -- "no — only which member" --> is["value is T
value is (A | B)"]
q -- "yes" --> m["match with a typed pattern
n: Number => n.value"]
```
| You want | Write | You get |
| ------------------------------- | -------------------------------- | --------------- |
| to know the member | `token is Number` | a `bool` |
| to know if it is one of several | `token is (Plus | Minus)` | a `bool` |
| the value inside | `match token { n: Number => … }` | `n`, a `Number` |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/SumTypes/Is){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Sometimes a program only needs to know *which* member a sum holds, not what is inside it.
// `value is T` answers that with a plain `bool`: true when the active member is `T`. Put a smaller
// sum in parentheses, `value is (Plus | Minus)`, and it is true when the active member is any one
// of them. The parentheses are required: `token is Plus | Minus` is refused with
// error: a sum type after 'is' must be grouped
// help: write 'value is (Plus | Minus)'
//
// Two things `is` does not do:
//
// - It does not narrow. Inside `if token is Number { ... }`, `token` is still the whole sum, so
// `token.value` is rejected:
// error: type 'Minus | Name | Number | Plus' has no field 'value'
// To use what is inside, bind it with a typed pattern in a `match`.
//
// - It does not quietly answer `false`. A test that could never be true is a mistake, and the
// compiler says so. `token is bool` is refused because `bool` is not one of the members:
// error: type 'bool8' is not a member or subset of sum 'Minus | Name | Number | Plus'
// On a value that is not a sum at all, `is` compares exact types, and a mismatch is an error
// too. The counter `numbers` is an `int`, so `numbers is bool` gives
// error: 'is bool8' can never be true for a value of type 'int'
import Io::PrintLine;
// The pieces of the expression `price + 2 - discount + 1`, as a tokenizer might hand them over.
struct Number {
value: int32;
}
struct Name {
text: char8[..];
}
struct Plus {}
struct Minus {}
type Token = Number | Name | Plus | Minus;
func Main() -> int {
// The annotation makes every element a `Token`, so one array holds all four member types.
let tokens: Token[7] = [
Name { text: "price" },
Plus {},
Number { value: 2 },
Minus {},
Name { text: "discount" },
Plus {},
Number { value: 1 }
];
var numbers = 0;
var operators = 0;
for token in tokens {
// `is` only asks; the token is untouched and keeps its type.
if token is Number {
numbers++;
}
if token is (Plus | Minus) {
operators++;
}
}
PrintLine("{} numbers, {} operators", numbers, operators);
// The result is an ordinary boolean, so it can be stored and printed like one.
let startsWithName = tokens[0] is Name;
PrintLine("starts with a name: {}", startsWithName);
PrintLine("ends with a number: {}", tokens[6] is Number);
return 0;
}
```
## Run it
```sh
cd Examples/SumTypes/Is
rux run
```
```text
2 numbers, 3 operators
starts with a name: true
ends with a number: true
```
## Common mistakes
::warning
**A sum after `is` without parentheses.**:br`token is Plus | Minus` fails with `error: a sum type after 'is' must be grouped`, and the help says `write 'value is (Plus | Minus)'`.
::
::warning
**Expecting `is` to narrow.**:br`if token is Number { PrintLine("{}", token.value); }` fails with `error: type 'Minus | Name | Number | Plus' has no field 'value'`. After the test `token` is still the whole sum. Use `match token { n: Number => …, else => {} }` to get at the number.
::
::warning
**Testing a type that is not a member.**:br`token is bool` fails with `error: type 'bool8' is not a member or subset of sum 'Minus | Name | Number | Plus'`. A test that can never be true is an error, not a constant `false`.
::
::warning
**Testing a value that is not a sum.**:br
The counter `numbers` is an `int`, so `numbers is bool` fails with `error: 'is bool8' can never be true for a value of type 'int'`. On an ordinary value, `is` compares exact types.
::
## Try it yourself
1. Count the `Name` tokens as well, and print all three counts.
2. Write `func IsOperator(token: Token) -> bool` with `is`, and use it in the loop.
3. Add up the values of all the `Number` tokens. Why does this one need a `match` with an `else => {}` arm rather than `is`?
## Learn more
- [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) and [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern) — the patterns `is` mirrors
- [Sum widening](https://rux-lang.dev/docs/learn/sum-widening) — the next lesson
- [Type tests](https://rux-lang.dev/docs/lang/sums/type-tests) in the Rux Reference
# Sum widening
::note
**You'll need**: [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern), [The is operator](https://rux-lang.dev/docs/learn/is)
::
A smaller sum fits wherever a larger one is expected. A value of `Square | Rectangle` is always one of those two, and both are members of `Circle | Square | Rectangle` — so the compiler accepts it there without a word. This is *widening*: the set of possible members grows, while the value itself, and which member is active, stays exactly as it was.
You have already seen widening once, in [Error sum](https://rux-lang.dev/docs/learn/error-sum), where a `DigitError` fitted into a function that fails with `DigitError | RangeError`. This lesson shows the same step with a whole sum on the narrow side.
## An honest return type
`Box` builds a square when the sides are equal and a rectangle otherwise. It can never produce a circle, so its return type says exactly that:
```rux
func Box(width: float64, height: float64) -> Square | Rectangle {
if width == height {
return Square { side: width };
}
return Rectangle { width: width, height: height };
}
```
Each `return` puts a single struct into the two-member sum — the step you met in [Sum type](https://rux-lang.dev/docs/learn/sum-type), a value going into a sum that has its type as a member.
Returning `Shape` would also compile, but it would throw information away: every caller would then have to be ready for a circle that never comes.
## One value, two functions
`Area` works only on boxes, and `Describe` on any shape:
```rux
func Area(box: Square | Rectangle) -> float64 {
return match box {
s: Square => s.side * s.side,
r: Rectangle => r.width * r.height
};
}
```
The same two values serve both — as they are for `Area`, widened for `Describe`:
```rux
let tile = Box(2.0, 2.0);
let door = Box(1.0, 2.5);
PrintLine("areas {} and {}", Area(tile), Area(door));
Describe(tile);
Describe(door);
```
`Describe(tile)` passes a `Square | Rectangle` to a `Shape` parameter. Nothing about the value changes: `tile` was a square, and inside `Describe` it is still a square, matched by the `s: Square` arm.
## Where widening happens
Widening happens in the same places a value is handed on — on assignment, on a call, and on `return`:
```rux
let anything: Shape = tile;
Describe(anything);
Describe(Circle { radius: 0.5 });
```
`anything` holds the square from `tile`, now with room for a circle too. The last line is the single-member step again: a `Circle` going straight into `Shape`.
| From | To | Accepted? |
| -------------------- | -------------------- | ------------------------- |
| `Circle` | `Shape` | yes — a member goes in |
| `Square | Rectangle` | `Shape` | yes — widening |
| `Shape` | `Square | Rectangle` | no — it might be a circle |
## Only one way
```mermaid
flowchart LR
sq["Square"] -- "member goes in" --> box["Square | Rectangle"]
box -- "widening" --> shape["Circle | Square | Rectangle"]
shape -. "refused — it might be a circle;
use a subset pattern" .-> box
```
A `Shape` might be a circle, and `Square | Rectangle` has no room for one, so the compiler refuses to narrow it — even when you know, this time, that it holds a square. Getting the smaller sum back out of the larger one takes a [subset pattern](https://rux-lang.dev/docs/learn/subset-pattern): `box: Square | Rectangle =>` is exactly the arm that proves the circle is not there.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/SumTypes/SumWidening){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A smaller sum fits wherever a larger one is expected. A value of `Square | Rectangle` is
// always one of those two, and both are members of `Circle | Square | Rectangle`, so the
// compiler accepts it there without a word. This is *widening*: the set of possible members
// grows, while the value itself, and which member is active, stays exactly as it was.
//
// It is the same step a single member takes when it goes into a sum, `let s: Shape = tile;`,
// just with more than one member at a time. It happens on assignment, on a call and on `return`.
//
// It only goes one way. A `Shape` might be a circle, which `Square | Rectangle` has no room for,
// so passing a `Shape` to `Area` below is refused:
// error: argument 1 to 'Area' has type 'Circle | Rectangle | Square', but parameter 'box'
// requires 'Rectangle | Square'
// Getting the smaller sum back out of the larger one takes a subset pattern.
import Io::PrintLine;
struct Circle {
radius: float64;
}
struct Square {
side: float64;
}
struct Rectangle {
width: float64;
height: float64;
}
type Shape = Circle | Square | Rectangle;
// Equal sides make a square, anything else a rectangle, so the honest result type is the
// two-member sum, not the whole `Shape`.
func Box(width: float64, height: float64) -> Square | Rectangle {
if width == height {
return Square { side: width };
}
return Rectangle { width: width, height: height };
}
func Area(box: Square | Rectangle) -> float64 {
return match box {
s: Square => s.side * s.side,
r: Rectangle => r.width * r.height
};
}
func Describe(shape: Shape) {
match shape {
c: Circle => PrintLine("circle radius {}", c.radius),
s: Square => PrintLine("square side {}", s.side),
r: Rectangle => PrintLine("rectangle {} by {}", r.width, r.height)
}
}
func Main() -> int {
let tile = Box(2.0, 2.0);
let door = Box(1.0, 2.5);
// The same two values serve both functions: as they are for `Area`, widened for `Describe`.
PrintLine("areas {} and {}", Area(tile), Area(door));
Describe(tile);
Describe(door);
// Widening on assignment: the square stays a square inside the larger sum.
let anything: Shape = tile;
Describe(anything);
Describe(Circle { radius: 0.5 });
return 0;
}
```
## Run it
```sh
cd Examples/SumTypes/SumWidening
rux run
```
```text
areas 4.0 and 2.5
square side 2.0
rectangle 1.0 by 2.5
square side 2.0
circle radius 0.5
```
## Common mistakes
::warning
**Passing the larger sum where the smaller one is expected.**:br`Area(anything)` with `anything: Shape` fails with `error: argument 1 to 'Area' has type 'Circle | Rectangle | Square', but parameter 'box' requires 'Rectangle | Square'`. Match `anything` with a `box: Square | Rectangle` arm and pass `box` instead.
::
::warning
**Narrowing on assignment.**:br`let b: Square | Rectangle = anything;` fails the same way, as `error: cannot assign 'Circle | Rectangle | Square' to 'Rectangle | Square'`. Knowing that `anything` holds a square right now is not enough — the type says it might not.
::
::warning
**Returning a wider type than the function produces.**:br
Not an error, but a cost: had `Box` returned `Shape`, `Area(tile)` would be refused, because `tile` would then be a `Shape`. Give a function the narrowest return type that is true, and let widening do the rest at the call site.
::
## Try it yourself
1. Change `Box`'s return type to `Shape` and run the program. Which line is now rejected, and why?
2. After `let anything: Shape = tile;`, print the area of `anything` using a `match` with a subset arm and a `Circle` arm.
3. Write `func Wheel(radius: float64) -> Circle` and pass its result to `Describe`. Is that widening, or a member going into a sum?
## Learn more
- [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern) — the way back from a larger sum to a smaller one
- [Error sum](https://rux-lang.dev/docs/learn/error-sum) — widening in a function's error channel
- [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) — sums built from type parameters, in the Generics part
# Part 11: Ownership
Every value in a Rux program has exactly one owner — a variable, a parameter, a field — and the owner decides when the value's life ends. Most of the time you never think about it: numbers and small structs are copied freely, and nothing needs cleaning up. This part is about the values where it matters — a hotel key that must be handed back once, a valve that must be closed on every path, a sheet whose copies must each be shredded — and about the rules the compiler checks so that cleanup happens exactly once, never twice and never not at all.
## What you will learn
- Why a `&var` borrow is exclusive, and why a borrow ends at its last use.
- What a copy is, and where one happens: binding, passing, returning and assigning.
- Handing a value on with `<-` in a binding, an argument and a return, and what happens to the old name.
- Writing a destructor, `func ~T(self: &var T)`, and when the compiler runs it — newest value first.
- Prohibiting copies with a bodyless `func =`, or writing your own copy with a body.
- Why one field cannot be moved out of a struct, and how a moving pattern takes it apart instead.
- Registering cleanup with `defer`, its reverse order, and how it meets `return`.
- Declaring a `var` without a value, and the compiler's check that every path assigns it.
## The life of a value
```mermaid
flowchart LR
b(["Begins
let, var, a constructor
11.10"]) --> h{"Handed on?"}
h -- "= copy
11.2, 11.6" --> two["two values,
each with its own end"]
h -- "<- move
11.3, 11.5, 11.7" --> one["one value,
a new owner"]
h -- "& or &var borrow
11.1" --> same["one value,
the same owner"]
two --> e(["Ends
scope, replacement, return
~T runs: 11.4"])
one --> e
same --> e
w["defer: cleanup for a piece
of work, 11.8, 11.9"] -.-> e
```
| A type that… | Write in `extend T` | Lesson |
| ------------------------------- | --------------------------------------- | ------------------------------------------------------------- |
| needs nothing special | nothing — copies field by field | [Copy](https://rux-lang.dev/docs/learn/copy) |
| must clean up when a value ends | `func ~T(self: &var T) { … }` | [Destructor](https://rux-lang.dev/docs/learn/destructor) |
| must never exist twice | `func =(self: &var T, other: &T);` | [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy) |
| needs real work to duplicate | `func =(self: &var T, other: &T) { … }` | [Custom copy](https://rux-lang.dev/docs/learn/custom-copy) |
## Lessons
| | Lesson | What you will learn |
| ----- | ---------------------------------------------------------------- | --------------------------------------------------------------------------- |
| 11.1 | [Exclusivity](https://rux-lang.dev/docs/learn/exclusivity) | one writer at a time: the rule that keeps borrows safe |
| 11.2 | [Copy](https://rux-lang.dev/docs/learn/copy) | assignment copies a value, and the copies are independent |
| 11.3 | [Move](https://rux-lang.dev/docs/learn/move) | hand a value over with `<-` instead of copying it |
| 11.4 | [Destructor](https://rux-lang.dev/docs/learn/destructor) | run cleanup when a value goes out of scope with `~T` |
| 11.5 | [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy) | forbid copying a type that owns a resource |
| 11.6 | [Custom copy](https://rux-lang.dev/docs/learn/custom-copy) | write your own copy for a type that needs real work to duplicate |
| 11.7 | [Partial move](https://rux-lang.dev/docs/learn/partial-move) | move one field out of a struct, and what is cleaned up after |
| 11.8 | [Defer](https://rux-lang.dev/docs/learn/defer) | schedule cleanup at the point you start the work, so it cannot be forgotten |
| 11.9 | [Defer return](https://rux-lang.dev/docs/learn/defer-return) | what a deferred action sees when the function returns a value |
| 11.10 | [Initialization](https://rux-lang.dev/docs/learn/initialization) | a variable must be assigned before it is read |
## Before you start
Finish Parts 1–10 first. This part leans most on [Part 6: Types](https://rux-lang.dev/docs/learn/types) — [Reference](https://rux-lang.dev/docs/learn/reference), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), [Method](https://rux-lang.dev/docs/learn/method) and [Constructor](https://rux-lang.dev/docs/learn/constructor) — and on [Destructure](https://rux-lang.dev/docs/learn/destructure) and [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern), which [Partial move](https://rux-lang.dev/docs/learn/partial-move) turns into a way of taking values apart. Each lesson's package is in the Examples repository's `Ownership/` folder:
```sh
cd Examples/Ownership/Exclusivity
rux run
```
## After this part
[Part 12: Interfaces](https://rux-lang.dev/docs/learn/interfaces) describes behaviour that many types share — and lets a type declare its own `==` and other operators the way this part declared its own `=`. Ownership comes back in [Part 15: Memory](https://rux-lang.dev/docs/learn/memory), where a [Box](https://rux-lang.dev/docs/learn/box) owns a block of memory and gives it back in its destructor, exactly as the keys and sheets here gave back their rooms and paper. The next checkpoint projects, [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic), come after Part 16.
For related rules, see [Mutability of structs](https://rux-lang.dev/docs/lang/bindings/overview#mutability), [`var`](https://rux-lang.dev/docs/lang/bindings/overview#var) and [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference.
# Exclusivity
::note
**You'll need**: [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), [Reference](https://rux-lang.dev/docs/learn/reference)
::
This part is about *ownership*: which name a value belongs to, who may change it, and when it is cleaned up. It starts with a rule you have already bumped into. A `&var` borrow is **exclusive** — while it is alive, it is the only way to reach the value. No second `&var` borrow, and no reading the value by its own name either. Read-only `&` borrows have no such limit, because any number of readers can look at a value without harming it.
"While it is alive" is shorter than it looks. A borrow ends at its **last use**, not at the end of the block it was made in, so a borrow that has finished its work is no obstacle.
## Two accounts, two borrows
`Merge` moves everything from one account into another. It takes both as `&var`, because it changes both:
```rux
func Merge(into: &var Account, from: &var Account) {
into.balance += from.balance;
from.balance = 0;
}
```
Called with two different accounts, the two borrows do not overlap, and all is well:
```rux
var alice = Account { owner: "Alice", balance: 100 };
var bob = Account { owner: "Bob", balance: 20 };
Merge(alice, bob);
```
Alice ends with 120 and Bob with 0.
## Why the rule exists
`Merge` is written for two *different* accounts. Follow what it would do if `into` and `from` were the same one, with a balance of 100:
1. `into.balance += from.balance` — the balance becomes 200.
2. `from.balance = 0` — but `from` is `into`, so the balance becomes 0.
Merging an account with itself would wipe out the money. Nothing in `Merge` is wrong; it simply assumes its two parameters are two things. Exclusivity makes that assumption safe, by making the bad call impossible to write:
```rux
Merge(alice, alice);
```
```text
error: call arguments create overlapping exclusive borrows of 'alice'
note: an earlier argument borrows the same storage at 40:11
help: split the accesses into non-overlapping calls
```
## Readers may share
Read-only borrows may overlap freely. `Richer` takes two `&Account` and changes neither, so the same account twice is fine:
```rux
func Richer(left: &Account, right: &Account) -> char8[..] {
let owner = left.balance >= right.balance ? left.owner : right.owner;
return owner;
}
```
```rux
PrintLine("richer of Alice and Alice: {}", Richer(alice, alice));
```
| While this is alive… | Another `&` borrow | Another `&var` borrow | Reading by name | Writing by name |
| -------------------- | ------------------ | --------------------- | --------------- | --------------- |
| a `&` borrow | yes | no | yes | no |
| a `&var` borrow | no | no | no | no |
## A borrow ends at its last use
A borrow does not have to be a parameter. Here `wallet` is a local `&var` borrow of `alice`:
```rux
let wallet: &var Account = alice;
wallet.balance -= 30;
wallet.balance -= 20;
PrintLine("after spending: Alice {}", alice.balance);
```
The second `wallet` line is its last use, and from there on `alice` is free again, so reading it by name on the next line is accepted.
```mermaid
flowchart LR
b["let wallet: &var Account = alice"] --> u1["wallet.balance -= 30"]
u1 --> u2["wallet.balance -= 20
last use — the borrow ends"]
u2 --> r["alice.balance
free again"]
u1 -. "reading alice here
is refused" .-> x["error: cannot read 'alice.balance'
while 'wallet' holds an exclusive borrow"]
```
Move that `PrintLine` above the second `wallet` line and `wallet` is still alive when `alice` is read. The compiler refuses, and its help names both ways out: read through `wallet`, or wait until its last use.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/Exclusivity){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `&var` borrow is exclusive. While it is alive, it is the only way to reach the value: no
// second `&var` borrow, and no reading the value by its own name either. Read-only `&` borrows
// have no such limit, because any number of readers can look at a value without harming it.
//
// "While it is alive" is shorter than it looks. A borrow ends at its last use, not at the end of
// the block it was made in, so a borrow that has finished its work is no obstacle.
//
// The rule exists for functions like `Merge` below. It takes two accounts and is written for two
// different ones. Handed the same account twice, it would add the balance to itself and then wipe
// it out. Exclusivity makes that call impossible to write.
import Io::PrintLine;
struct Account {
owner: char8[..];
balance: int32;
}
// Moves everything from one account into another.
func Merge(into: &var Account, from: &var Account) {
into.balance += from.balance;
from.balance = 0;
}
// Two read-only borrows of the same account are fine: neither can change it.
func Richer(left: &Account, right: &Account) -> char8[..] {
let owner = left.balance >= right.balance ? left.owner : right.owner;
return owner;
}
func Main() -> int {
var alice = Account { owner: "Alice", balance: 100 };
var bob = Account { owner: "Bob", balance: 20 };
// Two different accounts: two exclusive borrows that do not overlap.
Merge(alice, bob);
PrintLine("after merge: Alice {}, Bob {}", alice.balance, bob.balance);
// The same account twice is refused:
//
// Merge(alice, alice);
//
// error: call arguments create overlapping exclusive borrows of 'alice'
// note: an earlier argument borrows the same storage at 40:11
// help: split the accesses into non-overlapping calls
// Shared borrows may overlap freely.
PrintLine("richer of Alice and Alice: {}", Richer(alice, alice));
// A local `&var` borrow. Its last use is the second line below, and from there on `alice`
// is free again, so reading it by name is accepted.
let wallet: &var Account = alice;
wallet.balance -= 30;
wallet.balance -= 20;
PrintLine("after spending: Alice {}", alice.balance);
// Move that `PrintLine` above the second `wallet` line and `wallet` is still alive when
// `alice` is read:
//
// error: cannot read 'alice.balance' while 'wallet' holds an exclusive borrow
// note: exclusive borrow begins at 51:32
// help: read through 'wallet' or wait until its last use
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/Exclusivity
rux run
```
```text
after merge: Alice 120, Bob 0
richer of Alice and Alice: Alice
after spending: Alice 70
```
## Common mistakes
::warning
**Passing one value to two `&var` parameters.**:br`Merge(alice, alice)` fails with `error: call arguments create overlapping exclusive borrows of 'alice'`. Each `&var` argument must be a different value.
::
::warning
**Reading a value while a `&var` borrow of it is still in use.**:br
With a later `wallet` line still to come, reading `alice.balance` fails with `error: cannot read 'alice.balance' while 'wallet' holds an exclusive borrow`. Read through `wallet` instead, or move the read after `wallet`'s last use.
::
::warning
**Borrowing for writing while a reader is still in use.**:br`let view: &Account = alice;` followed by `let wallet: &var Account = alice;`, with `view` used again later, fails with `error: cannot borrow exclusively 'alice' while it is immutably borrowed`. A reader expects the value not to change under it, so a writer has to wait until the reader is done.
::
## Try it yourself
1. Add a third account, `carol`, and merge both others into her with two calls to `Merge`.
2. Move the `PrintLine` with `alice.balance` above the second `wallet` line and read the error. Then change it to print `wallet.balance` instead — is that accepted?
3. Make two local `&var` borrows of `alice`, one after the other, where the first is finished before the second begins. Does the compiler allow it?
## Learn more
- [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) and [Reference](https://rux-lang.dev/docs/learn/reference) — the two kinds of borrow
- [Copy](https://rux-lang.dev/docs/learn/copy) — the next lesson: what happens when a value is not borrowed at all
- [Mutability of structs](https://rux-lang.dev/docs/lang/bindings/overview#mutability) in the Rux Reference
# Copy
::note
**You'll need**: [Struct](https://rux-lang.dev/docs/learn/struct), [Array](https://rux-lang.dev/docs/learn/array), [Function](https://rux-lang.dev/docs/learn/function)
::
Every time a value is used **by value** — bound to a new name, passed to a parameter that is not a reference, returned, or assigned over another value — what arrives is a *copy*. From that moment the two are separate: changing one never shows up in the other.
That is the opposite of the [Reference](https://rux-lang.dev/docs/learn/reference) lessons, where a borrow shares one value instead of making a second. Most of the time a copy is exactly what you want, and you have been relying on it since Part 1 without a name for it. This lesson gives it the name, because the rest of the part is about values for which a plain copy is *not* right.
## Nothing to write
A struct needs no extra code to be copyable. The compiler copies a struct by copying each field, and an array by copying each element, as long as every part can itself be copied:
```rux
struct Point {
x: int32;
y: int32;
}
```
`Point` is two `int32`s, and an `int32` copies, so a `Point` copies.
## Binding a new name
`end` starts as a copy of `start`, then goes its own way:
```rux
let start = Point { x: 1, y: 2 };
var end = start;
end.y = 99;
```
`end` is now `(1, 99)`, and `start` is still `(1, 2)`. There are two points in memory, not one point with two names.
## Passing and returning
A parameter that is not a reference receives its own copy. A parameter cannot be changed, so `Nudged` copies it once more into a `var`, changes that, and returns it:
```rux
func Nudged(point: Point) -> Point {
var result = point;
result.x += 10;
return result;
}
```
```rux
let nudged = Nudged(start);
```
`nudged` is `(11, 2)`. The caller's `start` is never touched — the function only ever saw copies of it.
## Arrays copy too
An array is a value, so assigning it copies the whole array, element by element:
```rux
let scores = [3, 5, 8];
var adjusted = scores;
adjusted[0] = 100;
```
`scores[0]` is still 3. If you come from a language where an array variable is a pointer to shared storage, this is the place to slow down: in Rux, `adjusted` is a second array.
## Assigning over a value
Assigning to an existing variable copies again and replaces what was there:
```rux
end = start;
```
`end` was `(1, 99)`; now it is a fresh copy of `start`, `(1, 2)`.
## Copy or borrow?
```mermaid
flowchart LR
subgraph copy["By value: let end = start"]
s1["start (1, 2)"]
e1["end (1, 2)
a second Point"]
s1 -- "copied into" --> e1
end
subgraph borrow["By reference: let view: &Point = start"]
s2["start (1, 2)"]
v2["view"] -- "refers to" --> s2
end
```
| Written | What arrives | A change through it reaches the original? |
| --------------------------- | ------------------- | ----------------------------------------- |
| `var end = start;` | a copy | no |
| `func F(point: Point)` | a copy | no — and the parameter is read-only |
| `func F(point: &Point)` | a borrow, to read | it cannot change anything |
| `func F(point: &var Point)` | a borrow, to change | yes |
A copy is cheap for small values like a `Point`, and it is always *safe*: nothing you do to a copy can surprise the code holding the original. The [next lesson](https://rux-lang.dev/docs/learn/move) covers the other way to hand a value on — moving it, so that only one remains.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/Copy){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Every time a value is used by value — bound to a new name, passed to a parameter that is not a
// reference, returned, or assigned over another value — what arrives is a copy. From that moment
// the two are separate: changing one never shows up in the other.
//
// Nothing has to be written to make a struct copyable. The compiler copies a struct by copying
// each field, and an array by copying each element, as long as every part can itself be copied.
// The Reference lessons were the opposite case: a borrow shares one value instead of making a
// second one.
import Io::PrintLine;
struct Point {
x: int32;
y: int32;
}
// `point` is this function's own copy. A parameter cannot be changed, so it is copied once more
// into a `var`, which is changed and returned. The caller's point is never touched.
func Nudged(point: Point) -> Point {
var result = point;
result.x += 10;
return result;
}
func Main() -> int {
// Binding: `end` starts as a copy of `start`, then goes its own way.
let start = Point { x: 1, y: 2 };
var end = start;
end.y = 99;
PrintLine("start ({}, {}) end ({}, {})", start.x, start.y, end.x, end.y);
// Passing and returning: the function worked on a copy.
let nudged = Nudged(start);
PrintLine("start ({}, {}) nudged ({}, {})", start.x, start.y, nudged.x, nudged.y);
// An array is a value too, so the whole array is copied, element by element.
let scores = [3, 5, 8];
var adjusted = scores;
adjusted[0] = 100;
PrintLine("scores[0] {} adjusted[0] {}", scores[0], adjusted[0]);
// Assigning over an existing value copies again and replaces what was there.
end = start;
PrintLine("end ({}, {}) after end = start", end.x, end.y);
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/Copy
rux run
```
```text
start (1, 2) end (1, 99)
start (1, 2) nudged (11, 2)
scores[0] 3 adjusted[0] 100
end (1, 2) after end = start
```
## Common mistakes
::warning
**Expecting a change to reach the original.**:br
There is no error to warn you here, which is what makes it a mistake. `var end = start; end.y = 99;` changes `end` only. If the caller's value must change, the function needs a `&var` parameter, as in [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference).
::
::warning
**Changing a by-value parameter.**:br`func Nudged(point: Point) -> Point { point.x += 10; return point; }` fails with `error: cannot modify parameter 'point'`. The compiler's help names both fixes: take `point` as `&var Point` to change the caller's value, or move it into a `var` local — or, as `Nudged` does, copy it into one.
::
::warning
**Changing a `let` copy.**:br`let end = start; end.y = 99;` fails with `error: cannot modify immutable variable 'end'`. The copy is a new variable with its own mutability, and a `let` never changes. Declare it with `var`.
::
## Try it yourself
1. Write `func Nudge(point: &var Point)` that adds 10 to `x`, call it on a `var` point, and compare the result with `Nudged`.
2. Make an array of two `Point`s, copy it into a `var`, and change `x` of the copy's first point. Print the first point of both arrays.
3. Change the body of `Nudged` to `point.x += 10; return point;` and read the compiler's help.
## Learn more
- [Move](https://rux-lang.dev/docs/learn/move) — handing a value on without copying it
- [Reference](https://rux-lang.dev/docs/learn/reference) and [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — sharing one value instead of copying it
- [Struct](https://rux-lang.dev/docs/learn/struct) and [Array](https://rux-lang.dev/docs/learn/array) — the values copied in this lesson
# Move
::note
**You'll need**: [Copy](https://rux-lang.dev/docs/learn/copy)
::
A [copy](https://rux-lang.dev/docs/learn/copy) leaves two values behind. A *move* leaves one: the value is handed to its new owner, and the old name is finished. Rux writes a move as `<-`, the arrow pointing the way the value goes.
A relay baton is the picture to keep in mind. One runner holds it at a time, and once it has been handed on, the runner who passed it no longer has it. The compiler enforces exactly that: any later use of a moved-from name is an error.
For a `Baton` holding one number, the point of a move is not speed. It is what is left afterwards — and in the lessons after this one, that becomes the difference between a value cleaned up once and a value cleaned up twice.
## Three places to move
A move works in the same three places a copy happens. Only the arrow changes:
| Where | Copy | Move |
| ----------- | -------------------- | --------------------- |
| A binding | `let taken = value;` | `let taken <- value;` |
| An argument | `Run(value)` | `Run(<-value)` |
| A return | `return value;` | `return <-value;` |
## A binding
```rux
let baton = Baton { laps: 0 };
let first <- baton;
```
`first` now holds the baton. `baton` still exists as a name in the source, but it no longer holds anything, and the compiler will not let you read it:
```text
error: value 'baton' is used after it was moved
note: 'baton' was moved at 31:15
help: clone 'baton' before moving it if both uses are required
```
## An argument and a return
`Run` takes the baton, runs one lap with it, and hands it back:
```rux
func Run(baton: Baton, runner: char8[..]) -> Baton {
var held <- baton;
held.laps += 1;
PrintLine("{} runs lap {}", runner, held.laps);
return <-held;
}
```
A parameter cannot be changed, so `var held <- baton` moves it into a mutable local: still one baton, just under a new name. `return <-held` hands it on to the caller.
The caller passes the baton along the line of runners:
```rux
let second = Run(<-first, "Ana");
let third = Run(<-second, "Ben");
let last = Run(<-third, "Cleo");
```
Each `<-` gives up a name: after the first line, `first` is finished, after the second, `second`, and so on. One baton runs three laps, and `last.laps` is 3.
## Fresh values need no arrow
What a call *returns* is a fresh value that nobody else holds, so `=` simply takes it — there is no old name to give up, and nothing to copy from. `let second = Run(…)` above is already a move in all but spelling.
The same is true of any value made on the spot:
```rux
let spare = Run(Baton { laps: 0 }, "Dev");
```
The arrow is for a value that has a name. It is there so that a reader can see, at a glance, which names stop working on that line.
## Copy, move or borrow
You now have three ways to hand a value to someone else:
```mermaid
flowchart LR
a(["a: a value
with a name"])
a -- "let b = a
copy" --> c["two values
a and b both usable,
each changes alone"]
a -- "let b <- a
move" --> m["one value, now b's
a is finished"]
a -- "let b: &T = a
borrow" --> r["one value, still a's
b refers to it"]
```
| | Copy `=` | Move `<-` | Borrow `&` / `&var` |
| ----------------- | ------------------ | -------------- | -------------------- |
| Values afterwards | two | one | one |
| The old name | still usable | finished | still the owner |
| The new name | owns its own value | owns the value | owns nothing; refers |
## Moves on some paths
The compiler tracks moves through loops and branches. A move inside a loop is checked against the next pass too — the first pass would take the baton, and the second would find nothing left to take:
```rux
for lap in 0..3 {
let runner <- first;
}
```
```text
error: value 'first' may have been moved on some control-flow paths
```
The same error appears when a move happens inside an `if` and the name is used after it: on the path where the `if` ran, the value is gone.
A moved-from `var` is not ruined for good. Assigning it a new value with `=` gives it something to hold again, and from then on it can be read as before.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/Move){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A copy leaves two values behind. A move leaves one: the value is handed to its new owner and
// the old name is finished. Rux writes a move as `<-`, the arrow pointing the way the value goes,
// and it works in the same three places a copy happens.
//
// let taken <- value; a binding
// Run(<-value) an argument
// return <-value; a return
//
// A relay baton is the picture to keep in mind. One runner holds it at a time, and once it has
// been handed on, the runner who passed it no longer has it. The compiler enforces exactly that:
// any later use of a moved-from name is an error.
import Io::PrintLine;
struct Baton {
laps: int32;
}
// Takes the baton, runs one lap with it, and hands it back. A parameter cannot be changed, so
// `var held <- baton` moves it into a mutable local: still one baton, just under a new name.
func Run(baton: Baton, runner: char8[..]) -> Baton {
var held <- baton;
held.laps += 1;
PrintLine("{} runs lap {}", runner, held.laps);
return <-held;
}
func Main() -> int {
let baton = Baton { laps: 0 };
// A binding takes the baton over.
let first <- baton;
// From here on, `baton` cannot be used:
//
// PrintLine("{}", baton.laps);
//
// error: value 'baton' is used after it was moved
// note: 'baton' was moved at 31:15
// help: clone 'baton' before moving it if both uses are required
//
// A move inside a loop is checked against the next pass too. The first pass would take the
// baton, and the second would find nothing left to take:
//
// for lap in 0..3 {
// let runner <- first;
// }
//
// error: value 'first' may have been moved on some control-flow paths
// Arguments and returns. What a call returns is a fresh value that nobody else holds, so it
// needs no arrow: `=` simply takes it.
let second = Run(<-first, "Ana");
let third = Run(<-second, "Ben");
let last = Run(<-third, "Cleo");
PrintLine("laps run: {}", last.laps);
// The same is true of any value made on the spot. There is no old name to give up.
let spare = Run(Baton { laps: 0 }, "Dev");
PrintLine("spare laps: {}", spare.laps);
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/Move
rux run
```
```text
Ana runs lap 1
Ben runs lap 2
Cleo runs lap 3
laps run: 3
Dev runs lap 1
spare laps: 1
```
## Common mistakes
::warning
**Using a name after moving from it.**:br
After `let first <- baton;`, `PrintLine("{}", baton.laps)` fails with `error: value 'baton' is used after it was moved`. The note points at the line of the move. If both names really need a value, copy with `=` instead of moving.
::
::warning
**Moving inside a loop.**:br`let runner <- first;` in a loop body fails with `error: value 'first' may have been moved on some control-flow paths` — the second pass would have nothing to take. Move before the loop, or give each pass a value of its own.
::
::warning
**Moving inside a branch, then using the name.**:br`if ready { let first <- baton; }` followed by `baton.laps` fails the same way: `error: value 'baton' may have been moved on some control-flow paths`. The compiler does not know whether the `if` ran, so it assumes the worst.
::
## Try it yourself
1. Add a fourth runner, Dana, to the relay, and print the laps run.
2. Uncomment the `PrintLine` after `let first <- baton;` and read the error and its note.
3. Change `Run(<-first, "Ana")` to `Run(first, "Ana")`. The program still compiles — why? Print `first.laps` at the end: which baton does `first` still hold?
4. Declare `var baton`, move it into `first`, then assign it a new `Baton { laps: 7 }` and print both.
## Learn more
- [Copy](https://rux-lang.dev/docs/learn/copy) — the other way a value is handed on
- [Destructor](https://rux-lang.dev/docs/learn/destructor) — the next lesson, where a move decides who cleans up
- [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy) — values that can only be moved
# Destructor
::note
**You'll need**: [Move](https://rux-lang.dev/docs/learn/move), [Method](https://rux-lang.dev/docs/learn/method)
::
A *destructor* is code that runs when a value's life ends. Nobody calls it. The compiler does, exactly once for every value that is still alive when its life ends — when its scope closes, when a function returns, or when `=` replaces it with a new value.
In real code a destructor gives back what the value was holding: a file, a network connection, a block of memory. Because the compiler calls it, it cannot be forgotten, and it cannot be called twice. Here the destructor only prints, so that you can watch exactly when it runs.
## Writing a destructor
A destructor is written inside `extend`, named after the type with a leading `~`, and borrows the dying value mutably:
```rux
struct Guest {
name: char8[..];
}
extend Guest {
func ~Guest(self: &var Guest) {
PrintLine(" {} leaves", self.name);
}
}
```
It takes no other parameters and returns nothing. The signature is fixed: `func ~T(self: &var T)`, with the type's own name.
## When a scope ends
The guest in `Visit` lives only as long as the call:
```rux
func Visit(name: char8[..]) {
let guest = Guest { name: name };
PrintLine(" {} visits", guest.name);
}
```
```text
a short visit:
Ada visits
Ada leaves
back in Main
```
"Ada leaves" is printed by the destructor as `Visit` returns, before `Main` carries on. The same happens at the end of every block that owns a value — including each pass of a loop body.
## When a value is replaced
Replacing a value ends the old one's life first:
```rux
var seat = Guest { name: "Bob" };
seat = Guest { name: "Cy" };
```
Bob leaves at the `=`. Cy now holds the seat and will leave later. Replacing one field of a struct works the same way: the old field value is destroyed, the rest of the struct is untouched.
## When a value is moved
After a [move](https://rux-lang.dev/docs/learn/move), only the new owner is destroyed:
```rux
let host = Guest { name: "Dee" };
let moved <- host;
```
`host` gave its value away, so there is nothing left in `host` to destroy. Dee leaves once, at the end of `Main`, as `moved`.
## Newest first
Values that end together are destroyed in reverse order of creation — newest first. At the end of `Main`, `seat` (holding Cy) was created before `moved` (holding Dee), so Dee leaves first:
```mermaid
flowchart LR
subgraph made["Created, in order"]
direction LR
c1["seat — Cy"] --> c2["moved — Dee"]
end
subgraph gone["Destroyed at the end of Main"]
direction LR
d1["Dee leaves"] --> d2["Cy leaves"]
end
made -- "reversed" --> gone
```
The order is not arbitrary. A value made later may depend on one made earlier — a reader that uses an open file, say — so it has to go first, while the thing it depends on is still there.
| What happens | Destructor runs |
| ---------------------------- | ------------------------------------------------ |
| The owning scope ends | yes, newest value first |
| `=` replaces the value | yes, on the old value, at the assignment |
| The value is moved with `<-` | not here — the new owner destroys it later |
| The value is copied | the copy is a second value, destroyed on its own |
## Why this program never copies a guest
A [copy](https://rux-lang.dev/docs/learn/copy) of a guest would be a second guest, and it would leave too. With `let twin = host;`, the program would print "Dee leaves" twice for one person. For a guest that only prints, that is odd; for a value that frees memory or closes a file, it would free or close the same thing twice. The [next lesson](https://rux-lang.dev/docs/learn/no-copy) shows how a type turns copying off.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/Destructor){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A destructor is code that runs when a value's life ends. It is written inside `extend`, named
// after the type with a leading `~`, and borrows the dying value mutably:
//
// func ~Guest(self: &var Guest) { ... }
//
// Nobody calls it. The compiler does, exactly once for every value that is still alive when its
// life ends: when its scope closes, when a function returns, or when `=` replaces it with a new
// value, whether it is a whole variable or one field of a struct. A value that was moved away is
// not destroyed where it was, because its new owner will destroy it later. Values that end
// together are destroyed in reverse order of creation, newest first.
//
// Here the destructor only prints, so its timing can be watched. In real code it gives back what
// the value was holding: a file, a connection, a block of memory. Notice that this program never
// copies a guest: a copy would be a second guest that leaves too. The NoCopy lesson deals with it.
import Io::PrintLine;
struct Guest {
name: char8[..];
}
extend Guest {
func ~Guest(self: &var Guest) {
PrintLine(" {} leaves", self.name);
}
}
// The guest lives only as long as this call.
func Visit(name: char8[..]) {
let guest = Guest { name: name };
PrintLine(" {} visits", guest.name);
}
func Main() -> int {
PrintLine("a short visit:");
Visit("Ada");
PrintLine(" back in Main");
// Replacing a value ends the old one's life first.
PrintLine("swapping the seat:");
var seat = Guest { name: "Bob" };
seat = Guest { name: "Cy" };
// After a move only the new owner is destroyed, so Dee leaves once, at the end.
PrintLine("moving:");
let host = Guest { name: "Dee" };
let moved <- host;
// `moved` was created after `seat`, so it is destroyed before it.
PrintLine("end of Main:");
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/Destructor
rux run
```
```text
a short visit:
Ada visits
Ada leaves
back in Main
swapping the seat:
Bob leaves
moving:
end of Main:
Dee leaves
Cy leaves
```
## Common mistakes
::warning
**The wrong signature.**:br`func ~Guest(self: &Guest)` fails with `error: destructor for type 'Guest' must have signature 'func ~Guest(self: &var Guest)'`. The value is dying, and the destructor may need to change it on the way out — closing a handle, clearing a pointer — so it borrows it mutably.
::
::warning
**The wrong name.**:br`func ~Visitor(self: &var Guest)` inside `extend Guest` fails with `error: destructor '~Visitor' must be named '~Guest' for type 'Guest'`.
::
::warning
**Calling a destructor yourself.**:br`seat.~Guest();` does not even parse: `error: expected a field name or tuple index after '.' before '~'`. Destruction is the compiler's job. To end a value early, replace it with `=` or move it into a function that lets it go.
::
::warning
**Copying a value that has a destructor.**:br`let twin = host;` compiles, and both `twin` and `host` are destroyed — the destructor runs twice for what you meant as one guest. For a type that holds a resource, prohibit copying, as in [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy).
::
## Try it yourself
1. Create a guest inside a `for` loop over `0..3` and print the pass number. When does each guest leave?
2. Add `let twin = host;` before the move and count how many times Dee leaves.
3. Declare `struct Table { number: int32; guest: Guest; }`, make a `var` table, and assign a new guest to its `guest` field. Who leaves, and when?
## Learn more
- [Move](https://rux-lang.dev/docs/learn/move) — why a moved-from value is not destroyed
- [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy) — one value, one destructor call
- [Defer](https://rux-lang.dev/docs/learn/defer) — cleanup that belongs to a piece of work rather than a value
- [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference
# Non-copyable types
::note
**You'll need**: [Destructor](https://rux-lang.dev/docs/learn/destructor), [Move](https://rux-lang.dev/docs/learn/move)
::
A hotel key stands for a room. If keys could be copied, two of them would each be handed back at checkout — the [destructor](https://rux-lang.dev/docs/learn/destructor) would run twice for one room. For a type like this, the compiler's automatic [copy](https://rux-lang.dev/docs/learn/copy) is wrong, and Rux lets the type turn it off.
Once copying is off, the only way to hand a key on is to [move](https://rux-lang.dev/docs/learn/move) it. One key, one owner, one destructor call — and the compiler checks all three.
## Turning copying off
The copy operator is `=`. Declaring it inside `extend` **with no body** says "this type has no copy":
```rux
struct RoomKey {
room: int32;
}
extend RoomKey {
// No body: copying is prohibited.
func =(self: &var RoomKey, other: &RoomKey);
func ~RoomKey(self: &var RoomKey) {
PrintLine(" key {} handed back", self.room);
}
}
```
The signature is the one a real copy would have — `self` is the new value to fill in, `other` the value being copied — but with a `;` where the body would be. The [Custom copy](https://rux-lang.dev/docs/learn/custom-copy) lesson gives it a body.
## Moving instead
From then on every use that would copy a key is an error, and the diagnostic shows the move to write instead:
```rux
let spare = key;
```
```text
error: move-only value 'key' requires an explicit '<-' in initialization
note: plain by-value use copies its source, but 'RoomKey' prohibits copying
help: write 'let destination <- key' to transfer ownership
```
With the arrow, the key changes hands and `key` is finished:
```rux
let friend <- key;
PrintLine(" friend holds key {}", friend.room);
```
Passing by value is a copy too, so an argument needs the arrow as well. `CheckOut` takes ownership of the key, so the key's life ends when the function returns:
```rux
func CheckOut(key: RoomKey) {
PrintLine(" checking out of room {}", key.room);
}
```
```rux
CheckOut(<-friend);
PrintLine(" done");
```
That is why the output shows "key 12 handed back" between "checking out of room 12" and "done": the destructor runs as `CheckOut` returns, not at the end of `Main`.
## Fresh keys and borrowed keys
Two things still work without an arrow. A value made on the spot has no old owner, so `CheckIn` returns its new key with a plain `return`:
```rux
func CheckIn(room: int32) -> RoomKey {
PrintLine(" key {} issued", room);
return RoomKey { room: room };
}
```
And a borrow is not a copy. A function that only needs to look at a key takes `&RoomKey`, and the caller keeps the key.
```mermaid
flowchart LR
k(["key: RoomKey
no copy"]) -- "let spare = key" --> x["error: requires
an explicit '<-'"]
k -- "let friend <- key" --> f["friend owns the key
key is finished"]
k -- "Peek(key) with key: &RoomKey" --> b["borrowed for the call
key still owns it"]
```
## Everywhere a copy would happen
Every by-value use of a named key needs the arrow. The compiler names the place in each error:
| Where | Refused | Written with a move |
| -------------- | ---------------------- | ------------------------ |
| A binding | `let spare = key;` | `let spare <- key;` |
| An argument | `CheckOut(key)` | `CheckOut(<-key)` |
| A return | `return key;` | `return <-key;` |
| A struct field | `Booking { key: key }` | `Booking { key: <-key }` |
| An assignment | `key = other;` | `key <- other;` |
The prohibition also spreads outwards. A struct with a `RoomKey` field cannot be copied either — copying it would copy the key — so the compiler treats the whole struct as move-only.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/NoCopy){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A hotel key stands for a room. If keys could be copied, two of them would each be handed back
// at checkout: the destructor would run twice for one room. For a type like this the compiler's
// automatic copy is wrong, and Rux lets the type turn it off.
//
// The copy operator is `=`. Declaring it with no body says "this type has no copy":
//
// func =(self: &var RoomKey, other: &RoomKey);
//
// From then on every use that would copy a key is an error, and the only way to hand one on is
// to move it with `<-`. One key, one owner, one destructor call.
import Io::PrintLine;
struct RoomKey {
room: int32;
}
extend RoomKey {
// No body: copying is prohibited.
func =(self: &var RoomKey, other: &RoomKey);
func ~RoomKey(self: &var RoomKey) {
PrintLine(" key {} handed back", self.room);
}
}
func CheckIn(room: int32) -> RoomKey {
PrintLine(" key {} issued", room);
return RoomKey { room: room };
}
// Takes ownership of the key, so the key's life ends when this function returns.
func CheckOut(key: RoomKey) {
PrintLine(" checking out of room {}", key.room);
}
func Main() -> int {
PrintLine("check in:");
let key = CheckIn(12);
// A copy is refused, and the diagnostic shows the move to write instead:
//
// let spare = key;
//
// error: move-only value 'key' requires an explicit '<-' in initialization
// note: plain by-value use copies its source, but 'RoomKey' prohibits copying
// help: write 'let destination <- key' to transfer ownership
PrintLine("give the key to a friend:");
let friend <- key;
PrintLine(" friend holds key {}", friend.room);
// Passing by value is a copy too, so the argument needs the arrow as well. Without it:
// error: move-only value 'friend' requires an explicit '<-' in argument
PrintLine("check out:");
CheckOut(<-friend);
PrintLine(" done");
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/NoCopy
rux run
```
```text
check in:
key 12 issued
give the key to a friend:
friend holds key 12
check out:
checking out of room 12
key 12 handed back
done
```
## Common mistakes
::warning
**Binding or passing a key without the arrow.**:br`let spare = key;` fails with `error: move-only value 'key' requires an explicit '<-' in initialization`, and `CheckOut(friend)` with `error: move-only value 'friend' requires an explicit '<-' in argument`. Both notes say the same thing: plain by-value use copies its source, and this type prohibits copying.
::
::warning
**Returning a named key with a plain `return`.**:br
A function that builds a key in a local and ends with `return key;` fails with `error: move-only value 'key' requires an explicit '<-' in return`. Write `return <-key;`. Only a value made on the spot, like `return RoomKey { room: room };`, needs no arrow.
::
::warning
**Replacing a key with `=`.**:br
With two `var` keys, `key = other;` fails with `error: copying type 'RoomKey' is prohibited`. Assignment copies too. Write `key <- other;`, which hands back the old key first and then moves `other` in.
::
## Try it yourself
1. Write `func Peek(key: &RoomKey)` that prints the room number, and call it twice on `key` before giving it to the friend. Why does `Peek` need no arrow?
2. Write `func Spare(room: int32) -> RoomKey` that stores a new key in a `let` before returning it. Which `return` does it need?
3. Make two `var` keys and replace the first with `key <- other;`. When is each key handed back?
4. Declare `struct Booking { guest: char8[..]; key: RoomKey; }`, build one, and try to copy it with `=`. What does the error say about `Booking`?
## Learn more
- [Move](https://rux-lang.dev/docs/learn/move) — the arrow in all three places
- [Custom copy](https://rux-lang.dev/docs/learn/custom-copy) — giving `=` a body instead
- [Partial move](https://rux-lang.dev/docs/learn/partial-move) — taking a struct of move-only fields apart
# Custom copy
::note
**You'll need**: [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy), [Destructor](https://rux-lang.dev/docs/learn/destructor)
::
Between "the compiler copies each field" and "no copies at all" there is a third choice: write the copy yourself. Give `=` a body, and every copy of the type runs it.
In real code the body does what a field-by-field copy cannot — typically duplicating something the value owns, so the copy gets its own instead of sharing. A buffer that owns a block of memory, for example, would allocate a fresh block and copy the bytes into it. Here each copy of a sheet of paper is a *photocopy*, and remembers how many generations it is from the original.
## Three ways to copy
| In `extend T` | What a copy does | Lesson |
| --------------------------------------- | ----------------------------- | ------------------------------------------------------------- |
| nothing | copies each field | [Copy](https://rux-lang.dev/docs/learn/copy) |
| `func =(self: &var T, other: &T);` | nothing — copying is an error | [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy) |
| `func =(self: &var T, other: &T) { … }` | runs your body | this lesson |
## Writing the copy
```rux
struct Sheet {
text: char8[..];
generation: int32;
}
extend Sheet {
func =(self: &var Sheet, other: &Sheet) {
self.text = other.text;
self.generation = other.generation + 1;
PrintLine(" photocopy: generation {} -> {}", other.generation, self.generation);
}
func ~Sheet(self: &var Sheet) {
PrintLine(" shredding generation {}", self.generation);
}
}
```
`other` is the value being copied, borrowed read-only so the copy cannot change or consume it. `self` is the new value to fill in. Once you write the body, the field-by-field copy is gone: every field you want in the copy, you set yourself. Leave out `self.text = other.text;` and every photocopy comes out with empty text.
The destructor is there to show that every copy is a value of its own, with its own end.
## Every copy runs it
A binding copies:
```rux
let original = Sheet { text: "Minutes", generation: 1 };
let copy = original;
```
`copy` is generation 2. `original` is untouched, still generation 1.
Passing by value copies too. `Show` takes its sheet by value, so the caller's sheet is photocopied into the parameter, and the photocopy is shredded when `Show` returns:
```rux
func Show(sheet: Sheet) {
PrintLine(" showing '{}', generation {}", sheet.text, sheet.generation);
}
```
```text
passing by value:
photocopy: generation 2 -> 3
showing 'Minutes', generation 3
shredding generation 3
```
## Assigning over a live value
Assignment over a value that already exists does two things, in a fixed order — it makes the new copy first, then ends the old value's life:
```rux
var board = Sheet { text: "Agenda", generation: 1 };
board = copy;
```
```mermaid
flowchart LR
c["copy
generation 2"] -- "first, = runs:
a photocopy" --> n["new sheet
generation 3"]
b["board
generation 1"] -- "then the old value ends" --> s["shredding
generation 1"]
n -- "finally the new one
is installed" --> b2["board
generation 3"]
```
The output shows the order: the photocopy line comes before the shredding line.
## Only a copy runs `=`
`=` runs when an existing value is copied — one that keeps its value afterwards. A sheet made on the spot is not a copy of anything, so it moves straight in with no photocopy. That is why `var board = Sheet { … };` prints nothing, and why `board = Sheet { … };` would print only the shredding of the old sheet. A value handed over with `<-` is not copied either.
| Written | Runs `=`? |
| -------------------------- | --------------------- |
| `let copy = original;` | yes |
| `Show(copy)` | yes |
| `board = copy;` | yes |
| `var board = Sheet { … };` | no — made on the spot |
| `Show(<-copy)` | no — moved |
At the end of `Main`, three sheets are still alive — `original`, `copy` and `board` — and each is shredded once, newest first: generation 3, then 2, then 1.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/CustomCopy){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Between "the compiler copies each field" and "no copies at all" there is a third choice: write
// the copy yourself. Give `=` a body and every copy of the type runs it. `other` is the value
// being copied, borrowed read-only so the copy cannot change or consume it, and `self` is the
// new value to fill in.
//
// In real code the body does what a field-by-field copy cannot, typically duplicating something
// the value owns so the copy gets its own instead of sharing. Here each copy of a sheet is a
// photocopy and remembers how many generations it is from the original. The destructor shows
// that every copy is a value of its own, with its own end.
import Io::PrintLine;
struct Sheet {
text: char8[..];
generation: int32;
}
extend Sheet {
func =(self: &var Sheet, other: &Sheet) {
self.text = other.text;
self.generation = other.generation + 1;
PrintLine(" photocopy: generation {} -> {}", other.generation, self.generation);
}
func ~Sheet(self: &var Sheet) {
PrintLine(" shredding generation {}", self.generation);
}
}
// Taken by value, so the caller's sheet is copied into `sheet` by the custom copy.
func Show(sheet: Sheet) {
PrintLine(" showing '{}', generation {}", sheet.text, sheet.generation);
}
func Main() -> int {
PrintLine("binding:");
let original = Sheet { text: "Minutes", generation: 1 };
let copy = original;
PrintLine(" original {}, copy {}", original.generation, copy.generation);
PrintLine("passing by value:");
Show(copy);
// Assigning over a live value makes the new copy first, then ends the old value's life.
// Only a copy of an existing value runs `=`. A sheet made on the spot is not a copy of
// anything, so it moves straight in with no photocopy, here and in `board = Sheet { ... };`.
PrintLine("assigning:");
var board = Sheet { text: "Agenda", generation: 1 };
board = copy;
PrintLine(" board now holds '{}', generation {}", board.text, board.generation);
PrintLine("end of Main:");
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/CustomCopy
rux run
```
```text
binding:
photocopy: generation 1 -> 2
original 1, copy 2
passing by value:
photocopy: generation 2 -> 3
showing 'Minutes', generation 3
shredding generation 3
assigning:
photocopy: generation 2 -> 3
shredding generation 1
board now holds 'Minutes', generation 3
end of Main:
shredding generation 3
shredding generation 2
shredding generation 1
```
## Common mistakes
::warning
**Taking `other` by value.**:br`func =(self: &var Sheet, other: Sheet)` fails with `error: copy special operation for type 'Sheet' must have signature 'func =(self: &var Sheet, other: &Source)'`. Taking `other` by value would itself need a copy — the very operation being defined.
::
::warning
**Changing the original while copying it.**:br`other.generation = 0;` inside the body fails with `error: cannot modify data through immutable reference '&Sheet'`. A copy must leave its source exactly as it was.
::
::warning
**Expecting `=` to run for every assignment.**:br`board = Sheet { text: "Notes", generation: 5 };` runs no photocopy: the new sheet is made on the spot and moves straight in. Only the old board is shredded. If your body counts or logs copies, values built in place will not show up in the count.
::
## Try it yourself
1. Add `let third = copy;` after the binding. Predict every photocopy and shredding line before you run it.
2. Change `Show(copy)` to `Show(<-copy)`. The photocopy line disappears — and a later line no longer compiles. Which one, and why?
3. Remove `self.text = other.text;` from the body and run the program. What does each photocopy say it is showing?
## Learn more
- [Copy](https://rux-lang.dev/docs/learn/copy) — the copy the compiler writes for you
- [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy) — the bodyless `=`
- [Destructor](https://rux-lang.dev/docs/learn/destructor) — the other end of every copy's life
# Partial move
::note
**You'll need**: [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy), [Destructure](https://rux-lang.dev/docs/learn/destructure), [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern)
::
A parcel is wrapping paper around a gift. It is tempting to move just the gift out and leave the rest where it is — but Rux refuses: a field cannot be moved out of a value on its own. What works instead is taking the **whole** value apart, so that every field gets exactly one new owner.
This lesson brings together three things from earlier: [move-only types](https://rux-lang.dev/docs/learn/no-copy), [destructors](https://rux-lang.dev/docs/learn/destructor), and the [struct pattern](https://rux-lang.dev/docs/learn/struct-pattern) you used to [destructure](https://rux-lang.dev/docs/learn/destructure) values in `let`.
## The parcel
`Item` is move-only and announces its own destruction. `Parcel` holds two of them and has no destructor of its own:
```rux
struct Item {
name: char8[..];
}
extend Item {
func =(self: &var Item, other: &Item);
func ~Item(self: &var Item) {
PrintLine(" {} destroyed", self.name);
}
}
// No destructor of its own, so a moving pattern may take it apart.
struct Parcel {
wrapping: Item;
gift: Item;
}
```
Because both fields are move-only, `Parcel` is move-only too: copying a parcel would copy the items inside it.
## One field on its own is refused
The obvious way to get the gift out does not compile:
```rux
return <-parcel.gift;
```
```text
error: cannot move field 'gift' out of droppable value 'parcel'
note: partial moves would leave the aggregate with only some fields initialized
help: move the complete aggregate or borrow or clone the component explicitly
```
Half a parcel would be a value that is partly alive. When `parcel`'s scope ended, the compiler could no longer say what to destroy: the wrapping, yes, but not the gift, which has moved on. Rather than track a value that is part here and part gone, Rux keeps the rule simple — a value is moved whole or not at all.
## Taking the whole value apart
A struct pattern with `<-` moves the parcel into the pattern, and every field gets exactly one owner again:
```rux
func Unwrap(parcel: Parcel) -> Item {
let Parcel { wrapping: _, gift: gift } <- parcel;
PrintLine(" unwrapped the {}", gift.name);
return <-gift;
}
```
- A field bound to a name belongs to that binding. `gift` is a local `Item` now, and `return <-gift` hands it to the caller.
- A field bound to `_`, or left out of the pattern, belongs to nobody, so it is destroyed on the spot.
That is why the output reads "paper destroyed" *before* "unwrapped the book": the wrapping is gone by the time the `let` finishes.
```mermaid
flowchart LR
p(["parcel: Parcel"]) -- "let Parcel { … } <- parcel" --> pat{"each field
gets one owner"}
pat -- "wrapping: _" --> w["no owner —
destroyed at the let"]
pat -- "gift: gift" --> g["owned by gift"]
g -- "return <-gift" --> c["the caller's gift
destroyed at the end of Main"]
```
## Only a struct that is just its fields
A struct can be taken apart only when it has no destructor of its own. Give `Parcel` a `~Parcel`, and that destructor needs the whole parcel to run on — so the same pattern is refused:
```text
error: cannot split 'Parcel' with a moving pattern, because it declares destructor '~Parcel'
note: '~Parcel' runs on the whole value, so no part of it can be taken out on its own
help: bind the whole value and read its fields, or match a value that stays with its owner
```
| You want | Write | Allowed when |
| ------------------------------ | --------------------------------------------------- | ------------------------------ |
| one field, moved out | `<-parcel.gift` | never |
| every field, each to one owner | `let Parcel { wrapping: _, gift: gift } <- parcel;` | the struct has no destructor |
| to look at a field | `parcel.gift.name` | always — reading moves nothing |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/PartialMove){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A parcel is wrapping paper around a gift. It is tempting to move just the gift out and leave the
// rest where it is, but Rux refuses: a field cannot be moved out of a value on its own.
//
// return <-parcel.gift;
//
// error: cannot move field 'gift' out of droppable value 'parcel'
// note: partial moves would leave the aggregate with only some fields initialized
// help: move the complete aggregate or borrow or clone the component explicitly
//
// Half a parcel would be a value that is partly alive, and the compiler could no longer say what
// to destroy when its scope ends.
//
// What works is taking the whole value apart. A struct pattern with `<-` moves the parcel into
// the pattern, and every field gets exactly one owner again: a field bound to a name belongs to
// that binding, and a field bound to `_`, or left out of the pattern, belongs to nobody and is
// destroyed on the spot. So the gift moves on and the wrapping is gone before the next line runs.
//
// Only a struct that is nothing more than its fields can be taken apart. Give `Parcel` its own
// destructor and that destructor needs the whole parcel, so the same pattern is refused:
//
// error: cannot split 'Parcel' with a moving pattern, because it declares destructor '~Parcel'
// note: '~Parcel' runs on the whole value, so no part of it can be taken out on its own
// help: bind the whole value and read its fields, or match a value that stays with its owner
import Io::PrintLine;
struct Item {
name: char8[..];
}
extend Item {
func =(self: &var Item, other: &Item);
func ~Item(self: &var Item) {
PrintLine(" {} destroyed", self.name);
}
}
// No destructor of its own, so a moving pattern may take it apart.
struct Parcel {
wrapping: Item;
gift: Item;
}
// The gift moves out to the caller. The wrapping is bound to `_`, so it is destroyed at the `let`.
func Unwrap(parcel: Parcel) -> Item {
let Parcel { wrapping: _, gift: gift } <- parcel;
PrintLine(" unwrapped the {}", gift.name);
return <-gift;
}
func Main() -> int {
PrintLine("unwrapping:");
let gift = Unwrap(Parcel { wrapping: Item { name: "paper" }, gift: Item { name: "book" } });
PrintLine(" reading the {}", gift.name);
PrintLine("end of Main:");
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/PartialMove
rux run
```
```text
unwrapping:
paper destroyed
unwrapped the book
reading the book
end of Main:
book destroyed
```
## Common mistakes
::warning
**Moving a single field.**:br`return <-parcel.gift;` fails with `error: cannot move field 'gift' out of droppable value 'parcel'`. Take the parcel apart with a moving pattern instead, or read the field without moving it.
::
::warning
**Destructuring with `=` instead of `<-`.**:br`let Parcel { wrapping: _, gift: gift } = parcel;` fails with `error: move-only value 'parcel' requires an explicit '<-' in initialization`. A destructuring `let` owns what it takes apart, and a move-only parcel can only be handed over with the arrow.
::
::warning
**Splitting a struct that has its own destructor.**:br
With `~Parcel` declared, the pattern fails with `error: cannot split 'Parcel' with a moving pattern, because it declares destructor '~Parcel'`. Bind the parcel whole and read its fields, or drop the destructor if the struct does not really need one.
::
## Try it yourself
1. Leave `wrapping` out of the pattern altogether: `let Parcel { gift: gift } <- parcel;`. Is the paper still destroyed at the `let`?
2. In `Main`, build a parcel and take it apart into two bindings, `wrapping: w, gift: g`. At the end of `Main`, which item is destroyed first?
3. Add `func ~Parcel(self: &var Parcel)` that prints "parcel opened", and read the error. Then change `Unwrap` so it only prints `parcel.gift.name` and returns nothing.
## Learn more
- [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern) and [Destructure](https://rux-lang.dev/docs/learn/destructure) — the patterns used here
- [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy) — why `Item` and `Parcel` can only be moved
- [Initialization](https://rux-lang.dev/docs/learn/initialization) — the same "whole value" rule when a variable starts empty
# Defer
::note
**You'll need**: [Method](https://rux-lang.dev/docs/learn/method), [Return](https://rux-lang.dev/docs/learn/return)
::
Some work has to be undone however a function ends. A valve that is opened must be closed — on the normal path, and on every early `return` as well. Writing the close before each exit is easy to get wrong, and the mistake is silent: the program runs, and the valve stays open.
`defer` registers a statement now and runs it when the enclosing scope ends, by whichever way it ends. So the cleanup is written **once**, on the line right after the work it undoes, where a reader can check that the two match.
## Cleanup next to the work
A `Valve` can be opened and closed; each method prints, so you can see the order:
```rux
struct Valve {
name: char8[..];
}
extend Valve {
func Open(self: &Valve) {
PrintLine(" open {}", self.name);
}
func Close(self: &Valve) {
PrintLine(" close {}", self.name);
}
}
```
`Fill` opens two valves, and pairs each `Open` with a deferred `Close` on the very next line:
```rux
let supply = Valve { name: "supply" };
supply.Open();
defer supply.Close();
let drain = Valve { name: "drain" };
drain.Open();
defer drain.Close();
```
Nothing is closed yet. `defer` only remembers the statement; it runs later, when `Fill` ends.
## Every way out
`Fill` has two exits — an early `return false` for too much water, and the normal `return true`:
```rux
if litres > 100 {
PrintLine(" {} litres is too much, stopping", litres);
return false;
}
PrintLine(" filling {} litres", litres);
return true;
```
Neither `return` mentions a valve, yet both valves are closed on both paths. Add a third exit next year, and it will close them too — the cleanup belongs to the scope, not to any one `return`.
## Last registered, first run
Several defers run in **reverse** order: the last one registered runs first. The drain was opened last, so it is closed first:
```text
fill 40:
open supply
open drain
filling 40 litres
close drain
close supply
```
That is the order cleanup usually needs, because what was set up last tends to depend on what came before it. Values with [destructors](https://rux-lang.dev/docs/learn/destructor) follow the same rule, and when a scope has both, the deferred statements run first, newest first, and then the values are destroyed, newest first:
```mermaid
flowchart LR
subgraph reg["Registered while the scope runs"]
direction TB
r1["1. let ada = Guest"] --> r2["2. defer lights off"]
r2 --> r3["3. let bob = Guest"]
r3 --> r4["4. defer music off"]
end
subgraph run["At the end of the scope"]
direction TB
e1["music off"] --> e2["lights off"]
e2 --> e3["Bob leaves"]
e3 --> e4["Ada leaves"]
end
reg -- "defers in reverse,
then destructors in reverse" --> run
```
Here `Guest` is the type from [Destructor](https://rux-lang.dev/docs/learn/destructor), whose destructor prints "… leaves".
## A defer is registered when it is reached
`defer` is a statement like any other, and it counts only once execution reaches it. A `return` that comes before a `defer` line leaves without running it — which is exactly right, because the work it would undo has not been done yet either. Put a `return` between opening the supply and opening the drain, and only the supply is closed on that path.
A `defer` belongs to the scope it is written in. Inside a loop body, it runs at the end of every pass, not once after the loop.
## Defer or destructor?
| | Destructor `~T` | `defer` |
| ---------- | -------------------------- | --------------------------------- |
| Belongs to | a type — every value of it | one piece of work in one function |
| Written | once, in `extend T` | at the place the work starts |
| Runs | when a value's life ends | when the enclosing scope ends |
A destructor cleans up after a value. `defer` is for cleanup that belongs to a piece of work rather than to any one value's life — the valves here are ordinary values with no destructor, and opening one is something `Fill` does, not something every `Valve` needs undone.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/Defer){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Some work has to be undone however a function ends: a valve that is opened must be closed, on
// the normal path and on every early `return` as well. Writing the close before each exit is easy
// to get wrong, and the mistake is silent.
//
// `defer` registers a statement now and runs it when the enclosing scope ends, by whichever way
// it ends. So the cleanup is written once, on the line right after the work it undoes, where a
// reader can check that the two match.
//
// Several defers run in reverse order: the last one registered runs first. That is the order
// cleanup usually needs, because what was set up last tends to depend on what came before it.
//
// A destructor cleans up after a value. `defer` is for cleanup that belongs to a piece of work
// rather than to any one value's life.
import Io::PrintLine;
struct Valve {
name: char8[..];
}
extend Valve {
func Open(self: &Valve) {
PrintLine(" open {}", self.name);
}
func Close(self: &Valve) {
PrintLine(" close {}", self.name);
}
}
func Fill(litres: int32) -> bool {
let supply = Valve { name: "supply" };
supply.Open();
defer supply.Close();
let drain = Valve { name: "drain" };
drain.Open();
defer drain.Close();
// An early exit. Both valves are still closed, drain first.
if litres > 100 {
PrintLine(" {} litres is too much, stopping", litres);
return false;
}
PrintLine(" filling {} litres", litres);
return true;
}
func Main() -> int {
PrintLine("fill 40:");
let first = Fill(40);
PrintLine(" filled: {}", first);
PrintLine("fill 500:");
let second = Fill(500);
PrintLine(" filled: {}", second);
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/Defer
rux run
```
```text
fill 40:
open supply
open drain
filling 40 litres
close drain
close supply
filled: true
fill 500:
open supply
open drain
500 litres is too much, stopping
close drain
close supply
filled: false
```
## Common mistakes
::warning
**Deferring a block.**:br`defer { PrintLine("a"); PrintLine("b"); }` does not parse: `error: expected an expression before '{'`. A `defer` takes one statement. Write two defers — remembering they run in reverse — or put the steps in a method and defer the call.
::
::warning
**Registering the cleanup too late.**:br
A `defer` written at the end of a function, or after an early `return`, does not run on the paths that leave before it. There is no error for this; it is the very mistake `defer` exists to prevent. Write it on the line right after the work it undoes.
::
::warning
**Expecting defers to run in the order written.**:br
They run newest first. If the drain must close before the supply, register the supply's cleanup first — which is what opening them in that order already does.
::
## Try it yourself
1. Add a third valve, `vent`, opened after the drain. Predict the order of the three closes on both paths, then run it.
2. Add an early `return false` for `litres <= 0` between opening the supply and opening the drain. Which valves are closed when you call `Fill(0)`?
3. Write a `for` loop over `0..3` whose body defers a `PrintLine` of the pass number. Does each line print at the end of its own pass, or all after the loop?
## Learn more
- [Defer return](https://rux-lang.dev/docs/learn/defer-return) — what a deferred statement sees when a function returns a value
- [Destructor](https://rux-lang.dev/docs/learn/destructor) — cleanup that belongs to a value
- [Return](https://rux-lang.dev/docs/learn/return) — the early exits `defer` has to cover
# Defer return
::note
**You'll need**: [Defer](https://rux-lang.dev/docs/learn/defer), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method)
::
`return` and [`defer`](https://rux-lang.dev/docs/learn/defer) meet at the end of a function, and the order between them is fixed. `return value;` works out its value first and **keeps** it; only then do the deferred statements run. Whatever they change afterwards, the caller still receives the kept value.
That sounds like a detail, but it makes a useful pattern possible: "hand out the current value, then move on" becomes a two-line function, with no temporary variable to hold the old value.
## The order at the end of a function
`Countdown` registers two deferred statements and then returns its local:
```rux
func Countdown() -> int32 {
var remaining: int32 = 3;
defer PrintLine(" deferred code sees remaining = {}", remaining);
defer remaining = 0;
return remaining;
}
```
Step by step, as the function ends:
```mermaid
flowchart LR
r["return remaining
works out 3
and keeps it"] --> d1["defer remaining = 0
(registered last, runs first)"]
d1 --> d2["defer PrintLine(…)
prints remaining = 0"]
d2 --> c["the caller
receives 3"]
```
```text
countdown:
deferred code sees remaining = 0
returned 3
```
Two things are worth noticing in that output.
The `PrintLine` shows 0, not 3. A deferred statement is evaluated **when it runs**, not when it is registered — so it reads `remaining` after the reset has happened. And the reset *did* happen: the local really is 0. It simply makes no difference to the caller, who gets the 3 that `return` had already kept.
## Hand out, then advance
A ticket dispenser shows a number. Taking a ticket gives you that number, and the display moves on to the next:
```rux
struct Dispenser {
next: int32;
}
extend Dispenser {
// Returns the current ticket, then advances the dispenser.
func Take(self: &var Dispenser) -> int32 {
defer self.next += 1;
return self.next;
}
}
```
`return self.next` keeps the current number; the deferred `self.next += 1` then advances the dispenser. Without `defer`, the same method needs a temporary:
```rux
let ticket = self.next;
self.next += 1;
return ticket;
```
Both are correct. The `defer` version says what the method is for in its last line — return the current ticket — and puts the bookkeeping on the line before it.
```rux
var dispenser = Dispenser { next: 1 };
let first = dispenser.Take();
let second = dispenser.Take();
```
`first` is 1, `second` is 2, and the dispenser now shows 3. The method changes `self`, so it takes `&var Dispenser`, as in [Mutating method](https://rux-lang.dev/docs/learn/mutating-method).
| Statement | When it is evaluated | What the caller sees |
| ----------------------- | ------------------------------ | ---------------------------- |
| `return self.next;` | first — its value is kept | the kept value |
| `defer self.next += 1;` | after the return value is kept | the change, on its next call |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/DeferReturn){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `return` and `defer` meet at the end of a function, and the order between them is fixed:
// `return value;` works out its value first and keeps it, and only then do the deferred
// statements run. Whatever they change afterwards, the caller still receives the kept value.
//
// That makes "hand out the current value, then move on" a two-line function. A ticket dispenser
// returns the number it is showing and advances to the next one, without a temporary variable to
// hold the old number.
import Io::PrintLine;
// Both deferred statements run after the return value is kept. In reverse order, so the reset
// runs first and the `PrintLine` then shows that the local really changed. The caller gets 3.
func Countdown() -> int32 {
var remaining: int32 = 3;
defer PrintLine(" deferred code sees remaining = {}", remaining);
defer remaining = 0;
return remaining;
}
struct Dispenser {
next: int32;
}
extend Dispenser {
// Returns the current ticket, then advances the dispenser.
func Take(self: &var Dispenser) -> int32 {
defer self.next += 1;
return self.next;
}
}
func Main() -> int {
PrintLine("countdown:");
let result = Countdown();
PrintLine(" returned {}", result);
PrintLine("tickets:");
var dispenser = Dispenser { next: 1 };
let first = dispenser.Take();
let second = dispenser.Take();
PrintLine(" took {} and {}, now showing {}", first, second, dispenser.next);
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/DeferReturn
rux run
```
```text
countdown:
deferred code sees remaining = 0
returned 3
tickets:
took 1 and 2, now showing 3
```
## Common mistakes
::warning
**Expecting a deferred change to reach the caller.**:br`defer remaining = 0;` does reset the local, but `Countdown()` still returns 3. The return value was kept before any deferred statement ran. If the caller must see the change, make it before the `return`.
::
::warning
**Expecting a deferred statement to remember old values.**:br`defer PrintLine("{}", remaining);` does not capture `remaining` at the `defer` line. It reads the variable when it runs, at the end of the function, after everything else has changed it.
::
::warning
**Advancing through a read-only receiver.**:br
With `func Take(self: &Dispenser)`, the deferred `self.next += 1;` fails with `error: cannot modify data through immutable reference '&Dispenser'`. Deferred code is checked like any other code in the function: changing `self` needs `&var`.
::
## Try it yourself
1. Swap the two `defer` lines in `Countdown`. What does the deferred `PrintLine` show now, and does the returned value change?
2. Add a method `Peek(self: &Dispenser) -> int32` that returns the next ticket without taking it, and print it between the two `Take` calls.
3. Write `func Next(counter: &var int32) -> int32` that returns the counter's current value and then adds 1 to it, using `defer`. Call it twice on a `var` that starts at 5.
## Learn more
- [Defer](https://rux-lang.dev/docs/learn/defer) — registering cleanup, and the reverse order
- [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) — `self: &var T`
- [Return](https://rux-lang.dev/docs/learn/return) — returning a value from a function
# Initialization
::note
**You'll need**: [Else if](https://rux-lang.dev/docs/learn/else-if), [Constructor](https://rux-lang.dev/docs/learn/constructor)
::
A `var` may be declared before its value is known, and given one later. Until then it holds nothing, and the compiler tracks it: every path that reaches a read must have assigned it first. A read that some path can reach without an assignment is rejected at compile time — so a Rux program never sees leftover garbage from an unassigned variable.
This is the last lesson of the part, and it closes the circle on ownership. A value's life ends with its destructor; this lesson is about how it begins, and why the compiler must always know whether a variable holds a value at all.
## Declare now, assign later
Sometimes the value depends on a decision that has not been made yet. `grade` is declared with a type and no value, and each branch of an [`else if`](https://rux-lang.dev/docs/learn/else-if) chain assigns it:
```rux
func Grade(score: int32) -> char8[..] {
var grade: char8[..];
if score >= 90 {
grade = "excellent";
} else if score >= 50 {
grade = "pass";
} else {
grade = "retry";
}
return grade;
}
```
The compiler follows every path from the declaration to the `return`, and on each one `grade` has been assigned. The read is accepted.
```mermaid
flowchart LR
d["var grade: char8[..]
holds nothing"] --> q{"score?"}
q -- ">= 90" --> a1["grade = excellent"]
q -- ">= 50" --> a2["grade = pass"]
q -- "else" --> a3["grade = retry"]
a1 --> r["return grade
assigned on every path"]
a2 --> r
a3 --> r
q -. "without the final else" .-> x["error: value 'grade' may be
uninitialized on some
control-flow paths"]
```
Delete the final `else`, and a score below 50 reaches the `return` with nothing assigned. The compiler refuses the program and points at where that path starts.
## Reading too early
The simplest case is a read straight after the declaration:
```rux
var total: int32;
PrintLine("{}", total);
```
```text
error: variable 'total' is used before it is initialized
note: 'total' was declared without an initializer at 47:5
help: assign a value to 'total' before this use
```
Many languages would print whatever happened to be in that memory, or a silent zero. Rux makes you say what the value is.
## A let needs its value now
A `let` has no such freedom. It can never be assigned later, so it needs its value where it is declared: `let total: int32;` on its own is rejected with `error: immutable variable requires an initializer`.
## One field at a time
A plain struct of numbers may be filled in one field at a time, and it counts as assigned once every field has been written. The compiler tracks the fields individually: after `var p: Point; p.x = 1;`, reading `p.x` is fine, but reading `p.y` is refused, with a note that only `p.x` has been written.
A type that needs destroying is different. When a struct holds a `Guest` — the type with a destructor from the [Destructor](https://rux-lang.dev/docs/learn/destructor) lesson — a guest written into one field of an empty struct would belong to no whole value, and nothing would ever destroy it. So such a variable must get its value whole:
```rux
var table: Table;
table.guest = Guest { name: "Gus" };
```
```text
error: cannot write field 'guest' of 'table', which holds no value
help: initialize 'table' whole, as in 'table = Table { ... }'
```
It is the same rule as in [Partial move](https://rux-lang.dev/docs/learn/partial-move), seen from the other end: a value with a destructor is alive whole, or not at all.
## A constructor runs for you
There is one exception to "holds nothing". When the type has a [constructor](https://rux-lang.dev/docs/learn/constructor) that takes no arguments, `var value: T;` calls it, and the variable starts out initialized:
```rux
extend Tally {
func Tally() -> Tally {
PrintLine(" Tally() starts the count at zero");
return Tally { count: 0 };
}
}
```
```rux
var tally: Tally;
tally.count += 1;
```
`tally.count += 1` reads the count before changing it, and that read is fine — the constructor has already run, as the output shows.
| Declaration | Starts as |
| ---------------------------------- | ---------------------------------- |
| `var grade: char8[..];` | nothing — assign before reading |
| `var tally: Tally;` with `Tally()` | whatever `Tally()` returns |
| `let total: int32;` | an error — a `let` needs its value |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Ownership/Initialization){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `var` may be declared before its value is known, and given one later. Until then it holds
// nothing, and the compiler tracks it: every path that reaches a read must have assigned it
// first. A read that some path can reach without an assignment is rejected at compile time, so
// a Rux program never sees leftover garbage from an unassigned variable.
//
// A `let` has no such freedom. It cannot be assigned later, so it needs its value where it is
// declared.
//
// One exception to "holds nothing": when the type has a constructor taking no arguments,
// `var value: T;` calls it, and the variable starts out initialized.
import Io::PrintLine;
// Every branch assigns `grade`, so the read in `return` is accepted. Delete the final `else` and
// one path reaches the read with nothing assigned:
//
// error: value 'grade' may be uninitialized on some control-flow paths
// note: one unavailable path for 'grade' originates at 20:5
// help: initialize or preserve 'grade' on every path before this use
func Grade(score: int32) -> char8[..] {
var grade: char8[..];
if score >= 90 {
grade = "excellent";
} else if score >= 50 {
grade = "pass";
} else {
grade = "retry";
}
return grade;
}
struct Tally {
count: int32;
}
extend Tally {
func Tally() -> Tally {
PrintLine(" Tally() starts the count at zero");
return Tally { count: 0 };
}
}
func Main() -> int {
PrintLine("grades: {}, {}, {}", Grade(95), Grade(72), Grade(31));
// Declared without a value and read straight away:
//
// var total: int32;
// PrintLine("{}", total);
//
// error: variable 'total' is used before it is initialized
// note: 'total' was declared without an initializer at 47:5
// help: assign a value to 'total' before this use
// When the type needs destroying, a variable without a value must also get its value whole.
// Here `Table` is a struct holding a `Guest`, the type with a destructor from the Destructor
// lesson. A guest written into one field would belong to no whole table, and nothing would
// ever destroy it:
//
// var table: Table;
// table.guest = Guest { name: "Gus" };
//
// error: cannot write field 'guest' of 'table', which holds no value
// help: initialize 'table' whole, as in 'table = Table { ... }'
// `Tally` has a constructor without arguments, so this declaration runs it.
PrintLine("tally:");
var tally: Tally;
tally.count += 1;
PrintLine(" count {}", tally.count);
return 0;
}
```
## Run it
```sh
cd Examples/Ownership/Initialization
rux run
```
```text
grades: excellent, pass, retry
tally:
Tally() starts the count at zero
count 1
```
## Common mistakes
::warning
**A path that never assigns.**:br
Without the final `else` in `Grade`, `return grade;` fails with `error: value 'grade' may be uninitialized on some control-flow paths`. Every branch that can reach the read must assign — add the missing `else`, or give the variable a sensible starting value.
::
::warning
**Reading before the first assignment.**:br`var total: int32; PrintLine("{}", total);` fails with `error: variable 'total' is used before it is initialized`. There is no hidden zero. If zero is what you mean, write `var total: int32 = 0;`.
::
::warning
**A `let` without a value.**:br`let total: int32;` fails with `error: immutable variable requires an initializer`. Declare it with `var` if it must be assigned later, or give it its value on the spot.
::
::warning
**Filling a destroyable struct one field at a time.**:br`var table: Table; table.guest = Guest { name: "Gus" };` fails with `error: cannot write field 'guest' of 'table', which holds no value`. Assign the whole struct: `table = Table { guest: Guest { name: "Gus" } };`.
::
## Try it yourself
1. Delete the final `else` in `Grade` and read the error and its note.
2. Declare `struct Point { x: int32; y: int32; }`, then `var p: Point; p.x = 1;`, and print `p.y`. Then assign `p.y` too and print both.
3. Remove the `Tally()` constructor. Which lines in `Main` are now rejected, and what would you write instead?
## Learn more
- [Constructor](https://rux-lang.dev/docs/learn/constructor) — the function `var value: T;` calls
- [Mutable](https://rux-lang.dev/docs/learn/mutable) — `var` and `let`
- [`var`](https://rux-lang.dev/docs/lang/bindings/overview#var) and [`let`](https://rux-lang.dev/docs/lang/bindings/overview#let) in the Rux Reference
# Part 12: Interfaces
A circle and a rectangle both have an area; a price and a temperature can both be printed; a version number and a score can both be put in order. An **interface** names a piece of behaviour like that, and any type can promise to provide it. This part shows how to declare and keep such a promise, how code written against the promise works for every type that keeps it, and how your own types join in with the language's own machinery: `{}` placeholders, `==` and `<`, square brackets and `for` loops.
## What you will learn
- Declaring an `interface` and implementing it with `extend Type : Interface`.
- Interface values: one variable, array or loop holding many types, with dynamic dispatch choosing the method.
- Interface parameters, `&I` and `&var I`, and requirements that write with `self: &var Self`.
- The standard interfaces `Display`, `Equatable` and `Comparable`, and the `Ordering` a comparison answers with.
- Comparing structs, tuples, arrays and variants with `==`, field by field, for free.
- Declaring operators such as `+`, `==`, `<` and `[]` for your own types, and the four comparisons derived from two.
- Making a type walkable by `for` with `Next`, and a collection with `Iterate`.
## How a type joins in
```mermaid
flowchart LR
t(["Your type"]) --> own["interface + extend T : I
a promise the compiler checks
(12.1)"]
own --> dyn["interface values and parameters
one call, many types
(12.2–12.3)"]
t --> std["standard interfaces
printed with {}, Equals, Compare
(12.4–12.6)"]
t --> ops["operator functions
==, +, <, []
(12.7–12.10)"]
t --> loops["Next and Iterate
walked by for
(12.11–12.12)"]
```
## Lessons
| | Lesson | What you will learn |
| ----- | -------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| 12.1 | [Interface](https://rux-lang.dev/docs/learn/interface) | declare an interface and implement it for your own types |
| 12.2 | [Interface value](https://rux-lang.dev/docs/learn/interface-value) | hold any implementing type in one interface value |
| 12.3 | [Interface parameter](https://rux-lang.dev/docs/learn/interface-parameter) | write a function that accepts any type implementing an interface |
| 12.4 | [Display](https://rux-lang.dev/docs/learn/display) | make your own type printable with `{}` |
| 12.5 | [Equatable](https://rux-lang.dev/docs/learn/equatable) | define what it means for two of your values to be equal |
| 12.6 | [Comparable](https://rux-lang.dev/docs/learn/comparable) | define an order for your own type |
| 12.7 | [Structural equality](https://rux-lang.dev/docs/learn/structural-equality) | compare whole structs and tuples with `==` |
| 12.8 | [Operator overload](https://rux-lang.dev/docs/learn/operator-overload) | define `==`, `+` and the other operators for your own type |
| 12.9 | [Derived operator](https://rux-lang.dev/docs/learn/derived-operator) | define one operator and get its partners for free |
| 12.10 | [Indexer](https://rux-lang.dev/docs/learn/indexer) | let your own type be indexed with `[]` |
| 12.11 | [Iterator](https://rux-lang.dev/docs/learn/iterator) | implement `Next` so a type of your own can be used with `for` |
| 12.12 | [Iterable](https://rux-lang.dev/docs/learn/iterable) | let a container hand out an iterator for `for` |
## Before you start
Finish [Part 11: Ownership](https://rux-lang.dev/docs/learn/ownership) — an interface value is a [copy](https://rux-lang.dev/docs/learn/copy) of what it holds. The lessons also lean on [Method](https://rux-lang.dev/docs/learn/method), [Extension](https://rux-lang.dev/docs/learn/extension) and [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) from Part 6, on optionals from [Part 8](https://rux-lang.dev/docs/learn/optionals) for iterators, and on [Propagate](https://rux-lang.dev/docs/learn/propagate) from Part 9 for `Display`. Each lesson's package is in the Examples repository's `Interfaces/` folder:
```sh
cd Examples/Interfaces/Interface
rux run
```
## After this part
[Part 13: Generics](https://rux-lang.dev/docs/learn/generics) writes one function or type for many types at once, and interfaces come straight back as its *bounds*: `` accepts any type that keeps the `Display` promise, checked when the program is compiled rather than dispatched while it runs. After [Part 16: Numbers](https://rux-lang.dev/docs/learn/numbers) you are ready for the next checkpoint projects, [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic).
For the rules behind this part, see [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview), [Methods](https://rux-lang.dev/docs/lang/structs/methods) and [Comparison operators](https://rux-lang.dev/docs/lang/expressions/comparison) in the Rux Reference.
# Interface
::note
**You'll need**: [Method](https://rux-lang.dev/docs/learn/method), [Extension](https://rux-lang.dev/docs/learn/extension)
::
Different types often answer the same question in their own way. A circle and a rectangle both have an area, but each works it out differently. An **interface** gives that shared question a name: it lists the functions a type must provide, and a type that *implements* the interface promises to provide every one of them. This lesson is about making that promise and having the compiler hold you to it. The next two lessons show what other code can do with a type once it has made one.
## Declaring an interface
```rux
interface Shape {
func Area() -> float64;
func Name() -> char8[..];
}
```
An interface lists function headers — names, parameters and result types — and ends each one with `;` where a body would go. It has no fields and no code of its own. It only names what must exist.
The requirements carry no `self`. The interface says what each implementation must be able to answer; each implementation supplies its own receiver.
## Keeping the promise
A type makes the promise in an `extend` block, the same block you met in [Extension](https://rux-lang.dev/docs/learn/extension), with `: Shape` after the type name:
```rux
extend Circle : Shape {
func Area(self: &Circle) -> float64 {
return 3.14 * self.radius * self.radius;
}
func Name(self: &Circle) -> char8[..] {
return "circle";
}
}
```
Inside, each requirement is written as an ordinary [method](https://rux-lang.dev/docs/learn/method), now with `self: &Circle` as its first parameter. `Rectangle` keeps the same promise with its own formula. The two halves divide the work like this:
| Written | Who writes it | What it says |
| ----------------------------- | ---------------------- | ----------------------------------- |
| `interface Shape { … }` | the interface | which functions must exist |
| `extend Circle : Shape { … }` | each implementing type | how this type provides each of them |
The compiler checks the promise word for word. Delete `Name` from the `Circle` block and the program is refused: `error: implementation of interface 'Shape' for type 'Circle' is missing method 'Name'`.
Methods that keep a promise are called like any other method:
```rux
PrintLine("a {} with area {}", wheel.Name(), wheel.Area());
PrintLine("a {} with area {}", door.Name(), door.Area());
```
## More than the promise
An implementation may add methods beyond the promised ones:
```rux
// An implementation may add methods beyond the promised ones. This one belongs to
// Rectangle alone; Shape knows nothing about it.
func IsSquare(self: &Rectangle) -> bool {
return self.width == self.height;
}
```
`door.IsSquare()` works because `door` is a `Rectangle`. `Shape` knows nothing about `IsSquare`, which will matter in the next lesson, where a value is known only as "some `Shape`".
## Any type can make the promise
The implementing type does not have to be a struct you wrote. A primitive type such as `int32` can implement `Shape` just as well:
```rux
extend int32 : Shape {
func Area(self: &int32) -> float64 {
return 0.0;
}
func Name(self: &int32) -> char8[..] {
return "number";
}
}
```
After this, `let n: int32 = 4;` answers `n.Name()` with `number`. An interface describes behaviour, not a family of related types: anything that can answer the questions may join.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/Interface){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An interface is a promise a type can make: a list of functions it will provide. The interface
// has no fields and no code of its own. It only names what must exist.
//
// A type makes the promise with `extend Type : Interface { ... }`, and the compiler holds it to
// it: leave a function out and the `extend` is rejected. Copy each signature exactly as well — a
// different parameter or result type breaks the promise, though the compiler does not catch that
// yet. That checked promise is this lesson. The next two show what other code can do with a
// type once it has made one.
//
// Any type can make the promise, including one you did not write: `extend int32 : Shape` would be
// just as valid as the two below.
import Io::PrintLine;
// The requirements. They carry no `self`: the interface says what each implementation must be
// able to answer, and each implementation supplies its own receiver.
interface Shape {
func Area() -> float64;
func Name() -> char8[..];
}
struct Circle {
radius: float64;
}
struct Rectangle {
width: float64;
height: float64;
}
// `: Shape` after the type name is the promise. Inside, each requirement is written as an
// ordinary method, now with `self` as its first parameter.
extend Circle : Shape {
func Area(self: &Circle) -> float64 {
return 3.14 * self.radius * self.radius;
}
func Name(self: &Circle) -> char8[..] {
return "circle";
}
}
extend Rectangle : Shape {
func Area(self: &Rectangle) -> float64 {
return self.width * self.height;
}
func Name(self: &Rectangle) -> char8[..] {
return "rectangle";
}
// An implementation may add methods beyond the promised ones. This one belongs to
// Rectangle alone; Shape knows nothing about it.
func IsSquare(self: &Rectangle) -> bool {
return self.width == self.height;
}
}
func Main() -> int {
let wheel = Circle { radius: 2.0 };
let door = Rectangle { width: 0.9, height: 2.0 };
// Methods that keep a promise are called like any other method.
PrintLine("a {} with area {}", wheel.Name(), wheel.Area());
PrintLine("a {} with area {}", door.Name(), door.Area());
PrintLine("is the door square? {}", door.IsSquare());
// Delete `Name` from the Circle block and the compiler refuses the program: "implementation
// of interface 'Shape' for type 'Circle' is missing method 'Name'".
return 0;
}
```
## Run it
```sh
cd Examples/Interfaces/Interface
rux run
```
```text
a circle with area 12.56
a rectangle with area 1.8
is the door square? false
```
## Common mistakes
::warning
**Leaving out a requirement.**:br
Every function the interface lists must appear in the `extend` block. Without `Name`, the compiler stops with `error: implementation of interface 'Shape' for type 'Circle' is missing method 'Name'`.
::
::warning
**Changing a requirement's signature.**:br
Copy each header from the interface exactly — the same parameters and the same result type — and add only the `self` receiver in front. A `Circle` whose `Area` returned `float32` would not be answering the question `Shape` asks, even though the name matches.
::
::warning
**Forgetting `: Shape`.**:br
A plain `extend Circle { … }` with the right methods in it makes no promise: the methods exist, but `Circle` is not a `Shape`. Nothing complains until you use it as one — `let s: Shape = wheel;` then fails with `error: cannot assign 'Circle' to 'Shape'`.
::
::warning
**Misspelling the interface.**:br`extend Circle : Shap { … }` is refused with `error: interface 'Shap' is not defined`. The interface must be declared, or imported, before a type can implement it.
::
## Try it yourself
1. Add `struct Triangle { base: float64; height: float64; }` and make it keep the `Shape` promise. Its area is half the base times the height.
2. Add a third requirement, `func Perimeter() -> float64;`, to `Shape`. Read the errors, then implement it for every shape.
3. Make a square door, `Rectangle { width: 1.0, height: 1.0 }`, and check that `IsSquare` says so.
## Learn more
- [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) in the Rux Reference
- [Extension](https://rux-lang.dev/docs/learn/extension) — `extend` without an interface
- [Interface value](https://rux-lang.dev/docs/learn/interface-value) — one variable that can hold any `Shape`
- [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) — an interface as a requirement on a generic type
# Interface value
::note
**You'll need**: [Interface](https://rux-lang.dev/docs/learn/interface), [Copy](https://rux-lang.dev/docs/learn/copy)
::
In the [Interface](https://rux-lang.dev/docs/learn/interface) lesson, `wheel.Area()` could only ever mean Circle's `Area`: the compiler knew `wheel` was a `Circle`. An interface is also a **type** in its own right. A variable of type `Shape` can hold a circle now and a rectangle a moment later, and a call through it runs whichever `Area` belongs to the value inside. That is what lets values of different types share one variable, one array or one loop.
## Making an interface value
```rux
// The annotation is what turns a circle into a Shape.
var shape: Shape = wheel;
PrintLine("holding a {} with area {}", shape.Name(), shape.Area());
```
`shape` holds a copy of the circle, together with a note of which type that copy is. Without the annotation, `var shape = wheel;` would simply be another `Circle`.
## Dynamic dispatch
Now put a rectangle in the same variable:
```rux
// Same variable, different type inside, so the same calls now run Rectangle's code.
shape = door;
PrintLine("holding a {} with area {}", shape.Name(), shape.Area());
```
The call is written the same way both times, yet it runs different code. `shape.Area()` follows the note to the `Area` of whatever type is inside right now:
```mermaid
flowchart LR
call["shape.Area()"] --> note{"Which type is
inside shape now?"}
note -- "a Circle" --> c["Circle's Area
3.14 × radius × radius"]
note -- "a Rectangle" --> r["Rectangle's Area
width × height"]
c --> result["a float64"]
r --> result
```
Choosing the function while the program runs, by the value inside rather than by anything written at the call, is called **dynamic dispatch**. Compare it with a call on a concrete type:
| Call | Type of the receiver | Which `Area` runs is decided |
| -------------- | -------------------- | ---------------------------------------------- |
| `wheel.Area()` | `Circle` | when the program is compiled — always Circle's |
| `shape.Area()` | `Shape` | while it runs — by the value inside |
## Many types in one array
An array needs one element type, so a circle and a rectangle cannot share one directly. Two `Shape`s can:
```rux
let shapes: Shape[2] = [wheel, door];
var total = 0.0;
for each in shapes {
total += each.Area();
}
PrintLine("total area {}", total);
```
The annotation `Shape[2]` makes each element a `Shape`, exactly as `var shape: Shape` did. The loop then calls `Area` on every element without knowing, or caring, which shape it is: 12.56 + 2.0 = 14.56.
## A copy, not a view
```rux
// An interface value holds a copy. Changing the original afterwards leaves the copy alone.
var balloon = Circle { radius: 1.0 };
let photo: Shape = balloon;
balloon.radius = 2.0;
PrintLine("the balloon's area is {}, the photo's is {}", balloon.Area(), photo.Area());
```
The balloon grows to an area of 12.56, but the photo still shows the radius-1 circle it was given, 3.14. The [Copy](https://rux-lang.dev/docs/learn/copy) rules apply as they would to any assignment. The next lesson, [Interface parameter](https://rux-lang.dev/docs/learn/interface-parameter), shows how to *borrow* a value through an interface instead.
## Only the promise is visible
Through a `Shape`, you can reach only what `Shape` promises. Even while a rectangle is inside, `shape.width` is rejected with `error: interface type 'Shape' has no member 'width'`, and a method of Rectangle's own, such as `IsSquare` from the previous lesson, is refused the same way. The variable might hold a circle the next time that line runs, and a circle has neither.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/InterfaceValue){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A variable can have an interface as its type. `let shape: Shape = wheel;` makes an interface
// value: it holds a copy of the circle, together with a note of which type that copy is.
//
// A call such as `shape.Area()` follows the note to Circle's own `Area`. Put a rectangle in the
// same variable and the very same call runs Rectangle's `Area` instead. Which function runs is
// decided while the program runs, by the value inside, not by anything written at the call. That
// is called dynamic dispatch, and it is what lets values of different types share one variable,
// one array, or one loop.
import Io::PrintLine;
interface Shape {
func Area() -> float64;
func Name() -> char8[..];
}
struct Circle {
radius: float64;
}
struct Rectangle {
width: float64;
height: float64;
}
extend Circle : Shape {
func Area(self: &Circle) -> float64 {
return 3.14 * self.radius * self.radius;
}
func Name(self: &Circle) -> char8[..] {
return "circle";
}
}
extend Rectangle : Shape {
func Area(self: &Rectangle) -> float64 {
return self.width * self.height;
}
func Name(self: &Rectangle) -> char8[..] {
return "rectangle";
}
}
func Main() -> int {
let wheel = Circle { radius: 2.0 };
let door = Rectangle { width: 1.0, height: 2.0 };
// The annotation is what turns a circle into a Shape.
var shape: Shape = wheel;
PrintLine("holding a {} with area {}", shape.Name(), shape.Area());
// Same variable, different type inside, so the same calls now run Rectangle's code.
shape = door;
PrintLine("holding a {} with area {}", shape.Name(), shape.Area());
// A circle and a rectangle cannot share an array: `let shapes = [wheel, door];` is refused
// with "array element 2 has type 'Rectangle', but element 1 established element type
// 'Circle'". Two Shapes can, and the annotation makes each element a Shape, as it did above.
let shapes: Shape[2] = [wheel, door];
var total = 0.0;
for each in shapes {
total += each.Area();
}
PrintLine("total area {}", total);
// An interface value holds a copy. Changing the original afterwards leaves the copy alone.
var balloon = Circle { radius: 1.0 };
let photo: Shape = balloon;
balloon.radius = 2.0;
PrintLine("the balloon's area is {}, the photo's is {}", balloon.Area(), photo.Area());
// Only what Shape promises can be reached through it. `shape.width` is rejected even while a
// rectangle is inside: "interface type 'Shape' has no member 'width'".
return 0;
}
```
## Run it
```sh
cd Examples/Interfaces/InterfaceValue
rux run
```
```text
holding a circle with area 12.56
holding a rectangle with area 2.0
total area 14.56
the balloon's area is 12.56, the photo's is 3.14
```
## Common mistakes
::warning
**Leaving out the annotation.**:br`var shape = wheel;` makes `shape` a `Circle`, and `shape = door;` then fails with `error: cannot assign 'Rectangle' to 'Circle'`. Write `var shape: Shape = wheel;` to ask for an interface value.
::
::warning
**Mixing types in an unannotated array.**:br`let shapes = [wheel, door];` takes its element type from the first element and is refused with `error: array element 2 has type 'Rectangle', but element 1 established element type 'Circle'`. Annotate it as `Shape[2]`.
::
::warning
**Reaching past the interface.**:br`shape.width` fails with `error: interface type 'Shape' has no member 'width'`, and `shape.IsSquare()` with `error: interface type 'Shape' has no member 'IsSquare'`. If every shape needs it, add it to the interface; otherwise call it on a `Rectangle`.
::
::warning
**Expecting the interface value to follow the original.**:br
An interface value holds a copy. Changing `balloon` afterwards does not change `photo`.
::
## Try it yourself
1. Add a `Triangle` that keeps the `Shape` promise, and put it in the array as a third element. What does the annotation become?
2. Loop over the array and print the name of the shape with the largest area.
3. In the balloon example, assign `balloon` to `photo` *after* changing the radius. Predict the output, then run it.
## Learn more
- [Interface implementation](https://rux-lang.dev/docs/lang/interfaces/overview#implementing-an-interface) in the Rux Reference
- [Copy](https://rux-lang.dev/docs/learn/copy) — why an interface value is a separate copy
- [Interface parameter](https://rux-lang.dev/docs/learn/interface-parameter) — borrowing through an interface instead of copying
- [Sum type](https://rux-lang.dev/docs/learn/sum-type) — the other way to hold "one of several types", when the list of types is fixed
# Interface parameter
::note
**You'll need**: [Interface value](https://rux-lang.dev/docs/learn/interface-value), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method)
::
The [Interface value](https://rux-lang.dev/docs/learn/interface-value) lesson stored a copy of a shape. Often you want something else: a function that works on the caller's *own* value, whatever its type, as long as it keeps a promise. A parameter written `&Gauge` or `&var Gauge` does exactly that. It borrows, as `&T` did in [Reference](https://rux-lang.dev/docs/learn/reference), and it accepts any type that implements `Gauge`.
## A function for any gauge
```rux
// Only reads, so a shared borrow is enough.
func Show(gauge: &Gauge) {
PrintLine("the {} reads {}", gauge.Label(), gauge.Read());
}
```
`Show` never learns whether it was handed a thermostat or a volume knob. It calls `Label` and `Read`, and dynamic dispatch runs the right type's code. The caller passes its own variable, with no `&` and no conversion — the conversion to the interface happens at the call:
```rux
Show(heating);
Show(music);
```
Nothing is copied: `Show` sees the caller's value as it is right now.
## Requirements that write
Turning a gauge up changes it, and the interface has to say so:
```rux
interface Gauge {
func Label() -> char8[..];
func Read() -> int32;
func Adjust(self: &var Self, amount: int32);
}
```
A requirement with no `self` only reads the value. One that changes it declares `self: &var Self`, where `Self` stands for whichever type implements the interface. Each implementation then takes `&var` of its own type, as in [Mutating method](https://rux-lang.dev/docs/learn/mutating-method):
```rux
// A volume knob stops at 10, however far it is turned.
func Adjust(self: &var Volume, amount: int32) {
self.level += amount;
if self.level > 10 {
self.level = 10;
}
}
```
| Requirement in `Gauge` | Implementation's receiver | Callable through |
| ---------------------------------------------- | ------------------------- | ------------------------- |
| `func Read() -> int32;` | `self: &Thermostat` | `&Gauge` and `&var Gauge` |
| `func Adjust(self: &var Self, amount: int32);` | `self: &var Thermostat` | `&var Gauge` only |
## Changes reach the caller
To call `Adjust`, the parameter must be a writable borrow:
```rux
func TurnUp(gauge: &var Gauge, amount: int32) {
gauge.Adjust(amount);
}
```
```mermaid
flowchart LR
caller["var heating
(a Thermostat)"] -- "TurnUp(heating, 5)
borrowed as &var Gauge" --> call["gauge.Adjust(5)"]
call -- "dispatch" --> impl["Thermostat's Adjust"]
impl -- "writes through the borrow" --> caller
```
Each type's own `Adjust` runs: the thermostat goes from 19 to 24, while the knob, turned from 7 by 5, stops at 10. And the last line, `heating.degrees is now 24`, proves the change landed on the caller's variable itself, not on a copy.
Side by side with the previous lesson:
| Written | What the code gets | Can it change the original? |
| ----------------------------- | ------------------ | --------------------------- |
| `let photo: Shape = balloon;` | a copy | no — it has its own copy |
| `gauge: &Gauge` | a read-only borrow | no |
| `gauge: &var Gauge` | a writable borrow | yes |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/InterfaceParameter){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A function can ask for an interface instead of a concrete type, and then any type that keeps
// the promise may be passed to it. The caller hands over its own thermostat or volume knob, and
// the conversion to the interface happens at the call.
//
// Written with `&`, the parameter borrows, exactly as `&T` did in the Reference lesson: nothing is
// copied, and the function sees the caller's value as it is right now. Written with `&var`, the
// borrow is writable, so a change made through the interface lands on the caller's own variable.
//
// The interface has to say which of its requirements write. A requirement with no `self` only
// reads the value; one that changes it declares `self: &var Self`, where `Self` stands for
// whichever type implements the interface. Leave that out and every `Adjust` below is refused:
// "method 'Adjust' of 'Thermostat' writes through its receiver, but requirement 'Adjust' of
// interface 'Gauge' only reads it".
import Io::PrintLine;
interface Gauge {
func Label() -> char8[..];
func Read() -> int32;
func Adjust(self: &var Self, amount: int32);
}
struct Thermostat {
degrees: int32;
}
struct Volume {
level: int32;
}
extend Thermostat : Gauge {
func Label(self: &Thermostat) -> char8[..] {
return "thermostat";
}
func Read(self: &Thermostat) -> int32 {
return self.degrees;
}
// Adjusting changes the value, so this method takes `&var` self.
func Adjust(self: &var Thermostat, amount: int32) {
self.degrees += amount;
}
}
extend Volume : Gauge {
func Label(self: &Volume) -> char8[..] {
return "volume";
}
func Read(self: &Volume) -> int32 {
return self.level;
}
// A volume knob stops at 10, however far it is turned.
func Adjust(self: &var Volume, amount: int32) {
self.level += amount;
if self.level > 10 {
self.level = 10;
}
}
}
// Only reads, so a shared borrow is enough.
func Show(gauge: &Gauge) {
PrintLine("the {} reads {}", gauge.Label(), gauge.Read());
}
// Calls `Adjust`, which changes the value, so the borrow must be writable too. Written as
// `gauge: &Gauge`, the call below is refused: "cannot call 'Adjust' through immutable reference
// '&Gauge'".
func TurnUp(gauge: &var Gauge, amount: int32) {
gauge.Adjust(amount);
}
func Main() -> int {
var heating = Thermostat { degrees: 19 };
var music = Volume { level: 7 };
Show(heating);
Show(music);
// Each type's own `Adjust` runs: the knob stops at 10, the thermostat does not.
TurnUp(heating, 5);
TurnUp(music, 5);
// The changes were made to these very variables, not to copies of them.
Show(heating);
Show(music);
PrintLine("heating.degrees is now {}", heating.degrees);
// A writable borrow needs a writable variable. Declare `heating` with `let` and the call is
// refused: "argument 1 to 'TurnUp' cannot borrow immutable 'heating' as '&var Gauge'".
return 0;
}
```
## Run it
```sh
cd Examples/Interfaces/InterfaceParameter
rux run
```
```text
the thermostat reads 19
the volume reads 7
the thermostat reads 24
the volume reads 10
heating.degrees is now 24
```
## Common mistakes
::warning
**A requirement that does not say it writes.**:br
Declare `Adjust` in the interface as `func Adjust(amount: int32);` and every implementation is refused: `error: method 'Adjust' of 'Thermostat' writes through its receiver, but requirement 'Adjust' of interface 'Gauge' only reads it`. Add `self: &var Self` to the requirement.
::
::warning
**Calling a writing method through a read-only borrow.**:br
With `gauge: &Gauge`, the call `gauge.Adjust(amount)` fails with `error: cannot call 'Adjust' through immutable reference '&Gauge'`. A function that changes the value needs `&var Gauge`.
::
::warning
**Passing a `let` variable to a `&var` parameter.**:br
A writable borrow needs a writable variable. Declare `heating` with `let` and `TurnUp(heating, 5)` fails with `error: argument 1 to 'TurnUp' cannot borrow immutable 'heating' as '&var Gauge'`. `Show(heating)` still works, because it only reads.
::
## Try it yourself
1. Add a `Fan` with a `speed` that keeps the `Gauge` promise, and whose `Adjust` never lets the speed drop below 0. Turn it down by 5 from 3.
2. Write `func Reset(gauge: &var Gauge)` that brings any gauge back to 0 using only `Read` and `Adjust`.
3. Declare `music` with `let`. Which calls in `Main` still compile, and why?
## Learn more
- [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — `&var` for concrete types
- [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) — `self: &var T`
- [Display](https://rux-lang.dev/docs/learn/display) — a standard interface whose method takes a `&var TextWriter` parameter
- [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) in the Rux Reference
# Display
::note
**You'll need**: [Interface parameter](https://rux-lang.dev/docs/learn/interface-parameter), [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible), [Propagate](https://rux-lang.dev/docs/learn/propagate)
::
`PrintLine("{}", value)` has printed numbers, text and booleans since the very first lessons. That is not special treatment for built-in types: each of them implements one interface, `Display` from the Text package. A type of your own prints the same way as soon as it implements `Display` too. This lesson gives a `Money` type the text `$12.05`.
## The Display interface
`Display` asks for a single method. In the Text package it is declared as:
```rux
func WriteDisplay(writer: &var TextWriter, spec: FormatSpec) -> ! FormatError;
```
| Part | What it is |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `writer: &var TextWriter` | Wherever the text is going — the console, a string, a buffer. An [interface parameter](https://rux-lang.dev/docs/learn/interface-parameter), borrowed writable because writing moves it along. |
| `spec: FormatSpec` | What the placeholder asked for, such as the width in `{:>8}`. |
| `-> ! FormatError` | Writing can fail — the console may be closed, a buffer may be full — so the method is a [unit fallible](https://rux-lang.dev/docs/learn/unit-fallible). |
These names come from two packages, so the lesson imports them, and its `Rux.toml` lists `Format` and `Text`:
```rux
import Format::WriteFormat;
import Io::PrintLine;
import Text::{ Display, FormatError, FormatSpec, TextWriter, WriteBytes };
```
## Writing the text
```rux
extend Money : Display {
// `spec` carries what the placeholder asked for, such as the width in `{:>8}`. This
// implementation ignores it, which a type is free to do.
func WriteDisplay(self: &Money, writer: &var TextWriter, spec: FormatSpec) -> ! FormatError {
var cents = self.cents;
if cents < 0 {
// One write that may fail. `?` hands a failure to the caller and stops here.
WriteBytes(writer, "-")?;
cents = -cents;
}
// `WriteFormat` is PrintLine's twin: the same placeholders, written into the writer.
// `{:02}` pads the cents to two digits with zeros, so 5 cents shows as `05`.
return WriteFormat(writer, "${}.{:02}", cents / 100, cents % 100);
}
}
```
The method is built from two writes, and either may fail:
- `WriteBytes(writer, "-")?` writes the minus sign. The `?` from [Propagate](https://rux-lang.dev/docs/learn/propagate) passes a failure straight back to the caller and stops there.
- `return WriteFormat(…)` writes the rest and hands back whatever that write reported — success or failure.
`WriteFormat` takes the same placeholders as `PrintLine`. Here `{:02}` pads the cents to two digits, so 5 cents becomes `05` and the coin prints as `$0.05`.
## What happens when you print
```mermaid
flowchart LR
p["PrintLine with a {}
and a Money"] --> d["Money's WriteDisplay"]
d --> w1["WriteBytes: '-'
(only when negative)"]
w1 --> w2["WriteFormat: '$12.05'"]
w2 --> out["the console"]
w1 -.->|"a failed write
returns early"| p
```
`PrintLine` checks every argument against `Display` — its arguments are, in effect, a list of interface values. Money now passes, so it goes into a placeholder like any number would:
```rux
PrintLine("price: {}", price);
PrintLine("refund: {}", refund);
PrintLine("coin: {}", coin);
PrintLine("{} back from {}", refund, price);
```
## Ignoring the spec
`spec` carries the placeholder's width, alignment, fill and precision. Money ignores it, which a type is free to do, but it has a visible effect: `PrintLine("[{:>10}]", price)` prints `[$12.05]` with no padding, because nothing in `WriteDisplay` applies the width. The [Format](https://rux-lang.dev/docs/learn/format) lesson covers what those placeholder options mean.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/Display){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `PrintLine("{}", value)` prints numbers, text and booleans because each of those types
// implements one interface, `Display` from the Text package. A type of your own prints the same
// way as soon as it implements `Display` too.
//
// `Display` asks for a single method, `WriteDisplay`. It is handed a writer, which stands for
// wherever the text is going, and it writes the value's text into it. Writing can fail (the
// console may be closed, a buffer may be full), so the method returns `! FormatError`, and a
// failure from any step is passed straight on with `?` or `return`.
import Format::WriteFormat;
import Io::PrintLine;
import Text::{ Display, FormatError, FormatSpec, TextWriter, WriteBytes };
// An amount of money, kept as whole cents so nothing is lost to rounding.
struct Money {
cents: int64;
}
extend Money : Display {
// `spec` carries what the placeholder asked for, such as the width in `{:>8}`. This
// implementation ignores it, which a type is free to do.
func WriteDisplay(self: &Money, writer: &var TextWriter, spec: FormatSpec) -> ! FormatError {
var cents = self.cents;
if cents < 0 {
// One write that may fail. `?` hands a failure to the caller and stops here.
WriteBytes(writer, "-")?;
cents = -cents;
}
// `WriteFormat` is PrintLine's twin: the same placeholders, written into the writer.
// `{:02}` pads the cents to two digits with zeros, so 5 cents shows as `05`.
return WriteFormat(writer, "${}.{:02}", cents / 100, cents % 100);
}
}
func Main() -> int {
let price = Money { cents: 1205 };
let refund = Money { cents: -350 };
let coin = Money { cents: 5 };
// Now Money goes into a placeholder like any number would.
PrintLine("price: {}", price);
PrintLine("refund: {}", refund);
PrintLine("coin: {}", coin);
PrintLine("{} back from {}", refund, price);
// Take away the `extend` and every line above is refused: "argument 2 to 'PrintLine' has
// type 'Money', but variadic parameter 'args' requires 'Display'".
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Format` and `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Interfaces/Display
rux run
```
```text
price: $12.05
refund: -$3.50
coin: $0.05
-$3.50 back from $12.05
```
## Common mistakes
::warning
**Printing a type that does not implement Display.**:br
Take away the `extend` and every `PrintLine` with a `Money` in it is refused: `error: argument 2 to 'PrintLine' has type 'Money', but variadic parameter 'args' requires 'Display'`.
::
::warning
**Dropping a write's result.**:br`WriteBytes(writer, "-");` without the `?` fails with `error: fallible result of type '! FormatError' is discarded`. Every write can fail, so each one is either passed on with `?` or returned.
::
::warning
**Forgetting the `return` on the last write.**:br`WriteFormat(writer, …);` on its own line is a discarded fallible too, with the same error. Write `return WriteFormat(…);`, so its success or failure becomes the method's own.
::
## Try it yourself
1. Give `struct Point { x: int32; y: int32; }` a `Display` that prints `(1, 2)`.
2. Print `price` with `PrintLine("[{:>10}]", price)` and explain why no padding appears.
3. Make a `Temperature` with a `celsius: int32` field print as `21 C`, and `-4 C` below zero.
## Learn more
- [Format](https://rux-lang.dev/docs/learn/format) — widths, alignment and fill in placeholders
- [Render](https://rux-lang.dev/docs/learn/render) — formatting into a `String` you keep
- [Propagate](https://rux-lang.dev/docs/learn/propagate) — the `?` used on each write
- The [Text](https://rux-lang.dev/docs/api/text) and [Format](https://rux-lang.dev/docs/api/format) packages in the API reference
# Equatable
::note
**You'll need**: [Interface](https://rux-lang.dev/docs/learn/interface), [Reference](https://rux-lang.dev/docs/learn/reference)
::
Two values can be "the same" in more than one sense. The fractions 1/2 and 2/4 are written differently, yet they stand for the same number, and a program that calls them different will give wrong answers. `Equatable`, from the Core package, is the interface a type implements to say what equality means for it — once, in one place, so that every search and every comparison agrees.
## The Equatable interface
`Equatable` asks for one method. In Core it is declared as:
```rux
func Equals(other: &Self) -> bool;
```
`Self` stands for whichever type is implementing it, so a `Fraction` is only ever compared with another `Fraction`. The lesson imports it with `import Core::Equatable;`, and its `Rux.toml` lists `Core` under `[Dependencies]`.
```rux
extend Fraction : Equatable {
// Cross-multiplying compares the two numbers without dividing, so no remainder is lost:
// 1/2 and 2/4 are equal because 1 * 4 == 2 * 2.
func Equals(self: &Fraction, other: &Fraction) -> bool {
return self.top * other.bottom == other.top * self.bottom;
}
}
```
With integers, dividing would make 1/2 and 1/3 both 0 — and so "equal". Cross-multiplying keeps everything whole: two fractions `a/b` and `c/d` are equal exactly when `a × d` equals `c × b`.
```rux
PrintLine("1/2 equals 2/4: {}", half.Equals(twoQuarters));
PrintLine("1/2 equals 1/3: {}", half.Equals(third));
```
## Three promises
Implementing `Equatable` is also a promise about how `Equals` behaves, and any code relying on it is entitled to assume all three:
| Promise | In words | Checked in the program by |
| ----------------------------- | -------------------------------------- | ------------------------------------------------------ |
| Every value equals itself | 1/2 is 1/2 | `half.Equals(half)` |
| The order does not matter | if 1/2 equals 2/4, then 2/4 equals 1/2 | `half.Equals(twoQuarters) == twoQuarters.Equals(half)` |
| Equality passes along a chain | 1/2 = 2/4 and 2/4 = 4/8, so 1/2 = 4/8 | the three `Equals` calls joined with `&&` |
A search, a duplicate check or a hash table relies on these without asking. A type that cannot keep them should not implement `Equatable` at all. Core's own documentation gives the example of a floating-point NaN, which does not even equal itself.
## One meaning, used everywhere
Because the meaning is settled once, every loop that asks agrees with it:
```rux
let pile: Fraction[4] = [third, twoQuarters, fourEighths, half];
var halves = 0;
for each in pile {
if each.Equals(half) {
halves += 1;
}
}
```
`twoQuarters`, `fourEighths` and `half` itself all count, so the pile holds 3 halves.
## Equals is not ==
`Equals` is a method, not the `==` operator, and implementing `Equatable` leaves `==` exactly as it was. That is easy to trip over: `half == twoQuarters` still compiles, but it compares the fields one by one — 1 against 2, 2 against 4 — and answers `false`. The [Structural equality](https://rux-lang.dev/docs/learn/structural-equality) lesson explains that field-by-field `==`, and [Operator overload](https://rux-lang.dev/docs/learn/operator-overload) shows how a type gives `==` a meaning of its own.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/Equatable){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Two values can be "the same" in more than one sense. The fractions 1/2 and 2/4 are written
// differently, yet they stand for the same number, and a program that calls them different will
// give wrong answers.
//
// `Equatable`, from Core, is the interface a type implements to say what equality means for it.
// It asks for one method, `Equals(other: &Self) -> bool`. `Self` stands for whichever type is
// implementing it, so a Fraction is only ever compared with another Fraction.
//
// Implementing it is also a promise about how `Equals` behaves, which any code relying on it is
// entitled to assume:
// - every value equals itself;
// - `a.Equals(b)` and `b.Equals(a)` always agree;
// - if a equals b, and b equals c, then a equals c.
import Core::Equatable;
import Io::PrintLine;
struct Fraction {
top: int32;
bottom: int32;
}
extend Fraction : Equatable {
// Cross-multiplying compares the two numbers without dividing, so no remainder is lost:
// 1/2 and 2/4 are equal because 1 * 4 == 2 * 2.
func Equals(self: &Fraction, other: &Fraction) -> bool {
return self.top * other.bottom == other.top * self.bottom;
}
}
func Main() -> int {
let half = Fraction { top: 1, bottom: 2 };
let twoQuarters = Fraction { top: 2, bottom: 4 };
let fourEighths = Fraction { top: 4, bottom: 8 };
let third = Fraction { top: 1, bottom: 3 };
PrintLine("1/2 equals 2/4: {}", half.Equals(twoQuarters));
PrintLine("1/2 equals 1/3: {}", half.Equals(third));
// The three promises, checked on these values.
PrintLine("itself: {}", half.Equals(half));
PrintLine("either way: {}", half.Equals(twoQuarters) == twoQuarters.Equals(half));
PrintLine("in a chain: {}", half.Equals(twoQuarters) && twoQuarters.Equals(fourEighths)
&& half.Equals(fourEighths));
// Because the meaning is settled once, in one place, every search agrees with it.
let pile: Fraction[4] = [third, twoQuarters, fourEighths, half];
var halves = 0;
for each in pile {
if each.Equals(half) {
halves += 1;
}
}
PrintLine("halves in the pile: {}", halves);
// `Equals` is a method, not the `==` operator. Implementing Equatable leaves `==` exactly as
// it was; the lessons on structural equality and operator overloading cover `==`.
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Interfaces/Equatable
rux run
```
```text
1/2 equals 2/4: true
1/2 equals 1/3: false
itself: true
either way: true
in a chain: true
halves in the pile: 3
```
## Common mistakes
::warning
**Expecting `==` to call `Equals`.**:br`half == twoQuarters` is `false`, while `half.Equals(twoQuarters)` is `true`. The operator compares fields; the method uses your definition. Call `Equals` when you mean it.
::
::warning
**Forgetting the import.**:br
Without `import Core::Equatable;`, the `extend` line fails with `error: interface 'Equatable' is not defined`. Core must also be listed in `Rux.toml`.
::
::warning
**Comparing with a different type.**:br`Self` means a `Fraction` compares only with a `Fraction`. `half.Equals(5)` fails with `error: argument 1 to 'Equals' has type 'int', but parameter 'other' requires '&Fraction'`.
::
## Try it yourself
1. Is `Fraction { top: -1, bottom: -2 }` equal to `half`? Work it out by cross-multiplying, then check.
2. Consider `Fraction { top: 0, bottom: 0 }`. Compare it with `half` and with `third`. Which of the three promises does it break?
3. Count how many fractions in `pile` equal `third`.
## Learn more
- [Comparable](https://rux-lang.dev/docs/learn/comparable) — the next step: not just "equal?", but "which comes first?"
- [Structural equality](https://rux-lang.dev/docs/learn/structural-equality) — what `==` does on a struct
- [Reference](https://rux-lang.dev/docs/learn/reference) — the `&` in `other: &Self`
- The [Core](https://rux-lang.dev/docs/api/core) package in the API reference
# Comparable
::note
**You'll need**: [Equatable](https://rux-lang.dev/docs/learn/equatable), [Enum](https://rux-lang.dev/docs/learn/enum), [Match expression](https://rux-lang.dev/docs/learn/match-expression)
::
[Equatable](https://rux-lang.dev/docs/learn/equatable) answers "are these the same?". Sorting, searching and finding the largest need a different question: **which of two comes first?** `Comparable`, from the Core package, is the interface a type implements to answer it.
Release numbers show why a type may need an order of its own. As text, "1.10.0" sorts before "1.9.4", because the character `1` comes before `9`. Compared part by part, as numbers, 1.10.0 is the newer release.
## Ordering: three answers in one
`Comparable` asks for one method. In Core it is declared as:
```rux
func Compare(other: &Self) -> Ordering;
```
The answer is not a `bool` but an `Ordering`, an [enum](https://rux-lang.dev/docs/learn/enum) from Core with three cases. One call answers "before, same or after?", where a `bool` would need two questions to tell "after" from "the same".
| Case | Means |
| ------------------- | --------------------------- |
| `Ordering::Less` | this value comes first |
| `Ordering::Equal` | neither comes first |
| `Ordering::Greater` | the other value comes first |
`Ordering` has small methods that ask about the answer: `IsLess`, `IsEqual`, `IsGreater`, `IsLessOrEqual` and `IsGreaterOrEqual`, plus `Reverse`, which swaps `Less` and `Greater`. The program uses two of them. Both names are imported with `import Core::{ Comparable, Ordering };`.
## Comparing part by part
A helper orders two plain numbers:
```rux
// Orders two numbers. Release compares its three parts with it.
func CompareNumbers(left: int32, right: int32) -> Ordering {
if left < right {
return Ordering::Less;
}
if left > right {
return Ordering::Greater;
}
return Ordering::Equal;
}
```
`Release` then compares its parts in order of importance, and the first part that differs decides:
```rux
extend Release : Comparable {
// The first part that differs decides; a later part matters only on a tie.
func Compare(self: &Release, other: &Release) -> Ordering {
let major = CompareNumbers(self.major, other.major);
if !major.IsEqual() {
return major;
}
let minor = CompareNumbers(self.minor, other.minor);
if !minor.IsEqual() {
return minor;
}
return CompareNumbers(self.patch, other.patch);
}
}
```
```mermaid
flowchart LR
major{"major
differs?"} -- "yes" --> a1["that answer"]
major -- "no, a tie" --> minor{"minor
differs?"}
minor -- "yes" --> a2["that answer"]
minor -- "no, a tie" --> patch["compare patch:
its answer is final"]
```
For 1.9.4 against 1.10.0, the majors tie at 1, and the minors decide: 9 is less than 10, so the answer is `Less` and patch is never looked at.
## Turning an answer into words
A [match expression](https://rux-lang.dev/docs/learn/match-expression) maps each case to a phrase, and the compiler checks that all three are covered:
```rux
func Describe(order: Ordering) -> char8[..] {
return match order {
.Less => "older than",
.Equal => "the same as",
.Greater => "newer than"
};
}
```
## Finding the newest
```rux
var newest = releases[0];
for each in releases {
if each.Compare(newest).IsGreater() {
newest = each;
}
}
```
Keep whichever compares greater, and after the loop `newest` is 2.0.1. Sorting works on the same idea: the [Sort](https://rux-lang.dev/docs/learn/sort) lesson's `SortBy` takes a function that answers with an `Ordering`.
## The promise
As with `Equatable`, implementing `Comparable` is a promise. For any pair exactly one of the three answers holds; swapping the two sides swaps `Less` and `Greater`; and if a comes before b and b before c, then a comes before c. A type that implements both interfaces should also keep them in step: `Compare` says `Equal` exactly when `Equals` says `true`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/Comparable){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `Comparable`, from Core, is the interface a type implements to say how its values are ordered:
// which of two comes first. It asks for one method:
// func Compare(other: &Self) -> Ordering
// The answer is not a `bool` but an `Ordering`, an enum from Core with three cases: `Less`,
// `Equal` and `Greater`. One call answers "before, same or after?", where a `bool` would need two
// questions to tell "after" from "the same".
//
// Release numbers show why a type may need its own order. As text, "1.10.0" sorts before "1.9.4",
// because the character 1 comes before 9. Compared part by part, as numbers, 1.10.0 is newer.
//
// The promise that comes with Comparable: exactly one of the three answers holds for any pair,
// swapping the two sides swaps `Less` and `Greater`, and if a < b and b < c then a < c.
import Core::{ Comparable, Ordering };
import Io::PrintLine;
struct Release {
major: int32;
minor: int32;
patch: int32;
}
// Orders two numbers. Release compares its three parts with it.
func CompareNumbers(left: int32, right: int32) -> Ordering {
if left < right {
return Ordering::Less;
}
if left > right {
return Ordering::Greater;
}
return Ordering::Equal;
}
extend Release : Comparable {
// The first part that differs decides; a later part matters only on a tie.
func Compare(self: &Release, other: &Release) -> Ordering {
let major = CompareNumbers(self.major, other.major);
if !major.IsEqual() {
return major;
}
let minor = CompareNumbers(self.minor, other.minor);
if !minor.IsEqual() {
return minor;
}
return CompareNumbers(self.patch, other.patch);
}
}
func Describe(order: Ordering) -> char8[..] {
return match order {
.Less => "older than",
.Equal => "the same as",
.Greater => "newer than"
};
}
func Main() -> int {
let old = Release { major: 1, minor: 9, patch: 4 };
let fresh = Release { major: 1, minor: 10, patch: 0 };
PrintLine("1.9.4 is {} 1.10.0", Describe(old.Compare(fresh)));
PrintLine("1.10.0 is {} 1.9.4", Describe(fresh.Compare(old)));
PrintLine("1.9.4 is {} 1.9.4", Describe(old.Compare(old)));
// Finding the newest: keep whichever compares greater.
let releases: Release[4] = [old, Release { major: 2, minor: 0, patch: 1 }, fresh,
Release { major: 2, minor: 0, patch: 0 }];
var newest = releases[0];
for each in releases {
if each.Compare(newest).IsGreater() {
newest = each;
}
}
PrintLine("newest: {}.{}.{}", newest.major, newest.minor, newest.patch);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Interfaces/Comparable
rux run
```
```text
1.9.4 is older than 1.10.0
1.10.0 is newer than 1.9.4
1.9.4 is the same as 1.9.4
newest: 2.0.1
```
## Common mistakes
::warning
**Expecting `<` after implementing Comparable.**:br`Compare` is a method, and implementing `Comparable` adds no operators. `old < fresh` fails with `error: operator '<' is not defined for 'Release'`, and the compiler adds the note "a struct is compared through the operators it declares, never by its representation". Use `old.Compare(fresh).IsLess()`, or declare `<` as in [Operator overload](https://rux-lang.dev/docs/learn/operator-overload).
::
::warning
**Comparing the parts in the wrong order.**:br
The compiler cannot catch this one. Compare `patch` first and 1.9.4 comes out newer than 1.10.0, because 4 is greater than 0. The most important part must be compared first, and a later part consulted only on a tie.
::
## Try it yourself
1. Find the *oldest* release in the array, using `IsLess`.
2. Print `old.Compare(fresh).Reverse()` through `Describe`, and compare it with `fresh.Compare(old)`.
3. Implement `Comparable` for `struct Date { year: int32; month: int32; day: int32; }` and sort out which of two birthdays comes first.
## Learn more
- [Enum](https://rux-lang.dev/docs/learn/enum) and [Match expression](https://rux-lang.dev/docs/learn/match-expression) — the tools `Ordering` is built from
- [Sort](https://rux-lang.dev/docs/learn/sort) and [Tree map](https://rux-lang.dev/docs/learn/tree-map) — standard code that relies on an `Ordering`
- [Derived operator](https://rux-lang.dev/docs/learn/derived-operator) — `<`, `>`, `<=` and `>=` on your own type
- The [Core](https://rux-lang.dev/docs/api/core) package in the API reference
# Structural equality
::note
**You'll need**: [Struct](https://rux-lang.dev/docs/learn/struct), [Tuple](https://rux-lang.dev/docs/learn/tuple), [Variant](https://rux-lang.dev/docs/learn/variant), [Comparison](https://rux-lang.dev/docs/learn/comparison)
::
`==` and `!=` work on more than numbers. Two structs of the same type can be compared without writing anything at all: they are equal when every field is equal. The same goes for tuples, arrays and variants, and for any mix of them. This is called **structural equality** — equal shape, equal parts, equal values — and it is the right answer for plain data such as points and dates.
## Field by field
```rux
let a = Point { x: 1, y: 2 };
let b = Point { x: 1, y: 2 };
let flipped = Point { x: 2, y: 1 };
```
`a` and `b` are two separate values, but field by field they are the same, so `a == b` is `true`. `flipped` has the same numbers in different fields: `x` is compared with `x` and `y` with `y`, so `a == flipped` is `false` and `a != flipped` is `true`.
## One level down, and the next
A field that is itself a struct is compared by the same rule, one level down:
```rux
let first = Segment { start: a, end: flipped };
let second = Segment { start: b, end: flipped };
PrintLine("segments: {}", first == second);
```
```mermaid
flowchart LR
seg["first == second
(Segment)"] --> s["start == start
(Point)"]
seg --> e["end == end
(Point)"]
s --> sx["x: 1 == 1"]
s --> sy["y: 2 == 2"]
e --> ex["x: 2 == 2"]
e --> ey["y: 1 == 1"]
```
The comparison only ever bottoms out in values that have their own `==`, such as integers. All four are equal, so the segments are too.
## Tuples, arrays and variants
```rux
PrintLine("tuples: {}", (a, true) == (b, true));
let left: int32[3] = [1, 2, 3];
let right: int32[3] = [1, 2, 4];
PrintLine("arrays: {}", left == right);
```
```rux
let warm = Reading::Celsius(21);
let alsoWarm = Reading::Celsius(21);
let unknown = Reading::Missing;
```
| Kind of value | Equal when | Example in the program |
| ------------- | ----------------------------------------- | ---------------------------------------------------------- |
| Struct | every field is equal, compared in order | `a == b` is `true` |
| Tuple | every element is equal | `(a, true) == (b, true)` is `true` |
| Array | every element is equal | `[1, 2, 3]` vs `[1, 2, 4]` is `false` |
| Variant | the case matches, and so does its payload | `warm == alsoWarm` is `true`, `warm == unknown` is `false` |
## Equality, not order
Structural comparison stops at `==` and `!=`. There is no field-by-field `<`: is a point with a bigger `x` but a smaller `y` "less"? The compiler will not guess. `a < b` fails with `error: operator '<' is not defined for 'Point'`, and its note says why: "a struct is compared through the operators it declares, never by its representation".
Structural equality also has its limits. As [Equatable](https://rux-lang.dev/docs/learn/equatable) showed, 1/2 and 2/4 have different fields but mean the same number. When a type means something else by "equal", it declares its own `==`, which the next lesson, [Operator overload](https://rux-lang.dev/docs/learn/operator-overload), shows.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/StructuralEquality){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `==` and `!=` work on more than numbers. Two structs of the same type can be compared without
// writing anything: they are equal when every field is equal, compared in order. The same goes
// for tuples, arrays and variants, and for any mix of them, because comparing a field that is
// itself a struct simply repeats the rule one level down.
//
// This is called structural equality: equal shape, equal parts, equal values. It is the right
// answer for plain data such as points and dates. When a type means something else by "equal",
// as 1/2 and 2/4 did in the Equatable lesson, it defines its own `==`, which the next lesson
// shows.
import Io::PrintLine;
struct Point {
x: int32;
y: int32;
}
struct Segment {
start: Point;
end: Point;
}
variant Reading {
Missing,
Celsius(int32)
}
func Main() -> int {
let a = Point { x: 1, y: 2 };
let b = Point { x: 1, y: 2 };
let flipped = Point { x: 2, y: 1 };
// Two separate values, field by field the same.
PrintLine("a == b: {}", a == b);
PrintLine("a == flipped: {}", a == flipped);
PrintLine("a != flipped: {}", a != flipped);
// A struct inside a struct is compared by the same rule.
let first = Segment { start: a, end: flipped };
let second = Segment { start: b, end: flipped };
PrintLine("segments: {}", first == second);
// Tuples and arrays compare element by element.
PrintLine("tuples: {}", (a, true) == (b, true));
let left: int32[3] = [1, 2, 3];
let right: int32[3] = [1, 2, 4];
PrintLine("arrays: {}", left == right);
// Variants are equal when the case matches and so does the payload.
let warm = Reading::Celsius(21);
let alsoWarm = Reading::Celsius(21);
let unknown = Reading::Missing;
PrintLine("same reading: {}", warm == alsoWarm);
PrintLine("vs missing: {}", warm == unknown);
// Every part must have its own `==`, and a slice such as `char8[..]` has none. Add a
// `name: char8[..]` field to Point and `a == b` is refused: "structural equality for 'Point'
// is unavailable because element type 'char8[..]' has no '==' operator".
return 0;
}
```
## Run it
```sh
cd Examples/Interfaces/StructuralEquality
rux run
```
```text
a == b: true
a == flipped: false
a != flipped: true
segments: true
tuples: true
arrays: false
same reading: true
vs missing: false
```
## Common mistakes
::warning
**A field with no `==` of its own.**:br
Every part must be comparable, and a slice such as `char8[..]` is not. Add `name: char8[..]` to `Point` and `a == b` is refused: `error: structural equality for 'Point' is unavailable because element type 'char8[..]' has no '==' operator`. The compiler's help suggests the way out: compare the fields you can explicitly, or declare `==` for the type yourself.
::
::warning
**Expecting `<` on a struct.**:br
Only `==` and `!=` come for free. `a < b` fails with `error: operator '<' is not defined for 'Point'`.
::
::warning
**Expecting meaning, not shape.**:br
A `Fraction` of 1/2 and one of 2/4 are not `==`, because their fields differ. Field-by-field equality knows nothing about what a type stands for.
::
## Try it yourself
1. Make a third segment with `start` and `end` swapped. Is it `==` to `first`?
2. Add a case `Fahrenheit(int32)` to `Reading` and compare `Reading::Celsius(21)` with `Reading::Fahrenheit(21)`. Predict the answer first.
3. Compare two arrays of `Point`, `Point[2]`, that differ only in their last `y`.
## Learn more
- [Comparison](https://rux-lang.dev/docs/learn/comparison) — `==` and `!=` on numbers
- [Struct](https://rux-lang.dev/docs/learn/struct), [Tuple](https://rux-lang.dev/docs/learn/tuple) and [Variant](https://rux-lang.dev/docs/learn/variant) — the values compared here
- [Operator overload](https://rux-lang.dev/docs/learn/operator-overload) — giving a type its own `==`
- [Comparison operators](https://rux-lang.dev/docs/lang/expressions/comparison) in the Rux Reference
# Operator overload
::note
**You'll need**: [Method](https://rux-lang.dev/docs/learn/method), [Constructor](https://rux-lang.dev/docs/learn/constructor), [Structural equality](https://rux-lang.dev/docs/learn/structural-equality)
::
The operators from the [Operators](https://rux-lang.dev/docs/learn/operators) part are not reserved for numbers. A type of your own can say what `+`, `==` or `<` mean for it, by declaring a function whose name is the operator. The point is not cleverness: money adds to money, and `rent + food` says that plainly where `Money { cents: rent.cents + food.cents }` buries it.
## An operator is a function
Operator functions go in an ordinary `extend` block, next to the [constructor](https://rux-lang.dev/docs/learn/constructor):
```rux
// `self` is the left operand and the second parameter is the right one. Taking the right
// operand by value lets it be a value built on the spot, as in `rent == Money(120000)`.
// `other: &Money` works as well, but a borrow needs a named value on the right.
func +(self: &Money, other: Money) -> Money {
return Money { cents: self.cents + other.cents };
}
```
The function's name is the operator itself. When the compiler meets `rent + food` with a `Money` on the left, it calls this function with `self` borrowing `rent` and `other` holding `food`:
| You write | The compiler calls | `self` | Right operand | Result |
| ----------------------- | ------------------ | ------ | ------------- | ------- |
| `rent + food` | `+` | `rent` | `food` | `Money` |
| `rent - food` | `-` | `rent` | `food` | `Money` |
| `rent * 3` | `*` | `rent` | `3` | `Money` |
| `rent == Money(120000)` | `==` | `rent` | a new `Money` | `bool` |
| `food < rent` | `<` | `food` | `rent` | `bool` |
Once `+` is declared, the compound form works too: `sum += food` adds to a `var sum` of type `Money`.
## The two sides need not match
```rux
// The two sides need not have the same type. Money times a count makes sense; money times
// money does not, so only this one exists.
func *(self: &Money, count: int64) -> Money {
return Money { cents: self.cents * count };
}
```
The second parameter's type is whatever the right operand should be. Money times a count makes sense; money times money does not, so `rent * rent` is simply not defined. The order matters as well: `self` is always the *left* operand, so this function makes `rent * 3` work but not `3 * rent`.
## Comparisons answer with bool
```rux
// Comparisons answer with `bool`, not with Money.
func ==(self: &Money, other: Money) -> bool {
return self.cents == other.cents;
}
func <(self: &Money, other: Money) -> bool {
return self.cents < other.cents;
}
```
A declared `==` replaces the field-by-field comparison from [Structural equality](https://rux-lang.dev/docs/learn/structural-equality). For `Money` both give the same answer, but a type like the `Fraction` from [Equatable](https://rux-lang.dev/docs/learn/equatable) could now make 1/2 `==` 2/4. And from these two, the compiler can work out `!=`, `>`, `<=` and `>=` — the subject of the next lesson.
## Only what you declare
Money has no `/`, so `rent / 2` is an error rather than something invented on Money's behalf. Each operator you leave out is a decision: there is no sensible meaning for money divided by money, and dividing by a count would raise the question of what to do with the leftover cent.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/OperatorOverload){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The operators from the Operators part are not reserved for numbers. A type of your own can say
// what `+`, `==` or `<` mean for it, by declaring a function whose name is the operator.
//
// The point is not cleverness. Money adds to money, and `rent + food` says that plainly where
// `Money { cents: rent.cents + food.cents }` buries it. A declared `==` also replaces the
// field-by-field comparison from the previous lesson, so a type decides what "equal" means.
import Io::PrintLine;
// Money as a whole number of cents, so no fraction of a cent ever goes missing.
struct Money {
cents: int64;
}
extend Money {
func Money(cents: int64) -> Money {
return Money { cents: cents };
}
// `self` is the left operand and the second parameter is the right one. Taking the right
// operand by value lets it be a value built on the spot, as in `rent == Money(120000)`.
// `other: &Money` works as well, but a borrow needs a named value on the right.
func +(self: &Money, other: Money) -> Money {
return Money { cents: self.cents + other.cents };
}
func -(self: &Money, other: Money) -> Money {
return Money { cents: self.cents - other.cents };
}
// The two sides need not have the same type. Money times a count makes sense; money times
// money does not, so only this one exists.
func *(self: &Money, count: int64) -> Money {
return Money { cents: self.cents * count };
}
// Comparisons answer with `bool`, not with Money.
func ==(self: &Money, other: Money) -> bool {
return self.cents == other.cents;
}
func <(self: &Money, other: Money) -> bool {
return self.cents < other.cents;
}
}
func Main() -> int {
let rent = Money(120000);
let food = Money(35050);
// Each operator below calls one of the functions above.
let total = rent + food;
let left = rent - food;
let quarter = rent * 3;
PrintLine("rent + food = {} cents", total.cents);
PrintLine("rent - food = {} cents", left.cents);
PrintLine("rent * 3 = {} cents", quarter.cents);
PrintLine("rent == 120000 cents: {}", rent == Money(120000));
PrintLine("food < rent: {}", food < rent);
// Only the operators declared exist. Money has no `/`, so `rent / 2` is an error rather
// than something invented on Money's behalf.
return 0;
}
```
## Run it
```sh
cd Examples/Interfaces/OperatorOverload
rux run
```
```text
rent + food = 155050 cents
rent - food = 84950 cents
rent * 3 = 360000 cents
rent == 120000 cents: true
food < rent: true
```
## Common mistakes
::warning
**Using an operator that was never declared.**:br`rent / 2` fails with `error: operator '/' cannot combine left operand 'Money' with right operand 'int'`. Declare `/` if Money should have it.
::
::warning
**Putting the operands the other way round.**:br`*` was declared with `Money` on the left, so `3 * rent` fails with `error: operator '*' cannot combine left operand 'int' with right operand 'Money'`. Write `rent * 3`.
::
::warning
**Borrowing the right operand.**:br
Declare `==` as `func ==(self: &Money, other: &Money) -> bool` and `rent == Money(120000)` fails with `error: cannot pass 'Money' to parameter of type '&Money'`: a borrow needs a named value, not one built on the spot. Take the right operand by value, as the lesson does.
::
## Try it yourself
1. Declare `/` taking an `int64` count, and print `rent / 3`. What happens to the leftover cent?
2. Use `+=` in a loop to add up an array of `Money` values.
3. Write a `Vector { x: float64; y: float64; }` with `+` for two vectors and `*` for a vector and a `float64`.
## Learn more
- [Derived operator](https://rux-lang.dev/docs/learn/derived-operator) — `!=`, `>`, `<=` and `>=` for free
- [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic) — the operators on numbers
- [Constructor](https://rux-lang.dev/docs/learn/constructor) — the `Money(120000)` used on the right of `==`
- [Arithmetic operators](https://rux-lang.dev/docs/lang/expressions/arithmetic) in the Rux Reference
# Derived operator
::note
**You'll need**: [Operator overload](https://rux-lang.dev/docs/learn/operator-overload)
::
There are six comparison operators, but a type declares at most two of them. From `==` the compiler derives `!=`, and from `<` together with `==` it derives the other three. So there is less to write — and, more importantly, the six can never disagree with one another.
## Two declared
Players are ranked by score alone, so two players with the same score tie, whatever their names:
```rux
extend Player {
func ==(self: &Player, other: Player) -> bool {
return self.score == other.score;
}
func <(self: &Player, other: Player) -> bool {
return self.score < other.score;
}
}
```
`Player` could not use field-by-field `==` from [Structural equality](https://rux-lang.dev/docs/learn/structural-equality) at all, because its `name` is a slice. The declared `==` is what makes players comparable in the first place.
## Four derived
```rux
PrintLine("Ann != Bob {}", ann != bob);
PrintLine("Bob > Ann {}", bob > ann);
PrintLine("Ann <= Cy {}", ann <= cy);
PrintLine("Ann >= Bob {}", ann >= bob);
```
None of these four operators is declared on `Player`. The compiler rewrites each one in terms of the two that are:
| You write | The compiler uses | Ann 7, Bob 9, Cy 7 |
| --------- | ----------------- | ----------------------- |
| `a != b` | `!(a == b)` | `ann != bob` is `true` |
| `a > b` | `b < a` | `bob > ann` is `true` |
| `a <= b` | `a < b || a == b` | `ann <= cy` is `true` |
| `a >= b` | `b < a || a == b` | `ann >= bob` is `false` |
```mermaid
flowchart LR
eq["== (declared)"] --> ne["!="]
lt["< (declared)"] --> gt[">"]
lt --> le["<="]
eq --> le
lt --> ge[">="]
eq --> ge
```
Notice `ann <= cy`: Ann is not less than Cy, but the two tie at 7, so the `==` half makes it `true`.
## Why they cannot disagree
If a type wrote all six by hand, a slip in one — `>=` comparing names while `<` compares scores — would make `a >= b` and `a < b` true at once, and any code that sorts or searches would quietly misbehave. Deriving the four from two leaves nothing to slip.
A type may still declare one of the derived operators itself, and then its own declaration is used instead. That is rarely a good idea: it is exactly how the six come to disagree.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/DerivedOperator){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// There are six comparison operators, but a type declares at most two of them. From `==` the
// compiler derives `!=`, and from `<` together with `==` it derives the other three:
// a != b means !(a == b)
// a > b means b < a
// a <= b means a < b || a == b
// a >= b means b < a || a == b
// So there is less to write, and the six can never disagree with one another.
//
// A type may still declare one of the derived operators itself, and then its own declaration is
// used instead. That is rarely a good idea: it is exactly how the six come to disagree.
import Io::PrintLine;
// Players are ranked by score alone. Two players with the same score tie, whatever their names.
struct Player {
name: char8[..];
score: int32;
}
extend Player {
func ==(self: &Player, other: Player) -> bool {
return self.score == other.score;
}
func <(self: &Player, other: Player) -> bool {
return self.score < other.score;
}
}
func Main() -> int {
let ann = Player { name: "Ann", score: 7 };
let bob = Player { name: "Bob", score: 9 };
let cy = Player { name: "Cy", score: 7 };
// The two that were declared.
PrintLine("Ann == Cy {}", ann == cy);
PrintLine("Ann < Bob {}", ann < bob);
// The four that were derived.
PrintLine("Ann != Bob {}", ann != bob);
PrintLine("Bob > Ann {}", bob > ann);
PrintLine("Ann <= Cy {}", ann <= cy);
PrintLine("Ann >= Bob {}", ann >= bob);
// Derivation needs something to start from. Delete `<` and `bob > ann` is refused:
// "operator '>' is not defined for 'Player'", with the hint "declare '>' on 'Player', or the
// '<' it is derived from". And `Player` could not use field-by-field `==` at all, since its
// `name` is a slice; the declared `==` is what makes players comparable.
return 0;
}
```
## Run it
```sh
cd Examples/Interfaces/DerivedOperator
rux run
```
```text
Ann == Cy true
Ann < Bob true
Ann != Bob true
Bob > Ann true
Ann <= Cy true
Ann >= Bob false
```
## Common mistakes
::warning
**Nothing to derive from.**:br
Delete `<` and `bob > ann` is refused with `error: operator '>' is not defined for 'Player'`, together with the hint "declare '>' on 'Player', or the '<' it is derived from". `ann < bob` fails the same way.
::
::warning
**Deleting `==` from a type that cannot compare its fields.**:br
Without the declared `==`, `ann == cy` falls back to structural equality, which `Player` cannot have: `error: structural equality for 'Player' is unavailable because element type 'char8[..]' has no '==' operator`. `!=`, `<=` and `>=` are lost with it.
::
::warning
**Declaring a derived operator that disagrees.**:br
A hand-written `!=` replaces the derived one. Declare it to return `true` always, and `ann != cy` and `ann == cy` are both `true`. Leave `!=`, `>`, `<=` and `>=` to the compiler.
::
## Try it yourself
1. Add `let dee = Player { name: "Dee", score: 9 };`. Predict all six comparisons between Bob and Dee, then print them.
2. Which of the six already work on `Money` from the [Operator overload](https://rux-lang.dev/docs/learn/operator-overload) lesson? Try each one.
3. Change the ranking so that a *lower* score is better, as in golf. How many functions did you have to change?
## Learn more
- [Operator overload](https://rux-lang.dev/docs/learn/operator-overload) — declaring `==` and `<` in the first place
- [Comparable](https://rux-lang.dev/docs/learn/comparable) — ordering through a method that answers with an `Ordering`
- [Comparison](https://rux-lang.dev/docs/learn/comparison) — the six operators on numbers
- [Comparison operators](https://rux-lang.dev/docs/lang/expressions/comparison) in the Rux Reference
# Indexer
::note
**You'll need**: [Operator overload](https://rux-lang.dev/docs/learn/operator-overload), [Enum value](https://rux-lang.dev/docs/learn/enum-value), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method)
::
Square brackets are an operator too. A type that declares `func []` can be read as `week[day]`, and one that also declares `func []=` can be written as `week[day] = 21`. They are declared in an `extend` block, just like `+` and `==` in [Operator overload](https://rux-lang.dev/docs/learn/operator-overload). The index need not be a number: here a week of temperatures is indexed by the day itself.
## The type underneath
```rux
// The highest temperature of each day, stored in an ordinary array.
struct Week {
highs: int32[7];
}
```
A `Week` is an ordinary array in a struct. The indexer is what lets a reader write `week[Day::Friday]` instead of `week.highs[4]`, and lets the type decide which indexes exist.
## Reading and writing
```rux
// Reading: `week[day]` calls this and gives back a copy of the value.
func [](self: &Week, day: Day) -> int32 {
return self.highs[day as uint];
}
// Writing: `week[day] = value` calls this. It changes the week, so it takes `&var` self.
func []=(self: &var Week, day: Day, value: int32) {
self.highs[day as uint] = value;
}
```
`day as uint` turns the enum case into its position, 0 for `Monday` through 6 for `Sunday`, as in [Enum value](https://rux-lang.dev/docs/learn/enum-value). The two functions split the work like this:
| You write | The compiler calls | Receiver | Gets |
| ------------------------ | ------------------ | ----------------- | ------------------------ |
| `week[Day::Friday]` | `[]` | `self: &Week` | the day |
| `week[Day::Friday] = 25` | `[]=` | `self: &var Week` | the day and the value 25 |
Writing changes the week, so `[]=` takes a `&var` receiver, as a [mutating method](https://rux-lang.dev/docs/learn/mutating-method) does, and `week` must be declared with `var`.
```rux
var week = Week { highs: [0; 7] };
week[Day::Monday] = 18;
week[Day::Tuesday] = 21;
week[Day::Friday] = 25;
```
A day nobody wrote, such as Sunday, still holds the `0` it started with.
## An index that cannot be wrong
Because the index is a `Day`, an index the week does not have cannot even be written. `week[9]` is not an out-of-range day caught while the program runs — it is refused when the program is compiled, because a plain number is not a `Day`. The type of the index is part of the design.
## Two separate functions
```rux
// Reading and writing are two separate functions, so changing a value in place takes both.
week[Day::Friday] = week[Day::Friday] - 3;
```
```mermaid
flowchart LR
read["week[Day::Friday]
calls [] → 25"] --> sub["25 - 3 = 22"]
sub --> write["week[Day::Friday] = 22
calls []="]
```
The right-hand side reads through `[]`, and the assignment writes through `[]=`. Neither function both reads and writes, which is why the shorter `week[Day::Friday] -= 3` is refused (see below).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/Indexer){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Square brackets are an operator too. A type that declares `func []` can be read as `week[day]`,
// and one that also declares `func []=` can be written as `week[day] = 21`. They are declared in
// an `extend` block like `+` and `==` were.
//
// The index need not be a number. Here a week of temperatures is indexed by day, so a reader sees
// `week[Day::Friday]`, and an index the week does not have, such as 9, cannot even be written:
// a plain number is not a `Day`.
import Io::PrintLine;
enum Day {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
// The highest temperature of each day, stored in an ordinary array.
struct Week {
highs: int32[7];
}
extend Week {
// Reading: `week[day]` calls this and gives back a copy of the value.
func [](self: &Week, day: Day) -> int32 {
return self.highs[day as uint];
}
// Writing: `week[day] = value` calls this. It changes the week, so it takes `&var` self.
func []=(self: &var Week, day: Day, value: int32) {
self.highs[day as uint] = value;
}
}
func Main() -> int {
var week = Week { highs: [0; 7] };
week[Day::Monday] = 18;
week[Day::Tuesday] = 21;
week[Day::Friday] = 25;
PrintLine("Monday {}, Tuesday {}, Friday {}", week[Day::Monday], week[Day::Tuesday],
week[Day::Friday]);
// Reading and writing are two separate functions, so changing a value in place takes both.
week[Day::Friday] = week[Day::Friday] - 3;
PrintLine("Friday, corrected: {}", week[Day::Friday]);
// A day nobody wrote still holds the zero it started with.
PrintLine("Sunday {}", week[Day::Sunday]);
// Writing `week[Day::Friday] -= 3` is refused, because no single function both reads and
// writes: "operator '-=' cannot read and write through the '[]' operator on 'Week' at once".
return 0;
}
```
## Run it
```sh
cd Examples/Interfaces/Indexer
rux run
```
```text
Monday 18, Tuesday 21, Friday 25
Friday, corrected: 22
Sunday 0
```
## Common mistakes
::warning
**A compound assignment through the brackets.**:br`week[Day::Friday] -= 3` fails with `error: operator '-=' cannot read and write through the '[]' operator on 'Week' at once`. Spell it out as `week[Day::Friday] = week[Day::Friday] - 3`.
::
::warning
**Indexing with the wrong type.**:br`week[9]` fails with `error: no '[]' on 'Week' accepts an index of type 'int'`. The indexer takes a `Day`, so write `week[Day::Sunday]`.
::
::warning
**Writing to a `let` value.**:br
Declare `week` with `let` and every assignment fails with `error: cannot modify immutable variable 'week'`. `[]=` takes `&var self`, so the variable must be a `var`. Reading through `[]` still works on a `let`.
::
## Try it yourself
1. Add `func Warmest(self: &Week) -> int32` that returns the highest of the seven temperatures.
2. Make `[]=` refuse temperatures above 60 by leaving the old value in place.
3. Write a `Scores` type indexed by a `Player` enum of your own, with both `[]` and `[]=`.
## Learn more
- [Operator overload](https://rux-lang.dev/docs/learn/operator-overload) — the other operators a type can declare
- [Enum value](https://rux-lang.dev/docs/learn/enum-value) — `day as uint`
- [Array](https://rux-lang.dev/docs/learn/array) — indexing with a number
- [Array access](https://rux-lang.dev/docs/lang/arrays/overview#indexing) in the Rux Reference
# Iterator
::note
**You'll need**: [Interface](https://rux-lang.dev/docs/learn/interface), [For](https://rux-lang.dev/docs/learn/for), [Optional](https://rux-lang.dev/docs/learn/optional), [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit)
::
`for` has walked ranges and arrays since the [For](https://rux-lang.dev/docs/learn/for) lesson. It can walk a type of your own too, once that type has one method, `Next`, which hands out one item per call and an [optional](https://rux-lang.dev/docs/learn/optional) `none` when there are no more. A type with such a method is called an **iterator**. This lesson builds one that counts down to liftoff.
## The Next method
```rux
// Counts down from a starting number to 1.
struct Countdown {
remaining: int32;
}
extend Countdown : Iterator {
// `&var`, because handing out an item moves the countdown along.
func Next(self: &var Countdown) -> int32? {
if self.remaining == 0 {
return none;
}
let current = self.remaining;
self.remaining -= 1;
return current;
}
}
```
Three things make this an iterator:
| Part | Why |
| ---------------------- | -------------------------------------------------------------------------------- |
| the name `Next` | `for` looks for a method with exactly this name |
| `self: &var Countdown` | handing out an item moves the countdown along, so `Next` must write |
| `-> int32?` | an item, or `none` for "no more". The item type is whatever the optional carries |
## The role has a name
`extend Countdown : Iterator` uses the `Iterator` interface from Core, imported with `import Core::Iterator;`. But that interface lists no methods: an interface cannot spell out an item type that each iterator chooses for itself, so `for` goes by the shape of `Next` alone. Writing `: Iterator` names the role for a reader and records what the type is meant to be. Leave it out and the countdown still works with `for`.
## What for does
```rux
// `for` calls `Next` until it sees `none`. The parentheses keep the struct's braces apart
// from the loop body's.
Print("for: ");
for second in (Countdown { remaining: 5 }) {
Print(" {}", second);
}
PrintLine(" liftoff");
```
```mermaid
flowchart LR
start["for second in countdown"] --> next["call Next()"]
next --> q{"an item,
or none?"}
q -- "an item" --> body["run the body
with second = the item"]
body --> next
q -- "none" --> done["leave the loop"]
```
There is no magic in it. The same walk written by hand is a [`loop`](https://rux-lang.dev/docs/learn/loop) with [`?? break`](https://rux-lang.dev/docs/learn/coalesce-exit):
```rux
var countdown = Countdown { remaining: 5 };
Print("by hand:");
loop {
let second = countdown.Next() ?? break;
Print(" {}", second);
}
PrintLine(" liftoff");
```
Both print `5 4 3 2 1 liftoff`.
## Used up as it goes
```rux
// An iterator is used up as it goes. Once it has said `none`, it keeps saying `none`.
PrintLine("anything left? {}", countdown.Next() is int32);
```
After the hand-written loop, `countdown.remaining` is 0, so every further call answers `none`, and `is int32` reports `false`. An iterator is a position in a walk, not a collection: to count down again, you make a new `Countdown`. The next lesson, [Iterable](https://rux-lang.dev/docs/learn/iterable), is about collections that can hand out a fresh iterator whenever one is needed.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/Iterator){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `for` has walked ranges and arrays since the Control flow part. It can walk a type of your own
// too, once that type has one method:
// func Next(self: &var T) -> Item?
// Each call hands out the next item, or `none` when there are no more. `for` calls `Next` again
// and again, runs the loop body with every item it gets, and stops at the first `none`. The item
// type is whatever the optional carries: `int32` here.
//
// A type with such a `Next` is called an iterator. `extend Countdown : Iterator` names that role
// for a reader, but Core's `Iterator` interface lists no methods, because an interface cannot
// spell out an item type that each iterator chooses for itself. `for` goes by `Next` alone.
import Core::Iterator;
import Io::{ Print, PrintLine };
// Counts down from a starting number to 1.
struct Countdown {
remaining: int32;
}
extend Countdown : Iterator {
// `&var`, because handing out an item moves the countdown along.
func Next(self: &var Countdown) -> int32? {
if self.remaining == 0 {
return none;
}
let current = self.remaining;
self.remaining -= 1;
return current;
}
}
func Main() -> int {
// `for` calls `Next` until it sees `none`. The parentheses keep the struct's braces apart
// from the loop body's.
Print("for: ");
for second in (Countdown { remaining: 5 }) {
Print(" {}", second);
}
PrintLine(" liftoff");
// The same walk by hand. This is all that `for` does.
var countdown = Countdown { remaining: 5 };
Print("by hand:");
loop {
let second = countdown.Next() ?? break;
Print(" {}", second);
}
PrintLine(" liftoff");
// An iterator is used up as it goes. Once it has said `none`, it keeps saying `none`.
PrintLine("anything left? {}", countdown.Next() is int32);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Interfaces/Iterator
rux run
```
```text
for: 5 4 3 2 1 liftoff
by hand: 5 4 3 2 1 liftoff
anything left? false
```
## Common mistakes
::warning
**A read-only `Next`.**:br`func Next(self: &Countdown)` is refused: `error: iterator method 'Next' on 'Countdown' must take a mutable receiver`, with the note "advancing an iterator writes it, so 'Next' cannot borrow its receiver read-only".
::
::warning
**Returning the item without an optional.**:br
With `-> int32` instead of `-> int32?`, there is no way to say "no more", and `for` refuses the type: `error: cannot iterate over 'Countdown'`, with the note "type 'Countdown' declares 'Next', but not as 'func Next(self: \&var Countdown) -> T?' returning a native optional".
::
::warning
**Leaving out the parentheses.**:br`for second in Countdown { remaining: 5 } {` takes the struct's `{` for the start of the loop body, and the parser stops with `error: expected ';' after expression, but found ':'`. Wrap a struct literal in parentheses after `in`.
::
::warning
**A different method name.**:br`for` looks for `Next` by name. Call it `Step` and the loop fails with `error: cannot iterate over 'Countdown'`, and the help "iterate an array, a slice, a range, or a type declaring 'Next' or 'Iterate'".
::
## Try it yourself
1. Write an `Evens` iterator with `next` and `limit` fields that hands out 0, 2, 4, … up to `limit`.
2. Change `Countdown` so that it hands out 0 as its last item before `none`.
3. Declare `var countdown = Countdown { remaining: 3 };`, walk it with `for`, and then print `countdown.remaining`. Was the variable itself used up?
## Learn more
- [Iterable](https://rux-lang.dev/docs/learn/iterable) — a collection that hands out fresh iterators
- [For](https://rux-lang.dev/docs/learn/for) — the loop that drives `Next`
- [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) — the `?? break` in the hand-written loop
- [For loops](https://rux-lang.dev/docs/lang/statements/loops#for) in the Rux Reference
# Iterable
::note
**You'll need**: [Iterator](https://rux-lang.dev/docs/learn/iterator), [Slice](https://rux-lang.dev/docs/learn/slice)
::
The [Iterator](https://rux-lang.dev/docs/learn/iterator) lesson walked a countdown, which is used up as it goes. A collection is different: a shelf of books can be looked through any number of times, and looking does not use up the books. So a collection does not walk itself. It hands out a **fresh iterator** every time one is wanted, and the iterator keeps the position.
## Two types, two jobs
```rux
struct Shelf {
titles: char8[..][4];
}
// The iterator: a view of the shelf's titles, and how far along it has got.
struct ShelfCursor {
titles: char8[..][..];
index: uint;
}
```
`Shelf` holds four titles. `ShelfCursor` holds a [slice](https://rux-lang.dev/docs/learn/slice) that views them, and an index saying how far it has got. The cursor is an iterator exactly like `Countdown`:
```rux
extend ShelfCursor : Iterator {
func Next(self: &var ShelfCursor) -> char8[..]? {
if self.index == self.titles.length {
return none;
}
let title = self.titles[self.index];
self.index += 1;
return title;
}
}
```
## The Iterate method
```rux
extend Shelf : Iterable {
func Iterate(self: &Shelf) -> ShelfCursor {
return ShelfCursor { titles: self.titles[..], index: 0 };
}
}
```
`Iterate` borrows the shelf read-only and returns a new cursor that starts at the first book. As with `Iterator`, Core's `Iterable` lists no methods and only names the role; `for` goes by the method named `Iterate`.
| | Iterator (`ShelfCursor`) | Iterable (`Shelf`) |
| ----------------- | -------------------------------------------- | ------------------------------------------ |
| Its method | `Next(self: &var ShelfCursor) -> char8[..]?` | `Iterate(self: &Shelf) -> ShelfCursor` |
| Borrows itself as | `&var` — every call moves it along | `&` — handing out a cursor changes nothing |
| After a full walk | used up | unchanged, ready for another loop |
## What for does with a collection
```mermaid
flowchart LR
loop["for title in shelf"] --> it["shelf.Iterate()
a fresh ShelfCursor"]
it --> next["cursor.Next()"]
next --> q{"a title,
or none?"}
q -- "a title" --> body["run the body"]
body --> next
q -- "none" --> done["leave the loop"]
```
`for` calls `Iterate` once at the top of the loop, then drives that cursor with `Next` exactly as in the previous lesson. So a second loop over the same shelf gets a second cursor and starts from the first book again:
```rux
Print("on the shelf:");
for title in shelf {
Print(" {}", title);
}
PrintLine();
// A second loop gets a second iterator, so it starts from the first book again.
var letters: uint = 0;
for title in shelf {
letters += title.length;
}
```
4 + 4 + 7 + 7 gives 22 letters in all.
## Separate positions
You can also call `Iterate` yourself. Each call gives an independent cursor:
```rux
// Two iterators from one shelf keep separate positions.
var reader = shelf.Iterate();
var browser = shelf.Iterate();
reader.Next();
reader.Next();
PrintLine("one reader is at {}, the other at {}", reader.Next() ?? "", browser.Next() ?? "");
```
`reader` has moved past two books and is at *Ulysses*; `browser` has not moved and is at *Dune*. The shelf itself never changed — the position lives in the iterator.
A cursor borrows the shelf it views, so the shelf must outlive it, and must not be changed while a cursor over it is in use.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Interfaces/Iterable){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The Iterator lesson walked a countdown, which is used up as it goes. A collection is different:
// a shelf of books can be looked through any number of times, and looking does not use up the
// books.
//
// So a collection does not walk itself. It provides one method,
// func Iterate(self: &T) -> SomeIterator
// which hands out a fresh iterator, starting at the beginning, every time it is called. `for`
// calls it once at the top of each loop and then drives that iterator with `Next`. The collection
// is only borrowed, so it is unchanged afterwards; the position lives in the iterator.
//
// As with `Iterator`, Core's `Iterable` interface lists no methods and only names the role.
import Core::{ Iterable, Iterator };
import Io::{ Print, PrintLine };
struct Shelf {
titles: char8[..][4];
}
// The iterator: a view of the shelf's titles, and how far along it has got.
struct ShelfCursor {
titles: char8[..][..];
index: uint;
}
extend ShelfCursor : Iterator {
func Next(self: &var ShelfCursor) -> char8[..]? {
if self.index == self.titles.length {
return none;
}
let title = self.titles[self.index];
self.index += 1;
return title;
}
}
extend Shelf : Iterable {
func Iterate(self: &Shelf) -> ShelfCursor {
return ShelfCursor { titles: self.titles[..], index: 0 };
}
}
func Main() -> int {
let shelf = Shelf { titles: ["Dune", "Emma", "Ulysses", "Beloved"] };
Print("on the shelf:");
for title in shelf {
Print(" {}", title);
}
PrintLine();
// A second loop gets a second iterator, so it starts from the first book again.
var letters: uint = 0;
for title in shelf {
letters += title.length;
}
PrintLine("letters in all titles: {}", letters);
// Two iterators from one shelf keep separate positions.
var reader = shelf.Iterate();
var browser = shelf.Iterate();
reader.Next();
reader.Next();
PrintLine("one reader is at {}, the other at {}", reader.Next() ?? "", browser.Next() ?? "");
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Interfaces/Iterable
rux run
```
```text
on the shelf: Dune Emma Ulysses Beloved
letters in all titles: 22
one reader is at Ulysses, the other at Dune
```
## Common mistakes
::warning
**A collection with neither `Next` nor `Iterate`.**:br
Rename `Iterate` to `Cursor` and both loops fail with `error: cannot iterate over 'Shelf'`, and the help "iterate an array, a slice, a range, or a type declaring 'Next' or 'Iterate'". Keep the name `Iterate`.
::
::warning
**Taking `&var self` in `Iterate`.**:br
Handing out a cursor does not change the collection, so `Iterate` borrows read-only. Declare it with `self: &var Shelf` and each `shelf.Iterate()` on the `let` shelf fails with `error: cannot call 'Iterate' on immutable 'shelf'`.
::
::warning
**Expecting a used cursor to start again.**:br
A cursor is an iterator, and an iterator is used up: once `reader` has handed out *Beloved*, every further `reader.Next()` answers `none`. To walk the shelf again, ask it for a fresh cursor with `shelf.Iterate()` — which is exactly what each `for` loop does.
::
## Try it yourself
1. Count the titles longer than four letters with a `for` loop over `shelf`.
2. Add a method `Backwards(self: &Shelf)` that returns a cursor walking the titles from last to first, and loop over `shelf.Backwards()`.
3. Make three cursors from one shelf, advance each a different number of times, and print where each one is.
## Learn more
- [Iterator](https://rux-lang.dev/docs/learn/iterator) — the `Next` half of the protocol
- [Slice](https://rux-lang.dev/docs/learn/slice) — the view the cursor holds
- [Tree map](https://rux-lang.dev/docs/learn/tree-map) — a standard collection that `for` walks the same way
- [For loops](https://rux-lang.dev/docs/lang/statements/loops#for) in the Rux Reference
# Part 13: Generics
[Generic](https://rux-lang.dev/docs/learn/generic) in Part 4 gave a function a type parameter, so one body could serve `int`, `float64` and `char`. This part takes the idea all the way. Types get type parameters of their own — a `Pair`, a `Reading` — and so do their methods. Bounds turn "any `T`" into "any `T` that can do this", which is what lets a generic call methods, compare scores or print its values. And the language's own forms, `T?`, `T ! E` and `A | B`, turn out to be generic too, which is how a helper can be written once for every optional or every fallible there will ever be.
## What you will learn
- Declaring structs and variants with type parameters, and naming the type arguments in a literal.
- Giving a generic type methods with `extend Labeled`, including methods with type parameters of their own.
- Bounding a type parameter by an interface, ``, and where the compiler checks the bound.
- Requiring several interfaces at once with ``.
- Writing reusable helpers over `T ! E` and `T?`, which cannot be extended.
- Building sums from type parameters, `T | U`, and why they collapse when `T` and `U` are the same.
## What a bound decides
Everything in this part comes back to one question: what may the body do with a `T`? The bound is the answer, and the compiler checks it where the generic is used.
```mermaid
flowchart LR
t(["A type parameter T"]) --> none["No bound: <T>
store, pass, return a T
(13.1–13.2)"]
t --> one["One bound: <T: Scored>
+ call Scored's methods
(13.3)"]
t --> two["Several: <T: Scored + Display>
+ everything each one provides
(13.4)"]
one --> call{"At each call:
does the type argument
implement every bound?"}
two --> call
call -- "yes" --> ok["compiled for that type,
calling its methods directly"]
call -- "no" --> err["error at the call,
naming the missing method"]
```
| You want to… | Write | Lesson |
| ----------------------------------------------- | ------------------------------- | ------------------------------------------------------------------ |
| describe "two of something" | `struct Pair { … }` | [Generic type](https://rux-lang.dev/docs/learn/generic-type) |
| give every `Pair` a method | `extend Pair { … }` | [Generic method](https://rux-lang.dev/docs/learn/generic-method) |
| call a method on a `T` | `` | [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) |
| call methods from two interfaces | `` | [Multiple bounds](https://rux-lang.dev/docs/learn/multiple-bounds) |
| write one helper for every optional or fallible | `func F(outcome: T ! E)` | [Generic outcome](https://rux-lang.dev/docs/learn/generic-outcome) |
| return one of two types | `-> T | U`, matched with `else` | [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) |
## Lessons
| | Lesson | What you will learn |
| ---- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| 13.1 | [Generic type](https://rux-lang.dev/docs/learn/generic-type) | a struct with a type parameter: `Pair` |
| 13.2 | [Generic method](https://rux-lang.dev/docs/learn/generic-method) | methods on a generic type, and methods with type parameters of their own |
| 13.3 | [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) | require a type parameter to implement an interface |
| 13.4 | [Multiple bounds](https://rux-lang.dev/docs/learn/multiple-bounds) | require several interfaces at once with `A + B` |
| 13.5 | [Generic outcome](https://rux-lang.dev/docs/learn/generic-outcome) | write helpers that work for any optional or result |
| 13.6 | [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) | generic sums, and what happens when both members are the same type |
## Before you start
Finish [Part 12: Interfaces](https://rux-lang.dev/docs/learn/interfaces): a bound is an interface, and [Display](https://rux-lang.dev/docs/learn/display) is the one most generics end up needing. The lessons also build on [Generic](https://rux-lang.dev/docs/learn/generic) and [Callback](https://rux-lang.dev/docs/learn/callback) from Part 4, [Struct](https://rux-lang.dev/docs/learn/struct) and [Variant match](https://rux-lang.dev/docs/learn/variant-match) from Part 6, the outcome tools of [Part 8](https://rux-lang.dev/docs/learn/optionals) and [Part 9](https://rux-lang.dev/docs/learn/errors), and [Sum type](https://rux-lang.dev/docs/learn/sum-type) from Part 10. Each lesson's package is in the Examples repository's `Generics/` folder:
```sh
cd Examples/Generics/GenericType
rux run
```
## After this part
[Part 14: Text](https://rux-lang.dev/docs/learn/text) puts generics to work straight away: `String`, `StringView` and `StringBuilder` are ordinary types from the Text package, and every value you print goes through the `Display` interface you bounded on here. Later, [Part 17: Collections](https://rux-lang.dev/docs/learn/collections) is built from generic types — a vector, a map and a set of any element type.
For the full rules behind this part, see [Generic functions](https://rux-lang.dev/docs/lang/generics/overview), [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) and [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference.
# Generic type
::note
**You'll need**: [Generic](https://rux-lang.dev/docs/learn/generic), [Struct](https://rux-lang.dev/docs/learn/struct), [Variant match](https://rux-lang.dev/docs/learn/variant-match)
::
The [Generic](https://rux-lang.dev/docs/learn/generic) lesson gave a *function* a type parameter, so one body served `int`, `float64` and `char`. A *type* can take a type parameter too. `struct Pair` is not one struct but a pattern for many: `Pair` and `Pair` are two different types stamped out of the same declaration. This is how a single definition can describe "two of something" or "a reading of something" without deciding in advance what that something is.
## A struct with a type parameter
The parameter goes in angle brackets after the name, and the fields use it like any other type:
```rux
// Two values of the same type, whatever that type is.
struct Pair {
first: T;
second: T;
}
```
Both fields are the same `T`, so a pair is always two values of one type. Each use of `Pair` picks its own `T`, and the compiler lays out a separate struct for each one — a pair of `int32` holds two numbers, a pair of `char8[..]` holds two slices.
A type may take several parameters, and they need not agree:
```rux
// A type may take several parameters, and they need not agree.
struct Entry {
key: K;
value: V;
}
```
| Written | What it is |
| ------------------------- | ------------------------------------------- |
| `Pair` | the declaration — a pattern, not yet a type |
| `Pair` | a type: two `int32` fields |
| `Pair` | another type: two text fields |
| `Entry` | a type with two parameters, filled in order |
## Making a value: the literal names its type arguments
A struct literal spells out the type arguments after the name:
```rux
let point = Pair { first: 3, second: 4 };
let words = Pair { first: "left", second: "right" };
```
The literal does not guess `T` from its fields. `Pair { first: 3, second: 4 }` is refused, even though both fields are plainly numbers — and with `` written down, `3` and `4` become `int32` values, just as a literal beside a typed value does.
## A generic function over a generic type
A generic function can take a generic type. Here `T` is never written at the call: it is read out of the `Pair` passed in, the same way [Generic](https://rux-lang.dev/docs/learn/generic) inferred it from plain arguments.
```rux
func Swapped(pair: Pair) -> Pair {
return Pair { first: pair.second, second: pair.first };
}
```
`Swapped(point)` is `Swapped`, and it returns a `Pair` with the fields the other way round — `4 3`.
## A generic variant
Variants take type parameters the same way. A case's payload can be a `T`:
```rux
// A measurement that may be exact, a range, or missing altogether.
variant Reading {
Exact(T),
Between(T, T),
Missing
}
```
A case with a payload learns `T` from it, the way a function learns from its arguments. A case without one has nothing to learn from, so the type has to come from somewhere else — an annotation on the variable:
```rux
let temperature = Reading::Between(18.5, 21.0);
let floor: Reading = Reading::Exact(7);
let lost: Reading = Reading::Missing;
```
```mermaid
flowchart LR
c["Reading::…"] --> q{"Does the case
carry a payload?"}
q -- "yes: Between(18.5, 21.0)" --> p["T comes from the payload:
Reading<float64>"]
q -- "no: Missing" --> a{"Is there an annotation
or a parameter type?"}
a -- "yes" --> ok["T comes from it:
Reading<int32>"]
a -- "no" --> err["error: requires 1 type argument"]
```
An annotation also wins over the payload: `floor` is a `Reading`, so its `7` is an `int32` rather than an `int`.
`Lowest` then works for every reading at once. Its `match` is the one from [Variant match](https://rux-lang.dev/docs/learn/variant-match), with `T` standing in for the payload type:
```rux
func Lowest(reading: Reading, fallback: T) -> T {
return match reading {
.Exact(value) => value,
.Between(low, _) => low,
.Missing => fallback
};
}
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Generics/GenericType){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The Generic lesson gave a function a type parameter. A type can have one too: `struct Pair`
// is not one struct but a pattern for many, and `Pair` and `Pair` are two
// different types stamped out of it. Each one is laid out for its own `T`, so a pair of bytes is
// small and a pair of strings is wide.
//
// Variants work the same way. A generic variant's cases can carry a `T`, and that is how one
// declaration describes "a reading of something" without saying what is being read.
import Io::PrintLine;
// Two values of the same type, whatever that type is.
struct Pair {
first: T;
second: T;
}
// A type may take several parameters, and they need not agree.
struct Entry {
key: K;
value: V;
}
// A measurement that may be exact, a range, or missing altogether.
variant Reading {
Exact(T),
Between(T, T),
Missing
}
// A generic function can take a generic type. `T` is inferred from the pair it is given.
func Swapped(pair: Pair) -> Pair {
return Pair { first: pair.second, second: pair.first };
}
func Lowest(reading: Reading, fallback: T) -> T {
return match reading {
.Exact(value) => value,
.Between(low, _) => low,
.Missing => fallback
};
}
func Main() -> int {
// A struct literal names its type arguments. A literal does not guess `T` from its fields,
// so `Pair { first: 3, second: 4 }` is rejected: "struct initializer for 'Pair' requires 1
// type argument, but 0 were provided".
let point = Pair { first: 3, second: 4 };
let words = Pair { first: "left", second: "right" };
let flipped = Swapped(point);
PrintLine("point {} {}", flipped.first, flipped.second);
PrintLine("words {} {}", words.first, words.second);
let age = Entry { key: "age", value: 42 };
PrintLine("entry {} = {}", age.key, age.value);
// A case with a payload learns `T` from it, the way a generic function learns from its
// arguments, so this is a `Reading`. An annotation supplies `T` too: `floor` is a
// `Reading`, so its 7 is an `int32`. A case without a payload has nothing to learn
// from, so the annotation is what tells `Missing` which reading it is.
let temperature = Reading::Between(18.5, 21.0);
let floor: Reading = Reading::Exact(7);
let lost: Reading = Reading::Missing;
PrintLine("lowest {}", Lowest(temperature, 0.0));
PrintLine("lowest {}", Lowest(floor, 0));
PrintLine("lowest {}", Lowest(lost, -1));
return 0;
}
```
## Run it
```sh
cd Examples/Generics/GenericType
rux run
```
```text
point 4 3
words left right
entry age = 42
lowest 18.5
lowest 7
lowest -1
```
## Common mistakes
::warning
**Leaving the type arguments off a struct literal.**:br`Pair { first: 3, second: 4 }` fails with `error: struct initializer for 'Pair' requires 1 type argument, but 0 were provided`. Write `Pair { … }`. The same goes for a type with two parameters: `Entry { … }` is refused with `requires 2 type arguments, but 1 was provided`.
::
::warning
**A case with no payload and no annotation.**:br`let lost = Reading::Missing;` fails with `error: variant case 'Reading::Missing' requires 1 type argument, but 0 were provided`. Annotate the variable — `let lost: Reading = Reading::Missing;` — or name the type at the case: `Reading::Missing()`.
::
::warning
**Fields of a different type than `T`.**:br
Once `T` is fixed, every field declared as `T` must be that type. `Pair { first: 3, second: "four" }` fails with `error: field 'second' in initializer for 'Pair' has type 'char8[..]', but its declaration requires 'int32'`.
::
## Try it yourself
1. Make a `Pair` and pass it to `Swapped`. You do not need to change `Swapped` at all.
2. Write `Highest(reading: Reading, fallback: T) -> T`, which returns the *upper* end of a `Between`.
3. Add a case `Approximately(T)` to `Reading`. Which function stops compiling, and why?
## Learn more
- [Generic functions](https://rux-lang.dev/docs/lang/generics/overview) and [Variants with data](https://rux-lang.dev/docs/lang/variants/overview) in the Rux Reference
- [Generic](https://rux-lang.dev/docs/learn/generic) — type parameters on functions
- [Generic method](https://rux-lang.dev/docs/learn/generic-method) — giving `Pair` methods of its own
- [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) — another way to build a type out of type parameters
# Generic method
::note
**You'll need**: [Generic type](https://rux-lang.dev/docs/learn/generic-type), [Constructor](https://rux-lang.dev/docs/learn/constructor), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method), [Callback](https://rux-lang.dev/docs/learn/callback), [Tuple](https://rux-lang.dev/docs/learn/tuple)
::
A generic type gets methods the same way any type does: through `extend`. The difference is one pair of angle brackets. The block is written `extend Labeled`, and that `T` is in scope for every method inside, so a single block serves `Labeled`, `Labeled` and every other instantiation. A method can also bring a type parameter of its *own*, chosen afresh at each call.
## One extend block for every T
`Labeled` is a value with a name attached:
```rux
struct Labeled {
label: char8[..];
value: T;
}
```
Its methods live in `extend Labeled`. Inside, `T` means "whatever this instantiation's `T` is", and the receivers are written with it — `&Labeled` to read, `&var Labeled` to change, exactly as in [Mutating method](https://rux-lang.dev/docs/learn/mutating-method):
```rux
extend Labeled {
// A constructor, as for any struct. It returns the instantiation it was called on.
func Labeled(label: char8[..], value: T) -> Labeled {
return Labeled { label: label, value: value };
}
func Get(self: &Labeled) -> T {
return self.value;
}
func Set(self: &var Labeled, value: T) {
self.value = value;
}
```
To call the [constructor](https://rux-lang.dev/docs/learn/constructor), put the type argument on the type name, and the rest is an ordinary call:
```rux
var count = Labeled("count", 7);
count.Set(15);
```
From then on `count` is a `Labeled`, so `Get` returns an `int32` and `Set` accepts only an `int32`. Nothing is written per type: the compiler produces `Get` for `int32` because `count` needs it.
## A method with a type parameter of its own
`With` declares a second parameter, `U`, which has nothing to do with the type's `T`. `T` comes from the receiver; `U` comes from this call's argument:
```rux
// `T` comes from the receiver; `U` comes from this call's argument.
func With(self: &Labeled, other: U) -> (T, U) {
return (self.value, other);
}
```
The same `count` can be paired with text on one line and with a `bool` on the next. `U` is inferred from the argument, or written out after the method name when you want to be explicit:
```rux
let fruit = count.With("apples");
let flag = count.With(true);
```
| Parameter | Declared on | Fixed when | In `count.With("apples")` |
| --------- | ---------------------- | ---------------------------- | ------------------------- |
| `T` | the type, `Labeled` | the value is made | `int32` |
| `U` | the method, `With` | each call, from its argument | `char8[..]` |
## Changing the instantiation
A method's result can be a *different* instantiation of the same type. `Map` keeps the label and turns the value into something else through a [callback](https://rux-lang.dev/docs/learn/callback):
```rux
func Map(self: &Labeled, change: func(T) -> U) -> Labeled {
return Labeled { label: self.label, value: change(self.value) };
}
```
Here `U` is inferred from the callback's result type. `Half` returns a `float64` and `IsLarge` a `bool`, so the same `Labeled` becomes a `Labeled` in one call and a `Labeled` in the other:
```rux
let half = count.Map(Half);
let large = count.Map(IsLarge);
```
```mermaid
flowchart LR
c["count
Labeled<int32>"] -- "Map(Half)
Half: int32 → float64" --> h["half
Labeled<float64>"]
c -- "Map(IsLarge)
IsLarge: int32 → bool" --> l["large
Labeled<bool>"]
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Generics/GenericMethod){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A generic type gets methods the same way any type does, through `extend`. The block is
// written `extend Labeled`, and that `T` is in scope for every method inside it: one block
// serves `Labeled`, `Labeled` and every other instantiation.
//
// A method may also declare a type parameter of its own, which is chosen at each call and has
// nothing to do with the type's. `With` below pairs the stored `T` with a `U` that can be
// different every time it is called.
import Io::PrintLine;
// A value with a name attached to it.
struct Labeled {
label: char8[..];
value: T;
}
extend Labeled {
// A constructor, as for any struct. It returns the instantiation it was called on.
func Labeled(label: char8[..], value: T) -> Labeled {
return Labeled { label: label, value: value };
}
func Get(self: &Labeled) -> T {
return self.value;
}
func Set(self: &var Labeled, value: T) {
self.value = value;
}
// `T` comes from the receiver; `U` comes from this call's argument.
func With(self: &Labeled, other: U) -> (T, U) {
return (self.value, other);
}
// The result can be a different instantiation of the same type: a `Labeled` becomes a
// `Labeled`, keeping its label and changing its value through `change`.
func Map(self: &Labeled, change: func(T) -> U) -> Labeled {
return Labeled { label: self.label, value: change(self.value) };
}
}
func Half(value: int32) -> float64 {
return (value as float64) / 2.0;
}
func IsLarge(value: int32) -> bool {
return value > 100;
}
func Main() -> int {
// The type argument goes on the type name, then the constructor runs as usual.
var count = Labeled("count", 7);
count.Set(15);
PrintLine("{} = {}", count.label, count.Get());
// `U` is inferred from the argument, or written out after the method name.
let fruit = count.With("apples");
let flag = count.With(true);
PrintLine("with {} {}", fruit.0, fruit.1);
PrintLine("with {} {}", flag.0, flag.1);
// `U` inferred from the callback's result type.
let half = count.Map(Half);
let large = count.Map(IsLarge);
PrintLine("{} / 2 = {}", half.label, half.Get());
PrintLine("{} > 100 is {}", large.label, large.Get());
return 0;
}
```
## Run it
```sh
cd Examples/Generics/GenericMethod
rux run
```
```text
count = 15
with 15 apples
with 15 true
count / 2 = 7.5
count > 100 is false
```
## Common mistakes
::warning
**Writing `extend Labeled` without its parameter.**:br`extend Labeled { … }` fails with `error: struct type 'Labeled' requires 1 type argument, but 0 were provided`, followed by `error: type 'T' is not defined in this scope` for every method that mentions `T`. The block must declare the parameter it uses: `extend Labeled`.
::
::warning
**Calling the constructor without a type argument.**:br`Labeled("count", 7)` fails with `error: constructor for 'Labeled' requires 1 type argument, but 0 were provided`. Write `Labeled("count", 7)`.
::
::warning
**Changing a `let` binding.**:br`Set` takes `&var Labeled`, so it needs a `var`. With `let count = …`, `count.Set(15)` fails with `error: cannot call 'Set' on immutable 'count'` — generics change nothing about [mutability](https://rux-lang.dev/docs/learn/mutable).
::
## Try it yourself
1. Make a `Labeled` called `"name"` holding `"Ada"`, and print it through `Get`.
2. Add `func Relabel(self: &Labeled, label: char8[..]) -> Labeled`, which returns a copy with a new label.
3. Write a function `Describe(value: int32) -> char8[..]` that returns `"small"` or `"large"`, and pass it to `Map`. What type is the result?
4. Call `count.With(count)`. What are `T` and `U`, and what does the tuple hold?
## Learn more
- [Methods](https://rux-lang.dev/docs/lang/structs/methods) in the Rux Reference
- [Generic type](https://rux-lang.dev/docs/learn/generic-type) — declaring `Labeled` in the first place
- [Extension](https://rux-lang.dev/docs/learn/extension) and [Constructor](https://rux-lang.dev/docs/learn/constructor) — the same `extend` and constructors on ordinary types
- [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) — methods a `T` is promised to have
# Generic bound
::note
**You'll need**: [Generic](https://rux-lang.dev/docs/learn/generic), [Interface](https://rux-lang.dev/docs/learn/interface), [Extension](https://rux-lang.dev/docs/learn/extension)
::
An unconstrained `` promises nothing about `T`. The body may store a `T`, pass it on and hand it back, but it cannot call a method on it, because nothing says the method exists. A **bound** makes that promise. `` accepts only types that implement the interface `Scored`, and in return the body may call `Scored`'s methods on any `T`. It is the generic counterpart of the [Interface](https://rux-lang.dev/docs/learn/interface) lesson: the same interface, used to choose which types may come in.
## A bound is written after a colon
Here is the interface, and one type that implements it:
```rux
interface Scored {
func Score() -> int32;
}
extend Player : Scored {
func Score(self: &Player) -> int32 {
return self.points;
}
}
```
`Best` names the interface after its type parameter:
```rux
func Best(first: T, second: T) -> T {
if second.Score() > first.Score() {
return second;
}
return first;
}
```
`Score` is callable on `first` and `second` only because of the bound. Remove `: Scored` and the body itself is rejected — not a particular call, the definition — because a plain `T` has no `Score`.
The winner comes back as a `T`. Called with two `Player` values, `Best` returns a `Player`, so `winner.name` and `winner.points` are right there; it never turns into "some `Scored`".
## The check happens at the call
```mermaid
flowchart LR
call["Best(ana, bo)"] --> t["T = Player"]
t --> q{"Does Player
implement Scored?"}
q -- "yes" --> inst["Best is compiled for Player;
Score() calls Player's Score"]
q -- "no" --> err["error at the call:
type argument … does not satisfy
interface bound 'Scored'"]
```
The compiler works out `T` from the arguments first, then checks the bound. A type without an implementation is rejected where it is passed in, with a note naming the method it is missing — never somewhere deep inside the body. That makes the bound a contract you can read from the signature alone: anything `Scored` may be passed, and nothing else.
## Primitives qualify only through an implementation
`int32` has no `Score` until something gives it one. An `extend` block on a primitive does, and from then on a bare number is its own score:
```rux
extend int32 : Scored {
func Score(self: &int32) -> int32 {
return self;
}
}
```
Note the call in `Main`:
```rux
PrintLine("best number {}", Best(3, 9));
```
The `` matters. Two unsuffixed literals make `T` an `int` — the [Literal](https://rux-lang.dev/docs/learn/literal) rule — and `int` is a different type from `int32`, one that nothing has made `Scored`. Writing `T` out turns `3` and `9` into `int32` values.
## A bound travels
`Margin` has a bound of its own, and that is what lets it call `Best`:
```rux
func Margin(first: T, second: T) -> int32 {
let winner = Best(first, second);
return winner.Score() * 2 - first.Score() - second.Score();
}
```
Inside `Margin`, `T` is known only to be `Scored`. That is exactly what `Best` requires, so the call is accepted. A generic that calls another generic must promise at least what the callee demands.
## A bound costs nothing at run time
A bound and an [interface value](https://rux-lang.dev/docs/learn/interface-value) both use an interface, but they work differently:
| | Bound: `` | Interface value: `Scored` |
| ----------------------- | ------------------------------------------------ | --------------------------------------------- |
| Which type? | one concrete type per call, fixed when compiling | any implementing type, decided while running |
| Method call | direct, as if written for that type | looked up through the value at run time |
| What comes back | the same `T` that went in — a `Player` | a `Scored`; the `Player` is no longer visible |
| Mixed types in one call | no — `first` and `second` are both `T` | yes |
Each instantiation calls its own type's method directly, as if `Best` had been written out by hand for `Player` and again for `int32`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Generics/GenericBound){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An unconstrained `` promises nothing about `T`, so a generic body can only move a `T`
// around. A bound changes that: `` accepts only types that implement `Scored`, and
// in return the body may call `Scored`'s methods on any `T`.
//
// The check happens at the call. A type without an implementation is rejected where it is
// passed in, with a note naming the method it is missing, never somewhere deep in the body.
//
// Unlike an interface value, a bound costs nothing at run time. Each instantiation calls its
// own type's method directly, as if the function had been written out by hand for that type.
import Io::PrintLine;
interface Scored {
func Score() -> int32;
}
struct Player {
name: char8[..];
points: int32;
}
extend Player : Scored {
func Score(self: &Player) -> int32 {
return self.points;
}
}
// Primitives satisfy a bound the same way: only through an implementation. `int32` has no
// `Score` until this block gives it one, and then a bare number is its own score.
extend int32 : Scored {
func Score(self: &int32) -> int32 {
return self;
}
}
// `Score` is callable on `first` and `second` only because of the bound. The winner is handed
// back as a `T`, so a `Player` comes back as a `Player`, not as a `Scored`.
func Best(first: T, second: T) -> T {
if second.Score() > first.Score() {
return second;
}
return first;
}
// How far the winner leads. A bound travels: a `T` that is `Scored` here satisfies `Best` too.
func Margin(first: T, second: T) -> int32 {
let winner = Best(first, second);
return winner.Score() * 2 - first.Score() - second.Score();
}
func Main() -> int {
let ana = Player { name: "Ana", points: 12 };
let bo = Player { name: "Bo", points: 17 };
let winner = Best(ana, bo);
PrintLine("best player {} with {}", winner.name, winner.points);
PrintLine("margin {}", Margin(ana, bo));
// An unsuffixed literal is an `int`, which has no `Score`, so the type is written out.
PrintLine("best number {}", Best(3, 9));
// `Best(2.5, 1.5)` is rejected: "type argument 'float64' does not satisfy interface bound
// 'Scored'". A `float64` can be compared with `>`, but nothing has made it `Scored`.
return 0;
}
```
## Run it
```sh
cd Examples/Generics/GenericBound
rux run
```
```text
best player Bo with 17
margin 5
best number 9
```
## Common mistakes
::warning
**Calling a method on an unbound `T`.**:br
Without the bound, `second.Score()` fails with `error: no interface bound on type parameter 'T' provides method 'Score'`, and the compiler suggests the fix: `help: add a bound whose interface declares 'Score', as in 'T: SomeInterface'`.
::
::warning
**Passing a type that is not `Scored`.**:br`Best(2.5, 1.5)` fails with `error: type argument 'float64' does not satisfy interface bound 'Scored' on type parameter 'T'`, and a note: `interface 'Scored' requires method 'Score', which type 'float64' does not implement`. A `float64` can be compared with `>`, but nothing has made it `Scored`.
::
::warning
**Bare literals become `int`, not `int32`.**:br`Best(3, 9)` fails the same way, naming `'int'`: the literals have no typed partner, so `T` is `int`, and only `int32` was given a `Score`. Write `Best(3, 9)`, or pass `int32` variables.
::
::warning
**Calling a bounded generic from an unbounded one.**:br
If `Margin` were `` without the bound, its call `Best(first, second)` would fail with `error: type argument 'T' does not satisfy interface bound 'Scored' on type parameter 'T'`, and the help says what to do: `add the bound to the enclosing declaration, as in 'T: Scored'`.
::
## Try it yourself
1. Add a third player and write `BestOfThree(a: T, b: T, c: T) -> T` using `Best` twice.
2. Implement `Scored` for `float64` — perhaps the value rounded down with `as int32` — and make `Best(2.5, 1.5)` compile.
3. Declare a `struct Team` with a `wins` field, implement `Scored` for it, and pass two teams to `Margin`. Nothing in `Margin` or `Best` changes.
4. Try `Best(ana, 9)`. Why is a `Player` and an `int32` rejected, even though both are `Scored`?
## Learn more
- [Generic functions](https://rux-lang.dev/docs/lang/generics/overview) and [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) in the Rux Reference
- [Interface](https://rux-lang.dev/docs/learn/interface) and [Extension](https://rux-lang.dev/docs/learn/extension) — declaring and implementing `Scored`
- [Interface value](https://rux-lang.dev/docs/learn/interface-value) — the run-time alternative to a bound
- [Multiple bounds](https://rux-lang.dev/docs/learn/multiple-bounds) — requiring two interfaces at once
# Multiple bounds
::note
**You'll need**: [Generic bound](https://rux-lang.dev/docs/learn/generic-bound), [Display](https://rux-lang.dev/docs/learn/display)
::
One bound gives a generic one set of methods. Often it needs two: `Scored` to decide who wins, and [Display](https://rux-lang.dev/docs/learn/display) to print the winner with `{}`. Bounds are joined with `+`. `` accepts only a type that implements **both** interfaces, and the body may use everything either one provides.
## Joining bounds with +
```rux
func Announce(first: T, second: T) {
if second.Score() > first.Score() {
PrintLine("{} beats {}", second, first);
} else {
PrintLine("{} holds off {}", first, second);
}
}
```
`Score()` is allowed because of `Scored`. Passing `first` and `second` to `{}` is allowed because of `Display`. Inside the body a `T` is exactly what its bounds say it is, and nothing more — with only ``, each `PrintLine` here is rejected, as the [Generic](https://rux-lang.dev/docs/learn/generic) lesson warned it would be.
```mermaid
flowchart LR
t(["A type argument T"]) --> s{"implements
Scored?"}
s -- "no" --> e1["error: does not satisfy
interface bound 'Scored'"]
s -- "yes" --> d{"implements
Display?"}
d -- "no" --> e2["error: does not satisfy
interface bound 'Display'"]
d -- "yes" --> ok["Announce compiled for T:
may call Score() and print with {}"]
```
## Each type meets each bound its own way
`Player` needs both implementations. `Scored` is the one from [Generic bound](https://rux-lang.dev/docs/learn/generic-bound); `Display` comes from the Text package, and a player prints as their name:
```rux
// A player prints as their name.
extend Player : Display {
func WriteDisplay(self: &Player, writer: &var TextWriter, spec: FormatSpec) -> ! FormatError {
return writer.Write(self.name);
}
}
```
`int32` is already `Display` — the standard packages give every number that implementation, which is why numbers have always printed with `{}`. So it needs only `Scored`:
```rux
// `int32` already implements `Display` in the standard packages, so it needs only `Scored`.
extend int32 : Scored {
func Score(self: &int32) -> int32 {
return self;
}
}
```
| Type | `Scored` | `Display` | `Announce` accepts it? |
| --------- | ------------------------ | -------------------------- | ---------------------- |
| `Player` | `extend Player : Scored` | `extend Player : Display` | yes |
| `int32` | `extend int32 : Scored` | from the standard packages | yes |
| `float64` | none | from the standard packages | no — not `Scored` |
| `int` | none | from the standard packages | no — not `Scored` |
That last row is why `Main` declares its numbers as `int32` before passing them:
```rux
let low: int32 = 3;
let high: int32 = 9;
Announce(high, low);
```
## Every bound is checked on its own
The compiler checks each bound separately and reports the one that fails. `Announce(2.5, 1.5)` is rejected because `float64` is `Display` but not `Scored`. Delete the `extend Player : Display` block and `Announce(ana, bo)` is rejected the other way round — `Player` is `Scored` but cannot print. There is no partial credit: a type that meets one bound out of two is as unwelcome as one that meets none.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Generics/MultipleBounds){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// One bound gives a generic one set of methods. When it needs two, the bounds are joined with
// `+`: `` accepts only a type that implements both interfaces, and the
// body may use everything either one provides.
//
// Here `Scored` decides who wins and `Display` lets the body print a `T` with `{}`. With only
// ``, each `PrintLine` below is rejected: "argument 2 to 'PrintLine' has type 'T',
// but variadic parameter 'args' requires 'Display'". A placeholder needs `Display`, and a
// `T` is only what its bounds say it is.
import Io::PrintLine;
import Text::{ Display, FormatError, FormatSpec, TextWriter };
interface Scored {
func Score() -> int32;
}
struct Player {
name: char8[..];
points: int32;
}
extend Player : Scored {
func Score(self: &Player) -> int32 {
return self.points;
}
}
// A player prints as their name.
extend Player : Display {
func WriteDisplay(self: &Player, writer: &var TextWriter, spec: FormatSpec) -> ! FormatError {
return writer.Write(self.name);
}
}
// `int32` already implements `Display` in the standard packages, so it needs only `Scored`.
extend int32 : Scored {
func Score(self: &int32) -> int32 {
return self;
}
}
func Announce(first: T, second: T) {
if second.Score() > first.Score() {
PrintLine("{} beats {}", second, first);
} else {
PrintLine("{} holds off {}", first, second);
}
}
func Main() -> int {
let ana = Player { name: "Ana", points: 12 };
let bo = Player { name: "Bo", points: 17 };
Announce(ana, bo);
let low: int32 = 3;
let high: int32 = 9;
Announce(high, low);
// Each bound is checked on its own. `Announce(2.5, 1.5)` is rejected because `float64`
// is `Display` but not `Scored`; a type that is `Scored` but cannot print is rejected too.
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Generics/MultipleBounds
rux run
```
```text
Bo beats Ana
9 holds off 3
```
## Common mistakes
::warning
**Writing a comma instead of `+`.**:br`` does not mean "both". The comma starts a *second type parameter*, here confusingly named `Display`, so `T` is bound by `Scored` alone. Every `PrintLine` in the body then fails with `error: argument 2 to 'PrintLine' has type 'T', but variadic parameter 'args' requires 'Display'`, and every call with `error: function 'Announce' requires 2 type arguments, but 0 were provided`.
::
::warning
**Leaving out the bound the body uses.**:br
With only ``, printing a `T` fails with `has type 'T', but variadic parameter 'args' requires 'Display'`. Everything the body does with a `T` has to come from some bound.
::
::warning
**A type that meets only one bound.**:br
Without `extend Player : Display`, `Announce(ana, bo)` fails with `error: type argument 'Player' does not satisfy interface bound 'Display' on type parameter 'T'`, with the note `interface 'Display' requires method 'WriteDisplay', which type 'Player' does not implement`.
::
## Try it yourself
1. Swap `ana` and `bo` in the call to `Announce`. Which line is printed now?
2. Make a player print as `Bo (17)` by calling `WriteFormat` inside `WriteDisplay`, as `Money` did in [Display](https://rux-lang.dev/docs/learn/display). It comes from the Format package, so add `Format` to `[Dependencies]` too.
3. Add a `struct Team` that is `Scored` but not `Display`, and read the error when you pass two teams to `Announce`. Then give it a `Display` implementation.
## Learn more
- [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) and [Generic functions](https://rux-lang.dev/docs/lang/generics/overview) in the Rux Reference
- [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) — one bound, and how it is checked
- [Display](https://rux-lang.dev/docs/learn/display) — implementing `WriteDisplay`
- [Format](https://rux-lang.dev/docs/learn/format) — what the `spec` passed to `WriteDisplay` describes
# Generic outcome
::note
**You'll need**: [Generic](https://rux-lang.dev/docs/learn/generic), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback), [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate), [The is operator](https://rux-lang.dev/docs/learn/is)
::
Some questions get asked of outcomes again and again: "the value, or this fallback", "did it work?", "what went wrong, if anything?". For a struct you would answer them with methods. Optionals, fallibles and sums are different: they are built into the language rather than declared by a package, so there is no package to own methods on them, and `extend` refuses them. The answer is a **generic function** whose parameter spells the form out — `T ! E` or `T?` — and which therefore works for every success type and every error type at once.
## A parameter that spells out the form
`ValueOr` takes any fallible and a fallback of its success type:
```rux
// The success, or `fallback` when there is none.
func ValueOr(outcome: T ! E, fallback: T) -> T {
return outcome catch { else => fallback };
}
```
Nothing is written at the call. Given `Divide(12, 4)`, an `int32 ! DivisionByZero`, the compiler reads `T = int32` and `E = DivisionByZero` straight out of the argument's type, the way it read `T` out of a `Pair` in [Generic type](https://rux-lang.dev/docs/learn/generic-type). The body is the [catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) you already know; the only new thing is that it is written once for every fallible there will ever be.
| Parameter written | Accepts | Helpers in this lesson |
| ----------------- | --------------------------------- | --------------------------------- |
| `T ! E` | any fallible with a success value | `ValueOr`, `SuccessOf`, `ErrorOf` |
| `T?` | any optional | `Both` |
| `T?[..]` | a slice of optionals of one type | `CountPresent` |
## Turning one form into another
Two small helpers move between the forms. `SuccessOf` keeps the success and forgets why there might not be one; `ErrorOf` keeps the other channel:
```rux
func SuccessOf(outcome: T ! E) -> T? {
return match outcome {
.Success(value) => .Some(value),
.Failure(_) => none
};
}
func ErrorOf(outcome: T ! E) -> E? {
return match outcome {
.Success(_) => none,
.Failure(error) => .Some(error)
};
}
```
```mermaid
flowchart LR
f(["T ! E"]) -- "SuccessOf" --> s(["T?"])
f -- "ErrorOf" --> e(["E?"])
f -- "ValueOr(fallback)" --> t(["T"])
f -- "Succeeded / Failed" --> b(["bool"])
```
`ErrorOf(broken)` is a `DivisionByZero?`, so `Main` can open it with a presence pattern and read the error's own field:
```rux
match ErrorOf(broken) {
error? => PrintLine("ErrorOf cannot divide {} by zero", error.numerator),
none => PrintLine("ErrorOf nothing went wrong")
}
```
## Core ships two of these
The two questions everybody asks are already in `Core`, written exactly this way. `Succeeded(outcome)` and `Failed(outcome)` answer which channel an outcome holds, without unwrapping it:
```rux
import Core::{ Failed, Succeeded };
```
Their signatures are `Succeeded(outcome: T ! E) -> bool` and the same for `Failed` — generic functions over a fallible, like the ones in this lesson.
## Optionals too
`Both` combines two optionals of possibly different types. The postfix `?` from [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) does the work: if either is `none`, the function returns `none` on the spot.
```rux
func Both(first: T?, second: U?) -> (T, U)? {
return (first?, second?);
}
```
And a helper can take a whole slice of optionals. `T?[..]` is a slice of `T?`, so an `int32?[4]` array is passed straight to it, and [`is`](https://rux-lang.dev/docs/learn/is) asks whether each one holds a `T`:
```rux
func CountPresent(values: T?[..]) -> uint {
var count: uint = 0;
for value in values {
if value is T {
count++;
}
}
return count;
}
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Generics/GenericOutcome){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Optionals, fallibles and sums are built into the language rather than declared by a package,
// so there is no package to own methods on them. `extend int32 ! DivisionByZero { ... }` is
// rejected: "cannot extend native type 'int32 ! DivisionByZero'". A question you keep asking of
// an outcome is written instead as a generic function whose parameter spells the form out:
// `T ! E`, or `T?`.
//
// Such a helper works for every success type and every error type at once, and nothing needs
// to be written at the call: `T` and `E` are read straight out of the argument's type.
//
// `Core` ships the two everybody needs, written exactly this way: `Succeeded(outcome)` and
// `Failed(outcome)` answer which channel an outcome holds without unwrapping it.
import Core::{ Failed, Succeeded };
import Io::PrintLine;
struct DivisionByZero {
numerator: int32;
}
func Divide(numerator: int32, denominator: int32) -> int32 ! DivisionByZero {
if denominator == 0 {
fail DivisionByZero { numerator: numerator };
}
return numerator / denominator;
}
// The success, or `fallback` when there is none.
func ValueOr(outcome: T ! E, fallback: T) -> T {
return outcome catch { else => fallback };
}
// Keeps the success and forgets why there might not be one.
func SuccessOf(outcome: T ! E) -> T? {
return match outcome {
.Success(value) => .Some(value),
.Failure(_) => none
};
}
// The other channel: the error, if there is one.
func ErrorOf(outcome: T ! E) -> E? {
return match outcome {
.Success(_) => none,
.Failure(error) => .Some(error)
};
}
// Optionals too: both values when both are present, and nothing otherwise.
func Both(first: T?, second: U?) -> (T, U)? {
return (first?, second?);
}
// And a whole array of them: an `int32?[4]` is passed straight to the `T?[..]` slice.
func CountPresent(values: T?[..]) -> uint {
var count: uint = 0;
for value in values {
if value is T {
count++;
}
}
return count;
}
func Main() -> int {
let even = Divide(12, 4);
let broken = Divide(7, 0);
PrintLine("ValueOr {} {}", ValueOr(even, -1), ValueOr(broken, -1));
PrintLine("Succeeded {} {}", Succeeded(even), Succeeded(broken));
PrintLine("Failed {} {}", Failed(even), Failed(broken));
PrintLine("SuccessOf {}", SuccessOf(even) ?? 0);
match ErrorOf(broken) {
error? => PrintLine("ErrorOf cannot divide {} by zero", error.numerator),
none => PrintLine("ErrorOf nothing went wrong")
}
let width: int32? = 80;
let height: int32? = 24;
let missing: char8[..]? = none;
match Both(width, height) {
size? => PrintLine("Both {} x {}", size.0, size.1),
none => PrintLine("Both incomplete")
}
PrintLine("Both complete: {}", Both(width, missing) is (int32, char8[..]));
let readings: int32?[4] = [3, none, 5, none];
PrintLine("CountPresent {} of {}", CountPresent(readings), readings.length);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Generics/GenericOutcome
rux run
```
```text
ValueOr 3 -1
Succeeded true false
Failed false true
SuccessOf 3
ErrorOf cannot divide 7 by zero
Both 80 x 24
Both complete: false
CountPresent 2 of 4
```
## Common mistakes
::warning
**Trying to extend an optional or a fallible.**:br`extend int32 ! DivisionByZero { … }` fails with `error: cannot extend native type 'int32 ! DivisionByZero'`, and `extend int32? { … }` with `cannot extend native type 'int32?'`. The note explains why — `a sum, optional, fallible, or unit type has no declaring package to own methods or interface implementations` — and the help gives this lesson's answer: `write a generic function that takes the native type as a parameter`.
::
::warning
**Passing a plain value where a fallible is expected.**:br`ValueOr(12, -1)` fails with `error: function 'ValueOr' requires 2 type arguments, but 0 were provided`. A plain `int` has no error type, so there is nothing to read `E` from. Only a real `T ! E` fills in both parameters.
::
## Try it yourself
1. Write `OrElse(value: T?, fallback: T) -> T` with `??`, and call it with an `int32?` and a `char8[..]?`.
2. Write `IsPresent(value: T?) -> bool`, then rewrite `CountPresent` to use it.
3. Write `Either(first: T ! E, second: T ! E) -> T ! E` that returns `first` if it succeeded and `second` otherwise.
4. Use `ValueOr` on the result of a function from an earlier lesson that returns a different fallible. Did you have to change `ValueOr`?
## Learn more
- [Outcome](https://rux-lang.dev/docs/learn/outcome) and [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) — the forms these helpers take apart
- [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) and [Is](https://rux-lang.dev/docs/learn/is) — the tools inside `Both` and `CountPresent`
- [Error handling](https://rux-lang.dev/docs/lang/errors/overview) in the Rux Reference
- [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) — the third native form, `A | B`, with type parameters
# Generic sum
::note
**You'll need**: [Generic](https://rux-lang.dev/docs/learn/generic), [Sum type](https://rux-lang.dev/docs/learn/sum-type), [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern)
::
A [sum type](https://rux-lang.dev/docs/learn/sum-type) can be built from type parameters: `T | U` is "a `T` or a `U`", whatever they turn out to be. That raises a question the Sum type lesson already answered for ordinary types. A sum is a **set** of types, so when `T` and `U` are the same type, `T | U` has only one member — and collapses to that type. This lesson shows the collapse, and the one habit it asks of every generic `match`: end it in `else`.
## A sum of type parameters
`Choose` hands back one of two values of possibly different types:
```rux
func Choose(takeFirst: bool, first: T, second: U) -> T | U {
if takeFirst {
return first;
}
return second;
}
```
With two different types, the result keeps whichever member it was given. `port` holds the `int32` 8080; `flag` holds the `bool` `true`:
```rux
let port = Choose(true, 8080, false);
let flag = Choose(false, 8080, true);
```
## When T and U are the same type
With the same type twice, `int32 | int32` is just `int32`. The result is an ordinary number, ready for arithmetic, and no `match` is needed to get at it:
```rux
let count: int32 = Choose(false, 3, 4);
PrintLine("int32 | int32 {}", count + 1);
```
```mermaid
flowchart LR
s["T | U"] --> q{"Are T and U
the same type?"}
q -- "no: int32, bool" --> two["bool | int32
two members"]
q -- "yes: int32, int32" --> one["int32 | int32 = int32
one member — a plain int32"]
```
| Instantiation | `T | U` becomes | Members |
| ---------------------- | --------------- | ------- |
| `Choose` | `bool | int32` | 2 |
| `Choose` | `int32` | 1 |
## Why the match ends in else
`Side` reports which member a `T | U` holds. The natural way to write it would be one typed arm per member — but its second arm is `else`:
```rux
func Side(value: T | U) -> char8[..] {
return match value {
first: T => "first",
else => "second"
};
}
```
Consider `Side`. The sum has collapsed to `int32`, so the first arm, `first: int32 =>`, matches **every** value. A second arm `second: U =>` would be `second: int32 =>`, coming after an arm that already took everything — an unreachable arm, which the compiler rejects. It rejects it for that instantiation only, and the note names the call responsible:
```text
error: match arm is unreachable because an earlier pattern matches every value
note: in 'Side' instantiated with T = int32, U = int32 by the call at …
```
An `else` arm is never reported as unreachable. So a `match` over `T | U` ends in `else`, and stays valid for every pair of type arguments — including the pair where `else` is never used:
```rux
PrintLine("collapsed side {}", Side(count));
```
That call prints `first`. A collapsed sum has nothing to tell apart.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Generics/GenericSum){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A sum can be built from type parameters: `T | U` is "a T or a U", whatever they turn out to be.
// That raises a question the SumType lesson already answered. A sum is a set of types, so when
// `T` and `U` are the same type, `T | U` has only one member and collapses to that type.
// `Choose` returns a plain `int32`, ready for arithmetic.
//
// The collapse matters to a generic `match`. `Side` cannot write a second arm `second: U =>`:
// at `Side` that arm would come after `first: int32 =>`, which already matched
// everything, and the instantiation is rejected — "match arm is unreachable because an earlier
// pattern matches every value", with a note "in 'Side' instantiated with T = int32, U = int32
// by the call at ..." that points at the call in `Main` responsible. An `else` arm is never
// reported as unreachable, so a match over `T | U` ends in `else` and stays valid for every pair
// of type arguments.
import Io::PrintLine;
// Hands back one of two values of possibly different types.
func Choose(takeFirst: bool, first: T, second: U) -> T | U {
if takeFirst {
return first;
}
return second;
}
// Which member a `T | U` holds. When the sum has collapsed, the first arm takes every value.
func Side(value: T | U) -> char8[..] {
return match value {
first: T => "first",
else => "second"
};
}
func Main() -> int {
// Two different types: the result keeps whichever member it was given.
let port = Choose(true, 8080, false);
let flag = Choose(false, 8080, true);
PrintLine("int32 | bool {} {}", Side(port), Side(flag));
// The same type twice: `int32 | int32` is `int32`, so the result is an ordinary number.
let count: int32 = Choose(false, 3, 4);
PrintLine("int32 | int32 {}", count + 1);
// A collapsed sum has nothing to tell apart. The `else` arm is still there, unused.
PrintLine("collapsed side {}", Side(count));
return 0;
}
```
## Run it
```sh
cd Examples/Generics/GenericSum
rux run
```
```text
int32 | bool first second
int32 | int32 5
collapsed side first
```
## Common mistakes
::warning
**One typed arm per type parameter.**:br
Writing `second: U =>` as the last arm of `Side` builds for `Side` but fails for `Side` with `error: match arm is unreachable because an earlier pattern matches every value`, plus a note naming the instantiation and the call. End a generic `match` over a sum in `else`.
::
::warning
**Expecting T and U to be inferred from a sum.**:br`Side(port)` fails with `error: argument 1 to 'Side' has type 'bool8 | int32', but parameter 'value' requires 'T | U'` (`bool8` is the full name of `bool`). A sum is a set of types, and the compiler does not split one back into a `T` and a `U`. Write the type arguments: `Side(port)`.
::
::warning
**Leaving the arguments to choose the types.**:br`Choose(true, 8080, false)` compiles, but the bare literal makes `T` an `int`, and the result is a `bool8 | int` — a different type from the `bool8 | int32` that `Side` expects. The error says exactly that. Write `Choose(…)`, or pass `int32` variables.
::
## Try it yourself
1. Call `Choose` and `Side` on its result. What does `Side` print for each member?
2. Call `Choose(true, false, true)` and use the result directly in an `if`. Why is no `match` needed?
3. Write `FirstOr(value: T | U, fallback: T) -> T`, which returns the `T` member or the fallback, with a `first: T` arm and `else`. Try it with `` and with ``.
## Learn more
- [Sum type](https://rux-lang.dev/docs/learn/sum-type) and [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) — sums and their arms
- [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) — why the compiler checks every arm
- [Generic outcome](https://rux-lang.dev/docs/learn/generic-outcome) — generic helpers over the other native forms
- [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference
# Part 14: Text
Text has been in every program since Hello, World, and so far it has been simple: a literal in quotes, printed with `{}`. This part looks underneath. A literal turns out to be a slice of UTF-8 bytes, which is why its length can surprise you; the Text package adds views that promise their bytes are text, Strings that own their bytes, and builders that grow them. Then the part turns outwards — Unicode's three ways of counting characters, laying out columns of numbers, rendering text you keep, parsing numbers back out of it, and reading lines a person types.
## What you will learn
- What a string literal is — a read-only `char8[..]` of UTF-8 bytes — and the escapes it can hold.
- The three encodings, `c8`, `c16` and `c32`, and why lengths count code units rather than characters.
- Borrowing text with `StringView`, owning it with `String`, and building it with `StringBuilder`.
- Checking and decoding UTF-8, and telling bytes, scalars and graphemes apart.
- Changing case correctly beyond ASCII, where one letter can become two.
- Placeholder specs for width, alignment, precision, bases, zeros and signs.
- Rendering into a `String` with `Render`, parsing numbers with `ParseInt32`, and reading lines with `ReadLine`.
## The text types at a glance
```mermaid
flowchart LR
lit(["A literal
char8[..]"]) -- "StringView::FromValidated
or FromBytes (checked)" --> view["StringView
borrowed, known to be UTF-8
(14.3)"]
lit -- "String::FromBytes" --> str["String
owns its bytes
(14.4)"]
sb["StringBuilder
grows a buffer
(14.5)"] -- "IntoString" --> str
sb -- "View" --> view
str -- "View" --> view
r["Render(pattern, values)
(14.11)"] --> str
in["ReadLine
(14.13)"] -- "appends to" --> sb
view -- "ParseInt32
(14.12)" --> num(["a number"])
```
| You have… | And want to… | Use |
| ----------------------------- | ---------------------------------- | ------------------------------------- |
| bytes that might not be text | be sure they are UTF-8 | `StringView::FromBytes` or `Validate` |
| text someone else owns | trim, search, split it | `StringView` |
| text a function made | hand it back, keep it | `String` |
| pieces arriving one at a time | join them without copying them all | `StringBuilder` |
| values and a pattern | text you keep | `Render` |
| text that should be a number | the number, or why not | `ParseInt32` and friends |
## Lessons
| | Lesson | What you will learn |
| ----- | ---------------------------------------------------------------- | --------------------------------------------------------------------- |
| 14.1 | [String literal](https://rux-lang.dev/docs/learn/string-literal) | what a string literal is: read-only `char8` code units |
| 14.2 | [Encoding](https://rux-lang.dev/docs/learn/encoding) | `c8`, `c16` and `c32` literals, and code units versus characters |
| 14.3 | [String view](https://rux-lang.dev/docs/learn/string-view) | borrow text without copying it |
| 14.4 | [String](https://rux-lang.dev/docs/learn/string) | own text that lives as long as you need it |
| 14.5 | [String builder](https://rux-lang.dev/docs/learn/string-builder) | build text a piece at a time |
| 14.6 | [UTF-8](https://rux-lang.dev/docs/learn/utf8) | check and decode UTF-8 bytes |
| 14.7 | [Unicode](https://rux-lang.dev/docs/learn/unicode) | bytes, code points and grapheme clusters, and why their counts differ |
| 14.8 | [Unicode case](https://rux-lang.dev/docs/learn/unicode-case) | change case beyond ASCII, where one letter can become two |
| 14.9 | [Format](https://rux-lang.dev/docs/learn/format) | control width and alignment when formatting values |
| 14.10 | [Format number](https://rux-lang.dev/docs/learn/format-number) | control precision and number base when formatting numbers |
| 14.11 | [Render](https://rux-lang.dev/docs/learn/render) | format values into a `String` instead of the console |
| 14.12 | [Parse](https://rux-lang.dev/docs/learn/parse) | turn text into a number, and handle text that is not one |
| 14.13 | [Input](https://rux-lang.dev/docs/learn/input) | read a line typed by the user, and notice when input ends |
## Before you start
Finish [Part 13: Generics](https://rux-lang.dev/docs/learn/generics). Text leans on almost everything before it: slices from [Part 5](https://rux-lang.dev/docs/learn/sequences), optionals and fallibles from [Part 8](https://rux-lang.dev/docs/learn/optionals) and [Part 9](https://rux-lang.dev/docs/learn/errors) — nearly every text operation can fail — [Destructor](https://rux-lang.dev/docs/learn/destructor) from Part 11 for the memory a `String` gives back, and [Display](https://rux-lang.dev/docs/learn/display) and [Iterator](https://rux-lang.dev/docs/learn/iterator) from Part 12. Each lesson's package is in the Examples repository's `Text/` folder:
```sh
cd Examples/Text/StringLiteral
rux run
```
Several lessons pass an `Allocator` around without explaining it yet. For now it is simply where a `String`'s bytes come from; [Part 15: Memory](https://rux-lang.dev/docs/learn/memory) opens it up.
## After this part
[Part 15: Memory](https://rux-lang.dev/docs/learn/memory) explains the allocators and pointers this part used without looking inside — the `@written` of [Unicode case](https://rux-lang.dev/docs/learn/unicode-case) and the `Allocator` every `String` was given. [Part 16: Numbers](https://rux-lang.dev/docs/learn/numbers) then ends with the checkpoint projects [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic), which read numbers typed by the user with this part's `ReadLine` and the Format package's parsers.
For the language rules behind literals, see [String literals](https://rux-lang.dev/docs/lang/types/text), [Literals](https://rux-lang.dev/docs/lang/lexical/literals) and the [character types](https://rux-lang.dev/docs/lang/types/characters) in the Rux Reference, and the [Text](https://rux-lang.dev/docs/api/text), [Format](https://rux-lang.dev/docs/api/format) and [Io](https://rux-lang.dev/docs/api/io) packages in the API reference.
# String literal
::note
**You'll need**: [Character](https://rux-lang.dev/docs/learn/character), [For](https://rux-lang.dev/docs/learn/for), [Slice](https://rux-lang.dev/docs/learn/slice), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice)
::
Text has been in every lesson so far — every `PrintLine` starts with one — and it was never a special string type. A literal such as `"hello"` is a `char8[..]`: the read-only [slice](https://rux-lang.dev/docs/learn/slice) from the Sequences part, viewing UTF-8 bytes that the compiler stored inside the program. Everything a slice can do, a literal can do. This lesson looks at what that means — and at the two surprises that follow from it.
## A literal is a slice
```rux
let word = "hello";
PrintLine("{} has {} bytes", word, word.length);
PrintLine("first {}, last {}", word[0], word[word.length - 1]);
PrintLine("word[1..4] is {}", word[1..4]);
```
| Slice operation | On `word` | Result |
| --------------- | ----------------------- | ------------------------- |
| `.length` | `word.length` | `5` |
| indexing | `word[0]` | `h`, one `char8` |
| a range | `word[1..4]` | `ell`, another slice |
| passing it on | `PrintLine("{}", word)` | any `char8[..]` parameter |
| a `for` loop | `for c in word` | each `char8` in turn |
Because a literal is just a slice, a function that takes `char8[..]` accepts text without any conversion, and a range of a literal is text too.
## The length counts bytes
`.length` counts the slice's elements, and the elements of a `char8[..]` are bytes — UTF-8 *code units*, not characters. For plain English letters each character is one byte, so the difference is invisible. It shows up as soon as the text leaves ASCII:
```rux
let euro = "\u{20AC}";
PrintLine("{} is {} bytes", euro, euro.length);
```
A reader sees one character, the euro sign, but UTF-8 stores it in three bytes, so its length is 3. The next lesson, [Encoding](https://rux-lang.dev/docs/learn/encoding), is about exactly that gap.
## Escapes
A backslash starts an **escape**: a character that is awkward or impossible to type between quotes. Each escape is written with two or more characters but stores fewer bytes — `"\n"` is one byte, a newline.
```rux
PrintLine("quote \"to be\"");
PrintLine("backslash C:\Rux\Bin");
PrintLine("tab one\ttwo");
PrintLine("newline first\n second");
```
| Escape | Stores |
| ---------- | --------------------------------------- |
| `\"` | a double quote, without ending the text |
| `\'` | a single quote |
| `\` | one backslash |
| `\n` | a newline |
| `\t` | a tab |
| `\r` | a carriage return |
| `\0` | a zero byte |
| `\u{20AC}` | the Unicode character with that number |
The compiler also knows `\a`, `\b`, `\f` and `\v`, the old terminal control characters. Any other letter after a backslash is an error.
## Read-only: copy to change
The bytes of a literal live in a read-only part of the program, and a `char8[..]` is a read-only view of them. So a literal can be read, but never changed in place. To change text, copy its bytes into storage the program owns — here an array — and change the copy:
```rux
var letters: char8[5];
for i in 0..word.length {
letters[i] = word[i];
}
letters[0] = c8'j';
PrintLine("{} became {}", word, letters[..]);
```
`c8'j'` is a character literal of type `char8`, one UTF-8 byte. `letters[..]` is a slice of the whole array, which `{}` prints as text — `jello`, while `word` is still `hello`.
```mermaid
flowchart LR
ro["Read-only program data
h e l l o"] -- "viewed by" --> w["word: char8[..]
read only"]
ro -- "copied byte by byte" --> a["letters: char8[5]
owned by Main"]
a -- "letters[0] = c8'j'" --> j["j e l l o"]
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/StringLiteral){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Text has been in every lesson so far, and it was never a special string type. A literal such
// as "hello" is a `char8[..]` — the read-only slice from the Sequences part, viewing UTF-8 bytes
// that the compiler stored inside the program. Everything a slice can do, a literal can do:
// `.length`, indexing, ranges, and passing to a function that takes a `char8[..]`.
//
// Two things follow from that. The length counts bytes (the encoding's code units), not
// characters. And the bytes live in a read-only part of the program, so a literal can be read
// but never changed in place.
import Io::PrintLine;
func Main() -> int {
let word = "hello";
PrintLine("{} has {} bytes", word, word.length);
PrintLine("first {}, last {}", word[0], word[word.length - 1]);
PrintLine("word[1..4] is {}", word[1..4]);
// A backslash starts an escape: a character that is awkward or impossible to type between
// quotes. Each escape is written with two or more characters but stores fewer bytes.
PrintLine("quote \"to be\"");
PrintLine("backslash C:\\Rux\\Bin");
PrintLine("tab one\ttwo");
PrintLine("newline first\n second");
PrintLine("\"\\n\" is {} byte", "\n".length);
// `\u{...}` names any Unicode character by its number. The euro sign takes three bytes
// in UTF-8, so its length is 3 even though a reader sees one character. The next lesson
// is about exactly that gap.
let euro = "\u{20AC}";
PrintLine("{} is {} bytes", euro, euro.length);
// The literal itself cannot change: `word[0] = c8'j';` is rejected with "cannot modify
// elements through read-only slice 'char8[..]'". To change text, copy its bytes into storage
// the program owns — here an array — and change the copy.
var letters: char8[5];
for i in 0..word.length {
letters[i] = word[i];
}
letters[0] = c8'j';
PrintLine("{} became {}", word, letters[..]);
return 0;
}
```
## Run it
```sh
cd Examples/Text/StringLiteral
rux run
```
```text
hello has 5 bytes
first h, last o
word[1..4] is ell
quote "to be"
backslash C:\Rux\Bin
tab one two
newline first
second
"\n" is 1 byte
€ is 3 bytes
hello became jello
```
## Common mistakes
::warning
**Changing a literal in place.**:br`word[0] = c8'j';` fails with `error: cannot modify elements through read-only slice 'char8[..]'`. Copy the bytes into an array (or, later, a [String builder](https://rux-lang.dev/docs/learn/string-builder)) and change the copy.
::
::warning
**A single backslash in a path.**:br`"C:\Rux\Bin"` fails with `error: escape sequence '\R' is not recognized` (and again for `\B`). Double each backslash: `"C:\Rux\Bin"`. Worse, a path such as `"C:\new"` compiles without complaint — `\n` is a real escape, so the text quietly contains a newline.
::
::warning
**Indexing one past the end.**:br
The last byte is `word[word.length - 1]`. `word[word.length]` compiles, but stops the program with `Panic: index out of range`.
::
## Try it yourself
1. Count how many times `l` appears in `"hello"` with a `for` loop over the literal.
2. Print the length of `"naïve"`. Is it what you expected? Which character costs the extra byte?
3. Copy `"stressed"` into an array backwards and print it. (The [String](https://rux-lang.dev/docs/learn/string) lesson does the same thing and explains what can go wrong with non-ASCII text.)
4. Print a line that contains a tab, a quote and a backslash, using only escapes.
## Learn more
- [String literals](https://rux-lang.dev/docs/lang/types/text) and [Literals](https://rux-lang.dev/docs/lang/lexical/literals) in the Rux Reference
- [Slice](https://rux-lang.dev/docs/learn/slice) and [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) — what a `char8[..]` and `letters[..]` are
- [Encoding](https://rux-lang.dev/docs/learn/encoding) — why one character can be several bytes
# Encoding
::note
**You'll need**: [String literal](https://rux-lang.dev/docs/learn/string-literal), [Function](https://rux-lang.dev/docs/learn/function), [Convert](https://rux-lang.dev/docs/learn/convert)
::
Every character has a number, its **scalar value**: `A` is 65, `€` is 8364, the rocket emoji 🚀 is 128640. An **encoding** decides how that number is stored as a run of fixed-size *code units*. Rux can write a literal in three encodings, and the same text takes a different number of units in each. That is why [String literal](https://rux-lang.dev/docs/learn/string-literal) found the euro sign to be three "long": `.length` counts code units, not characters.
## Three encodings, three literal types
A prefix on the literal picks the encoding, and with it the element type of the slice:
| Literal | Type | Encoding | Code unit |
| ----------- | ------------ | ------------------ | --------- |
| `"text"` | `char8[..]` | UTF-8 | 1 byte |
| `c8"text"` | `char8[..]` | UTF-8, spelled out | 1 byte |
| `c16"text"` | `char16[..]` | UTF-16 | 2 bytes |
| `c32"text"` | `char32[..]` | UTF-32 | 4 bytes |
The same prefixes pick a character literal's width: `c8'A'` is one `char8` code unit, as in the previous lesson.
The program's `Show` takes all three slice types, so the same text can be measured in each encoding side by side:
```rux
func Show(eight: char8[..], sixteen: char16[..], thirtyTwo: char32[..]) {
PrintLine("{} UTF-8 {}, UTF-16 {}, UTF-32 {}",
thirtyTwo, eight.length, sixteen.length, thirtyTwo.length);
}
```
## How the counts drift apart
| Text | Scalar values | UTF-8 units | UTF-16 units | UTF-32 units |
| ------ | -------------- | ----------- | ------------ | ------------ |
| `Rux` | 82, 117, 120 | 3 | 3 | 3 |
| `café` | …, 233 for `é` | 5 | 4 | 4 |
| `€` | 8364 | 3 | 1 | 1 |
| `🚀` | 128640 | 4 | 2 | 1 |
- **ASCII** — every encoding uses one unit per character, and the counts agree. That is why the difference is so easy to miss.
- **`é`** — two UTF-8 bytes, but one unit in the wider encodings.
- **`€`** — three UTF-8 bytes, still one UTF-16 unit.
- **The rocket** lies beyond the 65,536 values one UTF-16 unit can hold, so UTF-16 splits it into two units called a **surrogate pair**. Only UTF-32 holds it whole.
```mermaid
flowchart LR
r(["🚀 scalar value 128640"]) --> u8["UTF-8
4 units of 1 byte"]
r --> u16["UTF-16
2 units of 2 bytes
(a surrogate pair)"]
r --> u32["UTF-32
1 unit of 4 bytes"]
```
A UTF-32 unit is wide enough for any scalar, so in a `char32[..]` the count of units *is* the count of scalars. The narrower encodings spend several units on one scalar once the text leaves plain ASCII.
## Indexing returns a code unit
`[i]` gives the `i`-th code unit of the slice's own encoding — not the `i`-th character:
```rux
let rocket16 = c16"🚀";
let rocket32 = c32"🚀";
PrintLine("rocket16[0] is {}", rocket16[0] as uint32);
PrintLine("rocket32[0] is {}", rocket32[0] as uint32);
```
`rocket32[0]` is the whole rocket, 128640. `rocket16[0]` is 55357: the first half of a surrogate pair, a number but no character at all on its own. The `as uint32` [conversion](https://rux-lang.dev/docs/learn/convert) prints the units as numbers, which is the honest way to look at half a character.
Which encoding to use? UTF-8 is the default for a reason: it is what files, terminals and the network speak, and the Text package works in it. The wider literals exist for the places that need them — Windows APIs take UTF-16, and the Unicode package works on `char32[..]`, where one unit is one scalar.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/Encoding){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A character has a number, its scalar value: `A` is 65, `€` is 8364, the rocket emoji is 128640.
// An encoding decides how that number is stored as a run of fixed-size code units, and Rux
// literals can be written in three of them:
//
// "text" char8[..] UTF-8, one-byte units — the default
// c8"text" char8[..] the same, with the encoding spelled out
// c16"text" char16[..] UTF-16, two-byte units
// c32"text" char32[..] UTF-32, four-byte units
//
// The same prefixes pick a character literal's width: `c8'A'` is one `char8` code unit.
//
// `.length` and indexing always count code units of the slice's own encoding. A UTF-32 unit is
// wide enough for any scalar, so there the count of units is the count of scalars. The narrower
// encodings spend several units on one scalar once the text leaves plain ASCII.
import Io::PrintLine;
// The three parameter types are the three literal types, so this function shows the same text
// in each encoding side by side.
func Show(eight: char8[..], sixteen: char16[..], thirtyTwo: char32[..]) {
PrintLine("{} UTF-8 {}, UTF-16 {}, UTF-32 {}",
thirtyTwo, eight.length, sixteen.length, thirtyTwo.length);
}
func Main() -> int {
// ASCII: every encoding uses one unit per character, and the counts agree.
Show("Rux", c16"Rux", c32"Rux");
// `é` takes two UTF-8 bytes but one unit in the wider encodings.
Show("café", c16"café", c32"café");
// The euro sign takes three UTF-8 bytes.
Show("€", c16"€", c32"€");
// The rocket lies beyond the 65,536 values one UTF-16 unit can hold, so UTF-16 splits it
// into two units called a surrogate pair. Only UTF-32 holds it whole.
Show("🚀", c16"🚀", c32"🚀");
// Indexing returns a code unit, not a character. The first unit of the UTF-16 rocket is
// half a surrogate pair: a number, but no character at all on its own.
let rocket16 = c16"🚀";
let rocket32 = c32"🚀";
PrintLine("rocket16[0] is {}", rocket16[0] as uint32);
PrintLine("rocket32[0] is {}", rocket32[0] as uint32);
return 0;
}
```
## Run it
```sh
cd Examples/Text/Encoding
rux run
```
```text
Rux UTF-8 3, UTF-16 3, UTF-32 3
café UTF-8 5, UTF-16 4, UTF-32 4
€ UTF-8 3, UTF-16 1, UTF-32 1
🚀 UTF-8 4, UTF-16 2, UTF-32 1
rocket16[0] is 55357
rocket32[0] is 128640
```
## Common mistakes
::warning
**Passing one encoding where another is expected.**:br
The three slice types are different types, and nothing converts between them silently. `Show("Rux", "Rux", c32"Rux")` fails with `error: argument 2 to 'Show' has type 'char8[..]', but parameter 'sixteen' requires 'char16[..]'`. Write the prefix: `c16"Rux"`.
::
::warning
**A character literal too wide for its unit.**:br`c8'€'` fails with `error: character '€' (U+20AC) does not fit one 'char8' code unit`, and `c16'🚀'` likewise for `char16`. The help suggests a string literal instead, such as `c8"€"`, which may use several units.
::
::warning
**Printing half a surrogate pair.**:br
Without `as uint32`, `rocket16[0]` prints as `�`, a replacement mark: half a pair is not a character, so there is nothing real to show.
::
## Try it yourself
1. Call `Show` with `naïve`, `日本` and `Ελλάδα`. Which encoding is shortest for each?
2. Print every unit of `c16"🚀"` as a number with a `for` loop.
3. Find a character other than an emoji that needs a surrogate pair in UTF-16. Hint: its scalar value must be above 65,535.
## Learn more
- [`char8`](https://rux-lang.dev/docs/lang/types/characters), [`char16`](https://rux-lang.dev/docs/lang/types/characters) and [`char32`](https://rux-lang.dev/docs/lang/types/characters) in the Rux Reference
- [Character](https://rux-lang.dev/docs/learn/character) — the character types from Part 1
- [UTF-8](https://rux-lang.dev/docs/learn/utf8) — checking and decoding UTF-8 bytes one scalar at a time
- [Unicode](https://rux-lang.dev/docs/learn/unicode) — a third way to count, the one a reader means
# String view
::note
**You'll need**: [Encoding](https://rux-lang.dev/docs/learn/encoding), [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Iterator](https://rux-lang.dev/docs/learn/iterator)
::
A `char8[..]` is just bytes. Nothing promises they are valid UTF-8, so every function handed one has to check them again — or simply hope. `StringView`, from the Text package, is the same borrowed slice with that promise attached. The bytes are checked **once**, when the view is made, and everything after may rely on them being text. A view also owns nothing: making one copies no bytes and allocates nothing, and every operation on it answers with another view into the same bytes.
## Making a view
There are two ways in, depending on who vouches for the bytes:
```rux
let line = StringView::FromValidated(" Ada Lovelace, mathematician ");
let checked = StringView::FromBytes("Grace Hopper") ?? StringView();
```
| Call | Checks the bytes? | Returns | Use it for |
| ------------------------------ | ----------------- | ------------- | ----------------------------------- |
| `StringView::FromValidated(b)` | no — trusts you | `StringView` | literals, which are always UTF-8 |
| `StringView::FromBytes(b)` | yes | `StringView?` | bytes from a file, a socket, a user |
| `StringView()` | — | an empty view | a fallback after `??` |
`FromBytes` returns `none` when the bytes are not text, so it pairs naturally with [`??`](https://rux-lang.dev/docs/learn/coalesce). A view prints with `{}` like any text.
## Trim, search and cut
Every one of these returns a new view of the same bytes. Nothing is copied, and the original text never changes.
```rux
let trimmed = line.Trim();
let comma = StringView::FromValidated(",");
let at = trimmed.IndexOf(comma) ?? trimmed.Length();
PrintLine("starts with Ada {}", trimmed.StartsWith(StringView::FromValidated("Ada")));
let name = trimmed.Part(0, at) ?? StringView();
```
- `Trim` drops white space — spaces, tabs and line ends — from both ends. `TrimStart` and `TrimEnd` do one end each.
- `IndexOf` answers with a **byte** position, or `none` when there is nothing to find — so `?? trimmed.Length()` means "the whole text, if there is no comma". `StartsWith`, `EndsWith` and `Contains` answer yes or no.
- `Part(start, end)` cuts out bytes `start` up to `end`, as an optional view.
Note that the needle is a view too. Every search takes a `StringView`, so a literal needle is wrapped with `FromValidated` first.
```mermaid
flowchart LR
b["The bytes of the literal
␣␣Ada Lovelace, mathematician␣␣"]
line["line
the whole literal"] --> b
t["trimmed = line.Trim()
without the spaces"] --> b
n["name = trimmed.Part(0, 12)
Ada Lovelace"] --> b
```
Because all three point into the same bytes, those bytes must outlive every view of them — exactly as an array must outlive a slice of it. A literal lives as long as the program, so here nothing can go wrong.
## Split into pieces
`Split` walks the pieces between separators. It is an [iterator](https://rux-lang.dev/docs/learn/iterator), so a `for` loop takes the pieces one at a time, each a view of its own:
```rux
let space = StringView::FromValidated(" ");
for word in name.Split(space) {
PrintLine("word [{}] {} bytes", word, word.Length());
}
```
## Positions are bytes, and cuts must fall between characters
`Length`, `IndexOf` and `Part` all count bytes, like the literals underneath. But a view promised to be text, so it will not cut a character in half. In `née` the `é` covers bytes 1 and 2:
```rux
let accented = StringView::FromValidated("née");
let whole = accented.Part(0, 4) ?? StringView();
let broken = accented.Part(0, 2) ?? StringView::FromValidated("(refused)");
```
`Part(0, 4)` is the whole word. `Part(0, 2)` would end in the middle of `é`, so it is refused and returns `none`. A plain slice, `"née"[0..2]`, would have cut it anyway — [UTF-8](https://rux-lang.dev/docs/learn/utf8) shows what such a cut leaves behind.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/StringView){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `char8[..]` is just bytes: nothing promises they are valid UTF-8, so every function handed
// one has to check them again or simply hope. `StringView`, from the Text package, is the same
// borrowed slice with that promise attached. The bytes are checked once, when the view is made,
// and everything after may rely on them being text.
//
// A view owns nothing. Making one copies no bytes and allocates nothing, and each operation
// below answers with another view into the same bytes — so the text it looks at must outlive
// it, exactly as an array must outlive a slice of it.
import Io::PrintLine;
import Text::StringView;
func Main() -> int {
// `FromValidated` takes the caller's word that the bytes are UTF-8, which a literal always
// is. `FromBytes` checks instead, and returns `none` when they are not text.
let line = StringView::FromValidated(" Ada Lovelace, mathematician ");
let checked = StringView::FromBytes("Grace Hopper") ?? StringView();
PrintLine("checked [{}]", checked);
// Trimming does not change the text. It returns a narrower view of the same bytes.
let trimmed = line.Trim();
PrintLine("trimmed [{}]", trimmed);
// Searching answers with a byte position, or `none` when there is nothing to find.
let comma = StringView::FromValidated(",");
let at = trimmed.IndexOf(comma) ?? trimmed.Length();
PrintLine("comma at byte {}", at);
PrintLine("starts with Ada {}", trimmed.StartsWith(StringView::FromValidated("Ada")));
// `Part` cuts out a smaller view by byte positions, here the name before the comma.
let name = trimmed.Part(0, at) ?? StringView();
PrintLine("name [{}]", name);
// `Split` walks the pieces between separators, each one a view of its own.
let space = StringView::FromValidated(" ");
for word in name.Split(space) {
PrintLine("word [{}] {} bytes", word, word.Length());
}
// Positions are bytes, and a cut inside a character is refused rather than made. In "née"
// the `é` covers bytes 1 and 2, so ending a part at byte 2 would split it in half.
let accented = StringView::FromValidated("née");
let whole = accented.Part(0, 4) ?? StringView();
let broken = accented.Part(0, 2) ?? StringView::FromValidated("(refused)");
PrintLine("part 0..4 {}", whole);
PrintLine("part 0..2 {}", broken);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/StringView
rux run
```
```text
checked [Grace Hopper]
trimmed [Ada Lovelace, mathematician]
comma at byte 12
starts with Ada true
name [Ada Lovelace]
word [Ada] 3 bytes
word [Lovelace] 8 bytes
part 0..4 née
part 0..2 (refused)
```
## Common mistakes
::warning
**Searching with a plain literal.**:br`trimmed.IndexOf(",")` fails with `error: argument 1 to 'IndexOf' has type 'char8[..]', but parameter 'needle' requires 'StringView'`. Wrap the needle: `StringView::FromValidated(",")`.
::
::warning
**Forgetting that `Part` and `FromBytes` may fail.**:br
Both return an optional. Without `??`, `PrintLine("{}", name)` fails with `has type 'StringView?', but variadic parameter 'args' requires 'Display'`, and `name.Split(space)` with `error: type 'StringView?' has no field 'Split'`.
::
::warning
**`.length` instead of `Length()`.**:br
A view is a struct, not a slice, so `word.length` fails with `error: struct 'StringView' has no field 'length'`. Its size is a method: `word.Length()`.
::
## Try it yourself
1. Use `LastIndexOf` to cut out the surname, `Lovelace`, without splitting.
2. Split `"red,green,,blue"` on a comma. How many pieces do you get, and what is the third one?
3. Check that the trimmed line `EndsWith` `"mathematician"` and `Contains` `"Love"`.
4. Print `accented.ScalarCount()` next to `accented.Length()`.
## Learn more
- [Text](https://rux-lang.dev/docs/api/text) in the API reference
- [Slice](https://rux-lang.dev/docs/learn/slice) — the borrowed view underneath
- [String](https://rux-lang.dev/docs/learn/string) — text that owns its bytes, for when a view is not enough
- [UTF-8](https://rux-lang.dev/docs/learn/utf8) — the check that `FromBytes` makes
# String
::note
**You'll need**: [String view](https://rux-lang.dev/docs/learn/string-view), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main), [Destructor](https://rux-lang.dev/docs/learn/destructor), [Interface value](https://rux-lang.dev/docs/learn/interface-value)
::
A [view](https://rux-lang.dev/docs/learn/string-view) borrows, so it can never outlive the bytes it looks at. Text that a function builds and hands back, or that must be kept after its source is gone, needs an **owner**. That is `String`: it keeps its own copy of the bytes, in memory it asked an allocator for, and gives that memory back in its [destructor](https://rux-lang.dev/docs/learn/destructor) when the value's life ends.
## Borrowed or owned
| | `StringView` | `String` |
| ---------- | --------------------------------- | -------------------------------------- |
| Holds | a view of someone else's bytes | its own copy of the bytes |
| Making one | free: no copy, no allocation | copies the bytes into allocated memory |
| Can fail? | only `FromBytes`, with `none` | yes: `String ! TextError` |
| Lives | no longer than the bytes it views | as long as the `String` itself |
| Cleans up | nothing to clean up | its destructor frees the memory |
## Where the memory comes from
A `String` needs an allocator to ask for memory. `Main` makes the system's allocator and passes it on as an [interface value](https://rux-lang.dev/docs/learn/interface-value) of type `Allocator`:
```rux
var system = SystemAllocator();
let allocator: Allocator = system;
```
The allocator itself is the subject of the [Memory](https://rux-lang.dev/docs/learn/memory) part. For now it is simply where a String's bytes come from.
## Returning text a function made
`Reversed` builds its letters in a local array, which is gone once the function returns. A view of that array would be left pointing at nothing, so the function returns a `String` that owns a copy:
```rux
func Reversed(allocator: Allocator, word: char8[..]) -> String ! TextError {
var letters: char8[16];
for i in 0..word.length {
letters[i] = word[word.length - 1 - i];
}
return String::FromBytes(allocator, letters[..word.length]);
}
```
Two things can go wrong, so the result is a [fallible](https://rux-lang.dev/docs/learn/fallible) `String ! TextError`:
| `TextError` case | When |
| ---------------- | ---------------------------------------------------------- |
| `OutOfMemory` | the allocator could not supply the storage |
| `InvalidUtf8` | `FromBytes` was given bytes that are not text |
| `LengthOverflow` | a length passed what a `uint` can hold |
| `NotABoundary` | a position fell inside a character rather than between two |
The UTF-8 check is not a formality here. Reversed byte by byte, `né` puts the two bytes of `é` in the wrong order, and `FromBytes` fails with `TextError::InvalidUtf8` rather than make a `String` that is not text.
`Main` is a [fallible main](https://rux-lang.dev/docs/learn/fallible-main), `func Main() -> ! TextError`, so `?` passes any such failure on and the program exits with status 1:
```rux
var word = Reversed(allocator, "stressed")?;
```
## Copying with Clone
`Clone` makes an independent copy with storage of its own, and it can fail too, because the copy needs memory:
```rux
let saved = word.Clone()?;
word.ReplaceWith(Reversed(allocator, "drawer")?);
```
`ReplaceWith` gives `word` new text and frees its old bytes. `saved` has its own copy, so it is untouched: `word` is now `reward`, `saved` is still `desserts`.
Plain assignment, `let copy = saved;`, would copy too — a `String` has a [custom copy](https://rux-lang.dev/docs/learn/custom-copy). But assignment has no way to report failure, so it ends the program if memory runs out. `Clone` hands that failure back as a `TextError`, which is why it is the one to use when failure must be handled.
## Lending a view
Whenever borrowed text is enough, a `String` lends out a view without copying anything:
```rux
let view = saved.View();
PrintLine("view {}, {} characters", view, view.ScalarCount());
```
The view is valid as long as `saved` is. No cleanup is written anywhere: `word` and `saved` each release their own memory at the end of `Main`, as the destructor lesson promised.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/String){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A view borrows, so it can never outlive the bytes it looks at. Text that a function builds
// and hands back, or that must be kept after its source is gone, needs an owner. That is
// `String`: it keeps its own copy of the bytes, in memory it asked an allocator for, and gives
// that memory back in its destructor when the value's life ends.
//
// Asking for memory can fail, and so can checking that bytes are UTF-8, so the calls that
// make a `String` return `String ! TextError`. `Main` is fallible here, and `?` passes any such
// failure on. The allocator itself is the subject of the Memory part; for now it is simply
// where a String's bytes come from.
import Allocator::{ Allocator, SystemAllocator };
import Io::PrintLine;
import Text::{ String, TextError };
// The reversed letters exist only in this function's array, which is gone once it returns. A
// view of them would be left pointing at nothing, so the function returns a String that owns
// a copy. `FromBytes` checks the bytes are text before copying them, and that check matters
// here: reversed byte by byte, "né" puts the two bytes of `é` in the wrong order, and the
// call fails with `TextError::InvalidUtf8`. The array has room for words of up to 16 bytes.
func Reversed(allocator: Allocator, word: char8[..]) -> String ! TextError {
var letters: char8[16];
for i in 0..word.length {
letters[i] = word[word.length - 1 - i];
}
return String::FromBytes(allocator, letters[..word.length]);
}
func Main() -> ! TextError {
var system = SystemAllocator();
let allocator: Allocator = system;
var word = Reversed(allocator, "stressed")?;
PrintLine("word {}, {} bytes", word, word.Length());
// `Clone` makes an independent copy with storage of its own. Replacing the original's text
// afterwards leaves the clone exactly as it was.
let saved = word.Clone()?;
word.ReplaceWith(Reversed(allocator, "drawer")?);
PrintLine("word {}", word);
PrintLine("saved {}", saved);
// `let copy = saved;` would also copy, but plain assignment has no way to report failure,
// so it ends the program if memory runs out. `Clone` hands that failure back as a
// `TextError` instead, which is why it is the one to use when failure must be handled.
// A String lends out a view whenever borrowed text is enough, without copying anything.
let view = saved.View();
PrintLine("view {}, {} characters", view, view.ScalarCount());
// No cleanup is written: `word` and `saved` each release their own memory at the end of
// `Main`, as the Destructor lesson promised.
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` and `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/String
rux run
```
```text
word desserts, 8 bytes
word reward
saved desserts
view desserts, 8 characters
```
## Common mistakes
::warning
**Forgetting the `?`.**:br`var word = Reversed(allocator, "stressed");` compiles on its own, but `word` is then a `String ! TextError`, not a `String`. The next use fails: `error: type 'String ! TextError' has no field 'Length'`, and printing it fails with `has type 'String ! TextError', but variadic parameter 'args' requires 'Display'`.
::
::warning
**Returning a slice of a local array.**:br
A function declared `-> char8[..]` that ends with `return letters[..word.length];` compiles — but the slice points into an array that no longer exists once the function returns, and whatever it reads afterwards is anyone's guess. Text a function makes must be returned as a `String`, which owns it.
::
::warning
**Ignoring that `FromBytes` checks.**:br
Change `"drawer"` to `"né"` and the program prints its first line, then stops with exit status 1: the reversed bytes are not UTF-8, `FromBytes` fails, and `?` passes `InvalidUtf8` out of `Main`.
::
## Try it yourself
1. Reverse `"né"` and handle the failure with a `match` on `.Success` and `.Failure` instead of `?`, printing a message of your own.
2. Make a `String` from a view with `String::FromView(allocator, view)`.
3. Use `Substring(start, end)` to copy the first four bytes of `saved` into a new `String`.
4. Compare `word` and `saved` with `Equals`, before and after `ReplaceWith`.
## Learn more
- [Text](https://rux-lang.dev/docs/api/text) in the API reference
- [String view](https://rux-lang.dev/docs/learn/string-view) — the borrowed form a `String` lends out
- [Destructor](https://rux-lang.dev/docs/learn/destructor) and [Custom copy](https://rux-lang.dev/docs/learn/custom-copy) — how a `String` frees and copies its memory
- [Allocator](https://rux-lang.dev/docs/learn/allocator) — where the memory comes from
# String builder
::note
**You'll need**: [String](https://rux-lang.dev/docs/learn/string), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible), [Propagate](https://rux-lang.dev/docs/learn/propagate)
::
A [`String`](https://rux-lang.dev/docs/learn/string) has no way to add to its text — it can only be replaced whole. Building text a piece at a time out of Strings alone would mean making a new String at every step and copying everything built so far into it — the longer the text, the more each step costs. `StringBuilder` is the tool for that job. It keeps one growing buffer, appends into it, and asks the allocator for a larger one only when the room runs out. When the text is finished, it hands the buffer over as a `String`.
## Length and capacity
A builder tracks two numbers: `Length`, the text so far, and `Capacity`, the room for it. A new builder has allocated nothing yet; its buffer appears with the first append.
```rux
var builder = StringBuilder(allocator);
```
`Join` appends the colours one at a time and reports both numbers after each:
```rux
func Join(builder: &var StringBuilder, words: char8[..][..]) -> ! TextError {
for i in 0..words.length {
if i > 0 {
let separator = i == words.length - 1 ? " and " : ", ";
builder.Append(separator)?;
}
builder.Append(words[i])?;
PrintLine("added {}: {} bytes, room for {}", words[i], builder.Length(),
builder.Capacity());
}
}
```
| After adding | Length | Capacity | What happened |
| ------------ | ------ | -------- | ------------------------------- |
| `red` | 3 | 16 | the first buffer is allocated |
| `orange` | 11 | 16 | fits; only the new bytes copied |
| `yellow` | 19 | 32 | out of room: the buffer doubles |
| `green` | 26 | 32 | fits |
| `blue` | 35 | 64 | doubles again |
The room doubles each time it runs out, so most appends copy just the bytes being added, and the rare move to a bigger buffer gets rarer as the text grows.
The builder is passed as `&var StringBuilder` — a [mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — so `Join` grows the caller's builder rather than a copy of it.
## Every append can fail
Growing the buffer means asking for memory, and that can fail, so every append returns `! TextError` — a [unit fallible](https://rux-lang.dev/docs/learn/unit-fallible). Each one is followed by `?`, which [propagates](https://rux-lang.dev/docs/learn/propagate) a failure and otherwise carries on.
| Method | Appends |
| ------------------- | --------------------------------------------- |
| `Append(text)` | a `char8[..]` or a `StringView` |
| `AppendScalar(c)` | one character, any `char32`, encoded as UTF-8 |
| `AppendAscii(byte)` | one ASCII `char8` |
```rux
builder.Append(" ")?;
builder.AppendScalar('✓')?;
```
The check mark is one character but three UTF-8 bytes, which is why the finished text is 39 bytes, not 37.
## Taking the text out
`View` looks at the text so far without taking it — a [string view](https://rux-lang.dev/docs/learn/string-view) of the builder's buffer. `IntoString` hands the buffer itself to a `String`, without copying a byte, and leaves the builder empty, ready to build something else:
```rux
PrintLine("view {}", builder.View());
let sentence = builder.IntoString();
PrintLine("string {}", sentence);
PrintLine("builder now holds {} bytes", builder.Length());
```
```mermaid
flowchart LR
b["StringBuilder
buffer: 39 bytes"] -- "View()
borrow, no copy" --> v["StringView"]
b -- "IntoString()
hand over the buffer;
builder left empty" --> s["String
owns the 39 bytes"]
b -- "ToString()
copy, may fail" --> c["String
a copy; builder keeps its text"]
```
| Method | Returns | Copies? | Builder afterwards |
| -------------- | -------------------- | ------- | ------------------ |
| `View()` | `StringView` | no | unchanged |
| `IntoString()` | `String` | no | empty |
| `ToString()` | `String ! TextError` | yes | unchanged |
`IntoString` cannot fail — nothing is allocated — which is why it needs no `?`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/StringBuilder){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `String` cannot grow: it can be cleared or replaced whole, but never appended to, so building
// text a piece at a time out of Strings alone would copy everything built so far at every step.
// `StringBuilder` is the tool for that job. It keeps one growing buffer, appends into it, and asks
// the allocator for a larger one only when the room runs out. `Length` is the text so far and
// `Capacity` the room for it. The room doubles each time it runs out, so most appends copy just
// the bytes being added, and the rare move to a bigger buffer gets rarer as the text grows.
//
// Each append can fail, because growing the buffer can, so each returns `! TextError`. When the
// text is finished, `IntoString` hands the buffer over as a String without copying it.
import Allocator::{ Allocator, SystemAllocator };
import Io::PrintLine;
import Text::{ StringBuilder, TextError };
// Joins words into a list a person would write: "a, b and c". The builder is borrowed `&var`,
// so the caller's builder is the one that grows. Watch the room: 16, then 32, then 64.
func Join(builder: &var StringBuilder, words: char8[..][..]) -> ! TextError {
for i in 0..words.length {
if i > 0 {
let separator = i == words.length - 1 ? " and " : ", ";
builder.Append(separator)?;
}
builder.Append(words[i])?;
PrintLine("added {}: {} bytes, room for {}", words[i], builder.Length(),
builder.Capacity());
}
}
func Main() -> ! TextError {
var system = SystemAllocator();
let allocator: Allocator = system;
// A new builder has allocated nothing yet. Its buffer appears with the first append.
var builder = StringBuilder(allocator);
let colours: char8[..][5] = ["red", "orange", "yellow", "green", "blue"];
Join(builder, colours)?;
// Single characters go in with `AppendScalar`, which encodes any character as UTF-8.
builder.Append(" ")?;
builder.AppendScalar('✓')?;
// `View` looks at the text so far without taking it.
PrintLine("view {}", builder.View());
// `IntoString` hands the buffer itself to a String. The builder is left empty, ready to
// build something else.
let sentence = builder.IntoString();
PrintLine("string {}", sentence);
PrintLine("builder now holds {} bytes", builder.Length());
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` and `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/StringBuilder
rux run
```
```text
added red: 3 bytes, room for 16
added orange: 11 bytes, room for 16
added yellow: 19 bytes, room for 32
added green: 26 bytes, room for 32
added blue: 35 bytes, room for 64
view red, orange, yellow, green and blue ✓
string red, orange, yellow, green and blue ✓
builder now holds 0 bytes
```
## Common mistakes
::warning
**Dropping an append's result.**:br`builder.Append(" ");` on its own fails with `error: fallible result of type '! TextError' is discarded`, and the note says why: `a failure that nothing handles is lost`. Add `?`, or handle it with `catch`.
::
::warning
**A builder declared with `let`.**:br
Appending changes the builder, so it must be a `var`. With `let builder`, the call `Join(builder, colours)` fails with `error: argument 1 to 'Join' cannot borrow immutable 'builder' as '&var StringBuilder'`, and each `builder.Append` with `cannot call 'Append' on immutable 'builder'`.
::
::warning
**Appending a character with `Append`.**:br`builder.Append('✓')` fails with `error: no matching overload for method 'Append' on type 'StringBuilder' with argument types (char32)` — `Append` takes text. A single character goes in with `AppendScalar`.
::
## Try it yourself
1. Replace `IntoString` with `ToString()?` and see what the last line prints now.
2. Make the builder with `StringBuilder::WithCapacity(allocator, 64)?` instead. How do the `room for` numbers change?
3. Write `Repeat(builder: &var StringBuilder, text: char8[..], count: int) -> ! TextError` and use it to build a line of 40 dashes.
4. After `IntoString`, build a second sentence in the same builder.
## Learn more
- [Text](https://rux-lang.dev/docs/api/text) in the API reference
- [String](https://rux-lang.dev/docs/learn/string) — what `IntoString` produces
- [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible) and [Propagate](https://rux-lang.dev/docs/learn/propagate) — `! TextError` and `?`
- [Input](https://rux-lang.dev/docs/learn/input) — a builder that `ReadLine` fills with each line typed
# UTF-8
::note
**You'll need**: [Encoding](https://rux-lang.dev/docs/learn/encoding), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Catch](https://rux-lang.dev/docs/learn/catch)
::
UTF-8 stores each character as one to four bytes, and the bytes follow strict rules: the first byte of a character says how many bytes follow, and every byte after it is a *continuation byte* of a particular shape. Not every run of bytes obeys them. Text cut in the wrong place, or bytes that were never text, break the rules. The Text package has the two tools that sit underneath every [view](https://rux-lang.dev/docs/learn/string-view) and [String](https://rux-lang.dev/docs/learn/string): one that checks, and one that decodes.
## The bytes of née
`"née"` is four bytes: `n`, then two for `é`, then `e`.
```mermaid
flowchart LR
b0["byte 0
n
a 1-byte character"] --- b1["byte 1
start of é
says: 2 bytes"] --- b2["byte 2
continuation
of é"] --- b3["byte 3
e
a 1-byte character"]
```
`word[..2]` takes bytes 0 and 1, so it ends with a character whose second byte is missing. `word[2..]` starts with byte 2, a continuation byte with nothing before it. Both are perfectly good `char8[..]` slices, and neither is valid UTF-8.
## Decoding one character at a time
```rux
DecodeAt(bytes, index) -> DecodedScalar ! Utf8Error
```
`DecodeAt` reads the character that starts at `index` and returns a `DecodedScalar` with two fields: `scalar`, the character as a `char32`, and `width`, how many bytes it took. Stepping `index` by each character's width walks the text one character at a time:
```rux
var index: uint = 0;
while index < word.length {
let decoded = DecodeAt(word, index) catch { else => return 1 };
PrintLine("byte {} {} {} byte(s)", index, decoded.scalar, decoded.width);
index += decoded.width;
}
```
The result is fallible, because `index` might not be the start of a character at all. Here a failure cannot happen — the literal is valid and the loop only ever lands on starts — so [`catch`](https://rux-lang.dev/docs/learn/catch) simply ends the program with status 1 if it somehow did.
## Checking the whole text
```rux
Validate(bytes) -> ! Utf8Failure
```
`Validate` checks every byte. On success there is nothing to return; on failure, a `Utf8Failure` says what was wrong and where:
| Field | Type | Meaning |
| -------- | ----------- | -------------------------------------- |
| `reason` | `Utf8Error` | what was wrong |
| `index` | `uint` | the byte where the bad sequence starts |
`Check` takes the outcome apart with a [`match`](https://rux-lang.dev/docs/learn/outcome) on `.Success` and `.Failure`:
```rux
func Check(label: char8[..], bytes: char8[..]) {
match Validate(bytes) {
.Success(_) => PrintLine("{} valid", label),
.Failure(failure) => PrintLine("{} {} at byte {}", label, failure.reason, failure.index)
}
}
```
`Utf8Error` has three cases, and each prints its own description:
| Case | Means | Printed as |
| --------------------- | ------------------------------------------------------------------------ | --------------------------------- |
| `Truncated` | a character starts, but the bytes run out before it ends | `truncated UTF-8 sequence` |
| `InvalidStart` | a byte that cannot start a character, such as a stray continuation byte | `invalid UTF-8 start byte` |
| `InvalidContinuation` | a byte where a continuation byte was required, or one of the wrong shape | `invalid UTF-8 continuation byte` |
`word[..2]` fails as `Truncated` at byte 1, where `é` starts and never finishes. `word[2..]` fails as `InvalidStart` at byte 0 — which is byte 2 of the original word.
`Truncated` is worth telling apart from the others. It is what the end of a buffer looks like when more bytes are still to come, so a program reading in chunks may just need to read more. The other two mean the bytes are not UTF-8, and no amount of reading will change that.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/Utf8){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// UTF-8 stores each character as one to four bytes, and not every run of bytes is valid UTF-8.
// Text cut in the wrong place, or bytes that were never text, break its rules. The Text package
// has the two tools underneath every view and String:
//
// Validate(bytes) -> ! Utf8Failure is all of it UTF-8, and if not, where?
// DecodeAt(bytes, index) -> DecodedScalar ! Utf8Error which character starts here?
//
// A `Utf8Failure` carries two fields: `reason`, a `Utf8Error` saying what was wrong, and `index`,
// the byte where the trouble starts. A `DecodedScalar` carries the character and its width.
import Io::PrintLine;
import Text::{ DecodeAt, Validate };
func Check(label: char8[..], bytes: char8[..]) {
match Validate(bytes) {
.Success(_) => PrintLine("{} valid", label),
.Failure(failure) => PrintLine("{} {} at byte {}", label, failure.reason, failure.index)
}
}
func Main() -> int {
// "née" is four bytes: `n`, then two for `é`, then `e`.
let word = "née";
// Decoding walks the text one character at a time, stepping by each character's width.
var index: uint = 0;
while index < word.length {
let decoded = DecodeAt(word, index) catch { else => return 1 };
PrintLine("byte {} {} {} byte(s)", index, decoded.scalar, decoded.width);
index += decoded.width;
}
// A slice of a literal counts bytes, so a careless range can cut `é` in half. Validation
// finds both kinds of damage: a character whose bytes run out, and a stray byte from the
// middle of a character with nothing before it.
Check("whole ", word);
Check("word[..2] ", word[..2]);
Check("word[2..] ", word[2..]);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/Utf8
rux run
```
```text
byte 0 n 1 byte(s)
byte 1 é 2 byte(s)
byte 3 e 1 byte(s)
whole valid
word[..2] truncated UTF-8 sequence at byte 1
word[2..] invalid UTF-8 start byte at byte 0
```
## Common mistakes
::warning
**Using a decode result as the character.**:br`DecodeAt` is fallible. Without `catch`, `decoded.scalar` fails with `error: type 'DecodedScalar ! Utf8Error' has no field 'scalar'`.
::
::warning
**Stepping one byte at a time.**:br
With `index += 1` instead of `index += decoded.width`, the loop lands on byte 2 — the middle of `é` — and `DecodeAt` fails there. Always step by the width you were given.
::
::warning
**Trusting a slice of text to be text.**:br
A range of a literal counts bytes, so a careless `word[..2]` cuts a character in half without any complaint. Ranges on a `char8[..]` are byte operations; a view's `Part` refuses such a cut, and `Validate` finds the damage afterwards.
::
## Try it yourself
1. Count the characters in `"née"` with `CountScalars` from the Text package, and check it against your decoding loop.
2. Run `Check` on `word[1..3]` and on `word[3..]`. Which ones are valid, and why?
3. Change the decoding loop to print each character's scalar value as a number, with `decoded.scalar as uint32`.
4. Decode `"🚀"`. What width does `DecodeAt` report?
## Learn more
- [Text](https://rux-lang.dev/docs/api/text) in the API reference
- [Encoding](https://rux-lang.dev/docs/learn/encoding) — scalar values and code units
- [Outcome](https://rux-lang.dev/docs/learn/outcome) and [Catch](https://rux-lang.dev/docs/learn/catch) — the two ways this program opens a fallible
- [Unicode](https://rux-lang.dev/docs/learn/unicode) — when one character is several scalars
# Unicode
::note
**You'll need**: [Encoding](https://rux-lang.dev/docs/learn/encoding), [UTF-8](https://rux-lang.dev/docs/learn/utf8)
::
[Encoding](https://rux-lang.dev/docs/learn/encoding) found two ways to count the same text: code units and scalars. There is a third, and it is the one a reader means. Ask a person how many characters are in `café` and they will say four — however the computer happens to store it. This lesson counts the same text all three ways, and finds text where all three answers differ.
## Three levels of text
| Level | What it is | Counted here with |
| ------------- | --------------------------------------------------- | ----------------- |
| **bytes** | how UTF-8 stores the text | `utf8.length` |
| **scalars** | the characters Unicode numbers, one `char32` each | `utf32.length` |
| **graphemes** | what a person would point at and call one character | `CountGraphemes` |
The program passes each text twice — as UTF-8 for the byte count, and as UTF-32, where one unit is one scalar:
```rux
func Count(utf8: char8[..], utf32: char32[..]) {
PrintLine("{} bytes {}, scalars {}, graphemes {}",
utf8, utf8.length, utf32.length, CountGraphemes(utf32));
}
```
`CountGraphemes` comes from the Unicode package, which knows where graphemes begin and end. Like everything in that package, it works on `char32[..]`.
## When the counts differ
```rux
Count("cafe", c32"cafe");
Count("caf\u{E9}", c32"caf\u{E9}");
Count("cafe\u{301}", c32"cafe\u{301}");
Count("\u{1F1FA}\u{1F1E6}", c32"\u{1F1FA}\u{1F1E6}");
```
| Text | Spelled as | Bytes | Scalars | Graphemes |
| ------ | -------------------------------- | ----- | ------- | --------- |
| `cafe` | four ASCII letters | 4 | 4 | 4 |
| `café` | `é` as one scalar, U+00E9 | 5 | 4 | 4 |
| `café` | `e` + U+0301, a combining accent | 6 | 5 | 4 |
| 🇺🇦 | two regional indicators | 8 | 2 | 1 |
**Plain ASCII** — all three counts agree, which is why the difference is so easy to miss.
**`é` as one scalar** — two bytes, but one scalar and one grapheme.
**`e` plus a combining accent** — U+0301 is a scalar of its own that attaches to the letter before it. On screen it looks exactly like the line above, yet every count differs: six bytes, five scalars, four graphemes.
**A flag** — the Ukrainian flag is two "regional indicator" letters, U and A, four bytes each. A screen draws the pair as one picture, so it is one grapheme.
```mermaid
flowchart LR
t(["café, written with
a combining accent"]) --> g["4 graphemes
c · a · f · é"]
g --> s["5 scalars
c · a · f · e · U+0301"]
s --> b["6 bytes
63 61 66 65 CC 81"]
```
## Which count is right?
It depends on the question:
| Question | Count |
| -------------------------------------------------------------- | --------- |
| How much room does it take in a file or a buffer? | bytes |
| What does an encoder or a case mapping work through? | scalars |
| Where does a cursor step? Does it fit "maximum 20 characters"? | graphemes |
Bytes measure storage, scalars are what an encoding works with, and graphemes are what a person sees. A text field that limits a name to 20 "characters" by counting bytes would reject a perfectly short name written in Ukrainian, and a cursor that stepped by scalars would stop between a letter and its accent.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/Unicode){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The Encoding lesson found two ways to count the same text: code units and scalars. There is a
// third, and it is the one a reader means. Text has three levels:
//
// bytes how UTF-8 stores it
// scalars the characters Unicode numbers, one `char32` each
// graphemes what a person would point at and call one character
//
// A grapheme can be made of several scalars. An accent may be its own scalar that attaches to
// the letter before it, and a flag is two "regional indicator" letters that a screen draws as
// one picture. The Unicode package knows where graphemes begin and end.
import Io::PrintLine;
import Unicode::CountGraphemes;
// The same text twice: as UTF-8 for the byte count, and as UTF-32, where one unit is one scalar.
func Count(utf8: char8[..], utf32: char32[..]) {
PrintLine("{} bytes {}, scalars {}, graphemes {}",
utf8, utf8.length, utf32.length, CountGraphemes(utf32));
}
func Main() -> int {
// Plain ASCII: all three counts agree, which is why the difference is easy to miss.
Count("cafe", c32"cafe");
// `é` as one scalar: two bytes, but one scalar and one grapheme.
Count("caf\u{E9}", c32"caf\u{E9}");
// `e` followed by U+0301, a combining acute accent. It looks the same as the line above,
// yet every count differs: six bytes, five scalars, four graphemes.
Count("cafe\u{301}", c32"cafe\u{301}");
// The Ukrainian flag: two regional indicators, four bytes each, drawn as one grapheme.
Count("\u{1F1FA}\u{1F1E6}", c32"\u{1F1FA}\u{1F1E6}");
// Which count is right depends on the question. Bytes measure storage, scalars are what an
// encoding works with, and graphemes are what a cursor steps over or a "maximum 20
// characters" rule should count.
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Unicode` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/Unicode
rux run
```
```text
cafe bytes 4, scalars 4, graphemes 4
café bytes 5, scalars 4, graphemes 4
café bytes 6, scalars 5, graphemes 4
🇺🇦 bytes 8, scalars 2, graphemes 1
```
## Common mistakes
::warning
**Counting graphemes in UTF-8.**:br`CountGraphemes` takes `char32[..]`. `CountGraphemes(utf8)` fails with `error: argument 1 to 'CountGraphemes' has type 'char8[..]', but parameter 'text' requires 'char32[..]'`.
::
::warning
**Assuming text that looks the same is the same.**:br
The two `café` lines print identically, but one is 5 bytes and the other 6. Compared byte by byte they are different texts. Deciding that they mean the same thing is called *normalisation*, and it is a separate step.
::
## Try it yourself
1. Count the family emoji 👨👩👧, written as `"\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}"`. How many scalars does one picture take?
2. Count a waving hand with a skin tone, `"\u{1F44B}\u{1F3FD}"`.
3. Count `"한국"`, then the same first syllable written as three separate jamo, `"\u{1112}\u{1161}\u{11AB}"`.
4. Write a function that reports whether a name fits in 20 graphemes.
## Learn more
- [Encoding](https://rux-lang.dev/docs/learn/encoding) — bytes and scalars
- [UTF-8](https://rux-lang.dev/docs/learn/utf8) — how the bytes are decoded into scalars
- [Unicode case](https://rux-lang.dev/docs/learn/unicode-case) — another Unicode operation that works on scalars
- [Format](https://rux-lang.dev/docs/learn/format) — what a placeholder's width counts
# Unicode case
::note
**You'll need**: [Unicode](https://rux-lang.dev/docs/learn/unicode), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice)
::
Changing case looks like a job for one character at a time: `a` becomes `A`, `é` becomes `É`. For most letters that is true. A few break the pattern. The German `ß` has no single uppercase letter — it becomes `SS`. The ligature `fi` becomes `FI`. A function that returns one character has nowhere to put the second letter, so the Unicode package offers two kinds of mapping: a **simple** one, one scalar in and one out, and a **full** one that may write several.
## The simple mapping
`ToUpperSimple` takes a `char32` and returns a `char32`. It is easy to use — print each result as it comes:
```rux
func UpperSimple(text: char32[..]) {
for c in text {
Print("{}", ToUpperSimple(c));
}
PrintLine("");
}
```
For a character whose uppercase form is more than one letter, there is no right single answer, so the simple mapping leaves it unchanged: `Straße` becomes `STRAßE`.
## The full mapping writes into a buffer
`ToUpperFull` writes its answer — up to three scalars — into a buffer you provide, and reports how many it wrote:
```rux
ToUpperFull(scalar: char32, into: var char32[..], written: *var uint) -> bool
```
```rux
var mapped: char32[3];
var written: uint = 0;
var total: uint = 0;
for c in text {
if ToUpperFull(c, mapped[..], @written) {
for i in 0..written {
Print("{}", mapped[i]);
}
total += written;
}
}
```
Three parts of that call deserve a closer look:
- **`mapped[..]`** is a [writable slice](https://rux-lang.dev/docs/learn/writable-slice) of the buffer, for `ToUpperFull` to fill. Three scalars is the most any character maps to.
- **`@written`** hands over *where* `written` lives, so the function can store the count there. That is a pointer — the subject of [Pointer](https://rux-lang.dev/docs/learn/pointer) and [Out parameter](https://rux-lang.dev/docs/learn/out-parameter) in the Memory part. For now, read it as "put the count in `written`".
- **The `bool` result** is `false` only if the buffer is too small for the answer.
## Where simple and full disagree
| Text | Simple | Full | Scalars |
| -------------- | -------------- | -------------- | ---------- |
| `crème brûlée` | `CRÈME BRÛLÉE` | `CRÈME BRÛLÉE` | 12 from 12 |
| `Straße` | `STRAßE` | `STRASSE` | 7 from 6 |
| `fix` | `fiX` | `FIX` | 3 from 2 |
```mermaid
flowchart LR
c["ß"] -- "ToUpperSimple" --> s["ß
no single uppercase,
left unchanged"]
c -- "ToUpperFull" --> f["S S
written = 2"]
```
Ordinary letters, accented or not, map one to one either way. Only the special cases separate the two — and when they do, the uppercase text is **longer** than the text it came from.
So uppercasing text is a text-to-text operation, not a loop that swaps each character in place. Code that converts a fixed-size buffer in place, character by character, is quietly wrong for German, and for a handful of other languages too.
Lowercase has its own pair, `ToLowerSimple` and `ToLowerFull`. It has special cases of its own: the Turkish capital `İ`, a dotted I, lowercases in full to `i` followed by a combining dot — two scalars.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/UnicodeCase){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Changing case looks like a job for one character at a time: `a` becomes `A`, `é` becomes `É`.
// For most letters that is true, and the Unicode package's "simple" mappings do exactly that,
// one scalar in and one scalar out.
//
// A few characters break the pattern. The German `ß` has no single uppercase letter; it becomes
// "SS". The ligature `fi` becomes "FI". A function that returns one `char32` has nowhere to put
// the second letter, so the simple mapping leaves such a character unchanged. The "full" mapping
// writes up to three scalars into a buffer instead, and so uppercase text can be longer than the
// text it came from.
import Io::{ Print, PrintLine };
import Unicode::{ ToUpperFull, ToUpperSimple };
func UpperSimple(text: char32[..]) {
for c in text {
Print("{}", ToUpperSimple(c));
}
PrintLine("");
}
func UpperFull(text: char32[..]) {
// Three scalars is the most any character maps to. `mapped[..]` is a writable view of the
// buffer for `ToUpperFull` to fill, and it stores how many scalars it wrote in `written`.
// The `@` hands over where `written` lives so the function can write there; that is a
// pointer, the subject of the Memory part. The call returns false only if the buffer is
// too small.
var mapped: char32[3];
var written: uint = 0;
var total: uint = 0;
for c in text {
if ToUpperFull(c, mapped[..], @written) {
for i in 0..written {
Print("{}", mapped[i]);
}
total += written;
}
}
PrintLine(" ({} scalars from {})", total, text.length);
}
func Main() -> int {
// Ordinary letters, accented or not, map one to one either way.
UpperSimple(c32"crème brûlée");
UpperFull(c32"crème brûlée");
PrintLine("");
// Here the two disagree. The simple mapping keeps `ß` and `fi` as they were; the full
// mapping spells them out, and the text grows.
UpperSimple(c32"Straße");
UpperFull(c32"Straße");
UpperSimple(c32"fix");
UpperFull(c32"fix");
// So uppercasing text is a text-to-text operation, not a loop that swaps each character
// in place. Lowercase has its own pair, `ToLowerSimple` and `ToLowerFull`.
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Unicode` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/UnicodeCase
rux run
```
```text
CRÈME BRÛLÉE
CRÈME BRÛLÉE (12 scalars from 12)
STRAßE
STRASSE (7 scalars from 6)
fiX
FIX (3 scalars from 2)
```
## Common mistakes
::warning
**A buffer too small for the answer.**:br`ToUpperFull('ß', small[..], @written)` with `var small: char32[1];` returns `false` and writes nothing usable — `SS` needs two scalars. Use a buffer of three, which fits every character.
::
::warning
**Expecting case changes to undo each other.**:br
Uppercasing `Straße` gives `STRASSE`, and lowercasing that gives `strasse`, not `straße`. The full mappings lose information, so a round trip does not bring back the original.
::
::warning
**Ignoring the result.**:br
If `ToUpperFull` returns `false`, `written` says nothing about this character. The program checks the result with `if` before reading the buffer; do the same.
::
## Try it yourself
1. Write `LowerFull` in the same shape as `UpperFull`, and lowercase `c32"İSTANBUL"`. How many scalars come out of eight?
2. Lowercase `c32"STRASSE"` with it. Do you get `straße` back?
3. Uppercase `c32"floor"` both ways. (`fl` is another ligature.)
4. Change `UpperFull` to count how many characters had no single-scalar answer, that is, how many times `written` was greater than 1.
## Learn more
- [Unicode](https://rux-lang.dev/docs/learn/unicode) — scalars and graphemes
- [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) — the `mapped[..]` the function fills
- [Out parameter](https://rux-lang.dev/docs/learn/out-parameter) — what `@written` is, properly explained
- [String builder](https://rux-lang.dev/docs/learn/string-builder) — `AppendScalar` collects the mapped scalars into text
# Format
::note
**You'll need**: [Console](https://rux-lang.dev/docs/learn/console), [Unicode](https://rux-lang.dev/docs/learn/unicode)
::
Every lesson so far has used `{}` to drop a value into a line. A placeholder can also say how much room the value gets and where in that room it sits — which is how output lines up in columns. The instructions go after a colon inside the braces, and are called the placeholder's **spec**. The spec belongs to the placeholder, not to the value, so the same value can be laid out differently in different places without being changed.
## The spec
| Placeholder | Means | `"Rux"` becomes |
| ----------- | -------------------------------- | --------------- |
| `{}` | the value as it is | `Rux` |
| `{:8}` | at least 8 characters wide | `Rux` |
| `{:<8}` | … with the value on the left | `Rux` |
| `{:^8}` | … in the middle | `Rux` |
| `{:>8}` | … on the right | `Rux` |
| `{:*^8}` | … in the middle, padded with `*` | `**Rux***` |
The parts always come in the same order: an optional fill character, an optional alignment (`<`, `^` or `>`), then the width.
```mermaid
flowchart LR
open["{:"] --> fill["fill
optional
* . - …"] --> align["alignment
optional
< ^ >"] --> width["width
8"] --> close["}"]
```
## Width is a minimum
A shorter value is padded up to the width. Numbers go to the right by default and text to the left, which is what a column of each usually wants:
```rux
PrintLine("number [{:8}]", count);
PrintLine("text [{:8}]", word);
```
Width never cuts. A value wider than its field is printed whole and pushes past it, so `{:2}` with `123456` prints all six digits:
```rux
PrintLine("narrow [{:2}]", 123456);
```
## Alignment and fill
An alignment overrides the default, for numbers and text alike:
```rux
PrintLine("left [{:<8}] [{:<8}]", count, word);
PrintLine("centre [{:^8}] [{:^8}]", count, word);
PrintLine("right [{:>8}] [{:>8}]", count, word);
```
When the padding does not split evenly, centring puts the extra space on the right: `Rux` in eight is two spaces, `Rux`, three spaces.
A fill character goes before the alignment and replaces the spaces:
```rux
PrintLine("dots [{:.<8}] [{:.>8}]", word, count);
PrintLine("stars [{:*^8}]", word);
```
## Width counts characters, not bytes
`née` is four bytes but three characters, and the width counts characters — Unicode scalars — so accented text lines up with plain text:
```rux
PrintLine("accent [{:8}] [{:8}]", "née", "nee");
```
That is what makes a table work, whatever is in it. Each row uses the same three placeholders, and the columns line up even though `crêpes` has a two-byte `ê`:
```rux
PrintLine("{:<10}{:>6}{:>8}", "item", "count", "price");
PrintLine("{:<10}{:>6}{:>8}", "widgets", 12, 450);
PrintLine("{:<10}{:>6}{:>8}", "sprockets", 5, 1975);
PrintLine("{:<10}{:>6}{:>8}", "crêpes", 1440, 25);
```
The name column is left-aligned in 10, and the two number columns are right-aligned in 6 and 8 — the usual choice for figures, so their units line up.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/Format){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Every lesson has used `{}` to drop a value into a line. A placeholder can also say how much
// room the value gets and where in that room it sits, which is how output lines up in columns.
// The part after a colon is the placeholder's spec:
//
// {:8} at least 8 characters wide
// {:<8} ... with the value on the left
// {:^8} ... in the middle
// {:>8} ... on the right
// {:*^8} ... in the middle, padded with `*` instead of spaces
//
// The spec belongs to the placeholder, not to the value, so the same value can be laid out
// differently in different places without being changed.
import Io::PrintLine;
func Main() -> int {
let count = 42;
let word = "Rux";
// Width is a minimum. Shorter values are padded; numbers go to the right by default and
// text to the left, which is what a column of each usually wants.
PrintLine("number [{:8}]", count);
PrintLine("text [{:8}]", word);
// Width never cuts. A value wider than its field is printed whole and pushes past it.
PrintLine("narrow [{:2}]", 123456);
// Alignment overrides the default, for numbers and text alike.
PrintLine("left [{:<8}] [{:<8}]", count, word);
PrintLine("centre [{:^8}] [{:^8}]", count, word);
PrintLine("right [{:>8}] [{:>8}]", count, word);
// A fill character before the alignment replaces the spaces.
PrintLine("dots [{:.<8}] [{:.>8}]", word, count);
PrintLine("stars [{:*^8}]", word);
// Width counts characters, not bytes, so accented text lines up with plain text.
PrintLine("accent [{:8}] [{:8}]", "née", "nee");
// What all of this is for: a table whose columns line up whatever is in them.
PrintLine("");
PrintLine("{:<10}{:>6}{:>8}", "item", "count", "price");
PrintLine("{:<10}{:>6}{:>8}", "widgets", 12, 450);
PrintLine("{:<10}{:>6}{:>8}", "sprockets", 5, 1975);
PrintLine("{:<10}{:>6}{:>8}", "crêpes", 1440, 25);
return 0;
}
```
## Run it
```sh
cd Examples/Text/Format
rux run
```
```text
number [ 42]
text [Rux ]
narrow [123456]
left [42 ] [Rux ]
centre [ 42 ] [ Rux ]
right [ 42] [ Rux]
dots [Rux.....] [......42]
stars [**Rux***]
accent [née ] [nee ]
item count price
widgets 12 450
sprockets 5 1975
crêpes 1440 25
```
## Common mistakes
::warning
**More placeholders than values, or fewer.**:br
The compiler counts the placeholders of a pattern written in the call. `PrintLine("{} and {}", 1)` fails with `error: format string has 2 placeholders, but 1 argument was provided`, and `PrintLine("{}", 1, 2)` with `format string has 1 placeholder, but 2 arguments were provided`.
::
::warning
**A spec the value does not understand.**:br
The compiler counts placeholders but does not check what each spec asks for. An integer has no `z` style, so `PrintLine("[{:z}]", 1)` prints only `[`, with no line end, and the rest of the line is lost — `PrintLine` stops at the placeholder it cannot honour and returns an `IoError`, which an ignored result never shows. [Render](https://rux-lang.dev/docs/learn/render) reports such a pattern properly.
::
::warning
**Expecting width to count what you see.**:br
Width counts scalars, not [graphemes](https://rux-lang.dev/docs/learn/unicode). A `café` spelled with a combining accent is five scalars, so `{:8}` pads it with three spaces and it looks one column short. Wide characters such as emoji take two columns on most terminals but count as one.
::
## Try it yourself
1. Add a fourth column, `total`, that is `count * price`, right-aligned in 10.
2. Print a title centred in 24 characters between rows of `=`, using a fill character for both.
3. Make the item column dotted — `widgets...` — so the eye can follow each row.
4. Put a name longer than 10 characters in the table. What happens to the columns?
## Learn more
- [Console](https://rux-lang.dev/docs/learn/console) — `{}` placeholders from Part 1
- [Format number](https://rux-lang.dev/docs/learn/format-number) — precision, bases, zeros and signs
- [Display](https://rux-lang.dev/docs/learn/display) — how your own type receives the spec
- [Render](https://rux-lang.dev/docs/learn/render) — the same patterns, formatted into a `String`
# Format number
::note
**You'll need**: [Format](https://rux-lang.dev/docs/learn/format), [Float](https://rux-lang.dev/docs/learn/float), [Literal](https://rux-lang.dev/docs/learn/literal)
::
[Format](https://rux-lang.dev/docs/learn/format) gave a value room and a place in it. Numbers have a few more choices of their own: how many digits after the point, which base, whether to pad with zeros and whether to show a `+`. They are written in the same spec after the colon, and none of them changes the number — they only decide how it is spelled.
## The number spec
| Spec | Means | Example | Prints |
| ------- | ------------------------------------------------ | --------------- | ---------------- |
| `{:.2}` | two digits after the decimal point | `3.14159…` | `3.14` |
| `{:e}` | scientific notation; `E` for a capital mark | `6.02214076e23` | `6.02214076e+23` |
| `{:x}` | hexadecimal; `X` capitals, `o` octal, `b` binary | `255` | `ff` |
| `{:#x}` | … with the prefix that names the base | `255` | `0xff` |
| `{:06}` | padded to 6 with zeros instead of spaces | `42` | `000042` |
| `{:+}` | a sign even when the number is positive | `7` | `+7` |
They combine with the width and alignment from the Format lesson, in a fixed order: fill and alignment, then `+`, then `#`, then `0` and the width, then the precision, then the letter for the base or notation. So `{:>10.2}` is "right-aligned in 10, two digits after the point".
```mermaid
flowchart LR
a["fill + align
>"] --> s["sign
+"] --> h["prefix
#"] --> z["zeros + width
08"] --> p["precision
.2"] --> k["kind
x X o b e E"]
```
## Digits after the point
With no precision, a float prints with as few digits as it takes to read back as the same value — which is why `0.1` prints as `0.1` and not as the long binary approximation. A precision fixes the number of digits instead, rounding the rest:
```rux
PrintLine("pi {}", pi);
PrintLine("pi .2 {:.2}", pi);
PrintLine("pi .0 {:.0}", pi);
PrintLine("0.1 {}", 0.1);
```
Rounding can carry all the way up: 9.99 to one digit is `10.0`, not `9.10`. And exact halves round to the **even** digit, so `0.125` becomes `0.12` while `0.375` becomes `0.38`:
```rux
PrintLine("9.99 .1 {:.1}", 9.99);
PrintLine("halves .2 {:.2} {:.2}", 0.125, 0.375);
```
Rounding halves to even, rather than always up, keeps a long column of rounded figures from drifting upwards as a whole.
A precision combines with a width, which is how money lines up:
```rux
PrintLine("money [{:>10.2}]", 1234.5);
```
## Scientific notation
Scientific notation puts one digit before the point and moves the rest into the exponent. It is the readable way to print very large or very small values, and it takes a precision too:
```rux
PrintLine("scientific {:e} {:E} {:.2e}", 6.02214076e23, 0.00025, 1234.5678);
```
`6.02214076e+23` means 6.02214076 × 10²³, and `2.5E-04` means 2.5 × 10⁻⁴.
## Bases, prefixes, zeros and signs
The same 255 in four spellings, then with the prefixes that name each base — the same prefixes a [literal](https://rux-lang.dev/docs/learn/literal) uses:
```rux
PrintLine("bases {} {:x} {:X} {:o} {:b}", value, value, value, value, value);
PrintLine("prefixed {:#x} {:#o} {:#b}", value, value, value);
```
Zeros pad **between** the sign or prefix and the digits, where spaces would look wrong — so `-42` padded to six is `-00042`, not `000-42`:
```rux
PrintLine("zeros {:06} {:06} {:#06x}", 42, -42, value);
```
And `+` keeps a column of mixed signs aligned, because every number then starts with a sign:
```rux
PrintLine("signs {:+} {:+} {:+.1}", 7, -7, 0.25);
```
Note `0.25` to one digit with `+`: `+0.2`, the even neighbour again.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/FormatNumber){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The Format lesson gave a value room and a place in it. Numbers have a few more choices of
// their own, written in the same spec after the colon:
//
// {:.2} two digits after the decimal point
// {:x} hexadecimal; `X` for capitals, `o` octal, `b` binary
// {:#x} ... with the prefix that names the base: 0x, 0o, 0b
// {:08} padded to 8 with zeros instead of spaces
// {:+} a sign even when the number is positive
// {:e} scientific notation, 1.5e+03; `E` for a capital mark
//
// None of this changes the number. It only decides how the number is spelled.
import Io::PrintLine;
func Main() -> int {
let pi = 3.14159265358979;
// A float prints with as few digits as it takes to read back as the same value. A
// precision fixes the number of digits after the point instead, rounding the rest.
PrintLine("pi {}", pi);
PrintLine("pi .2 {:.2}", pi);
PrintLine("pi .0 {:.0}", pi);
PrintLine("0.1 {}", 0.1);
// Rounding can carry all the way up: 9.99 to one digit is 10.0, not 9.10.
PrintLine("9.99 .1 {:.1}", 9.99);
// Exact halves round to the even digit, so 0.125 becomes 0.12 and 0.375 becomes 0.38.
PrintLine("halves .2 {:.2} {:.2}", 0.125, 0.375);
// Precision combines with the width and alignment from the Format lesson.
PrintLine("money [{:>10.2}]", 1234.5);
// Scientific notation puts one digit before the point and moves the rest into the exponent.
// It is the readable way to print very large or very small values, and takes a precision too.
PrintLine("scientific {:e} {:E} {:.2e}", 6.02214076e23, 0.00025, 1234.5678);
// Bases. The same 255 in four spellings, then with their prefixes.
let value = 255;
PrintLine("bases {} {:x} {:X} {:o} {:b}", value, value, value, value, value);
PrintLine("prefixed {:#x} {:#o} {:#b}", value, value, value);
// Zeros pad between the sign or prefix and the digits, where spaces would look wrong.
PrintLine("zeros {:06} {:06} {:#06x}", 42, -42, value);
// `+` keeps a column of mixed signs aligned.
PrintLine("signs {:+} {:+} {:+.1}", 7, -7, 0.25);
return 0;
}
```
## Run it
```sh
cd Examples/Text/FormatNumber
rux run
```
```text
pi 3.14159265358979
pi .2 3.14
pi .0 3
0.1 0.1
9.99 .1 10.0
halves .2 0.12 0.38
money [ 1234.50]
scientific 6.02214076e+23 2.5E-04 1.23e+03
bases 255 ff FF 377 11111111
prefixed 0xff 0o377 0b11111111
zeros 000042 -00042 0x00ff
signs +7 -7 +0.2
```
## Common mistakes
::warning
**Expecting a decimal half to round up.**:br`{:.2}` of `2.675` prints `2.67`, and `{:.1}` of `0.35` prints `0.3`. A float cannot hold most decimal fractions exactly: the stored value is a hair below the half, so it rounds down. If amounts must round exactly, keep them as whole cents in an integer, as `Money` did in [Display](https://rux-lang.dev/docs/learn/display).
::
::warning
**Expecting hexadecimal to show a negative number's bits.**:br`{:x}` of `-1` prints `-1`: a sign and the magnitude, not two's complement. To see the bits, convert first — `{:x}` of `-1 as uint8` prints `ff`.
::
::warning
**A spec the value cannot honour.**:br`PrintLine("[{:x}]", 2.5)` compiles, but a float has no hexadecimal spelling: it prints only `[`, with no line end, and `PrintLine` returns an `IoError`. [Render](https://rux-lang.dev/docs/learn/render) reports the same request as `FormatError::UnsupportedRequest`.
::
## Try it yourself
1. Print `pi` with 0, 2, 4 and 8 digits after the point, in a right-aligned column of width 12.
2. Print the numbers 0 to 15 as two-digit hexadecimal: `00`, `01`, … `0f`.
3. Print `42` as an 8-bit binary number with its prefix: `0b00101010`.
4. Print the speed of light, `2.998e8`, and the charge of an electron, `1.6e-19`, in scientific notation with two digits after the point.
## Learn more
- [Format](https://rux-lang.dev/docs/learn/format) — width, alignment and fill
- [Literal](https://rux-lang.dev/docs/learn/literal) — the same bases, written in source
- [Float](https://rux-lang.dev/docs/learn/float) — why `0.1 + 0.2` is not `0.3`, and why `2.675` rounds down
- [Parse](https://rux-lang.dev/docs/learn/parse) — reading these spellings back into numbers
# Render
::note
**You'll need**: [Format number](https://rux-lang.dev/docs/learn/format-number), [String](https://rux-lang.dev/docs/learn/string), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Variant match](https://rux-lang.dev/docs/learn/variant-match)
::
`PrintLine` formats text and sends it straight to the console. Often you want the formatted text itself — to keep it, measure it, pass it on, or print it later. `Render`, from the Format package, does the same formatting but hands the text back as a [`String`](https://rux-lang.dev/docs/learn/string):
```rux
Render(allocator, pattern, values...) -> String ! FormatError
```
Its result is fallible, and the way it fails is the other half of this lesson: a wrong pattern is found **before** anything is written, and the error says exactly what was wrong.
## Text you keep
`InvoiceCode` builds a code such as `INV-0042` once, as a value the caller owns:
```rux
func InvoiceCode(allocator: Allocator, number: int) -> String ! FormatError {
return Render(allocator, "INV-{:04}", number);
}
```
The pattern is the same as `PrintLine`'s — `{:04}` is the zero padding from [Format number](https://rux-lang.dev/docs/learn/format-number). Each call produces a `String` that lives on after it, unlike a printed line, so `Main` can print both codes in one line and measure one of them:
```rux
let first = InvoiceCode(allocator, 7)?;
let second = InvoiceCode(allocator, 42)?;
PrintLine("codes {} and {}, {} bytes each", first, second, first.Length());
```
## Why it can fail
Two kinds of thing can go wrong. The `String` needs memory, and the pattern itself can be wrong. `FormatError` is a variant with a case for each:
| Case | When |
| -------------------------------------- | ------------------------------------------------------------------ |
| `InvalidSpecification(at)` | a spec does not parse; `at` is the byte where it stopped |
| `UnsupportedRequest` | a spec parses, but asks for something this value does not support |
| `ArgumentCountMismatch(wanted, given)` | placeholders and values do not match in number |
| `InsufficientCapacity` | a destination of fixed size cannot take the whole text |
| `ValueOutOfRange` | a valid value cannot be shown in the form asked for |
| `TextFailure(kind)` | text handling failed — this is where running out of memory arrives |
| `WriterFailure` | the destination failed on its own terms |
`Show` takes an outcome apart with two nested `match`es. The cases that carry a position or counts are taken apart; everything else prints its own description through `else`:
```rux
func Show(outcome: String ! FormatError) {
match outcome {
.Success(text) => PrintLine("rendered {}", text),
.Failure(error) => match error {
.InvalidSpecification(at) => PrintLine("refused bad spec at byte {}", at),
.ArgumentCountMismatch(wanted, given) =>
PrintLine("refused {} placeholders but {} values", wanted, given),
else => PrintLine("refused {}", error)
}
}
}
```
## One right pattern and three wrong ones
```rux
Show(Render(allocator, "{} of {}", 3, 10));
Show(Render(allocator, "[{:>>>}]", 3));
let pattern = "{}, {} and {}";
Show(Render(allocator, pattern, 3, 4));
Show(Render(allocator, "{:x}", "text"));
```
| Pattern | Values | Result |
| ------------ | -------- | --------------------------------------------------------------------------------------- |
| `"{} of {}"` | 3, 10 | `3 of 10` |
| `"[{:>>>}]"` | 3 | `InvalidSpecification(5)` — `>>` is a fill and an alignment; the third `>` fits nowhere |
| `pattern` | 3, 4 | `ArgumentCountMismatch(3, 2)` |
| `"{:x}"` | `"text"` | `UnsupportedRequest` — text has no hexadecimal |
```mermaid
flowchart LR
r["Render(allocator, pattern, values…)"] --> m{"Memory for
the String?"}
m -- "no" --> f2["Failure:
TextFailure(OutOfMemory)"]
m -- "yes" --> c{"Is the pattern right
for these values?"}
c -- "no" --> f["Failure: the FormatError case;
no half rendering is returned"]
c -- "yes" --> s["Success: the String"]
```
## Counted while compiling, or while running
Why is the third pattern in a variable? Because the compiler counts the placeholders of a pattern written in the call itself. `Render(allocator, "{}, {} and {}", 3, 4)` does not build at all. A pattern that arrives as a value — read from a file, chosen at run time — can be counted only when it runs, and that is when `ArgumentCountMismatch` appears.
`PrintLine` refuses the same patterns too, but its `IoError` can only say that the request was invalid, not where or why. When a pattern might be wrong, `Render` is the one that tells you.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/Render){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `PrintLine` formats text and sends it straight to the console. `Render`, from the Format
// package, does the same formatting but hands the text back as a `String`, to be kept, measured,
// passed on or printed later:
//
// Render(allocator, pattern, values...) -> String ! FormatError
//
// The result is fallible because two things can go wrong. The String needs memory, and the
// pattern itself can be wrong: a spec that does not parse, a spec the value cannot honour, or
// a different number of placeholders than values. A wrong pattern is found before anything is
// written, so a failure never leaves half a rendering behind.
import Allocator::{ Allocator, SystemAllocator };
import Format::Render;
import Io::PrintLine;
import Text::{ FormatError, String };
// Builds a code such as "INV-0042" once, as a value the caller owns.
func InvoiceCode(allocator: Allocator, number: int) -> String ! FormatError {
return Render(allocator, "INV-{:04}", number);
}
// Reports either outcome of a render. `FormatError` is a variant, so the cases that carry a
// position or counts can be taken apart; the rest print their own description.
func Show(outcome: String ! FormatError) {
match outcome {
.Success(text) => PrintLine("rendered {}", text),
.Failure(error) => match error {
.InvalidSpecification(at) => PrintLine("refused bad spec at byte {}", at),
.ArgumentCountMismatch(wanted, given) =>
PrintLine("refused {} placeholders but {} values", wanted, given),
else => PrintLine("refused {}", error)
}
}
}
func Main() -> ! FormatError {
var system = SystemAllocator();
let allocator: Allocator = system;
// Each call produces a String that lives on after it, unlike a printed line.
let first = InvoiceCode(allocator, 7)?;
let second = InvoiceCode(allocator, 42)?;
PrintLine("codes {} and {}, {} bytes each", first, second, first.Length());
// A pattern that works, then three ways a pattern can be wrong, each refused with its own
// case. `PrintLine` refuses the same patterns too, but its `IoError` can only say the
// request was invalid, not where or why.
Show(Render(allocator, "{} of {}", 3, 10));
Show(Render(allocator, "[{:>>>}]", 3));
// The compiler counts the placeholders of a pattern written in the call itself, so
// `Render(allocator, "{}, {} and {}", 3, 4)` does not build: "format string has 3
// placeholders, but 2 arguments were provided". A pattern that arrives as a value is
// counted only when it runs.
let pattern = "{}, {} and {}";
Show(Render(allocator, pattern, 3, 4));
Show(Render(allocator, "{:x}", "text"));
}
```
Besides `Io`, its `Rux.toml` lists `Allocator`, `Format` and `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/Render
rux run
```
```text
codes INV-0007 and INV-0042, 8 bytes each
rendered 3 of 10
refused bad spec at byte 5
refused 3 placeholders but 2 values
refused unsupported formatting request
```
## Common mistakes
::warning
**Using the result without `?`.**:br`let first = InvoiceCode(allocator, 7);` leaves `first` a `String ! FormatError`. Its next use fails: `error: type 'String ! FormatError' has no field 'Length'`.
::
::warning
**A literal pattern with the wrong number of values.**:br`Render(allocator, "{}, {} and {}", 3, 4)` fails while compiling: `error: format string has 3 placeholders, but 2 arguments were provided`.
::
::warning
**Matching `FormatError` without `else`.**:br
Leave out the `else` arm and the inner `match` fails with `error: match on 'FormatError' is not exhaustive; missing FormatError::UnsupportedRequest, FormatError::InsufficientCapacity, …`. Handle the cases you care about and let `else` print the rest.
::
## Try it yourself
1. Render a price with two digits after the point, `"{:.2}"`, and print its `Length()`.
2. Write `Label(allocator: Allocator, name: char8[..], score: int) -> String ! FormatError` that produces lines such as `Ada.......17`.
3. Pass `Render(allocator, "{:q}", 5)` to `Show`. Which case is it?
4. Change `"[{:>>>}]"` to `"[{:>>}]"`. What does it render?
## Learn more
- [Format](https://rux-lang.dev/docs/api/format) in the API reference
- [Format](https://rux-lang.dev/docs/learn/format) and [Format number](https://rux-lang.dev/docs/learn/format-number) — the pattern language
- [String](https://rux-lang.dev/docs/learn/string) — what `Render` returns
- [Variant match](https://rux-lang.dev/docs/learn/variant-match) — taking `FormatError` apart
# Parse
::note
**You'll need**: [Format number](https://rux-lang.dev/docs/learn/format-number), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback)
::
Parsing is formatting in reverse: text in, value out. Unlike formatting, it fails all the time, because the text usually comes from a person or a file and may not spell a number at all — `"seven"`, `"12abc"`, or nothing. So every parser in the Format package returns a [fallible](https://rux-lang.dev/docs/learn/fallible), and the failure says what was wrong and where:
```rux
ParseInt32(text) -> int32 ! ParseError
```
## What can go wrong
`ParseError` is a variant, and every case but the first carries the byte position where the trouble was found:
| Case | Means |
| ---------------------- | --------------------------------------------------------------- |
| `Empty` | there was no text at all |
| `InvalidCharacter(at)` | the byte at `at` is not part of a number |
| `Overflow(at)` | a well-formed number too large for the type, usually at its end |
| `OutOfMemory(at)` | only the very widest number types can hit this one |
`Describe` names each case. A `match` on a variant must be [exhaustive](https://rux-lang.dev/docs/learn/exhaustive), so even the case an `int32` can never hit gets an arm:
```rux
func Describe(error: ParseError) {
match error {
.Empty => PrintLine("empty text"),
.InvalidCharacter(at) => PrintLine("not a digit at byte {}", at),
.Overflow(at) => PrintLine("too large for an int32, at byte {}", at),
.OutOfMemory(_) => PrintLine("out of memory")
}
}
```
`Read` then opens the outcome with `.Success` and `.Failure`, as in [Outcome](https://rux-lang.dev/docs/learn/outcome):
```rux
func Read(text: char8[..]) {
match ParseInt32(text) {
.Success(value) => PrintLine("[{}] {}", text, value),
.Failure(error) => {
Print("[{}] refused, ", text);
Describe(error);
}
}
}
```
## What counts as a number
A sign is understood, and so is a base prefix of the kind `{:#x}` writes, so `"0x1F"` reads as 31. Everything else must be digits. The parser does not skip spaces or stop at the first non-digit: the **whole** text must be a number, or the answer is a failure saying which byte is wrong.
| Text | Result | Why |
| --------------- | --------------------- | ----------------------------------------- |
| `"42"` | `42` | |
| `"-17"` | `-17` | a sign |
| `"0x1F"` | `31` | a base prefix, as `{:#x}` writes |
| `""` | `Empty` | nothing to read |
| `" 42"` | `InvalidCharacter(0)` | a space is not a digit, even at the start |
| `"12abc"` | `InvalidCharacter(2)` | `a` is the first byte that is not a digit |
| `"2147483647"` | `2147483647` | the largest `int32` |
| `"2147483648"` | `Overflow(10)` | one more does not fit |
| `"-2147483648"` | `-2147483648` | the negative side holds one more |
```mermaid
flowchart LR
t(["text"]) --> e{"empty?"}
e -- "yes" --> E["Empty"]
e -- "no" --> d{"sign, prefix and digits
— and nothing else?"}
d -- "no" --> I["InvalidCharacter(at)"]
d -- "yes" --> f{"fits an int32?"}
f -- "no" --> O["Overflow(at)"]
f -- "yes" --> S["Success(value)"]
```
An `int32` holds −2147483648 to 2147483647. The negative side holds one more than the positive, so `-2147483648` parses even though `2147483648` does not.
## A fallback in one expression
Often a failure just means "use a default". With `catch`, as in [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback), that takes one expression:
```rux
let fallback = ParseInt32("seven") catch { else => 0 };
```
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/Parse){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Parsing is formatting in reverse: text in, value out. Unlike formatting, it fails all the time,
// because the text usually comes from a person or a file and may not spell a number at all.
// So every parser in the Format package returns a fallible:
//
// ParseInt32(text) -> int32 ! ParseError
//
// `ParseError` is a variant, and every case says where in the text the trouble was found:
//
// Empty there was no text at all
// InvalidCharacter(at) the byte at `at` is not part of a number
// Overflow(at) a well-formed number too large for an `int32`, usually at its end
// OutOfMemory(at) only the very widest number types can hit this one
import Format::{ ParseError, ParseInt32 };
import Io::{ Print, PrintLine };
func Describe(error: ParseError) {
match error {
.Empty => PrintLine("empty text"),
.InvalidCharacter(at) => PrintLine("not a digit at byte {}", at),
.Overflow(at) => PrintLine("too large for an int32, at byte {}", at),
.OutOfMemory(_) => PrintLine("out of memory")
}
}
func Read(text: char8[..]) {
match ParseInt32(text) {
.Success(value) => PrintLine("[{}] {}", text, value),
.Failure(error) => {
Print("[{}] refused, ", text);
Describe(error);
}
}
}
func Main() -> int {
// A sign, and a base prefix of the kind `{:#x}` writes, are both understood.
Read("42");
Read("-17");
Read("0x1F");
// Malformed text. The parser does not skip spaces or stop at the first non-digit: the
// whole text must be a number, or the answer is a failure saying which byte is wrong.
Read("");
Read(" 42");
Read("12abc");
// Well-formed but too large. An `int32` holds -2147483648 to 2147483647, and the
// negative side holds one more than the positive.
Read("2147483647");
Read("2147483648");
Read("-2147483648");
// With `catch`, a failure can become a fallback value in one expression.
let fallback = ParseInt32("seven") catch { else => 0 };
PrintLine("[seven] with a fallback {}", fallback);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Format` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/Parse
rux run
```
```text
[42] 42
[-17] -17
[0x1F] 31
[] refused, empty text
[ 42] refused, not a digit at byte 0
[12abc] refused, not a digit at byte 2
[2147483647] 2147483647
[2147483648] refused, too large for an int32, at byte 10
[-2147483648] -2147483648
[seven] with a fallback 0
```
## Common mistakes
::warning
**Using the result as a number.**:br`ParseInt32("41") + 1` fails with `error: operator '+' cannot combine left operand 'int32 ! ParseError' with right operand 'int'`. Open the outcome first — with `match`, `catch`, or `?` in a fallible function.
::
::warning
**Expecting spaces to be ignored.**:br`" 42"` fails at byte 0 and `"42 "` at byte 2. Text read from a person or a file often has spaces or a line end around it: trim it first with a [string view](https://rux-lang.dev/docs/learn/string-view) — `ParseInt32` accepts a `StringView` as well as a `char8[..]`.
::
::warning
**Writing digit separators.**:br`1_000` is a fine [literal](https://rux-lang.dev/docs/learn/literal) in source, but `ParseInt32("1_000")` fails with `InvalidCharacter(1)`. The parser reads numbers as people type them, not as Rux source spells them.
::
## Try it yourself
1. Parse `"0b101"` and `"0o17"`. Which other prefixes does the parser understand?
2. Parse `"300"` and `"-1"` with `ParseUint8`. What does each report?
3. Trim `" 42\n"` with `StringView::FromValidated(…).Trim()` and parse the view. You will need `Text` in `[Dependencies]` and `import Text::StringView;`.
4. Parse `"3.75"` with `ParseFloat64`, then try `"3,75"`.
## Learn more
- [Format](https://rux-lang.dev/docs/api/format) in the API reference
- [Format number](https://rux-lang.dev/docs/learn/format-number) — the spellings `ParseInt32` reads back
- [Outcome](https://rux-lang.dev/docs/learn/outcome) and [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) — opening the result
- [Input](https://rux-lang.dev/docs/learn/input) — text typed by a person, the usual thing to parse
# Input
::note
**You'll need**: [String builder](https://rux-lang.dev/docs/learn/string-builder), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Guard](https://rux-lang.dev/docs/learn/guard), [Break](https://rux-lang.dev/docs/learn/break), [Continue](https://rux-lang.dev/docs/learn/continue)
::
Every program so far has only talked. This one listens. Reading what a person types needs somewhere to put text that does not exist yet — a job for a [`StringBuilder`](https://rux-lang.dev/docs/learn/string-builder). `ReadLine`, from the Io package, appends the next line of standard input to one:
```rux
ReadLine(builder: &var StringBuilder) -> ! IoError
```
A success means a line arrived, without its line ending; an empty line is still a success. And reaching the end of the input is not a special return value. It is a failure like any other, so a reading loop handles it in the same `match` as a real error — and stops.
## One builder for every line
`Main` makes one builder and reuses it. `Clear` empties it before each line but keeps its memory, so after the first few lines the builder rarely needs to grow:
```rux
var builder = StringBuilder(allocator);
var count = 0;
PrintLine("Type some lines, then end the input.");
while true {
builder.Clear();
match ReadLine(builder) {
```
After a successful read, `builder.View()` is the line, as a [string view](https://rux-lang.dev/docs/learn/string-view):
```rux
count += 1;
let line = builder.View();
if line.IsEmpty() {
PrintLine("line {} is empty", count);
} else {
PrintLine("line {} is \"{}\", {} bytes", count, line, line.Length());
}
```
## Telling the failures apart
An `IoError` carries a `kind`, an `IoErrorKind`. Two kinds matter to a reading loop, and [guards](https://rux-lang.dev/docs/learn/guard) on the `.Failure` arms pick them out:
```rux
match ReadLine(builder) {
.Success(_) => {},
.Failure(error) if error.kind == IoErrorKind::EndOfStream => break,
.Failure(error) if error.kind == IoErrorKind::InvalidText => {
count += 1;
PrintLine("line {} is not UTF-8 text", count);
continue;
},
.Failure(_) => {
PrintLine("the input could not be read");
return 1;
}
}
```
| Outcome | Means | The loop |
| ----------------- | ------------------------------------------- | ---------- |
| `.Success` | a line is in the builder | prints it |
| `EndOfStream` | there is no more input | `break` |
| `InvalidText` | the line was not UTF-8; it has been skipped | `continue` |
| any other failure | the input really could not be read | `return 1` |
```mermaid
flowchart LR
c["builder.Clear()"] --> r["ReadLine(builder)"]
r -- "Success" --> p["print the line"] --> c
r -- "InvalidText" --> s["report it"] --> c
r -- "EndOfStream" --> e["break: report
how many lines"]
r -- "anything else" --> x["return 1"]
```
A line that is not valid UTF-8 fails with `InvalidText`, but only after the rest of it has been read. So the next call starts cleanly on the next line, and the loop can carry on as if the bad line had been a comment.
## Ending the input
Typing at a terminal, nothing ends the input until you say so: **Ctrl+Z** then **Enter** on Windows, **Ctrl+D** elsewhere. Input piped in from another program or a file ends by itself, and an empty pipe ends at once — the very first `ReadLine` fails with `EndOfStream`, and the program reports 0 lines.
The line ending is not part of the line, whether it was a Unix `\n` or a Windows `\r\n`, so `"Ada"` is three bytes on every system. A last line with no line ending at all still arrives as a line.
`ReadLine` reads from standard input. `ReadLineFrom` reads the same way from any other source of bytes.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Text/Input){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Reading what a person types needs somewhere to put text that does not exist yet, and that is
// a job for a `StringBuilder`. `ReadLine` from the Io package appends the next line to one:
//
// ReadLine(builder: &var StringBuilder) -> ! IoError
//
// A success means a line arrived, without its line ending; an empty line is still a success.
// Running out of input is not a special return value. It is a failure like any other, an
// `IoError` whose `kind` is `IoErrorKind::EndOfStream`, so a reading loop handles it in the same
// `match` as a real error, and stops.
//
// A line is UTF-8 text, so "héllo" arrives as six bytes. A line that is not valid UTF-8 fails
// with the kind `IoErrorKind::InvalidText`, but only after the rest of it has been read, so the
// next call starts cleanly on the next line and the loop can carry on. `ReadLineFrom` reads the
// same way from any other source of bytes.
//
// Interactively, end the input with Ctrl+Z and Enter on Windows, or Ctrl+D elsewhere. Piped
// input ends by itself.
import Allocator::{ Allocator, SystemAllocator };
import Io::{ IoErrorKind, PrintLine, ReadLine };
import Text::StringBuilder;
func Main() -> int {
var system = SystemAllocator();
let allocator: Allocator = system;
// One builder serves every line. `Clear` empties it but keeps its memory for the next one.
var builder = StringBuilder(allocator);
var count = 0;
PrintLine("Type some lines, then end the input.");
while true {
builder.Clear();
match ReadLine(builder) {
.Success(_) => {},
.Failure(error) if error.kind == IoErrorKind::EndOfStream => break,
.Failure(error) if error.kind == IoErrorKind::InvalidText => {
count += 1;
PrintLine("line {} is not UTF-8 text", count);
continue;
},
.Failure(_) => {
PrintLine("the input could not be read");
return 1;
}
}
count += 1;
let line = builder.View();
if line.IsEmpty() {
PrintLine("line {} is empty", count);
} else {
PrintLine("line {} is \"{}\", {} bytes", count, line, line.Length());
}
}
PrintLine("end of input after {} line(s)", count);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` and `Text` under `[Dependencies]`.
## Run it
```sh
cd Examples/Text/Input
rux run
```
```text
Type some lines, then end the input.
Ada
line 1 is "Ada", 3 bytes
line 2 is empty
Grace Hopper
line 3 is "Grace Hopper", 12 bytes
^Z
end of input after 3 line(s)
```
Piped input ends by itself, and no input at all ends at once:
```sh
"Ada", "", "Grace Hopper" | rux run
$null | rux run
```
A line is UTF-8 text, and one that is not is reported and skipped. From a POSIX shell:
```sh
printf 'h\xc3\xa9llo\n\xff\nGrace Hopper\n' | rux run
```
```text
Type some lines, then end the input.
line 1 is "héllo", 6 bytes
line 2 is not UTF-8 text
line 3 is "Grace Hopper", 12 bytes
end of input after 3 line(s)
```
## Common mistakes
::warning
**Ignoring the result of `ReadLine`.**:br`ReadLine(builder);` on its own fails with `error: fallible result of type '! IoError' is discarded` — and the end of the input is one of the failures, so a loop that could ignore it would never stop.
::
::warning
**Forgetting to clear the builder.**:br`ReadLine` adds to whatever the builder already holds. Without `builder.Clear()` at the top of the loop, each line is added to the ones before it: type `Ada`, an empty line and `Grace`, and the third line reads `"AdaGrace"`.
::
::warning
**Treating end of input as an error.**:br`EndOfStream` arrives through `.Failure`, but it is how every reading loop is meant to end. Match it first, with its own guard, and `break` — only the failures after it are real problems.
::
## Try it yourself
1. Stop the loop early when the line is `quit`. Make `let quit = StringView::FromValidated("quit");` (import `Text::StringView`) and compare with `line.Equals(quit)`.
2. Add up numbers typed one per line with `ParseInt32(line)`, skipping lines that are not numbers, and print the total at the end. `ParseInt32` comes from the Format package, so add `Format` to `[Dependencies]`.
3. Count the words on each line with `line.Split(…)`.
4. Pipe a file into the program: `Get-Content notes.txt | rux run` in PowerShell, or `rux run < notes.txt` in a POSIX shell.
## Learn more
- [Io](https://rux-lang.dev/docs/api/io) in the API reference
- [String builder](https://rux-lang.dev/docs/learn/string-builder) — what `ReadLine` appends to
- [Guard](https://rux-lang.dev/docs/learn/guard) and [Outcome](https://rux-lang.dev/docs/learn/outcome) — the `match` that tells the failures apart
- [Parse](https://rux-lang.dev/docs/learn/parse) — turning a line into a number
# Part 15: Memory
Until now every value had a size the compiler knew and a home it chose. This part opens the box underneath: pointers that hold addresses, memory you ask for while the program runs, and the layout of the bytes themselves. It starts with the rawest tools, where every rule is yours to keep, and ends with allocators that keep most of those rules for you — so by the end you know both what they do and why they exist.
## What you will learn
- Taking an address with `@`, reaching through it with `*`, and the difference between `*T` and `*var T`.
- Returning extra answers through an out-parameter, and why a fallible is usually better.
- Asking for memory with `Alloc`, clearing it with `Zero` and returning it with `Free`.
- Pointer arithmetic, and turning a pointer and a count into an ordinary slice with `p[..n]`.
- Optional pointers `(*T)?` versus pointers to optionals `*T?`, and why `null` is not `none`.
- How big a type is and how it is aligned, and the padding a struct carries.
- Unions, which lay several members over the same bytes.
- The `Allocator` interface, and four ways to stand behind it: `SystemAllocator`, `Arena`, `FixedBuffer` and `Pool` — plus `Box`, which owns one allocated value.
- Wiping a secret with `Zeroize`, a clear the compiler never removes.
## The allocator family
```mermaid
flowchart LR
code["your code,
written against Allocator"] --> iface{{"Allocator
Allocate · Deallocate · Reallocate"}}
box["Box<T>
owns one value"] --> iface
iface --- sys["SystemAllocator
pages from the system"]
iface --- arena["Arena
a moving marker; Reset frees all"]
iface --- fixed["FixedBuffer
storage you own; a hard limit"]
iface --- pool["Pool
fixed-size blocks, reused"]
sys -. "backs" .-> arena
sys -. "backs" .-> pool
```
Code that takes an `Allocator` works with any of the four. `SystemAllocator` asks the operating system and is usually the one an arena or a pool draws its large blocks from; `FixedBuffer` draws on nothing but the storage you give it.
## Lessons
| | Lesson | What you will learn |
| ----- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
| 15.1 | [Pointer](https://rux-lang.dev/docs/learn/pointer) | the difference between `*T` and `*var T`, and taking an address with `@` |
| 15.2 | [Out parameter](https://rux-lang.dev/docs/learn/out-parameter) | return extra information through a pointer parameter |
| 15.3 | [Raw memory](https://rux-lang.dev/docs/learn/raw-memory) | allocate, use and free memory by hand (`Alloc`, `Zero`, `Free`) |
| 15.4 | [Pointer arithmetic](https://rux-lang.dev/docs/learn/pointer-arithmetic) | step a pointer through memory one element at a time |
| 15.5 | [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice) | turn a pointer and a length into a slice |
| 15.6 | [Optional pointer](https://rux-lang.dev/docs/learn/optional-pointer) | `*T?` and `(*T)?`: a pointer to an optional, or an optional pointer |
| 15.7 | [Layout](https://rux-lang.dev/docs/learn/layout) | how big a type is and how it is aligned: `sizeof` and `alignof` |
| 15.8 | [Union](https://rux-lang.dev/docs/learn/union) | overlay one piece of storage with several types, and why that needs care |
| 15.9 | [Allocator](https://rux-lang.dev/docs/learn/allocator) | allocate through the `Allocator` interface instead of straight from the system |
| 15.10 | [Box](https://rux-lang.dev/docs/learn/box) | own one value allocated on the heap |
| 15.11 | [Arena](https://rux-lang.dev/docs/learn/arena) | allocate many values and free them all at once |
| 15.12 | [Fixed buffer](https://rux-lang.dev/docs/learn/fixed-buffer) | allocate from a buffer you provide |
| 15.13 | [Pool](https://rux-lang.dev/docs/learn/pool) | reuse fixed-size blocks instead of allocating new ones |
| 15.14 | [Zeroize](https://rux-lang.dev/docs/learn/zeroize) | clear sensitive memory explicitly |
## Before you start
This part leans on much of what came before. Pointers build on [Reference](https://rux-lang.dev/docs/learn/reference) from Part 6; allocators report failure with the fallibles of [Part 9: Errors](https://rux-lang.dev/docs/learn/errors); cleaning up relies on [Destructor](https://rux-lang.dev/docs/learn/destructor), [Move](https://rux-lang.dev/docs/learn/move) and [Defer](https://rux-lang.dev/docs/learn/defer) from [Part 11: Ownership](https://rux-lang.dev/docs/learn/ownership); and `Allocator` is used as in [Interface value](https://rux-lang.dev/docs/learn/interface-value) from [Part 12: Interfaces](https://rux-lang.dev/docs/learn/interfaces). Each lesson's package is in the Examples repository's `Memory/` folder:
```sh
cd Examples/Memory/Pointer
rux run
```
## After this part
[Part 16: Numbers](https://rux-lang.dev/docs/learn/numbers) looks at numbers in depth — wide integers, limits, bit operations, checked and wrapping arithmetic. After Part 16 you are ready for the checkpoint projects [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic). Then [Part 17: Collections](https://rux-lang.dev/docs/learn/collections) puts this part to work: its containers take an `Allocator` and grow by asking it for memory.
For the full rules, see [Pointers](https://rux-lang.dev/docs/lang/pointers/overview), [Pointer arithmetic](https://rux-lang.dev/docs/lang/pointers/arithmetic), [Slices and pointers](https://rux-lang.dev/docs/lang/slices/overview#members) and [Unions](https://rux-lang.dev/docs/lang/unions/overview) in the Rux Reference, and the [Memory](https://rux-lang.dev/docs/api/memory) package in the API reference.
# Pointer
::note
**You'll need**: [Mutable](https://rux-lang.dev/docs/learn/mutable), [Reference](https://rux-lang.dev/docs/learn/reference), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference)
::
Every value lives somewhere in memory, and that place has an *address*: a number that says where it is. A **pointer** is a value that holds such an address. With one, a part of the program can reach a value that lives somewhere else, read it and — when it is allowed to — change it.
You have met a close relative already: the reference, `&T` and `&var T`, from [Reference](https://rux-lang.dev/docs/learn/reference). A pointer does the same job with more freedom and fewer checks. The whole of this part is built on that freedom, so this first lesson takes it slowly.
## Taking an address and reaching through it
Two operators do all the work. `@score` takes the address of `score`, and `*pointer` reaches the value at an address:
```rux
var score: int = 10;
// Two pointers to one variable. Neither is a copy of `score`; both lead back to it.
let reader: *int = @score;
let writer: *var int = @score;
*writer = 25;
```
Neither pointer holds a copy of 10. Both hold the address of `score`, so after `*writer = 25` the variable itself says 25, and reading `*reader` says 25 too.
```mermaid
flowchart LR
reader["reader: *int"] -- "reads" --> score[("score: int
25")]
writer["writer: *var int"] -- "reads and writes" --> score
```
| Operator | Read it as | Gives |
| ---------- | ------------------------ | -------------------------------------- |
| `@value` | "the address of `value`" | a pointer |
| `*pointer` | "the value at `pointer`" | the value itself, to read or to assign |
## `*T` reads, `*var T` writes
Pointers split the same way bindings do. The `var` after the `*` describes the value pointed at — the *pointee* — not the pointer:
| Type | Through it you may | Like a binding made with |
| ---------- | ------------------- | ------------------------ |
| `*int` | read `*p` | `let` |
| `*var int` | read and write `*p` | `var` |
The split follows the original binding, too. The address of a `let` binding is always a read-only `*int`, so a pointer can never be used to change something that was declared unchangeable:
```rux
let limit: int = 100;
let limitPointer: *int = @limit;
```
Asking for a `*var int` there is an error; the exact message is under Common mistakes below.
## Fields through a pointer
To reach a field through a pointer, use a plain `.`. There is no need to write `*` first:
```rux
var point = Point { x: 1, y: 2 };
let cursor: *var Point = @point;
cursor.x = 7;
```
The write lands in `point` itself, which then prints as `(7, 2)`.
## `null`, the pointer to nowhere
Unlike a reference, a pointer can be stored, compared and set to `null`, which means "no address at all":
```rux
var target: *int = null;
PrintLine("target is null {}", target == null);
target = @score;
if target != null {
PrintLine("target now reads {}", *target);
}
```
Comparing with `==` and `!=` is always safe. Reaching through `null` with `*` is not, and the compiler will not stop you: the program simply crashes when it gets there. Check before you reach.
Two pointers are equal when they hold the same address. That is why the last line prints `true` — `target` and `reader` both lead to `score`.
| | Reference `&T` | Pointer `*T` |
| ------------------------------------ | ------------------------ | ------------------ |
| Checked by the compiler | yes — it is always valid | no |
| Can be `null` | no | yes |
| Can be stored in a field or returned | no | yes |
| Reached with | the name alone | `*p`, or `p.field` |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Pointer){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A pointer is an address: a plain value that says where another value lives. `@score` takes
// the address of `score`, and `*pointer` reaches the value at that address, to read it or to
// write it.
//
// Pointers split the same way bindings do. A `*int` may only read what it points at; a
// `*var int` may also write. And the address of a `let` binding is always a read-only `*int`,
// so a pointer can never be used to change something declared unchangeable.
//
// A reference (`&T`, from the Types part) also reaches another value, but the compiler checks
// that it stays valid. A pointer is not checked: it can be stored, compared, and set to `null`,
// which means "no address at all". The rest of this part is built on that freedom, and on the
// care it asks for.
import Io::PrintLine;
struct Point {
x: int;
y: int;
}
func Main() -> int {
var score: int = 10;
// Two pointers to one variable. Neither is a copy of `score`; both lead back to it.
let reader: *int = @score;
let writer: *var int = @score;
*writer = 25;
PrintLine("score is now {}", score);
PrintLine("read via pointer {}", *reader);
// The address of a `let` binding is read-only. Asking for `*var int` here is an error:
// "'@limit' yields a read-only '*T'; declare 'limit' with 'var' for a '*var T'".
let limit: int = 100;
let limitPointer: *int = @limit;
PrintLine("limit is {}", *limitPointer);
// A field is reached through a pointer with a plain `.`, without writing `*` first.
var point = Point { x: 1, y: 2 };
let cursor: *var Point = @point;
cursor.x = 7;
PrintLine("point is ({}, {})", point.x, point.y);
// `null` is the pointer that points nowhere. Comparing with `==` is safe; reaching through
// it with `*` is not, and the compiler will not stop you. Check before you reach.
var target: *int = null;
PrintLine("target is null {}", target == null);
target = @score;
if target != null {
PrintLine("target now reads {}", *target);
}
// Two pointers are equal when they hold the same address.
PrintLine("same address {}", target == reader);
return 0;
}
```
## Run it
```sh
cd Examples/Memory/Pointer
rux run
```
```text
score is now 25
read via pointer 25
limit is 100
point is (7, 2)
target is null true
target now reads 25
same address true
```
## Common mistakes
::warning
**A writable pointer to a `let` binding.**:br`let limit: int = 100;` followed by `let w: *var int = @limit;` fails with `error: cannot assign '*int' to '*var int': '@limit' yields a read-only '*T'; declare 'limit' with 'var' for a '*var T'`. Do what the message says, or settle for a `*int` if you only need to read.
::
::warning
**Writing through a read-only pointer.**:br`*reader = 5;` with `reader: *int` fails with `error: cannot modify data through read-only pointer '*int'`. The pointee type decides what you may do, whatever the variable it points at was declared with.
::
::warning
**Forgetting the `@`.**:br`let reader: *int = score;` hands over the value 10, not the place it lives, and fails with `error: cannot assign 'int' to '*int'`. A pointer is made from an address: `@score`.
::
::warning
**Reaching through `null`.**:br`*target` on a `null` pointer compiles without a word and crashes the program when that line runs. Compare with `null` first wherever a pointer might not have been set.
::
## Try it yourself
1. Add a second variable, `var bonus: int = 5;`, point `target` at it, and check that `target == reader` now prints `false`.
2. Write `func Swap(a: *var int, b: *var int)` that exchanges two values, and call it as `Swap(@first, @second)`.
3. A pointer is a value, so it has an address too. Make `var p: *var int = @score;` and `let pp: **var int = @p;`, then write `**pp = 42` and print `score`.
## Learn more
- [Pointers](https://rux-lang.dev/docs/lang/pointers/overview), [Pointer types](https://rux-lang.dev/docs/lang/pointers/overview#conversions) and [The `null` pointer](https://rux-lang.dev/docs/lang/pointers/overview#null) in the Rux Reference
- [Reference](https://rux-lang.dev/docs/learn/reference) and [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) — the checked relatives of `*T` and `*var T`
- [Out parameter](https://rux-lang.dev/docs/learn/out-parameter) — the next lesson, where a function writes through a pointer it was given
# Out parameter
::note
**You'll need**: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Fallible](https://rux-lang.dev/docs/learn/fallible), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback)
::
A function returns one value. Sometimes you want two things back: the answer, and whether the answer is any good. One long-standing way to get both is the **out-parameter** — the caller hands over a pointer to its own variable, the function writes the answer through it, and the return value is left free to say something else.
You will meet out-parameters in older code, in the standard library and in C libraries, so it pays to read them fluently. This lesson also shows their weak spots, and why a fallible `T ! E` is usually the better choice for code you write yourself.
## The shape of an out-parameter
The answer travels through `result`, a `*var int`. The `bool` that comes back says whether to trust it:
```rux
func PercentOf(part: int, whole: int, result: *var int) -> bool {
if whole == 0 {
return false;
}
*result = part * 100 / whole;
return true;
}
```
The caller prepares a variable first and passes its address with `@`:
```rux
var share: int = 0;
if PercentOf(3, 4, @share) {
PrintLine("3 of 4 is {}%", share);
}
```
```mermaid
flowchart LR
main["Main
var share = 0"] -- "@share" --> f["PercentOf(3, 4, result)"]
f -- "*result = 75" --> share[("share")]
f -- "returns true" --> check{"if"}
check --> print["print share"]
```
The variable must be a `var`, because the function writes into it through a `*var int`.
## The flag nobody has to read
Here is the weak spot. Nothing forces the caller to look at the `bool`:
```rux
PercentOf(1, 0, @share);
PrintLine("1 of 0, flag ignored: {}%", share);
```
The whole is zero, so `PercentOf` returns `false` and writes nothing. The flag is dropped, and `share` still holds 75 from the call before. The program prints a confident `75%` for a question that has no answer, and the compiler has no reason to object.
## Reading someone else's flag
The standard library uses out-parameters too. `ConvertChecked` writes the converted value through its second argument and returns a `bool`:
```rux
var narrowed: int8 = 0;
let lost = ConvertChecked(300, @narrowed);
```
| Function | `true` means |
| ---------------- | -------------------------------------------- |
| `PercentOf` | the answer is good |
| `ConvertChecked` | something was lost — the answer is not exact |
The two read in opposite directions. With a bare `bool`, only the documentation tells you which way round it is. Here 300 does not fit in an `int8`, so `lost` is `true` and `narrowed` holds the wrapped value 44.
## The fallible alternative
The same question as a fallible puts the answer and the failure on separate channels:
```rux
func Percent(part: int, whole: int) -> int ! ZeroWhole {
if whole == 0 {
fail ZeroWhole {};
}
return part * 100 / whole;
}
```
```rux
let good = Percent(3, 4) catch { else => -1 };
let bad = Percent(1, 0) catch { else => -1 };
```
| | Out-parameter | Fallible `T ! E` |
| --------------------------------- | ------------------------------------- | ------------------------------------ |
| Where the answer goes | a variable the caller prepared first | the return value |
| How a failure is reported | a `bool`, read whichever way it reads | a typed error on the failure channel |
| A caller that ignores the failure | compiles, and keeps a stale value | is rejected by the compiler |
| An answer on failure | whatever the variable held before | none — there is nothing to misuse |
For new code, prefer the fallible. Reach for an out-parameter when an existing API asks for one.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/OutParameter){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An out-parameter is a pointer the caller hands over so a function can write its answer into
// the caller's own variable. The caller passes `@share`; the function writes `*result = ...`.
// The return value is then free to say something else, usually whether the answer is good.
//
// Older code and some of the standard library work this way, so it is worth reading fluently.
// But it has two weak spots. The caller must have a variable ready before the call, and nothing
// forces anyone to look at the flag: ignore it, and the variable quietly keeps a stale value.
//
// A fallible `T ! E` closes both gaps. The answer only exists on success, and a result that is
// left unhandled is a compile error. For new code, prefer it; reach for an out-parameter when
// an existing API asks for one.
import Core::ConvertChecked;
import Io::PrintLine;
// Out-parameter style: the answer goes through `result`, and the `bool` says whether to trust it.
func PercentOf(part: int, whole: int, result: *var int) -> bool {
if whole == 0 {
return false;
}
*result = part * 100 / whole;
return true;
}
struct ZeroWhole {}
// The same question as a fallible. The answer and the failure travel on separate channels.
func Percent(part: int, whole: int) -> int ! ZeroWhole {
if whole == 0 {
fail ZeroWhole {};
}
return part * 100 / whole;
}
func Main() -> int {
var share: int = 0;
if PercentOf(3, 4, @share) {
PrintLine("3 of 4 is {}%", share);
}
// The mistake the compiler cannot see: the flag is dropped, nothing was written, and `share`
// still says 75 from the call before.
PercentOf(1, 0, @share);
PrintLine("1 of 0, flag ignored: {}%", share);
// The standard library uses out-parameters too. `ConvertChecked` writes the converted value
// and returns `true` when something was lost, which is the opposite of `PercentOf` above.
// With a bare `bool`, only the documentation tells you which way round it reads.
var narrowed: int8 = 0;
let lost = ConvertChecked(300, @narrowed);
PrintLine("300 into int8: lost {}, wrote {}", lost, narrowed);
// The fallible needs no variable prepared in advance, and it cannot be ignored: a bare
// `Percent(1, 0);` is rejected. The failure has to be dealt with, here by `catch`.
let good = Percent(3, 4) catch { else => -1 };
let bad = Percent(1, 0) catch { else => -1 };
PrintLine("fallible: {}% and {}%", good, bad);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/OutParameter
rux run
```
```text
3 of 4 is 75%
1 of 0, flag ignored: 75%
300 into int8: lost true, wrote 44
fallible: 75% and -1%
```
## Common mistakes
::warning
**Passing the value instead of its address.**:br`PercentOf(3, 4, share)` hands over 0, not the variable, and fails with `error: argument 3 to 'PercentOf' has type 'int', but parameter 'result' requires '*var int'`. Write `@share`.
::
::warning
**An out-parameter declared with `let`.**:br
With `let share: int = 0;`, the address `@share` is a read-only `*int`, so the call fails with `error: argument 3 to 'PercentOf' has type '*int', but parameter 'result' requires '*var int'`. The function has to write into it: declare it with `var`.
::
::warning
**Ignoring the flag.**:br`PercentOf(1, 0, @share);` compiles, and `share` silently keeps the 75 from before. Always test the `bool` — or use a function that reports failure as a fallible.
::
::warning
**Ignoring a fallible instead.**:br
The fallible version refuses to be ignored: a bare `Percent(1, 0);` fails with `error: fallible result of type 'int ! ZeroWhole' is discarded`, and the help line suggests `?`, `catch` or a `match`.
::
## Try it yourself
1. Write `func Divide(a: int, b: int, quotient: *var int, remainder: *var int) -> bool` that hands back two answers at once, and call it with `@q` and `@r`.
2. Change `PercentOf` to write `0` into `*result` before it returns `false`. Does that cure the stale `75%`? What does the caller still not know?
3. Convert 100 into an `int8` with `ConvertChecked` and check that `lost` is `false`.
## Learn more
- [Fallible](https://rux-lang.dev/docs/learn/fallible) and [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) — the alternative this lesson recommends
- [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) — `ConvertChecked` and its relatives in full
- [Pointer types](https://rux-lang.dev/docs/lang/pointers/overview#conversions) in the Rux Reference
# Raw memory
::note
**You'll need**: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Defer](https://rux-lang.dev/docs/learn/defer), [For](https://rux-lang.dev/docs/learn/for)
::
Every value so far had its size fixed when the program was compiled: a `uint`, an array of four `int64`s, a struct. But often you only learn how much memory you need while the program runs — how many lines are in a file, how many players joined. That memory has to be **asked for**, and this lesson shows the rawest way to do it.
Nothing here is checked for you. That is the point: once you have seen every rule you must keep by hand, the tools in the rest of this part will make sense.
## Three functions
The `Memory` package gives you three functions, and between them they cover a block's whole life:
| Function | Does |
| -------------- | ----------------------------------------------------------------------- |
| `Alloc(bytes)` | finds a fresh block and returns its address, or `null` if there is none |
| `Zero(p, n)` | fills `n` bytes at `p` with zeros |
| `Free(p)` | gives the block back |
## Ask in bytes
`Alloc` counts in bytes, not in elements, so the request is the element count times the size of one element:
```rux
let count: uint = 8;
let size = count * sizeof(uint);
```
`sizeof(uint)` is 8 on a 64-bit machine, so eight elements need 64 bytes. [Layout](https://rux-lang.dev/docs/learn/layout) later in this part looks at sizes properly.
## Say what lives there
The block arrives as `*var opaque`: "writable memory, contents unknown". An `opaque` pointee has no type, so you cannot read or write through it yet. A cast says what will live there:
```rux
var values = Alloc(size) as *var uint;
```
From then on the pointer indexes like an array: `values[i]` is the `i`-th `uint` after the address.
## Check, then register the release
Allocation can fail, and the only sign is a `null`. Check before the first use, and register the `Free` straight after the check:
```rux
if values == null {
PrintLine("out of memory");
return 1;
}
defer Free(values);
```
The `defer` makes sure that every later path out of `Main` — the normal `return 0`, or any early `return` you add later — frees the block exactly once.
```mermaid
flowchart LR
alloc["Alloc(size)"] --> q{"null?"}
q -- "yes" --> oom["report it,
return 1"]
q -- "no" --> d["defer Free(values)"]
d --> z["Zero(values, size)"]
z --> use["values[i] = i * i"]
use --> free["Free runs as
Main returns"]
```
## Fresh memory is not empty
A new block holds whatever bytes were last left there. `Zero` makes its contents definite before anything reads them:
```rux
Zero(values, size);
```
After that, the program fills the block with squares and adds them up — ordinary indexing, on memory that did not exist when the program was compiled.
## After `Free`
When the deferred `Free` has run, `values` still holds the old address, but the memory is no longer yours. Reading it, writing it or freeing it a second time are all bugs, and nothing reports them: the program may seem to work, crash later somewhere unrelated, or quietly corrupt other data.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/RawMemory){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Every value so far had its size fixed when the program was compiled. When the amount of memory
// is only known while the program runs, it has to be asked for, and this is the rawest way:
//
// Alloc(bytes) hands back the address of a fresh block, or `null` if there is none to give
// Zero(p, n) fills the block with zero bytes
// Free(p) gives the block back
//
// The block arrives as `*var opaque`: "writable memory, contents unknown". A cast says what will
// live there, and from then on the pointer indexes like an array: `values[i]` is the i-th element
// after the address.
//
// Nothing here is checked for you. A `null` must be caught before use, the block must be freed
// exactly once, and it must not be touched afterwards. The rest of this part is about tools that
// make those rules harder to break.
import Io::{ Print, PrintLine };
import Memory::{ Alloc, Free, Zero };
func Main() -> int {
// A count decided at run time. `sizeof(uint)` is the size of one element in bytes.
let count: uint = 8;
let size = count * sizeof(uint);
PrintLine("asking for {} elements, {} bytes", count, size);
var values = Alloc(size) as *var uint;
// Allocation can fail, and the only sign is `null`. Check before the first use.
if values == null {
PrintLine("out of memory");
return 1;
}
// Registered right after the check, so every later path out of `Main` frees the block once.
defer Free(values);
// Fresh memory holds whatever was there before. `Zero` makes its contents definite.
Zero(values, size);
Print("after Zero:");
for i in 0..count {
Print(" {}", values[i]);
}
PrintLine();
for i in 0..count {
values[i] = i * i;
}
Print("squares: ");
var total: uint = 0;
for i in 0..count {
Print(" {}", values[i]);
total += values[i];
}
PrintLine();
PrintLine("total: {}", total);
// After the deferred `Free` runs, `values` still holds the old address, but the memory is no
// longer yours. Reading it, or freeing it again, is a bug that nothing reports.
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Memory` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/RawMemory
rux run
```
```text
asking for 8 elements, 64 bytes
after Zero: 0 0 0 0 0 0 0 0
squares: 0 1 4 9 16 25 36 49
total: 140
```
## Common mistakes
::warning
**Using the block before the cast.**:br
Without the cast, `values` is a `*var opaque`, and `values[0] = 1` fails with `error: cannot assign 'int' to 'opaque'`. Cast once, straight after `Alloc`, to the type the block will hold.
::
::warning
**Casting to a read-only pointer.**:br`Alloc(size) as *uint` gives a pointer you cannot write through: `values[0] = 1` then fails with `error: cannot modify data through read-only pointer '*uint'`. A block you mean to fill needs `*var`.
::
::warning
**Asking for elements instead of bytes.**:br`Alloc(count)` asks for 8 bytes, not for 8 `uint`s. It compiles and may even appear to work, while every write past the first element lands in memory that belongs to something else. Always multiply by `sizeof`.
::
::warning
**Skipping the `null` check, or freeing twice.**:br
None of these are compile errors: forgetting to check for `null`, forgetting to `Free`, freeing the same block twice, or using it after `Free`. Keep the pattern from this lesson — check, then `defer Free` — and the next lessons give you tools that make these mistakes harder to write.
::
## Try it yourself
1. Change `count` to 20 and predict the byte count on the first line before you run.
2. Store `int32` values instead of `uint`s. What must change besides the cast?
3. Add an early `return 2;` inside the loop that adds up `total`, taken once `total` passes 100. The `defer` still frees the block — convince yourself why.
## Learn more
- [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc), [`Zero`](https://rux-lang.dev/docs/api/memory/zero) and [`Free`](https://rux-lang.dev/docs/api/memory/free) in the API reference
- [Defer](https://rux-lang.dev/docs/learn/defer) — how deferred statements run on every path out
- [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice) — turning this block into an ordinary slice
# Pointer arithmetic
::note
**You'll need**: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Raw memory](https://rux-lang.dev/docs/learn/raw-memory), [Array](https://rux-lang.dev/docs/learn/array), [While](https://rux-lang.dev/docs/learn/while)
::
In [Raw memory](https://rux-lang.dev/docs/learn/raw-memory), a pointer from `Alloc` was indexed like an array: `values[i]`. That works because a pointer can do arithmetic. Adding a number to it gives a new pointer further along in memory — and the step is measured in whole **elements**, not in bytes.
## A step is one element
The program takes the address of the first element of three arrays and measures how far `+ 1` moves each pointer. Converting an address to `uint` shows it as a plain number of bytes, so two addresses can be subtracted:
```rux
let b = @bytes[0];
let c = @counts[0];
let x = @pixels[0];
PrintLine("+1 on *uint8 moves {} byte", ((b + 1) as uint) - (b as uint));
PrintLine("+1 on *int64 moves {} bytes", ((c + 1) as uint) - (c as uint));
PrintLine("+1 on *Pixel moves {} bytes", ((x + 1) as uint) - (x as uint));
```
| Pointer | One element is | `+ 1` moves |
| -------- | ----------------------- | ----------- |
| `*uint8` | one byte | 1 byte |
| `*int64` | eight bytes | 8 bytes |
| `*Pixel` | three `int32`s together | 12 bytes |
The compiler multiplies by the element size for you. You always count in elements, whatever their size.
## Offset and index are one thing
Indexing a pointer is just arithmetic followed by a dereference: `p[i]` means exactly `*(p + i)`.
```rux
PrintLine("*(c + 2) is {}, c[2] is {}", *(c + 2), c[2]);
PrintLine("(x + 1).blue is {}", (x + 1).blue);
```
A field is reached through the moved pointer with a plain `.`, as in [Pointer](https://rux-lang.dev/docs/learn/pointer).
## Walking to an end pointer
A pointer can also walk. The loop below keeps a second pointer, `end`, one past the last element:
```rux
var cursor = c;
let end = c + 4;
Print("doubled:");
while cursor < end {
*cursor = *cursor * 2;
Print(" {}", *cursor);
cursor += 1;
}
```
```mermaid
flowchart LR
c0["c
10"] --- c1["c + 1
20"] --- c2["c + 2
30"] --- c3["c + 3
40"] --- e["end = c + 4
never read"]
```
Pointers compare by address, so `cursor < end` means "not there yet". `end` itself is never read through: it marks the place just past the array. Because `cursor` points into `counts`, the doubled values are written into the array itself, which the last line confirms.
## Nothing is checked
The arithmetic is never checked. A pointer moved past the end of its storage is still a pointer, and reaching through it reads or writes memory that belongs to something else — another variable, or nothing at all. Keep an end in view, as the loop does, and never step past it.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/PointerArithmetic){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Adding a number to a pointer moves it by whole elements, not by bytes. If `p` points at an
// `int64`, then `p + 1` points at the next `int64`, eight bytes further on. The compiler
// multiplies by the element size for you, so the same `+ 1` steps one byte through `uint8`s
// and twelve bytes through a twelve-byte struct.
//
// That is also what indexing a pointer means: `p[i]` is exactly `*(p + i)`.
//
// The arithmetic itself is never checked. A pointer moved past the end of its storage is still
// a pointer, and reaching through it reads or writes memory that belongs to something else.
// Keep an end in view: here, a second pointer one past the last element.
import Io::{ Print, PrintLine };
struct Pixel {
red: int32;
green: int32;
blue: int32;
}
func Main() -> int {
var bytes: uint8[4] = [1, 2, 3, 4];
var counts: int64[4] = [10, 20, 30, 40];
var pixels: Pixel[2] = [Pixel { red: 255, green: 0, blue: 0 },
Pixel { red: 0, green: 0, blue: 255 }];
// Converting an address to `uint` shows it as a number of bytes, so the step `+ 1` takes
// can be measured. It is the element size each time.
let b = @bytes[0];
let c = @counts[0];
let x = @pixels[0];
PrintLine("+1 on *uint8 moves {} byte", ((b + 1) as uint) - (b as uint));
PrintLine("+1 on *int64 moves {} bytes", ((c + 1) as uint) - (c as uint));
PrintLine("+1 on *Pixel moves {} bytes", ((x + 1) as uint) - (x as uint));
// Offset and index are two spellings of one thing.
PrintLine("*(c + 2) is {}, c[2] is {}", *(c + 2), c[2]);
PrintLine("(x + 1).blue is {}", (x + 1).blue);
// Walking with a pointer: `end` is one past the last element and is never read through.
// Pointers compare by address, so `cursor < end` means "not there yet".
var cursor = c;
let end = c + 4;
Print("doubled:");
while cursor < end {
*cursor = *cursor * 2;
Print(" {}", *cursor);
cursor += 1;
}
PrintLine();
// The writes went through to the array itself.
PrintLine("counts[3] is now {}", counts[3]);
return 0;
}
```
## Run it
```sh
cd Examples/Memory/PointerArithmetic
rux run
```
```text
+1 on *uint8 moves 1 byte
+1 on *int64 moves 8 bytes
+1 on *Pixel moves 12 bytes
*(c + 2) is 30, c[2] is 30
(x + 1).blue is 255
doubled: 20 40 60 80
counts[3] is now 80
```
## Common mistakes
::warning
**Adding bytes instead of elements.**:br
To skip one `int64` you add 1, not 8. `c + 8` compiles and moves 64 bytes — far past the end of a four-element array.
::
::warning
**Forgetting the parentheses.**:br`*c + 2` reads the first element and adds 2 to it, giving 12. To reach two elements on, write `*(c + 2)`, or simply `c[2]`.
::
::warning
**Reading the end.**:br`while cursor <= end` runs once too often and reads one element past the array. Nothing reports it. `end` marks the stopping place; compare with `<`.
::
::warning
**Subtracting two pointers.**:br`end - c` is not allowed: it fails with `error: operator '-' cannot combine left operand '*var int64' with right operand '*var int64'`. To count the elements between them, convert both to `uint`, subtract, and divide by `sizeof(int64)`.
::
## Try it yourself
1. Print the counts in reverse: start a cursor at `end`, and in a `while cursor > c` loop step it back with `cursor -= 1` before each read.
2. Count the elements between `c` and `end` with `((end as uint) - (c as uint)) / sizeof(int64)`.
3. Walk `pixels` with a cursor and print each pixel's `red` and `blue`.
## Learn more
- [Pointer arithmetic](https://rux-lang.dev/docs/lang/pointers/arithmetic) in the Rux Reference
- [Raw memory](https://rux-lang.dev/docs/learn/raw-memory) — where indexing a pointer first appeared
- [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice) — keeping the length beside the pointer, so the end is checked for you
# Pointer slice
::note
**You'll need**: [Raw memory](https://rux-lang.dev/docs/learn/raw-memory), [Pointer arithmetic](https://rux-lang.dev/docs/learn/pointer-arithmetic), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice)
::
A pointer knows where its storage starts, but not how long it is. A [slice](https://rux-lang.dev/docs/learn/slice) knows both. Indexing a pointer with a **range** joins the two, and that is how raw storage meets the rest of the language: a block from `Alloc` becomes an ordinary `int[..]`, so `for`, `.length` and every function that takes a slice work on it.
## `p[..n]` and `p[a..b]`
After the usual allocation, the program builds a view of the whole block:
```rux
let all = block[..count];
```
`block[..count]` is a slice of the `count` elements starting at `block`. With two bounds, `block[a..b]` is the elements from `a` up to, but not including, `b`:
| Expression | Elements | Length |
| ---------------- | ------------------------------- | ------ |
| `block[..count]` | 0 to 5 | 6 |
| `block[2..4]` | 2 and 3 | 2 |
| `block[4..=5]` | 4 and 5 | 2 |
| `block[..]` | rejected — a pointer has no end | — |
The last row is the important one. An array or a slice can be sliced with `[..]` because it knows its own length; a pointer does not, so the range must always say where to stop.
## The view goes wherever a slice goes
From here on the program works with the view, which carries the count with it. These two functions know nothing about pointers or `Alloc`:
```rux
func Fill(values: var int[..]) {
for i in 0..values.length {
values[i] = (i as int + 1) * 10;
}
}
```
```rux
Fill(all);
Show("all: ", all);
PrintLine("length: {}", all.length);
```
A view of a `*var int` is writable, just as a view of a `var` array is, so `all` can be passed where a `var int[..]` is wanted. A view of a read-only `*int` would be a read-only `int[..]`.
## Views share the storage
A narrower view of the same block copies nothing. Both views look at the same memory, so a write through one shows up in the other:
```rux
let middle = block[2..4];
Show("middle:", middle);
middle[0] = 0;
Show("after: ", all);
```
```mermaid
flowchart LR
ptr["block: *var int"] --> s0
subgraph mem["the allocated block"]
direction LR
s0["10"] --- s1["20"] --- s2["30"] --- s3["40"] --- s4["50"] --- s5["60"]
end
all["all = block[..count]"] -.-> s0
middle["middle = block[2..4]"] -.-> s2
```
`middle[0]` is the third element of the block, so the `30` in `all` becomes `0`.
## Checked index, trusted length
Indexing a view is checked against its length, as for any slice. `all[6]` would stop the program with `Panic: index out of range`.
But the length itself is yours to get right. A pointer cannot say how big its block is, so `n` in `block[..n]` is taken on trust: a view longer than the block is accepted, and its extra elements are someone else's memory. The rule that follows is simple — build the view once, from the count you allocated, and pass the view around instead of the pointer.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/PointerSlice){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A pointer knows where its storage starts but not how long it is. A slice knows both. Indexing
// a pointer with a range joins the two: `p[..n]` is a slice of the `n` elements starting at `p`,
// and `p[a..b]` the elements from `a` up to, not including, `b`.
//
// That is how raw storage meets the rest of the language. A block from `Alloc` becomes an
// ordinary `int[..]`, so `for`, `.length` and every function that takes a slice work on it.
// A view of a `*var int` is writable, just as a view of a `var` array is.
//
// Indexing a view is checked against its length, as for any slice: `all[6]` below would stop the
// program with `Panic: index out of range`. But the length itself is yours to get right. A pointer
// cannot say how big its block is, so `n` is taken on trust: a view longer than the block is
// accepted, and its extra elements are someone else's memory. Build the view once, from the count
// you allocated, and pass the view around instead of the pointer.
import Io::{ Print, PrintLine };
import Memory::{ Alloc, Free };
func Fill(values: var int[..]) {
for i in 0..values.length {
values[i] = (i as int + 1) * 10;
}
}
func Show(label: char8[..], values: int[..]) {
Print("{}", label);
for value in values {
Print(" {}", value);
}
PrintLine();
}
func Main() -> int {
let count: uint = 6;
let block = Alloc(count * sizeof(int)) as *var int;
if block == null {
PrintLine("out of memory");
return 1;
}
defer Free(block);
// From here on the program works with the view, which carries the count with it.
let all = block[..count];
Fill(all);
Show("all: ", all);
PrintLine("length: {}", all.length);
// A narrower view of the same block. Nothing is copied: both views share the storage.
let middle = block[2..4];
Show("middle:", middle);
middle[0] = 0;
Show("after: ", all);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Memory` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/PointerSlice
rux run
```
```text
all: 10 20 30 40 50 60
length: 6
middle: 30 40
after: 10 20 0 40 50 60
```
## Common mistakes
::warning
**Slicing a pointer with no end.**:br`block[..]` and `block[2..]` fail with `error: cannot slice pointer '*var int' without an end bound`, and the help line says what to write instead: `p[..n]` or `p[a..b]`.
::
::warning
**Asking a pointer for its length.**:br`block.length` fails with `error: type '*var int' has no field 'length'`. Only the view knows its length: `all.length`.
::
::warning
**A read-only view for a writing function.**:br
Cast the block to `*int` instead of `*var int`, and `Fill(block[..count])` fails with `error: argument 1 to 'Fill' has type 'int[..]', but parameter 'values' requires 'var int[..]'`. The view inherits the pointer's writability.
::
::warning
**A view longer than the block.**:br`block[..100]` on a six-element block compiles, and its indexes are checked against 100, not 6. Build every view from the count you passed to `Alloc`.
::
## Try it yourself
1. Add `all[6]` to a `PrintLine` and read the panic, including the line it points to.
2. Write `func Sum(values: int[..]) -> int` and call it with `all` and with `middle`.
3. Change `middle[0] = 0` to `middle[1] = 0`. Predict the `after:` line before you run.
4. Print the last two elements with an inclusive range, `block[4..=5]`.
## Learn more
- [Slices and pointers](https://rux-lang.dev/docs/lang/slices/overview#members) in the Rux Reference
- [Slice](https://rux-lang.dev/docs/learn/slice) and [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) — the views this lesson produces
- [Allocator](https://rux-lang.dev/docs/learn/allocator) — where this pattern is used with a better way of asking for memory
# Optional pointer
::note
**You'll need**: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [The is operator](https://rux-lang.dev/docs/learn/is)
::
Pointers and [optionals](https://rux-lang.dev/docs/learn/optional) combine in two ways that look almost the same and mean different things. This lesson puts them side by side, gives each its own job, and clears up the difference between `null` and `none`.
## Where the `?` belongs
A `?` belongs to the type just before it, as every type suffix does. So in `*var int?` it attaches to `int`, and parentheses are needed to put it on the pointer instead:
| Type | Reads as | A value of it is |
| ------------- | --------------------------------------- | -------------------------------------------------- |
| `(*var int)?` | an optional pointer | either an address, or `none` |
| `*var int?` | `*var (int?)`, a pointer to an optional | always an address — of a slot that holds an `int?` |
## An optional pointer: maybe an address
`Find` answers "where is `wanted`?" with an address the caller can write through — or with `none`:
```rux
func Find(values: var int[..], wanted: int) -> (*var int)? {
for i in 0..values.length {
if values[i] == wanted {
return @values[i];
}
}
return none;
}
```
A function that returned a bare pointer would use `null` for "not found", and leave remembering the check to you. One that returns `(*var int)?` cannot be used until a `match` or `??` has dealt with `none`:
```rux
match Find(scores[..], 3) {
found? => {
*found = 30;
PrintLine("found 3, changed it to {}", scores[1]);
},
none => {
PrintLine("3 is not there");
}
}
```
```mermaid
flowchart LR
f["Find(scores, 3)"] --> q{"present?"}
q -- "found? — an address" --> w["*found = 30
writes into scores"]
q -- "none" --> n["3 is not there"]
```
Inside the `found?` arm, `found` is a plain `*var int`, and the write lands in the array: `scores[1]` becomes 30. The second search, for 5, takes the `none` arm.
## A pointer to an optional
The other type is just a pointer — one that reaches an optional living somewhere else, the same way a `*var int` reaches an `int`:
```rux
func KeepLargest(best: *var int?, value: int) {
let current = *best ?? value;
*best = value > current ? value : current;
}
```
`*best` is the caller's `int?`. While it is still `none`, `?? value` makes the first value the largest so far. The caller passes the address of its own optional, which starts out empty:
```rux
var best: int? = none;
for score in scores {
KeepLargest(@best, score);
}
```
## `null` is not `none`
`null` is a pointer value — the address of nowhere. So an optional pointer given `null` is *present*: it holds a pointer, which happens to point nowhere.
```rux
let wrapped: (*var int)? = null;
PrintLine("null wrapped is present: {}", wrapped is *var int);
```
| You write | The optional is | Safe to reach through? |
| ------------------------------ | --------------- | ------------------------- |
| `let p: (*var int)? = none;` | absent | there is nothing to reach |
| `let p: (*var int)? = null;` | present | no — it points nowhere |
| `let p: (*var int)? = @score;` | present | yes |
Use `none` to mean "no pointer". Mixing the two brings back exactly the unchecked `null` the optional was meant to remove.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/OptionalPointer){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Two types that look almost the same, and mean different things:
//
// (*var int)? an optional pointer: either an address, or `none`
// *var int? a pointer to an optional: always an address, of a slot that holds an `int?`
//
// A `?` belongs to the type just before it, as every suffix does, so `*var int?` reads as
// `*var (int?)`. Parentheses put the `?` on the pointer instead.
//
// An optional pointer is the honest way to say "maybe an address". A function that returns a
// bare pointer and uses `null` for "not found" leaves the check to memory; one that returns
// `(*var int)?` cannot be used until a `match` or `??` has dealt with `none`.
//
// A pointer to an optional is just a pointer, used to read or replace an optional that lives
// somewhere else, the same way a `*var int` reaches an `int`.
import Io::PrintLine;
// Where is `wanted`? The answer is an address the caller can write through, or `none`.
func Find(values: var int[..], wanted: int) -> (*var int)? {
for i in 0..values.length {
if values[i] == wanted {
return @values[i];
}
}
return none;
}
// Keeps the largest value seen so far in the caller's `int?`, which starts out empty.
func KeepLargest(best: *var int?, value: int) {
let current = *best ?? value;
*best = value > current ? value : current;
}
func Main() -> int {
var scores: int[4] = [7, 3, 9, 3];
// Absence has to be handled before the address can be used.
match Find(scores[..], 3) {
found? => {
*found = 30;
PrintLine("found 3, changed it to {}", scores[1]);
},
none => {
PrintLine("3 is not there");
}
}
match Find(scores[..], 5) {
found? => {
*found = 50;
},
none => {
PrintLine("5 is not there");
}
}
// Writing through a pointer to an optional.
var best: int? = none;
PrintLine("best before: {}", best ?? -1);
for score in scores {
KeepLargest(@best, score);
}
PrintLine("best after: {}", best ?? -1);
// `null` and `none` are different. `null` is a pointer value, so an optional pointer given
// `null` is present: it holds a pointer, which happens to point nowhere. Use `none` to mean
// "no pointer".
let wrapped: (*var int)? = null;
PrintLine("null wrapped is present: {}", wrapped is *var int);
return 0;
}
```
## Run it
```sh
cd Examples/Memory/OptionalPointer
rux run
```
```text
found 3, changed it to 30
5 is not there
best before: -1
best after: 30
null wrapped is present: true
```
## Common mistakes
::warning
**Reaching through an optional pointer without unwrapping it.**:br`*Find(scores[..], 3) = 30;` fails with `error: operator '*' requires a pointer operand, but found '(*var int)?'`. Handle `none` first, with a `match` or with `??`.
::
::warning
**Leaving out the parentheses.**:br
Declare `Find` as returning `*var int?` and it promises a pointer to an optional instead. `return none;` then fails with `error: 'none' needs an expected optional type, but found '*var (int?)'` — the message spells out how the compiler read the type.
::
::warning
**Using `null` to mean "no pointer".**:br`(*var int)? = null` is present, so a `match` takes the `found?` arm and the code reaches through `null`. Write `none`.
::
## Try it yourself
1. Give the missing 5 a place to go: `let slot = Find(scores[..], 5) ?? @spare;` with `var spare: int = 0;`, then write `*slot = 50` and print `spare`.
2. Write `KeepSmallest` and check that it finds 3.
3. Change `Find` so that it returns the *last* match rather than the first, and see which element becomes 30.
4. Set `wrapped` to `none` instead of `null`. What does the last line print now?
## Learn more
- [Presence](https://rux-lang.dev/docs/learn/presence) and [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — the two ways to deal with `none`
- [Is](https://rux-lang.dev/docs/learn/is) — the type test used on the last line
- [The `null` pointer](https://rux-lang.dev/docs/lang/pointers/overview#null) in the Rux Reference
# Layout
::note
**You'll need**: [Struct](https://rux-lang.dev/docs/learn/struct), [Pointer](https://rux-lang.dev/docs/learn/pointer), [Pointer arithmetic](https://rux-lang.dev/docs/learn/pointer-arithmetic)
::
You have been multiplying by `sizeof` since [Raw memory](https://rux-lang.dev/docs/learn/raw-memory). This lesson looks at what that number really is, at its partner `alignof`, and at the hidden bytes that make a struct bigger than the sum of its fields.
## Size and alignment
Every type has two numbers:
- its **size** — how many bytes one value occupies, from `sizeof(T)`;
- its **alignment** — every value of the type must sit at an address that is a multiple of this number, from `alignof(T)`.
```rux
PrintLine("int64 {} {}", sizeof(int64), alignof(int64));
```
For the plain number types the two are the same: a `uint8` is one byte and may sit anywhere, an `int64` is eight bytes and must sit at a multiple of eight. The processor reads aligned values fastest, and on some machines can only read them aligned at all.
## Padding inside a struct
A struct keeps its fields in the order they are written, and each field must sit at an address that suits its own alignment. Look at `Loose`:
```rux
struct Loose {
flag: bool;
total: int64;
mark: uint8;
}
```
`flag` takes byte 0. `total` needs a multiple of eight, so it cannot start at byte 1: seven unused bytes of **padding** go in between, and `total` starts at 8. `mark` lands at 16. Then the whole struct is rounded up to a multiple of its largest alignment, 8, so that in an array the next element lines up too. That makes 24 bytes for 10 bytes of data.
`Tight` has the same three fields, widest first:
```rux
struct Tight {
total: int64;
flag: bool;
mark: uint8;
}
```
```mermaid
flowchart TB
subgraph loose["Loose — 24 bytes"]
direction LR
l1["flag
0"] --- l2["padding
1–7"] --- l3["total
8–15"] --- l4["mark
16"] --- l5["padding
17–23"]
end
subgraph tight["Tight — 16 bytes"]
direction LR
t1["total
0–7"] --- t2["flag
8"] --- t3["mark
9"] --- t4["padding
10–15"]
end
```
Putting the widest fields first usually leaves the least padding.
## Measuring where a field landed
The offsets in the output are not guessed — the program measures them. Converting an address to `uint` turns it into a number, so the distance from the start of the value to a field is a subtraction:
```rux
var loose = Loose { flag: true, total: 1, mark: 2 };
let start = @loose as uint;
PrintLine("Loose: flag at {}, total at {}, mark at {}", (@loose.flag as uint) - start,
(@loose.total as uint) - start, (@loose.mark as uint) - start);
```
## Arrays have no gaps between elements
An array is its elements side by side, each one `sizeof` apart, with nothing in between. The padding already inside each element is what keeps the next one aligned:
```rux
PrintLine("Loose[4] takes {} bytes, Tight[4] takes {}", sizeof(Loose[4]), sizeof(Tight[4]));
```
Four `Loose` values cost 96 bytes; four `Tight` ones 64. In a large array, field order is real memory.
## Facts about this build
The numbers in the output are for a 64-bit target. Sizes and alignments are decided by the target the program is built for, so treat them as facts about this build, not as constants of the language — and let `sizeof` and `alignof` tell you rather than writing the numbers down.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Layout){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Every type has a size, the bytes one value occupies, and an alignment: its address must be a
// multiple of that number. `sizeof(T)` and `alignof(T)` ask the compiler for both.
//
// A struct keeps its fields in the order they are written, and each field must sit at an
// address that suits its own alignment. So when a one-byte `bool` is followed by an eight-byte
// `int64`, seven unused bytes of padding go between them. The whole struct is then rounded up
// to its largest alignment, so that in an array every element lines up too.
//
// The same three fields can therefore cost different amounts depending on their order. Putting
// the widest fields first usually leaves the least padding.
//
// The numbers below are for a 64-bit target. Sizes and alignments are decided by the target the
// program is built for, so treat them as facts about this build, not constants of the language.
import Io::PrintLine;
struct Loose {
flag: bool;
total: int64;
mark: uint8;
}
struct Tight {
total: int64;
flag: bool;
mark: uint8;
}
func Main() -> int {
PrintLine("type size align");
PrintLine("uint8 {} {}", sizeof(uint8), alignof(uint8));
PrintLine("int32 {} {}", sizeof(int32), alignof(int32));
PrintLine("int64 {} {}", sizeof(int64), alignof(int64));
PrintLine("int {} {}", sizeof(int), alignof(int));
PrintLine("Loose {} {}", sizeof(Loose), alignof(Loose));
PrintLine("Tight {} {}", sizeof(Tight), alignof(Tight));
// Where each field landed, measured from the start of the value. The gaps are padding.
var loose = Loose { flag: true, total: 1, mark: 2 };
let start = @loose as uint;
PrintLine("Loose: flag at {}, total at {}, mark at {}", (@loose.flag as uint) - start,
(@loose.total as uint) - start, (@loose.mark as uint) - start);
var tight = Tight { total: 1, flag: true, mark: 2 };
let base = @tight as uint;
PrintLine("Tight: total at {}, flag at {}, mark at {}", (@tight.total as uint) - base,
(@tight.flag as uint) - base, (@tight.mark as uint) - base);
// An array is its elements side by side, each one `sizeof` apart, with nothing in between.
PrintLine("Loose[4] takes {} bytes, Tight[4] takes {}", sizeof(Loose[4]), sizeof(Tight[4]));
return 0;
}
```
## Run it
```sh
cd Examples/Memory/Layout
rux run
```
```text
type size align
uint8 1 1
int32 4 4
int64 8 8
int 8 8
Loose 24 8
Tight 16 8
Loose: flag at 0, total at 8, mark at 16
Tight: total at 0, flag at 8, mark at 9
Loose[4] takes 96 bytes, Tight[4] takes 64
```
This output is from a 64-bit build; sizes and alignments depend on the target.
## Common mistakes
::warning
**Adding up the fields.**:br`Loose` holds 1 + 8 + 1 bytes of data but occupies 24. Allocating "10 bytes per `Loose`" would leave every element short. Ask `sizeof(Loose)`.
::
::warning
**Writing sizes down as numbers.**:br`count * 8` for an array of `int` is right on a 64-bit target and wrong elsewhere. `count * sizeof(int)` is right everywhere.
::
::warning
**Expecting the compiler to reorder fields.**:br
Rux keeps fields in the order you write them, padding and all. If size matters, order the fields yourself, widest first.
::
## Try it yourself
1. Declare `struct Mixed { a: uint8; b: int32; c: uint8; d: int16; }`. Predict its size and alignment, check with `sizeof` and `alignof`, then reorder the fields to make it 8 bytes.
2. Print `sizeof(*int)` and `sizeof(char8[..])`. Why is a slice twice the size of a pointer?
3. A struct of three `uint8` fields: what are its size and alignment? Is there any padding?
## Learn more
- [Structs](https://rux-lang.dev/docs/lang/structs/overview) in the Rux Reference
- [Pointer arithmetic](https://rux-lang.dev/docs/learn/pointer-arithmetic) — why a `+ 1` steps by `sizeof`
- [Union](https://rux-lang.dev/docs/learn/union) — a type whose size is its largest member, not the sum
# Union
::note
**You'll need**: [Layout](https://rux-lang.dev/docs/learn/layout), [Enum](https://rux-lang.dev/docs/learn/enum), [Variant](https://rux-lang.dev/docs/learn/variant), [Match](https://rux-lang.dev/docs/learn/match)
::
A struct lays its fields side by side. A **union** lays its members over the *same* bytes: it is as large as its largest member, not the sum of them, because only one member is meant to be in use at a time.
That saves space, and it gives exact control over layout. What a union does not do is remember which member is in use. This lesson shows what follows from that, and why a [variant](https://rux-lang.dev/docs/learn/variant) is usually the better tool.
## Declaring a union
A union looks like a struct, except that its members are separated by commas rather than ended with semicolons:
```rux
union Payload {
whole: int64,
real: float64,
flag: bool
}
```
The program compares it with a struct of the same three fields:
| Type | Holds | Size on this build |
| ---------- | ------------------------ | ------------------ |
| `AllThree` | all three values at once | 24 bytes |
| `Payload` | one of them at a time | 8 bytes |
```mermaid
flowchart LR
bytes[("8 bytes of storage")] --> w["read as whole: int64"]
bytes --> r["read as real: float64"]
bytes --> f["read as flag: bool"]
```
A union literal names exactly one member — the one that becomes active:
```rux
Payload { whole: 42 }
```
## A union does not remember
A `variant` keeps a hidden tag and will only hand out the case that was stored. A union keeps nothing. Reading a member other than the one last written is not a conversion: it hands back the same bytes reinterpreted as another type. Write `real = 2.25` and then read `whole`, and on this machine you get 4612248968380809216 — the bit pattern of 2.25 read as an integer. A `bool` read that way may not even be `true` or `false`. The compiler will not stop such a read.
| | `struct` | `variant` | `union` |
| ------------------------- | ----------------- | ----------------------- | ------------------------ |
| Holds | every field | one case | one member |
| Size | sum, plus padding | largest case plus a tag | largest member |
| Knows which one is in use | — | yes | no |
| Reading the wrong one | — | impossible | compiles, gives nonsense |
## Keeping the tag yourself
So keeping track is the program's job, usually with an explicit tag beside the union. That is what `Setting` does:
```rux
struct Setting {
kind: Kind;
payload: Payload;
}
```
Every reader checks the tag first, and only then touches the member it names:
```rux
func Describe(setting: Setting) {
match setting.kind {
.Whole => PrintLine("whole number {}", setting.payload.whole),
.Real => PrintLine("real number {}", setting.payload.real),
.Flag => PrintLine("flag {}", setting.payload.flag)
}
}
```
## Switching members
Switching to another member means writing the new member and the tag together:
```rux
var setting = Setting { kind: Kind::Whole, payload: Payload { whole: 7 } };
setting.payload.real = 2.25;
setting.kind = Kind::Real;
```
After this, `whole` is no longer meaningful: its bytes now belong to `real`. Forget the tag on the last line, and `Describe` would print 4612248968380809216 as a "whole number".
## When a union is the right tool
Prefer a `variant` whenever it will do — it is the tagged union, with the tag kept for you and checked on every read. Reach for a plain union when the exact layout is the point: a fixed format shared with other code, such as a C library, or saving space where the tag is already known from somewhere else.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Union){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A union lays several members over the same bytes. It is as large as its largest member, not
// the sum of them, because only one member is meant to be in use at a time.
//
// What a union does not do is remember which one that is. A `variant` keeps a hidden tag and
// will only hand out the case that was stored; a union keeps nothing. Keeping track is the
// program's job, usually with an explicit tag beside it, as `Setting` does below.
//
// That job matters because reading a member other than the one last written is not a
// conversion. It hands back the same bytes reinterpreted as another type: what comes out depends
// on how the target lays out numbers, and it may not even be a valid value of that type, such
// as a `bool` that is neither `true` nor `false`. The compiler will not stop such a read.
//
// So prefer a `variant` whenever it will do. Reach for a union when the exact layout is the
// point: a fixed format shared with other code, or saving space where the tag is already known.
import Io::PrintLine;
// Members are separated by commas, not ended with semicolons as struct fields are.
union Payload {
whole: int64,
real: float64,
flag: bool
}
// For comparison: the same three values side by side.
struct AllThree {
whole: int64;
real: float64;
flag: bool;
}
enum Kind {
Whole,
Real,
Flag
}
// The tag says which member of `payload` holds a value. Every function that builds a `Setting`
// must set both together, and every reader must check the tag first.
struct Setting {
kind: Kind;
payload: Payload;
}
func Describe(setting: Setting) {
match setting.kind {
.Whole => PrintLine("whole number {}", setting.payload.whole),
.Real => PrintLine("real number {}", setting.payload.real),
.Flag => PrintLine("flag {}", setting.payload.flag)
}
}
func Main() -> int {
PrintLine("sizeof(Payload) {}", sizeof(Payload));
PrintLine("sizeof(AllThree) {}", sizeof(AllThree));
// A union literal names exactly one member: the one that becomes active.
Describe(Setting { kind: Kind::Whole, payload: Payload { whole: 42 } });
Describe(Setting { kind: Kind::Real, payload: Payload { real: 0.5 } });
Describe(Setting { kind: Kind::Flag, payload: Payload { flag: true } });
// Switching members means writing the new member and the tag together. After this, `whole`
// is no longer meaningful: its bytes now belong to `real`.
var setting = Setting { kind: Kind::Whole, payload: Payload { whole: 7 } };
setting.payload.real = 2.25;
setting.kind = Kind::Real;
Describe(setting);
return 0;
}
```
## Run it
```sh
cd Examples/Memory/Union
rux run
```
```text
sizeof(Payload) 8
sizeof(AllThree) 24
whole number 42
real number 0.5
flag true
real number 2.25
```
## Common mistakes
::warning
**Ending union members with semicolons.**:br
Struct habits die hard. `whole: int64;` inside a union fails with `error: expected ',' between union fields before ';'`, and the help line says to separate the members with commas.
::
::warning
**Naming more than one member in a literal.**:br`Payload { whole: 1, real: 2.0 }` fails with `error: union initializer for 'Payload' must select exactly one field, but 2 were provided`. An empty `Payload {}` fails the same way with `but 0 were provided`.
::
::warning
**Changing the member without the tag.**:br`setting.payload.real = 2.25;` on its own compiles, and the next `Describe` reads those bytes as a whole number. Change the member and the tag together, and keep that in one function if you can.
::
## Try it yourself
1. Read `whole` from `Payload { real: 1.0 }` and print it. Can you see why a float and an integer that look alike in source are nothing alike in memory?
2. Add a fourth member, `text: char8[..]`, to `Payload`, with a matching `Kind::Text` and a `Describe` arm. What is `sizeof(Payload)` now?
3. Rewrite `Setting` as a `variant` with `Whole`, `Real` and `Flag` cases, and compare its `sizeof` with `Setting`'s.
## Learn more
- [Unions](https://rux-lang.dev/docs/lang/unions/overview) in the Rux Reference
- [Variant](https://rux-lang.dev/docs/learn/variant) — the tagged union that keeps the tag for you
- [Layout](https://rux-lang.dev/docs/learn/layout) — sizes and alignment, which decide how big a union is
# Allocator
::note
**You'll need**: [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice), [Interface value](https://rux-lang.dev/docs/learn/interface-value), [Propagate](https://rux-lang.dev/docs/learn/propagate), [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error), [Defer](https://rux-lang.dev/docs/learn/defer)
::
`Alloc` and `Free` always go to the same place, and they report failure with a `null` that is easy to forget. An **allocator** fixes both. It turns "where memory comes from" into a value you can pass around, and it reports failure as a real error that has to be handled before there is any address to misuse.
This lesson uses the simplest allocator, `SystemAllocator`. Later lessons in this part swap in others — an arena, a fixed buffer, a pool — without changing how they are called.
## The `Allocator` interface
`Allocator`, from the `Allocator` package, is an [interface](https://rux-lang.dev/docs/learn/interface). It promises three operations, and any type that keeps those promises can stand behind it:
| Operation | Does | Returns |
| ----------------------------------------- | ------------------------------------- | ---------------------------- |
| `Allocate(layout)` | takes storage that matches the layout | `(*var opaque) ! AllocError` |
| `Deallocate(block, layout)` | gives the storage back | `! AllocError` |
| `Reallocate(block, oldLayout, newLayout)` | grows or shrinks a block | `(*var opaque) ! AllocError` |
A function written against the interface works with whichever allocator its caller passes in:
```rux
func SumOfSquares(allocator: Allocator, count: uint) -> int64 ! AllocError {
```
```mermaid
flowchart LR
f["SumOfSquares"] -- "Allocate(layout)" --> i{{"Allocator"}}
i --> s["SystemAllocator"]
s -- "asks for pages" --> os[("the operating
system")]
i -- "an address,
or an AllocError" --> f
```
## A request is a `Layout`
`Alloc` took a byte count. `Allocate` takes a `Layout`: a size and an alignment together, so the two always travel as a pair. `Layout::ForArray(count)` builds one for `count` values of `T`:
```rux
let layout = Layout::ForArray(count) ?? fail AllocError::Unsupported;
```
It returns `Layout?` — `none` when `count` elements could not even be described, because the size would overflow. Here `??` turns that into an `AllocError`, as in [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error). `Layout::ForValue()` describes a single value and cannot fail; you will meet it in the next lessons.
## Failure is an error, not a `null`
`Allocate` returns a fallible, so the address only exists once the failure has been dealt with. Here `?` passes a refusal on to the caller:
```rux
let block = allocator.Allocate(layout)?;
```
Past this line, `block` is real storage. The release is registered at once, with the same layout the block was asked for with:
```rux
defer allocator.Deallocate(block, layout) catch { else => {} };
```
`Deallocate` is fallible too. A refused release here could only be the allocator's own bug, and there is nobody to report it to, so `catch { else => {} }` discards it on purpose.
The failure itself says why it happened:
| `AllocError` | Means |
| -------------- | -------------------------------------------------------------------------- |
| `Unsupported` | this allocator cannot serve the request at all; asking again will not help |
| `OutOfMemory` | there is no storage left right now |
| `InvalidBlock` | a release named a block or a layout this allocator did not hand out |
## Getting an `Allocator`
`Main` builds the concrete allocator, then names it at the interface type:
```rux
var system = SystemAllocator();
let allocator: Allocator = system;
```
`SystemAllocator` carries no state of its own: it asks the operating system for whole pages every time. That is fine for a few large requests like these, and wasteful for many small ones — which is what the arena and the pool, later in this part, are for.
## The refused request
The last call asks for a billion billion `int64`s. `ForArray` can still describe that many, but no machine can provide them, so the allocator refuses and `Try` prints the reason. Which reason you see depends on how the operating system answers, which is why the output note says it may differ.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Allocator){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `Alloc` and `Free` always go to the same place. An allocator turns "where memory comes from"
// into a value: the `Allocator` interface promises three operations, and any type that keeps
// those promises can stand behind it. A function written against the interface works with
// whichever allocator its caller passes in.
//
// This lesson uses `SystemAllocator`, which asks the operating system. The next lessons swap in
// others without changing how they are called.
//
// Two things differ from `Alloc`. A request is a `Layout`, a size and an alignment together,
// built here by `Layout::ForArray(count)`. And failure is a real error, not a `null`:
// `Allocate` returns `(*var opaque) ! AllocError`, so there is no address to misuse until the
// failure has been handled. A block must go back through `Deallocate` with the same layout it
// was asked for with.
import Allocator::{ AllocError, Allocator, Layout, SystemAllocator };
import Io::PrintLine;
// Borrows `count` numbers' worth of memory, uses it, and gives it back.
func SumOfSquares(allocator: Allocator, count: uint) -> int64 ! AllocError {
// `ForArray` is `none` when `count` elements could not even be described.
let layout = Layout::ForArray(count) ?? fail AllocError::Unsupported;
// `?` passes a refusal on to the caller. Past this line, `block` is real storage.
let block = allocator.Allocate(layout)?;
// Registered at once, so every path out returns the block with its layout. A refused release
// here would be the allocator's own bug, and there is nobody to report it to.
defer allocator.Deallocate(block, layout) catch { else => {} };
let numbers = (block as *var int64)[..count];
var total: int64 = 0;
for i in 0..count {
numbers[i] = (i * i) as int64;
total += numbers[i];
}
return total;
}
func Reason(error: AllocError) -> char8[..] {
return match error {
.Unsupported => "the request cannot be served",
.OutOfMemory => "there is not enough memory",
.InvalidBlock => "the block was not from this allocator"
};
}
func Try(allocator: Allocator, count: uint) {
match SumOfSquares(allocator, count) {
.Success(total) => PrintLine("{} squares add up to {}", count, total),
.Failure(error) => PrintLine("{} squares: refused, {}", count, Reason(error))
}
}
func Main() -> int {
// Build the concrete allocator, then name it at the interface type to get an `Allocator`.
var system = SystemAllocator();
let allocator: Allocator = system;
Try(allocator, 10);
Try(allocator, 1000);
// Eight bytes times this count is far more memory than any machine has.
Try(allocator, 1000000000000000000);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/Allocator
rux run
```
```text
10 squares add up to 285
1000 squares add up to 332833500
1000000000000000000 squares: refused, the request cannot be served
```
The reason given on the last line comes from the operating system's answer, so it can differ between systems.
## Common mistakes
::warning
**Using the block before handling the failure.**:br`let block = allocator.Allocate(layout);` without `?` holds a fallible, not an address, so `block as *var int64` fails with `error: cannot cast value of type '*var opaque ! AllocError' to '*var int64'`. Unwrap it first: `?`, `catch` or a `match`.
::
::warning
**Passing the optional layout.**:br
Leave out the `?? fail …` and `layout` is a `Layout?`. `Allocate(layout)` then fails with `error: argument 1 to 'Allocate' has type 'Layout?', but parameter 'layout' requires 'Layout'`.
::
::warning
**A bare deferred release.**:br`defer allocator.Deallocate(block, layout);` fails with `error: fallible result of type '! AllocError' is discarded`. Decide what a refused release means, or discard it on purpose with `catch { else => {} }`.
::
::warning
**Releasing with a different layout.**:br
A block must go back with the layout it was asked for with. `SystemAllocator` notices a mismatch and refuses with `AllocError::InvalidBlock`; other allocators may not be able to tell, and quietly go wrong instead.
::
## Try it yourself
1. Add `Try(allocator, 0);`. What does a zero-element request give back, and does it still need releasing?
2. Make `Try` also print `error.IsTransient()`, which is `true` only for `OutOfMemory` — the one failure that might go away if you ask again later.
3. Write `SumOfCubes` against the same interface and call it with the same allocator.
## Learn more
- [Interface value](https://rux-lang.dev/docs/learn/interface-value) — how a concrete value stands behind an interface type
- [Propagate](https://rux-lang.dev/docs/learn/propagate) and [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) — the `?` and `?? fail` used here
- [Box](https://rux-lang.dev/docs/learn/box) — the next lesson, which stops you from having to call `Deallocate` at all
# Box
::note
**You'll need**: [Allocator](https://rux-lang.dev/docs/learn/allocator), [Destructor](https://rux-lang.dev/docs/learn/destructor), [Move](https://rux-lang.dev/docs/learn/move), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main)
::
The raw lessons kept leaving one job to you: remember to give the memory back, exactly once, and never touch it afterwards. A `Box` takes that job over. It owns one value that lives in allocated memory, and — like any value with a [destructor](https://rux-lang.dev/docs/learn/destructor) — it cleans up after itself when its life ends.
## Creating a box
`Box::Create(allocator, value)` asks the allocator for room, moves the value in, and hands back the box:
```rux
let boxed = Box::Create(allocator, Tally { name: name, count: 1 })?;
```
Asking can fail, so `Create` returns `Box ! AllocError`, and `?` passes a refusal on. From this line on, the box is the value's one owner.
## Reaching the value
`Get()` returns a `*var T` into the box's storage, so fields are reached through it with a plain `.`, as through any pointer:
```rux
apples.Get().count += 2;
PrintLine(" {} {}", apples.Get().count, apples.Get().name);
```
That pointer does not own anything. It is valid only while the box is alive — the box decides when the storage goes, not the pointer.
## The box cleans up
Nobody calls `Deallocate` in this program. When a box's life ends, its destructor destroys the value — which runs `~Tally` and prints a line — and then gives the memory back to the allocator it came from:
```mermaid
flowchart LR
create["Box::Create(allocator, value)"] --> alloc["the allocator gives room,
the value moves in"]
alloc --> use["boxed.Get() — use it"]
use --> end_["the box's life ends"]
end_ --> drop["~Tally runs"]
drop --> back["memory goes back to
the same allocator"]
```
You can see the timing in the output. The box in `Count` dies when `Count` returns, so `destroying the pears tally` is printed before `Main` carries on with `a box in Main:`.
## Moving a box
A box cannot be copied: two owners would both release the memory, and the second release would hit memory that was no longer theirs. It can be **moved** with `<-`, which hands the ownership on and retires the old name:
```rux
let storage = apples.Get();
let owner <- apples;
PrintLine(" moved, same storage: {}", owner.Get() == storage);
```
The value itself stays where it was allocated, so the new owner points at the very same storage — the comparison prints `true`. Only the right to release it has moved. At the end of `Main`, `owner` releases the memory, once, and `apples` releases nothing because it no longer owns anything.
| You write | What happens |
| ---------------------- | ----------------------------------------------- |
| `let owner <- apples;` | ownership moves; `apples` can no longer be used |
| `let owner = apples;` | rejected — a box cannot be copied |
| `apples.Get()` | a borrowed `*var T`, valid while the box lives |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Box){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `Box` owns one value that lives in allocated memory. `Box::Create(allocator, value)`
// asks the allocator for room, moves the value in, and hands back the box, or an `AllocError`
// if there was no room. From then on the box is the value's one owner.
//
// That ownership is what the raw lessons were missing. Nobody calls `Deallocate`: when the box's
// life ends, its destructor destroys the value and gives the memory back to the allocator it
// came from. And a box cannot be copied, because two owners would release the memory twice.
// Hand it on with `<-`, and the old binding can no longer be used.
//
// `Get()` returns a `*var T` into the box's storage. That pointer does not own anything: it is
// valid only while the box is alive.
import Allocator::{ AllocError, Allocator, Box, SystemAllocator };
import Io::PrintLine;
struct Tally {
name: char8[..];
count: int;
}
extend Tally {
func ~Tally(self: &var Tally) {
PrintLine(" destroying the {} tally", self.name);
}
}
// The box is created and dies inside this call, so its memory is returned before `Main` resumes.
func Count(allocator: Allocator, name: char8[..]) -> ! AllocError {
let boxed = Box::Create(allocator, Tally { name: name, count: 1 })?;
PrintLine(" counted {} {}", boxed.Get().count, boxed.Get().name);
}
func Main() -> ! AllocError {
var system = SystemAllocator();
let allocator: Allocator = system;
PrintLine("a short-lived box:");
Count(allocator, "pears")?;
PrintLine("a box in Main:");
var apples = Box::Create(allocator, Tally { name: "apples", count: 3 })?;
apples.Get().count += 2;
PrintLine(" {} {}", apples.Get().count, apples.Get().name);
// Moving the box moves the ownership. The value itself stays where it was allocated, so the
// new owner points at the very same storage.
let storage = apples.Get();
let owner <- apples;
PrintLine(" moved, same storage: {}", owner.Get() == storage);
// Using `apples` from here on is rejected: it was moved out. `owner` releases the memory, once.
PrintLine("end of Main:");
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/Box
rux run
```
```text
a short-lived box:
counted 1 pears
destroying the pears tally
a box in Main:
5 apples
moved, same storage: true
end of Main:
destroying the apples tally
```
## Common mistakes
::warning
**Copying a box.**:br`let owner = apples;` fails with `error: move-only value 'apples' requires an explicit '<-' in initialization`, and the help line suggests `let destination <- apples`. Move it, or keep using the one box.
::
::warning
**Using a box after moving it.**:br
After `let owner <- apples;`, any use of `apples` fails with `error: value 'apples' is used after it was moved`. Use `owner` from then on.
::
::warning
**Forgetting the `?`.**:br`let apples = Box::Create(…);` without `?` holds a fallible, so `apples.Get()` fails with `error: type 'Box ! AllocError' has no field 'Get'`. Handle the `AllocError` first.
::
::warning
**Keeping the pointer from `Get()` longer than the box.**:br
A function that creates a box and returns `boxed.Get()` compiles — but the box dies on the way out, and the caller is left holding the address of released memory. Return the box itself, moved with `<-`, instead.
::
## Try it yourself
1. Create a second box, `plums`, in `Main` before `apples`. Predict the order of the two `destroying` lines at the end, then run.
2. Write `func Report(owner: Box)` that prints the tally, and call it as `Report(<-apples)`. Where does `destroying the apples tally` appear now?
3. Change `Count` to return the box, `-> Box ! AllocError`, with `return <-boxed;`, and keep it alive in `Main`.
## Learn more
- [Move](https://rux-lang.dev/docs/learn/move) and [Destructor](https://rux-lang.dev/docs/learn/destructor) — the ownership rules a box is built on
- [Allocator](https://rux-lang.dev/docs/learn/allocator) — where a box's memory comes from
- [Arena](https://rux-lang.dev/docs/learn/arena) — an allocator a box can draw from, with one extra rule
# Arena
::note
**You'll need**: [Allocator](https://rux-lang.dev/docs/learn/allocator), [Box](https://rux-lang.dev/docs/learn/box), [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main)
::
Plenty of work produces many small pieces of memory whose lives all end together: everything built while handling one request, one frame of a game, one round of a loop. Releasing each piece separately is slow and easy to get wrong. An **arena** takes a different approach — it never takes back a single piece, and instead takes back *everything* at once.
## How an arena hands out memory
An arena holds a large block and a marker. Each request is served from the marker onwards, and the marker moves forward past it. That is all an allocation costs: an addition and a check that the block still has room.
When a block runs out, the arena takes a bigger one from the allocator behind it, its *backing* allocator. `Reset` moves the marker back to the start, keeping the largest block for next time, and the arena's destructor returns all its blocks when the arena itself dies.
```mermaid
flowchart LR
sys["SystemAllocator"] -- "large blocks" --> arena["Arena"]
arena -- "Handle()" --> h["Allocator"]
h -- "Allocate:
move the marker" --> work["one round
of work"]
work -- "Reset:
marker back to the start" --> arena
arena -- "~Arena:
blocks returned" --> sys
```
## Setting one up
The arena is built over a backing allocator, with the size of its first block:
```rux
var arena = Arena(backing, 256);
var handle = arena.Handle();
let allocator: Allocator = handle;
```
The arena is used through `Handle()`, an `Allocator` that points back at it. Code like `Squares` takes that `Allocator` and has no idea an arena is behind it.
## Nobody releases anything
`Squares` asks for storage and never gives it back:
```rux
let numbers = (allocator.Allocate(layout)? as *var int64)[..count];
```
With `SystemAllocator` that would be a leak. With an arena it is the intended use: the memory belongs to the round, and the round ends with one call.
```rux
arena.Reset();
```
## Watching it grow
The three `Squares` calls of a round ask for 10, 20 and 30 numbers of eight bytes — 480 bytes in all. The first round outgrows the 256-byte block and takes a second, larger one, so it ends holding two blocks. `Reset` keeps only the larger block, and every later round fits inside it: the output shows `blocks held 1` from then on, and the arena never asks the system for anything again. `BytesUsed` counts across every block the arena holds.
## The rule the compiler does not check
After `Reset`, every address the arena handed out is invalid, even though the pointers holding them still look fine. Finish with them first. That includes any [Box](https://rux-lang.dev/docs/learn/box) built on the arena: destroy the box before the reset, or its destructor will later run on memory the arena has already handed out again.
The handle has a rule of its own. It holds a pointer back to the arena, so it must not outlive the arena, and it must not be used after the arena moves.
| Shape of the work | Arena? |
| --------------------------------------------- | ---------------------------------------------------------------- |
| Many pieces that all end together | yes — one `Reset` frees them all |
| Pieces that end one by one, in no fixed order | no — see [Pool](https://rux-lang.dev/docs/learn/pool) |
| A fixed amount of memory you already have | see [Fixed buffer](https://rux-lang.dev/docs/learn/fixed-buffer) |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Arena){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An arena hands out memory by moving a marker forward through a large block, and never takes
// back a single piece. Instead, `Reset` takes everything back at once, and the arena's
// destructor returns its blocks to the allocator behind it.
//
// That fits work whose pieces all end together: one request, one frame, one round of a loop.
// Asking is fast, nothing has to be released piece by piece, and nothing can be released twice.
// When a block runs out, the arena takes a bigger one from its backing allocator; `Reset` keeps
// the largest, so the next round usually needs nothing new.
//
// The price is one rule the compiler does not check: after `Reset`, every address the arena
// handed out is invalid. Finish with them first, and that includes any `Box` built on the
// arena, which must be destroyed before the reset. The arena is used through `Handle()`, an
// `Allocator` that points back at it, so the handle must not outlive the arena or be used after
// the arena moves.
import Allocator::{ AllocError, Allocator, Arena, Layout, SystemAllocator };
import Io::PrintLine;
// Scratch storage for one round. Nothing here releases it; the arena's reset will.
func Squares(allocator: Allocator, count: uint) -> int64 ! AllocError {
let layout = Layout::ForArray(count) ?? fail AllocError::Unsupported;
let numbers = (allocator.Allocate(layout)? as *var int64)[..count];
var total: int64 = 0;
for i in 0..count {
numbers[i] = (i * i) as int64;
total += numbers[i];
}
return total;
}
func Main() -> ! AllocError {
var system = SystemAllocator();
let backing: Allocator = system;
// The first block holds 256 bytes; later ones grow as needed.
var arena = Arena(backing, 256);
var handle = arena.Handle();
let allocator: Allocator = handle;
for round in 1..=3 {
// 10, 20 and 30 numbers of eight bytes: 480 bytes in all. The first round outgrows the
// 256-byte block and takes a second, larger one. The reset keeps that larger block, and
// every later round fits inside it. `BytesUsed` counts across every block the arena holds.
let total = Squares(allocator, 10)? + Squares(allocator, 20)? + Squares(allocator, 30)?;
PrintLine("round {}: total {}, bytes in use {}, blocks held {}",
round, total, arena.BytesUsed(), arena.BlockCount());
// The round is over and nothing from it is still in use, so all of it goes at once.
arena.Reset();
PrintLine(" reset: bytes in use {}, blocks held {}",
arena.BytesUsed(), arena.BlockCount());
}
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/Arena
rux run
```
```text
round 1: total 11310, bytes in use 480, blocks held 2
reset: bytes in use 0, blocks held 1
round 2: total 11310, bytes in use 480, blocks held 1
reset: bytes in use 0, blocks held 1
round 3: total 11310, bytes in use 480, blocks held 1
reset: bytes in use 0, blocks held 1
```
## Common mistakes
::warning
**An arena declared with `let`.**:br`Handle()` needs to point at an arena it can change, so with `let arena = …` the call fails with `error: cannot call 'Handle' on immutable 'arena'`, and the help line suggests declaring it with `var`.
::
::warning
**Copying the arena.**:br`let copy = arena;` fails with `error: move-only value 'arena' requires an explicit '<-' in initialization`. Two arenas sharing the same blocks would release them twice. And if you do move it with `<-`, every handle taken before the move points at the old place — take a new one.
::
::warning
**Using memory after `Reset`.**:br
A pointer or a view from the last round still compiles after `arena.Reset()`, and reads whatever the next round wrote there. Nothing reports it. Let every pointer from a round go out of use before the reset.
::
::warning
**Expecting `Deallocate` to give memory back.**:br
An arena accepts `Deallocate`, so code written for any allocator still works, but it only rewinds the most recent allocation. Releasing anything older does nothing until the next `Reset`.
::
## Try it yourself
1. Change the first block size from 256 to 1024. How many blocks does round 1 hold now?
2. Remove `arena.Reset()` and watch `BytesUsed` and `BlockCount` grow from round to round.
3. In `Main`, allocate two blocks in a row through `allocator`. Release the *first* and print `BytesUsed`, then release the second and print it again. Which release made a difference, and why?
## Learn more
- [Allocator](https://rux-lang.dev/docs/learn/allocator) — the interface the handle implements
- [Fixed buffer](https://rux-lang.dev/docs/learn/fixed-buffer) — the same idea over storage you already have
- [Pool](https://rux-lang.dev/docs/learn/pool) — for pieces that come and go in no particular order
# Fixed buffer
::note
**You'll need**: [Allocator](https://rux-lang.dev/docs/learn/allocator), [Arena](https://rux-lang.dev/docs/learn/arena), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Loop](https://rux-lang.dev/docs/learn/loop)
::
Every allocator so far could, in the end, ask the operating system for more. Sometimes that is exactly what you must not do: on a small device with no system allocator at all, in code that has to run within a fixed budget, or when one part of a program must never be able to use up memory the rest needs. A `FixedBuffer` is the allocator for those places. It hands out memory you already have, and when that runs out, it says no.
## Storage you already own
Here the storage is a 64-byte array on the stack. The buffer is built over its first byte and its size:
```rux
var storage: byte[64];
var buffer = FixedBuffer(@storage[0] as *var opaque, 64);
var handle = buffer.Handle();
let allocator: Allocator = handle;
```
Like an arena, the buffer hands that memory out piece by piece from a moving marker, and is used through a `Handle()` that implements `Allocator`. Unlike an arena, it never takes a new block from anyone.
The storage belongs to you, not to the buffer. It must outlive the buffer and everything handed out of it — here all three live in `Main`, so that holds.
## Asking until it says no
A `Point` is two `int64`s, 16 bytes, so four fit in 64 bytes. The loop keeps asking and stops at the first refusal:
```rux
match allocator.Allocate(layout) {
.Success(block) => {
made += 1;
let point = block as *var Point;
*point = Point { x: made, y: made * made };
PrintLine("point {} at ({}, {}), {} bytes left",
made, point.x, point.y, buffer.BytesRemaining());
},
.Failure(error) => {
let full = error == AllocError::OutOfMemory;
PrintLine("point {} refused, out of memory: {}", made + 1, full);
break;
}
}
```
This time the failure is not passed on with `?`. It is the expected way out of the loop, so a `match` takes it apart directly, as in [Outcome](https://rux-lang.dev/docs/learn/outcome). Code written against `Allocator` cannot tell a fixed buffer from any other allocator — it just sees a refusal sooner.
## Reset
As with an arena, `Reset` makes the whole buffer available again, and invalidates every address it gave out before:
```rux
buffer.Reset();
```
The next request then starts at the front of `storage` again, which the last line of the output confirms.
## Three allocators side by side
| | `SystemAllocator` | `Arena` | `FixedBuffer` |
| ------------------------- | -------------------- | ------------------------------- | ---------------------------- |
| Memory comes from | the operating system | blocks from a backing allocator | storage you pass in |
| When it runs out | the system refuses | it takes a bigger block | `OutOfMemory` |
| `Deallocate` of one block | gives it back | rewinds only the most recent | rewinds only the most recent |
| Everything at once | — | `Reset`, and the destructor | `Reset` |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/FixedBuffer){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A `FixedBuffer` is an allocator over memory you already have: here, a 64-byte array on the
// stack. It hands that memory out piece by piece, like an arena, but it never asks anyone for
// more. When the buffer is full, `Allocate` fails with `AllocError::OutOfMemory`.
//
// That makes it the allocator for places where real allocation is not allowed or not
// available, and a way to put a hard limit on how much some piece of code may use. Code
// written against `Allocator` does not know the difference; it just sees a refusal sooner.
//
// The storage belongs to you, not to the buffer, so it must outlive the buffer and everything
// handed out of it. As with an arena, `Reset` makes the whole buffer available again and
// invalidates every address it gave out before.
import Allocator::{ AllocError, Allocator, FixedBuffer, Layout };
import Io::PrintLine;
struct Point {
x: int64;
y: int64;
}
func Main() -> ! AllocError {
var storage: byte[64];
var buffer = FixedBuffer(@storage[0] as *var opaque, 64);
var handle = buffer.Handle();
let allocator: Allocator = handle;
// Each point needs 16 bytes, so four fit.
let layout = Layout::ForValue();
var made: int64 = 0;
loop {
match allocator.Allocate(layout) {
.Success(block) => {
made += 1;
let point = block as *var Point;
*point = Point { x: made, y: made * made };
PrintLine("point {} at ({}, {}), {} bytes left",
made, point.x, point.y, buffer.BytesRemaining());
},
.Failure(error) => {
let full = error == AllocError::OutOfMemory;
PrintLine("point {} refused, out of memory: {}", made + 1, full);
break;
}
}
}
// After a reset the next request starts again at the front of `storage`.
buffer.Reset();
PrintLine("after reset, {} bytes left", buffer.BytesRemaining());
let again = allocator.Allocate(layout)?;
PrintLine("starts at storage[0]: {}", (again as uint) == (@storage[0] as uint));
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/FixedBuffer
rux run
```
```text
point 1 at (1, 1), 48 bytes left
point 2 at (2, 4), 32 bytes left
point 3 at (3, 9), 16 bytes left
point 4 at (4, 16), 0 bytes left
point 5 refused, out of memory: true
after reset, 64 bytes left
starts at storage[0]: true
```
## Common mistakes
::warning
**Storage that dies before the buffer.**:br
If the array lives in a function that returns while the buffer, or anything allocated from it, is still in use, every address points into a stack frame that no longer exists. Nothing reports it. Declare the storage where it outlives everything that uses it.
::
::warning
**A capacity that does not match the storage.**:br
The buffer believes the number you give it. `FixedBuffer(@storage[0] as *var opaque, 128)` over a 64-byte array compiles, and happily hands out the 64 bytes that come after it. Use `sizeof` of the storage rather than typing the number twice.
::
::warning
**Expecting every byte of `BytesRemaining` to be usable.**:br
Each request is aligned first. With 63 bytes remaining after a one-byte request, a 63-byte request at alignment 8 is still refused with `OutOfMemory`: aligning it would push it past the end.
::
## Try it yourself
1. Make `storage` 100 bytes, and pass `sizeof(byte[100])` as the capacity. How many points fit, and how many bytes are left over?
2. Before the loop, allocate one `int32` with `Layout::ForValue()`. How many points fit now, and where did the rest of the bytes go?
3. Move the loop into `func CountPoints(allocator: Allocator) -> int64` and call it with the fixed buffer's handle. The function never learns which allocator it was given.
## Learn more
- [Arena](https://rux-lang.dev/docs/learn/arena) — the same marker-based approach, with room to grow
- [Outcome](https://rux-lang.dev/docs/learn/outcome) — matching `.Success` and `.Failure` directly
- [Allocator](https://rux-lang.dev/docs/learn/allocator) — `AllocError` and what each case means
# Pool
::note
**You'll need**: [Allocator](https://rux-lang.dev/docs/learn/allocator), [Arena](https://rux-lang.dev/docs/learn/arena), [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main)
::
An arena suits values that all end together. A **pool** suits the opposite shape: many small values that come and go in no particular order — the nodes of a list, the entries of a table, the bullets in a game. Each one is released on its own, and the very next request of that size gets the released block straight back.
## Blocks of a few fixed sizes
A pool cuts large chunks of memory into equal blocks and keeps the free ones on a list. To keep that fast, it only serves a handful of block sizes, called *classes*, and rounds every request up to the nearest one:
```rux
PrintLine("block sizes: {} {} {} {} {}", pool.ClassSize(0), pool.ClassSize(1),
pool.ClassSize(2), pool.ClassSize(3), pool.ClassSize(4));
let odd = Layout::New(20, 8) ?? fail AllocError::Unsupported;
PrintLine("a 20-byte request uses a {}-byte block", pool.ClassSize(ClassIndexFor(odd)));
```
| Request | Block it takes | Unused bytes |
| ------------- | ----------------------------------------------- | ------------ |
| 1–16 bytes | 16 | up to 15 |
| 17–32 bytes | 32 | up to 15 |
| 33–64 bytes | 64 | up to 31 |
| 65–128 bytes | 128 | up to 63 |
| 129–256 bytes | 256 | up to 127 |
| more than 256 | none — passed straight to the backing allocator | — |
Rounding is the price of speed. A 20-byte request takes a 32-byte block, and the other 12 bytes sit unused until that block comes back. `Layout::New(size, alignment)`, used for `odd`, builds a layout from two plain numbers; it is `none` when the pair cannot be a layout, such as an alignment that is not a power of two.
## Taking and returning blocks
The pool is created over a backing allocator with the number of blocks per chunk, and used through its handle like every allocator in this part:
```rux
var pool = Pool(backing, 8);
var handle = pool.Handle();
let allocator: Allocator = handle;
```
Three 16-byte points come out of the first chunk, and the middle one goes back first:
```rux
let first = allocator.Allocate(layout)?;
let second = allocator.Allocate(layout)?;
let third = allocator.Allocate(layout)?;
PrintLine("three out: {} live blocks, {} chunk", pool.LiveBlocks(), pool.ChunkCount());
allocator.Deallocate(second, layout)?;
```
```mermaid
flowchart LR
chunk["a chunk from the
backing allocator"] -- "cut into
8 blocks" --> list[("free list
16-byte class")]
list -- "Allocate
takes one" --> use["in use"]
use -- "Deallocate
puts it back" --> list
```
## The next request reuses the block
A released block goes straight back on the free list of its class, so the next request of that size gets that very block:
```rux
let fourth = allocator.Allocate(layout)?;
PrintLine("fourth reuses the second's block: {}", fourth == second);
```
No search, no new chunk — a handful of instructions. When every block is back, `LiveBlocks` is 0, but the chunk stays: it is ready for the next requests, and goes back to the backing allocator only when the pool is destroyed.
## Arena or pool?
| | Arena | Pool |
| ------------------- | --------------------------------------- | ----------------------------- |
| Values end | all together | one by one, in any order |
| Releasing one value | does nothing (unless it was the latest) | puts its block back for reuse |
| You must release | nothing — `Reset` takes it all | every block, with its layout |
| Wasted space | only padding for alignment | the rounding up to a class |
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Pool){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A pool suits many small values that come and go in no particular order: the opposite of an
// arena, where everything ends together. It cuts large chunks into equal blocks and keeps the
// free ones on a list. Allocating takes a block off the list, and releasing puts it straight
// back, ready for the next request of that size.
//
// Requests are rounded up to a few fixed block sizes, called classes: 16, 32, 64, 128 and 256
// bytes. Rounding is the price of speed. A 20-byte request takes a 32-byte block, and the other
// 12 bytes sit unused until that block comes back.
//
// Unlike an arena, a pool expects each block back: `Deallocate` with the same layout, through
// the pool it came from. The chunks themselves go back to the backing allocator when the pool
// is destroyed.
import Allocator::{ AllocError, Allocator, ClassIndexFor, Layout, Pool, SystemAllocator };
import Io::PrintLine;
struct Point {
x: int64;
y: int64;
}
func Main() -> ! AllocError {
var system = SystemAllocator();
let backing: Allocator = system;
// Each chunk is cut into eight blocks.
var pool = Pool(backing, 8);
var handle = pool.Handle();
let allocator: Allocator = handle;
PrintLine("block sizes: {} {} {} {} {}", pool.ClassSize(0), pool.ClassSize(1),
pool.ClassSize(2), pool.ClassSize(3), pool.ClassSize(4));
let odd = Layout::New(20, 8) ?? fail AllocError::Unsupported;
PrintLine("a 20-byte request uses a {}-byte block", pool.ClassSize(ClassIndexFor(odd)));
// Three 16-byte points, all out of the first chunk.
let layout = Layout::ForValue();
let first = allocator.Allocate(layout)?;
let second = allocator.Allocate(layout)?;
let third = allocator.Allocate(layout)?;
PrintLine("three out: {} live blocks, {} chunk", pool.LiveBlocks(), pool.ChunkCount());
// Blocks can come back in any order. The middle one goes first.
allocator.Deallocate(second, layout)?;
PrintLine("one back: {} live blocks", pool.LiveBlocks());
// The next request of the same size gets that very block back.
let fourth = allocator.Allocate(layout)?;
PrintLine("fourth reuses the second's block: {}", fourth == second);
allocator.Deallocate(first, layout)?;
allocator.Deallocate(third, layout)?;
allocator.Deallocate(fourth, layout)?;
// Every block is back, and the chunk stays for the next requests.
PrintLine("all back: {} live blocks, {} chunk", pool.LiveBlocks(), pool.ChunkCount());
}
```
Besides `Io`, its `Rux.toml` lists `Allocator` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/Pool
rux run
```
```text
block sizes: 16 32 64 128 256
a 20-byte request uses a 32-byte block
three out: 3 live blocks, 1 chunk
one back: 2 live blocks
fourth reuses the second's block: true
all back: 0 live blocks, 1 chunk
```
## Common mistakes
::warning
**Releasing with a different layout.**:br
The pool works out a block's class from the layout alone, so it cannot tell when the layout is wrong. Release a 16-byte point with the 20-byte `odd` layout and the call succeeds — and the block lands on the 32-byte list, where the next 20-byte request receives a block that is only 16 bytes long. Always release with the layout you allocated with.
::
::warning
**Forgetting to release.**:br
A pool expects each block back. A block never released stays counted in `LiveBlocks` and cannot be reused; its memory only returns when the whole pool is destroyed.
::
::warning
**Using a block after releasing it.**:br
As `fourth == second` shows, a released block is handed out again at the very next request. Anything still writing through the old pointer now writes into someone else's value.
::
## Try it yourself
1. Allocate nine points instead of three, with eight blocks per chunk. What does `ChunkCount` say now?
2. Ask `ClassIndexFor` about a 300-byte layout. It returns 5, one past the last class — what does that mean for where the memory comes from?
3. Release `first` and `third` before allocating `fourth`. Which of the two does `fourth` reuse?
## Learn more
- [Arena](https://rux-lang.dev/docs/learn/arena) — the opposite trade: nothing released one by one
- [Allocator](https://rux-lang.dev/docs/learn/allocator) — the `Deallocate` contract every allocator shares
- [Part 17: Collections](https://rux-lang.dev/docs/learn/collections) — containers that take an `Allocator` and allocate as they grow
# Zeroize
::note
**You'll need**: [Raw memory](https://rux-lang.dev/docs/learn/raw-memory), [Pointer](https://rux-lang.dev/docs/learn/pointer), [Layout](https://rux-lang.dev/docs/learn/layout), [Copy](https://rux-lang.dev/docs/learn/copy)
::
When a program is done with a secret — a password, a key, a PIN — it should wipe the memory that held it. Otherwise the secret can turn up later where nobody expected it: in a crash dump, or in memory that is reused by some other part of the program. This last lesson of the part shows the one tool for the job, and why the obvious tool is not it.
## Why an ordinary clear can vanish
An optimizing compiler looks for work whose result nobody uses, and deletes it. Writing zeros into an array that is never read again is exactly such work. So a clear at the end of a function — with `Zero` from [Raw memory](https://rux-lang.dev/docs/learn/raw-memory), or with a loop — may simply not be there in the optimized program, while the secret stays in memory.
`Zeroize`, from `Core`, is the version the compiler promises to keep, however unread the bytes are afterwards:
| Function | Clears the bytes | May the optimizer remove it? | Use it for |
| -------------------------- | ---------------- | ---------------------------- | ---------------------------- |
| `Zero(p, n)` | yes | yes, if nothing reads them | making fresh memory definite |
| `Zeroize(address, length)` | yes | never | wiping secrets |
## Wiping the PIN
`Zeroize` takes a `*var uint8` — the address of the first byte — and how many bytes to clear:
```rux
Zeroize(@pin[0], sizeof(uint8[4]));
```
`sizeof(uint8[4])` is the size of the whole array, so all four bytes go. After the call, the PIN prints as zeros.
## A copy is not wiped
`Zeroize` clears the bytes it is given and nothing else. The program makes a careless copy before the wipe:
```rux
let copy = pin;
```
An array is copied by value, so `copy` is separate storage with its own four bytes, and it still holds `4 7 1 9` after the wipe. The same goes for any copy the program made along the way — passed by value, returned, stored in a struct. So keep a secret in one place from the start, pass it on as a view such as `pin[..]` rather than as a copy, and wipe that one place when you are done.
```mermaid
flowchart LR
arrive["the secret arrives
in one place"] --> d["defer Zeroize(…)"]
d --> use["use it through views"]
use --> wipe["leaving the function:
the bytes are cleared"]
arrive -. "a copy made
along the way" .-> copy["never wiped"]
```
## Wipe with `defer`
A wipe that is skipped on an early `return` does not help, so register it with [defer](https://rux-lang.dev/docs/learn/defer) right after the secret arrives. A secret that is not stored as bytes needs its address cast to `*var uint8`:
```rux
var key: uint32[4] = [1, 2, 3, 4];
defer Zeroize(@key[0] as *var uint8, sizeof(uint32[4]));
```
Whatever path the function leaves by, the key is cleared on the way out.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Memory/Zeroize){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// When a program is done with a secret, such as a password, a key or a PIN, it should wipe the
// memory that held it, so the secret cannot turn up later in a crash dump or in memory that is
// reused by something else.
//
// The surprise is that an ordinary clear may not happen. Writing zeros that nothing reads
// afterwards looks pointless to an optimizing compiler, and it is allowed to delete such writes.
// `Zero` from the RawMemory lesson is exactly that kind of write. `Zeroize(address, length)`
// is the version the compiler promises to keep, however unread the bytes are afterwards.
//
// It clears the bytes it is given and nothing else. A copy made earlier still holds the secret,
// so keep a secret in one place from the start, and wipe that place when you are done. A
// `defer` right after the secret arrives is a good way to make sure the wipe always happens.
import Core::Zeroize;
import Io::{ Print, PrintLine };
func Show(label: char8[..], bytes: uint8[..]) {
Print("{}", label);
for value in bytes {
Print(" {}", value);
}
PrintLine();
}
func Matches(entered: uint8[..], secret: uint8[..]) -> bool {
for i in 0..secret.length {
if entered[i] != secret[i] {
return false;
}
}
return true;
}
func Main() -> int {
var pin: uint8[4] = [4, 7, 1, 9];
let entered: uint8[4] = [4, 7, 1, 9];
PrintLine("pin accepted: {}", Matches(entered[..], pin[..]));
// A careless copy, made before the wipe. It is separate storage with its own bytes.
let copy = pin;
// Done with the PIN: wipe its four bytes, starting at the address of the first one.
Zeroize(@pin[0], sizeof(uint8[4]));
Show("pin after Zeroize:", pin[..]);
Show("the earlier copy: ", copy[..]);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Memory/Zeroize
rux run
```
```text
pin accepted: true
pin after Zeroize: 0 0 0 0
the earlier copy: 4 7 1 9
```
## Common mistakes
::warning
**Wiping a secret with `Zero`.**:br
It looks the same in the source, and in an optimized build it may not happen at all. For secrets, always `Zeroize`.
::
::warning
**A secret held in a `let`.**:br`let pin: uint8[4] = …` makes `@pin[0]` a read-only `*uint8`, and `Zeroize(@pin[0], …)` fails with `error: argument 1 to 'Zeroize' has type '*uint8', but parameter 'memory' requires '*var uint8'`. A secret you will wipe must be a `var`.
::
::warning
**A secret that is not bytes.**:br
For `var key: uint32[4]`, `Zeroize(@key[0], …)` fails with `error: argument 1 to 'Zeroize' has type '*var uint32', but parameter 'memory' requires '*var uint8'`. Cast the address, `@key[0] as *var uint8`, and give the length in bytes.
::
::warning
**The wrong length.**:br`Zeroize(@pin[0], sizeof(uint8))` clears one byte and leaves `7 1 9` behind. Measure the whole storage: `sizeof(uint8[4])`.
::
## Try it yourself
1. Wipe `copy` as well, and check that both lines print zeros. What did you have to change about `copy` first?
2. Replace the `Zeroize` call with a `defer Zeroize(…)` placed right after `pin` is declared. What does the `pin after Zeroize` line print now, and why?
3. Write `func Check(entered: uint8[..]) -> bool` that keeps the stored PIN in its own `var` array, registers a `defer Zeroize(…)` for it straight away, and returns `Matches(entered, pin[..])`.
## Learn more
- [Raw memory](https://rux-lang.dev/docs/learn/raw-memory) — `Zero`, and where clearing memory first came up
- [Copy](https://rux-lang.dev/docs/learn/copy) — when a value is copied, and so when a secret can multiply
- [Defer](https://rux-lang.dev/docs/learn/defer) — making the wipe happen on every path out
# Part 16: Numbers
Part 1 introduced the number types; this part takes them apart. You will count past 64 bits, ask any type for its limits, meet the float values that are not numbers at all, and work on integers one bit at a time. Above all, you will learn what happens at the edges — when a result does not fit — and how to choose, on purpose, between reporting it, wrapping round and stopping at the limit.
## What you will learn
- The wide integers `int128` to `uint512`, and the constants every number type carries: `Min`, `Max`, `Bits`, `Epsilon` and the rest.
- Infinity and NaN — where they come from, and why `NaN == NaN` is `false`.
- Working with bits: `& | ^ ~`, the shifts `<<`, `>>` and `>>>`, and `Core`'s counting and rotating functions.
- Overflow handled on purpose: checked, wrapping and saturating arithmetic, and conversions that report a value that does not fit.
- Writing a number's bytes in a chosen order, for files and network protocols.
- Roots, powers, logarithms, trigonometry and the four kinds of rounding from the `Math` package.
## The part at a glance
```mermaid
flowchart LR
n(["Numbers"]) --> i["Integers"]
n --> f["Floats"]
i --> range["How big?
Wide integer, Number limit"]
i --> bits["Bit by bit
Bitwise, Shift,
Bit operation, Endian"]
i --> edge["When it does not fit
Checked arithmetic,
Wrapping arithmetic,
Checked convert"]
f --> flimit["Limits and precision
Number limit"]
f --> special["Beyond the limits
Float special"]
f --> math["Functions
Math"]
```
## Lessons
| | Lesson | What you will learn |
| ----- | -------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| 16.1 | [Wide integer](https://rux-lang.dev/docs/learn/wide-integer) | 128-, 256- and 512-bit integers |
| 16.2 | [Number limit](https://rux-lang.dev/docs/learn/number-limit) | the smallest and largest value each number type can hold |
| 16.3 | [Float special](https://rux-lang.dev/docs/learn/float-special) | infinity and NaN, and how they compare |
| 16.4 | [Bitwise](https://rux-lang.dev/docs/learn/bitwise) | `&`, `|`, `^` and `~`: set, clear, flip and test bits with masks |
| 16.5 | [Shift](https://rux-lang.dev/docs/learn/shift) | move bits left and right with `<<`, `>>` and `>>>` |
| 16.6 | [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) | detect overflow instead of getting a wrong answer |
| 16.7 | [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) | arithmetic that wraps around or saturates on purpose |
| 16.8 | [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) | convert between number types and detect values that do not fit |
| 16.9 | [Bit operation](https://rux-lang.dev/docs/learn/bit-operation) | count, find and rotate bits |
| 16.10 | [Endian](https://rux-lang.dev/docs/learn/endian) | write a number's bytes in a chosen byte order |
| 16.11 | [Math](https://rux-lang.dev/docs/learn/math) | roots, powers, logarithms, trigonometry, and rounding |
## Before you start
This part draws on almost everything before it. Lessons hand back answers through [out-parameters](https://rux-lang.dev/docs/learn/out-parameter) and [pointers](https://rux-lang.dev/docs/learn/pointer) from [Part 15: Memory](https://rux-lang.dev/docs/learn/memory), report results as [optionals](https://rux-lang.dev/docs/learn/optionals) from Part 8, and use the [generic](https://rux-lang.dev/docs/learn/generic) functions of `Core`. Each lesson's package is in the Examples repository's `Numbers/` folder:
```sh
cd Examples/Numbers/WideInteger
rux run
```
## After this part
[Part 17: Collections](https://rux-lang.dev/docs/learn/collections) moves from single values to containers of them — vectors, maps, sets and queues. Before moving on, try the checkpoint projects [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic): both put the `Math` package to work and have to deal with the special values a float calculation can produce.
For the rules behind this part, see [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic), [Bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise) and [Shift](https://rux-lang.dev/docs/lang/expressions/shift) operations, [Type casts](https://rux-lang.dev/docs/lang/expressions/casts) and the [primitive types](https://rux-lang.dev/docs/lang/appendix/primitives) in the Rux Reference, and the [Math API](https://rux-lang.dev/docs/api/math).
# Wide integer
::note
**You'll need**: [Integer](https://rux-lang.dev/docs/learn/integer), [Literal](https://rux-lang.dev/docs/learn/literal), [Convert](https://rux-lang.dev/docs/learn/convert), [For](https://rux-lang.dev/docs/learn/for)
::
Sixty-four bits go a long way — about eighteen quintillion — but not all the way. A factorial outgrows them by 21!, a 128-bit identifier needs twice the room, and the arithmetic inside cryptography works on numbers hundreds of bits long. For those, Rux has integers wider than any machine register: `int128`, `int256` and `int512`, and their unsigned twins `uint128`, `uint256` and `uint512`.
The compiler spreads each wide integer over several machine words and carries between them, so a wide integer is slower than an `int64` — but every bit as exact. Everything else you know about integers still holds.
## Six more widths
| Type | Bytes | Largest value, roughly | Digits |
| --------- | ----- | ---------------------- | ------ |
| `int64` | 8 | 9.2 × 10¹⁸ | 19 |
| `int128` | 16 | 1.7 × 10³⁸ | 39 |
| `uint128` | 16 | 3.4 × 10³⁸ | 39 |
| `int256` | 32 | 5.8 × 10⁷⁶ | 77 |
| `uint256` | 32 | 1.2 × 10⁷⁷ | 78 |
| `int512` | 64 | 6.7 × 10¹⁵³ | 154 |
| `uint512` | 64 | 1.3 × 10¹⁵⁴ | 155 |
The signed types reach as far below zero as above it, plus one, exactly like `int8` or `int32`. The limits come from `Core`, the same `Min` and `Max` you use for any other integer — the program imports each type it asks about:
```rux
import Core::{ int128, int64, uint128, uint256, uint512, uint64 };
```
## Wide literals
2⁶⁴ is one more than the largest `uint64`, so it needs a wider home. Give the binding a wide type and the literal takes it:
```rux
let next: uint128 = 18446744073709551616;
```
Hex digits and `_` separators work at any width, exactly as they do for narrow integers:
```rux
let avogadro: uint128 = 602214076000000000000000;
let mask: uint128 = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF;
```
Thirty-two `F`s are 128 set bits, so `mask == uint128::Max` prints `true`.
## Counting past 64 bits
A `uint64` can hold 20! but not 21!. A `uint128` reaches 34!:
```rux
var factorial: uint128 = 1;
let first: uint128 = 1;
for n in first..=34 {
factorial *= n;
}
```
The range starts at a `uint128`, so `n` counts in `uint128` as well, and `factorial *= n` multiplies two values of the same type. Had the range been the plain `1..=34`, `n` would be an `int`, and the multiplication would be refused.
A shift needs the same care. The **left** side of a shift decides its type, so the width goes on the literal there, as a suffix:
```rux
let power = 1u256 << 200;
```
A plain `1 << 200` is an `int`, which has no bit 200 — it quietly prints `256` instead of a 61-digit number.
## Widening and narrowing
Wide integers follow the conversion rules of [Convert](https://rux-lang.dev/docs/learn/convert). Widening loses nothing, so it needs nothing written; an unsuffixed literal grows to the width of the value beside it:
```rux
let balance: int64 = -42;
let wide: int128 = balance;
let large = wide * 1_000_000_000_000_000_000_000;
```
`1_000_000_000_000_000_000_000` is far too large for an `int64`, but it sits next to `wide`, so it is an `int128` and the product is exact: −42 × 10²¹.
Narrowing can lose bits, so it is never silent. You ask for it with `as`, which keeps the low 64 bits — whatever they happen to mean:
```rux
PrintLine("narrowed {}", large as int64);
```
−42 × 10²¹ does not fit in 64 bits, and what is left is the unrelated `3236255836649029632`.
```mermaid
flowchart LR
narrow["int64"] -- "silently" --> wide["int128"]
wide -- "only with as:
keeps the low 64 bits" --> narrow
```
## The edges
One past the maximum wraps to the minimum, as it does for every other integer:
```rux
PrintLine("max + 1 {}", int128::Max + 1);
```
A wider type moves the edge further away; it does not remove it. [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) shows how to find out when you cross it.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/WideInteger){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `int128`, `int256` and `int512`, and their unsigned twins `uint128` to `uint512`, are integers
// wider than any machine register. The compiler spreads each one over several machine words and
// carries between them, so a wide integer is slower than an `int64` but every bit as exact. Use
// one when a number really can outgrow 64 bits: a large factorial, a 128-bit identifier, the
// arithmetic inside cryptography.
//
// Everything you know about integers still applies: the same operators, the same `Min` and `Max`,
// the same wrap-around at the edges, and the same widening. An `int64` becomes an `int128` as
// silently as an `int32` becomes an `int64`, and an unsuffixed literal takes the type of the other
// operand, however wide. Only the way back, from wide to narrow, has to be written with `as`.
import Core::{ int128, int64, uint128, uint256, uint512, uint64 };
import Io::PrintLine;
func Main() -> int {
// 2^64 is one more than the largest `uint64`, so it needs a wider home.
PrintLine("uint64 max {}", uint64::Max);
let next: uint128 = 18446744073709551616;
PrintLine("one more {}", next);
// Hex digits and `_` separators work at any width.
let avogadro: uint128 = 602214076000000000000000;
let mask: uint128 = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF;
PrintLine("avogadro {}", avogadro);
PrintLine("mask is max {}", mask == uint128::Max);
// A `uint64` can hold 20! but not 21!. A `uint128` reaches 34!. The range starts at a
// `uint128`, so `n` counts in `uint128` as well.
var factorial: uint128 = 1;
let first: uint128 = 1;
for n in first..=34 {
factorial *= n;
}
PrintLine("34! {}", factorial);
// The left side of a shift decides its type, so it carries the suffix. A plain `1 << 200`
// would be an `int`, which has no bit 200: it prints 256.
let power = 1u256 << 200;
PrintLine("2^200 {}", power);
// Widening needs nothing written, and the literal grows to the width of `wide`.
let balance: int64 = -42;
let wide: int128 = balance;
let large = wide * 1_000_000_000_000_000_000_000;
PrintLine("widened {}", large);
// Narrowing can lose bits, so it is never silent: `let back: int64 = large;` is refused with
// "cannot assign 'int128' to 'int64'". `as` keeps the low 64 bits, whatever they mean.
PrintLine("narrowed {}", large as int64);
// The edges behave like every other integer's: one past the maximum wraps to the minimum.
PrintLine("int128 max {}", int128::Max);
PrintLine("max + 1 {}", int128::Max + 1);
PrintLine("uint512 max {}", uint512::Max);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/WideInteger
rux run
```
```text
uint64 max 18446744073709551615
one more 18446744073709551616
avogadro 602214076000000000000000
mask is max true
34! 295232799039604140847618609643520000000
2^200 1606938044258990275541962092341162602522202993782792835301376
widened -42000000000000000000000
narrowed 3236255836649029632
int128 max 170141183460469231731687303715884105727
max + 1 -170141183460469231731687303715884105728
uint512 max 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095
```
## Common mistakes
::warning
**A huge literal with no type to take.**:br
A literal standing alone is an `int`, and `let x = 18446744073709551616;` fails with `error: integer literal is out of range for type 'int'`. Annotate the binding — `let x: uint128 = …` — or add a suffix such as `u128`.
::
::warning
**A loop counter of the wrong type.**:br
With `for n in 1..=34`, `n` is an `int`, and `factorial *= n` fails with `error: operator '*=' cannot combine left operand 'uint128' with right operand 'int'`. Start the range at a `uint128`, as the program does with `first`.
::
::warning
**Narrowing without `as`.**:br`let back: int64 = large;` is refused with `error: cannot assign 'int128' to 'int64'`, because 64 bits cannot hold every `int128`. Write `large as int64` when losing the high bits is what you want — and check the value first when it is not.
::
::warning
**A shift that stays an `int`.**:br`1 << 200` compiles, but the `1` is an `int`, and the result is `256`, not 2²⁰⁰. Put the width on the left operand: `1u256 << 200`.
::
::warning
**Asking for a limit without importing the type.**:br`Max` and `Min` are declared in `Core`. Without `int128` in the `import Core::{ … }` list, `int128::Max` fails with `error: 'Max' not found in extend for type 'int128'`.
::
## Try it yourself
1. Change the loop to run to 35. What does `factorial` print now, and why?
2. Compute 21! in a `uint64` and compare it with the `uint128` answer. Does the program warn you?
3. Import `int256` and `uint256` from `Core` and print their `Max`. Count the digits.
4. Write `let x = 18446744073709551616;` and read the error. Then fix it two ways: with a type annotation, and with a suffix.
## Learn more
- [int128](https://rux-lang.dev/docs/lang/types/integers), [uint128](https://rux-lang.dev/docs/lang/types/integers) and the [primitive types](https://rux-lang.dev/docs/lang/appendix/primitives) in the Rux Reference
- [Integer](https://rux-lang.dev/docs/learn/integer) and [Literal](https://rux-lang.dev/docs/learn/literal) — the narrow integers and how literals get their types
- [Number limit](https://rux-lang.dev/docs/learn/number-limit) — the constants every number type carries
- [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) — noticing when a result does not fit
# Number limit
::note
**You'll need**: [Integer](https://rux-lang.dev/docs/learn/integer), [Float](https://rux-lang.dev/docs/learn/float), [Const](https://rux-lang.dev/docs/learn/const), [Generic](https://rux-lang.dev/docs/learn/generic)
::
How large can an `int16` get? How many bytes does a `uint8` take? What is the smallest step a `float64` can take away from 1.0? You could look the answers up and type them into your program — or ask the type itself. Every number type carries facts about itself as **associated constants**, written with `::` after the type's name: `int16::Max`, `uint8::Bits`, `float64::Epsilon`.
Using them instead of typed-in numbers makes code say what it means, and keeps it right if you later change a type.
## Asking a type about itself
The constants are declared in `Core`, so the program imports each type it asks about and lists `Core` as a dependency:
```rux
import Core::{ MaximumOf, MinimumOf, float32, float64, int16, uint16, uint8 };
```
Every integer type answers the same four questions:
```rux
PrintLine("int16 {} to {}, {} bits in {} bytes", int16::Min, int16::Max, int16::Bits,
int16::Bytes);
```
| Constant | Meaning | `int16` | `uint8` |
| -------- | -------------------------- | ------- | ------- |
| `Min` | The smallest value | −32768 | 0 |
| `Max` | The largest value | 32767 | 255 |
| `Bits` | The width in bits | 16 | 8 |
| `Bytes` | The storage size, in bytes | 2 | 1 |
Because they are constants, they work anywhere a constant does — including inside another `const`:
```rux
const LargestPort: uint16 = uint16::Max;
```
A limit also makes a bounds check read like what it means. Will this reading fit in an `int16`?
```rux
let reading: int32 = 40000;
PrintLine("fits int16 {}", reading <= int16::Max && reading >= int16::Min);
```
It does not: the line prints `false`.
## Limits inside a generic function
The constants belong to a concrete type. A generic function cannot reach them through its type parameter — `T::Max` is rejected, because the compiler does not know which type `T` will be. For that case `Core` offers `MaximumOf` and `MinimumOf`, which work the limit out from a sample value of the type:
```rux
func Span(sample: T) -> T {
return MaximumOf(sample) - MinimumOf(sample);
}
```
The sample's value does not matter, only its type. `Span(0)` is 255 − 0 = `255`.
## Float limits
Floats have more limits than integers, because "smallest" means two different things for them:
```rux
PrintLine("float32 {} to {}", float32::Lowest, float32::Max);
PrintLine("float64 {} to {}", float64::Lowest, float64::Max);
PrintLine("smallest {}", float64::MinPositive);
PrintLine("epsilon {}", float64::Epsilon);
```
| Constant | Meaning | `float64` |
| ------------- | ----------------------------------------- | ------------------------ |
| `Lowest` | The most negative finite value | −1.7976931348623157e+308 |
| `Max` | The largest finite value | 1.7976931348623157e+308 |
| `MinPositive` | The smallest normal value above zero | 2.2250738585072014e-308 |
| `Epsilon` | The gap between 1.0 and the next float up | 2.220446049250313e-16 |
There is no `float64::Min`: instead of one ambiguous name, the two meanings get a name each.
```mermaid
flowchart LR
low["Lowest
most negative"] --- neg["…"] --- mp["−MinPositive"] --- zero["0"] --- pos["MinPositive
closest to zero"] --- more["…"] --- max["Max
largest"]
```
## Epsilon, the smallest step
`Epsilon` is how finely the numbers near 1.0 are divided. Add it to 1.0 and you get the next float up; add anything smaller and the sum rounds straight back to 1.0:
```rux
let one = 1.0;
PrintLine("1 + e {}", one + float64::Epsilon);
PrintLine("1 + e / 2 {}", one + float64::Epsilon / 2.0);
```
The first prints `1.0000000000000002`, the second `1.0` — half an epsilon was lost. That is why `0.1 + 0.2` is not exactly `0.3`, as you saw in [Float](https://rux-lang.dev/docs/learn/float).
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/NumberLimit){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Every number type carries facts about itself as associated constants, written with `::` after
// the type's name: `int16::Max`, `uint8::Bits`, `float64::Epsilon`. They are declared in `Core`,
// so a lesson that uses them imports each type it asks about and lists `Core` as a dependency.
//
// Because they are constants, they work anywhere a constant does, including inside another
// `const`. And because they belong to a concrete type, a generic function cannot reach them
// through its type parameter: `T::Max` is rejected with "'Max' not found in extend for type 'T'".
// For that case `Core` offers `MaximumOf` and `MinimumOf`, which work the limit out from a sample
// value of the type.
//
// Floats have more limits than integers, because "smallest" means two different things: `Lowest`
// is the most negative finite value, and `MinPositive` the smallest normal value above zero.
// `Epsilon` is the gap between 1.0 and the next float up.
import Core::{ MaximumOf, MinimumOf, float32, float64, int16, uint16, uint8 };
import Io::PrintLine;
// A limit can define another constant.
const LargestPort: uint16 = uint16::Max;
// A generic function asks `Core` for the limits of its own type parameter.
func Span(sample: T) -> T {
return MaximumOf(sample) - MinimumOf(sample);
}
func Main() -> int {
// Integers: the range, and the storage behind it.
PrintLine("int16 {} to {}, {} bits in {} bytes", int16::Min, int16::Max, int16::Bits,
int16::Bytes);
PrintLine("port up to {}", LargestPort);
PrintLine("uint8 span {}", Span(0));
// Floats: the finite range, and how finely it is divided.
PrintLine("float32 {} to {}", float32::Lowest, float32::Max);
PrintLine("float64 {} to {}", float64::Lowest, float64::Max);
PrintLine("smallest {}", float64::MinPositive);
PrintLine("epsilon {}", float64::Epsilon);
// Epsilon is the smallest step 1.0 can take. Anything smaller added to 1.0 is lost.
let one = 1.0;
PrintLine("1 + e {}", one + float64::Epsilon);
PrintLine("1 + e / 2 {}", one + float64::Epsilon / 2.0);
// A limit makes a bounds check read like what it means.
let reading: int32 = 40000;
PrintLine("fits int16 {}", reading <= int16::Max && reading >= int16::Min);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/NumberLimit
rux run
```
```text
int16 -32768 to 32767, 16 bits in 2 bytes
port up to 65535
uint8 span 255
float32 -3.4028235e+38 to 3.4028235e+38
float64 -1.7976931348623157e+308 to 1.7976931348623157e+308
smallest 2.2250738585072014e-308
epsilon 2.220446049250313e-16
1 + e 1.0000000000000002
1 + e / 2 1.0
fits int16 false
```
## Common mistakes
::warning
**Asking a float for `Min`.**:br`float64::Min` fails with `error: 'Min' not found in extend for type 'float64'`. Use `Lowest` for the most negative value, or `MinPositive` for the one closest to zero.
::
::warning
**Asking a type parameter for a limit.**:br
Inside `func Top(sample: T)`, `T::Max` fails with `error: 'Max' not found in extend for type 'T'`. Use `MaximumOf(sample)` and `MinimumOf(sample)`.
::
::warning
**Forgetting to import the type.**:br
The constants live in `Core`. Without `int16` in the import list, `int16::Max` fails with `error: 'Max' not found in extend for type 'int16'`.
::
::warning
**Comparing with a limit of the other signedness.**:br`reading <= uint8::Max` with an `int32` called `reading` fails with `error: operator '<=' cannot compare left operand 'int32' with right operand 'uint8'`. Convert the limit, which always fits: `reading <= uint8::Max as int32`.
::
## Try it yourself
1. Print `Min`, `Max`, `Bits` and `Bytes` for `int8`, `uint32` and `int64`.
2. Call `Span(0)`. Predict the answer first — then explain the one you get.
3. Print `float32::Epsilon` next to `float64::Epsilon`. How many more significant digits does a `float64` keep?
4. Change the bounds check so it asks whether `reading` fits in a `uint16`.
## Learn more
- The [primitive types](https://rux-lang.dev/docs/lang/appendix/primitives) and [int16](https://rux-lang.dev/docs/lang/types/integers) in the Rux Reference
- [Integer](https://rux-lang.dev/docs/learn/integer) and [Float](https://rux-lang.dev/docs/learn/float) — the types these limits describe
- [Float special](https://rux-lang.dev/docs/learn/float-special) — what lies beyond `Max`: infinity and NaN
- [Generic](https://rux-lang.dev/docs/learn/generic) — functions with type parameters
# Float special
::note
**You'll need**: [Float](https://rux-lang.dev/docs/learn/float), [Comparison](https://rux-lang.dev/docs/learn/comparison), [Number limit](https://rux-lang.dev/docs/learn/number-limit), [Format number](https://rux-lang.dev/docs/learn/format-number)
::
An integer has nowhere to go past its limits: divide one by zero and the program stops. A float is different. It has a few values that are not ordinary numbers at all, and arithmetic that runs off the edge lands on one of them and carries on:
- **Infinity**, `Inf`, when a result is too big to represent — or a non-zero number is divided by zero.
- **NaN**, "not a number", when a result has no sensible value at all, such as zero divided by zero.
Neither is an error. The special value simply flows into everything computed from it. This lesson shows where they come from, and the one rule about them that catches everybody: how NaN compares.
## Where the special values come from
```rux
let zero = 0.0;
PrintLine("1 / 0 {}", 1.0 / zero);
PrintLine("-1 / 0 {}", -1.0 / zero);
PrintLine("0 / 0 {}", zero / zero);
PrintLine("Max * 2 {}", float64::Max * 2.0);
PrintLine("Inf - Inf {}", float64::Infinity - float64::Infinity);
```
| Expression | Result | Why |
| -------------------- | ------ | -------------------------------------------- |
| `1.0 / 0.0` | `Inf` | The quotient grows without bound |
| `-1.0 / 0.0` | `-Inf` | The same, below zero |
| `0.0 / 0.0` | `NaN` | No single answer makes sense |
| `float64::Max * 2.0` | `Inf` | Too large to represent: it overflows upwards |
| `Inf - Inf` | `NaN` | Again, no single answer |
Both values are also available by name, `float64::Infinity` and `float64::NaN`, from `Core`. Formatting with a precision leaves them alone — there are no digits to round — so `{:.3}` still prints `Inf` and `NaN`.
Compare an integer: `10 / n` with a zero `n` stops the program with `Panic: division by zero` and the line it happened on. Integers have no special values to fall back on.
## Infinity compares like a number
Infinity is larger than every finite number, and equal to itself:
```rux
let inf = float64::Infinity;
PrintLine("Inf > Max {}", inf > float64::Max);
PrintLine("Inf == Inf {}", inf == inf);
```
Both lines print `true`. No surprises there.
## NaN compares false with everything
NaN is **unordered**. Every comparison with it is `false` — even with itself — except `!=`, which is `true`:
```rux
let nan = float64::NaN;
PrintLine("NaN == NaN {}", nan == nan);
PrintLine("NaN != NaN {}", nan != nan);
PrintLine("NaN < 1 {}", nan < 1.0);
PrintLine("NaN > 1 {}", nan > 1.0);
```
NaN is neither less than 1 nor greater than it, nor equal to anything. The rule makes NaN spread instead of hiding: a test such as `if total > limit` cannot be fooled into `true` by a NaN `total`. But it also means a test written as `x == float64::NaN` can **never** succeed.
## Asking what a value is
The reliable way to ask is a function from `Core`:
```rux
PrintLine("IsNaN {}", IsNaN(nan));
PrintLine("IsInfinite {}", IsInfinite(inf));
PrintLine("IsFinite {}", IsFinite(float64::Max));
```
```mermaid
flowchart LR
x["A float64 value"] --> n{"IsNaN?"}
n -- "yes" --> isnan["NaN"]
n -- "no" --> i{"IsInfinite?"}
i -- "yes" --> isinf["Inf or -Inf"]
i -- "no" --> fin["An ordinary number:
IsFinite is true"]
```
`IsFinite` is true exactly when the value is neither NaN nor an infinity — the right check before you use a result as an ordinary number.
## Two zeros
Zero has a sign too. `-0.0` and `0.0` compare equal, yet dividing by them gives infinities of opposite signs:
```rux
let negativeZero = -0.0;
PrintLine("-0 == 0 {}", negativeZero == zero);
PrintLine("1 / -0 {}", 1.0 / negativeZero);
```
The first line prints `true`, the second `-Inf`. A negative zero turns up when a tiny negative result rounds to zero; it remembers which side it came from.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/FloatSpecial){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A float does not stop at its largest number. When a result is too big to represent, it becomes
// infinity, `Inf`; when a result has no sensible value at all, such as zero divided by zero, it
// becomes NaN, "not a number". Neither is an error: the arithmetic carries on and the special
// value flows into everything computed from it. Integers have neither, which is why an integer
// division by zero stops the program with "Panic: division by zero" and the line it happened on,
// while a float division by zero carries on.
//
// The surprise is in comparison. Infinity compares as you would expect, larger than every finite
// number. NaN compares false with everything, including itself, so `nan == nan` is `false` and
// `nan != nan` is `true`. A test written as `x == float64::NaN` can therefore never succeed; ask
// `Core::IsNaN` instead. Zero has a sign too: `-0.0 == 0.0`, yet dividing by them gives
// infinities of opposite signs.
import Core::{ IsFinite, IsInfinite, IsNaN, float64 };
import Io::PrintLine;
func Main() -> int {
let zero = 0.0;
// Where the special values come from.
PrintLine("1 / 0 {}", 1.0 / zero);
PrintLine("-1 / 0 {}", -1.0 / zero);
PrintLine("0 / 0 {}", zero / zero);
PrintLine("Max * 2 {}", float64::Max * 2.0);
PrintLine("Inf - Inf {}", float64::Infinity - float64::Infinity);
// A precision has no digits to round here, so the words come out unchanged.
PrintLine("rounded {:.3} {:.3}", 1.0 / zero, zero / zero);
// Infinity is ordered like a number.
let inf = float64::Infinity;
PrintLine("Inf > Max {}", inf > float64::Max);
PrintLine("Inf == Inf {}", inf == inf);
// NaN is unordered: every comparison with it is false, except `!=`.
let nan = float64::NaN;
PrintLine("NaN == NaN {}", nan == nan);
PrintLine("NaN != NaN {}", nan != nan);
PrintLine("NaN < 1 {}", nan < 1.0);
PrintLine("NaN > 1 {}", nan > 1.0);
// The reliable way to ask what a value is.
PrintLine("IsNaN {}", IsNaN(nan));
PrintLine("IsInfinite {}", IsInfinite(inf));
PrintLine("IsFinite {}", IsFinite(float64::Max));
// Two zeros that are equal and still different.
let negativeZero = -0.0;
PrintLine("-0 == 0 {}", negativeZero == zero);
PrintLine("1 / -0 {}", 1.0 / negativeZero);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/FloatSpecial
rux run
```
```text
1 / 0 Inf
-1 / 0 -Inf
0 / 0 NaN
Max * 2 Inf
Inf - Inf NaN
rounded Inf NaN
Inf > Max true
Inf == Inf true
NaN == NaN false
NaN != NaN true
NaN < 1 false
NaN > 1 false
IsNaN true
IsInfinite true
IsFinite true
-0 == 0 true
1 / -0 -Inf
```
## Common mistakes
::warning
**Testing for NaN with `==`.**:br`x == float64::NaN` is always `false`, even when `x` is NaN — the compiler accepts it, and it simply never matches. Use `IsNaN(x)`.
::
::warning
**Expecting an error from a float division by zero.**:br`1.0 / 0.0` is `Inf`, and the program carries on with it. Only an integer division by zero stops the program. Check a divisor that might be zero, or check the result with `IsFinite`.
::
::warning
**Converting a special value to an integer.**:br`as` never fails, so it has to produce something: `NaN as int32` is `0`, and `Inf as int32` saturates to `2147483647`. Neither says anything went wrong. [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) reports it.
::
## Try it yourself
1. Compute `float32::Max * 2.0f32`. Is a `float32` infinity printed any differently?
2. Start from `let nan = zero / zero;` and work out `nan + 1.0`, `nan * 0.0` and `float64::Infinity * 0.0`. Which are NaN?
3. Write `func SafeDivide(a: float64, b: float64) -> float64?` that returns `none` when the result is not finite.
4. Import `IsNegativeZero` from `Core` and use it to tell `-0.0` from `0.0`, which `==` cannot.
## Learn more
- [float64](https://rux-lang.dev/docs/lang/types/floating-point) in the Rux Reference
- [Float](https://rux-lang.dev/docs/learn/float) — the float types and their precision
- [Number limit](https://rux-lang.dev/docs/learn/number-limit) — `Max`, `Lowest` and `Epsilon`
- [Math](https://rux-lang.dev/docs/learn/math) — functions whose answers outside their domain are NaN and infinities
# Bitwise
::note
**You'll need**: [Integer](https://rux-lang.dev/docs/learn/integer), [Literal](https://rux-lang.dev/docs/learn/literal), [Const](https://rux-lang.dev/docs/learn/const), [Assignment](https://rux-lang.dev/docs/learn/assignment)
::
Underneath, every integer is a row of bits. Arithmetic treats that row as one number; the **bitwise operators** treat it as a row of separate yes-or-no switches and work on each position on its own. That makes them the tool for packing several small facts into one value — eight permissions in a single byte, a set of options in one argument — and for reading them back out.
## Four operators, one bit at a time
| Operator | Name | A result bit is 1 when… |
| -------- | ---- | ------------------------------------- |
| `a & b` | AND | both bits are 1 |
| `a | b` | OR | either bit is 1 |
| `a ^ b` | XOR | exactly one of the two bits is 1 |
| `~a` | NOT | the bit in `a` is 0 — every bit flips |
The program prints them side by side. `{:08b}` formats a number in binary, padded with zeros to eight digits, so the columns line up:
```rux
let a: uint8 = 0b1100_1010;
let b: uint8 = 0b1010_0110;
PrintLine("a {:08b}", a);
PrintLine("b {:08b}", b);
PrintLine("a & b {:08b}", a & b);
PrintLine("a | b {:08b}", a | b);
PrintLine("a ^ b {:08b}", a ^ b);
PrintLine("~a {:08b}", ~a);
```
```text
a 11001010
b 10100110
a & b 10000010
a | b 11101110
a ^ b 01101100
~a 00110101
```
Read any column top to bottom and the rule of the table holds. `~` depends on the width of its operand: `~` of a `uint8` flips eight bits, `~` of a `uint32` flips thirty-two. Unsigned types are the usual choice for bit work, because no bit doubles as a sign.
## Flags and masks
A **mask** is a value whose set bits pick out the positions you care about. Give each flag its own bit, and one byte holds eight of them. Written in binary, the constants show which bit each one owns:
```rux
const Read: uint8 = 0b001;
const Write: uint8 = 0b010;
const Execute: uint8 = 0b100;
```
Four idioms cover almost everything done with flags:
| Goal | Idiom | Why it works |
| ------------ | ---------------------- | -------------------------------------------------- |
| Set a flag | `flags |= Write` | OR turns that bit on and leaves the rest alone |
| Clear a flag | `flags &= ~Read` | `~Read` has every bit but one set; AND keeps those |
| Flip a flag | `flags ^= Execute` | XOR with 1 flips a bit, XOR with 0 keeps it |
| Test a flag | `(flags & Write) != 0` | AND keeps only that bit; non-zero means it was set |
In the program they run one after another on `flags`:
```rux
var flags: uint8 = Read;
flags |= Write;
flags ^= Execute;
flags &= ~Read;
```
The byte goes `001` → `011` → `111` → `110`: it started with Read, gained Write, gained Execute by a flip, and lost Read.
```mermaid
flowchart LR
s["001
Read"] -- "set Write" --> w["011"]
w -- "flip Execute" --> x["111"]
x -- "clear Read" --> r["110
Write, Execute"]
```
## Testing flags
Testing asks whether a bit survives the mask:
```rux
PrintLine("can write? {}", (flags & Write) != 0);
PrintLine("can read? {}", (flags & Read) != 0);
```
A mask with several bits tests them together. `!= 0` would mean "any of them"; comparing with the mask itself means "all of them":
```rux
let both = Write | Execute;
PrintLine("write and run? {}", (flags & both) == both);
```
## Watch the precedence
`&`, `|` and `^` bind more loosely than `==` and `!=`. So `flags & Write == 0` reads as `flags & (Write == 0)` — a byte AND a boolean — and the compiler rejects it. Parenthesise the mask every time: `(flags & Write) == 0`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/Bitwise){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The bitwise operators treat an integer as a row of bits and work on each position separately.
// `a & b` keeps a bit only where both have it, `a | b` where either has it, and `a ^ b` where
// exactly one has it. `~a` flips every bit, so its result depends on the width: `~` of a `uint8`
// flips eight bits, of a `uint32` thirty-two.
//
// Their everyday use is the mask: a value whose set bits pick out the positions you care about.
// One byte can then hold eight yes-or-no flags, and four idioms cover almost everything done with
// them: `|` sets a flag, `& ~` clears it, `^` flips it, and `&` tests it.
//
// Watch the precedence. `&`, `|` and `^` bind more loosely than `==`, so `flags & Write == 0` reads
// as `flags & (Write == 0)` and is rejected for mixing a `uint8` with a `bool`. Parenthesize the
// mask: `(flags & Write) == 0`.
import Io::PrintLine;
// One bit per permission. Written in binary, the masks show which bit each one owns.
const Read: uint8 = 0b001;
const Write: uint8 = 0b010;
const Execute: uint8 = 0b100;
func Main() -> int {
// The four operators, side by side.
let a: uint8 = 0b1100_1010;
let b: uint8 = 0b1010_0110;
PrintLine("a {:08b}", a);
PrintLine("b {:08b}", b);
PrintLine("a & b {:08b}", a & b);
PrintLine("a | b {:08b}", a | b);
PrintLine("a ^ b {:08b}", a ^ b);
PrintLine("~a {:08b}", ~a);
PrintLine("");
// Masks at work on a set of flags.
var flags: uint8 = Read;
PrintLine("start {:03b}", flags);
flags |= Write;
PrintLine("set Write {:03b}", flags);
flags ^= Execute;
PrintLine("flip Execute {:03b}", flags);
flags &= ~Read;
PrintLine("clear Read {:03b}", flags);
PrintLine("can write? {}", (flags & Write) != 0);
PrintLine("can read? {}", (flags & Read) != 0);
// A mask with several bits tests them together.
let both = Write | Execute;
PrintLine("write and run? {}", (flags & both) == both);
return 0;
}
```
## Run it
```sh
cd Examples/Numbers/Bitwise
rux run
```
```text
a 11001010
b 10100110
a & b 10000010
a | b 11101110
a ^ b 01101100
~a 00110101
start 001
set Write 011
flip Execute 111
clear Read 110
can write? true
can read? false
write and run? true
```
## Common mistakes
::warning
**Leaving out the parentheses.**:br`flags & Write == 0` groups as `flags & (Write == 0)` and fails with `error: operator '&' cannot combine left operand 'uint8' with right operand 'bool8'`. Write `(flags & Write) == 0`.
::
::warning
**Using `!` to invert a mask.**:br`!` is logical NOT, for booleans only: `flags & !Write` fails with `error: operator '!' requires a bool operand, but found 'uint8'`. The bitwise NOT is `~`: `flags & ~Write`.
::
::warning
**Using a masked value as a condition.**:br
Rux does not treat a non-zero number as true. `if flags & Write { … }` fails with `error: condition for 'if' must have type 'bool', but found 'uint8'`. Compare it: `if (flags & Write) != 0 { … }`.
::
## Try it yourself
1. Add a fourth flag, `Delete: uint8 = 0b1000`, set it, and print `flags` with `{:04b}`.
2. Write `func Has(flags: uint8, mask: uint8) -> bool` that returns whether every bit of `mask` is set in `flags`.
3. XOR a value with the same mask twice. What do you get back, and why?
4. Change `a` and `b` to `uint32` and print `~a` with `{:032b}`. How many bits flipped this time?
## Learn more
- [Bitwise operations](https://rux-lang.dev/docs/lang/expressions/bitwise) in the Rux Reference
- [Shift](https://rux-lang.dev/docs/learn/shift) — moving bits along, and building masks with `1 << n`
- [Bit operation](https://rux-lang.dev/docs/learn/bit-operation) — counting and rotating bits
- [Format number](https://rux-lang.dev/docs/learn/format-number) — `{:b}`, `{:x}` and padding
# Shift
::note
**You'll need**: [Bitwise](https://rux-lang.dev/docs/learn/bitwise), [Convert](https://rux-lang.dev/docs/learn/convert)
::
A **shift** slides every bit of an integer along by a number of places. Bits that move past the end are dropped, and new bits come in at the other end. Because each place is a power of two, shifting is also a fast way to multiply and divide by 2, 4, 8 and so on — but its everyday use is building masks and packing several small fields into one number.
## Left shifts multiply
`x << n` moves the bits towards the high end and brings zeros in at the bottom. As long as nothing falls off the top, that multiplies by 2ⁿ:
```rux
let one: uint32 = 1;
PrintLine("1 << 4 {}", one << 4);
PrintLine("5 << 3 {}", 5 << 3);
```
`1 << 4` is 16, and `5 << 3` is 5 × 8 = 40. `1 << n` is also how a mask for bit `n` is made — the single-bit masks of [Bitwise](https://rux-lang.dev/docs/learn/bitwise) could be written `1 << 0`, `1 << 1` and `1 << 2`.
The result has the type of the **left** operand, and bits pushed past its width are gone. A `uint8` holding 200 shifted left by one is not 400 but `144` — the top bit fell off.
## Right shifts divide
Shifting the other way divides by 2ⁿ and drops the remainder:
```rux
let value: int32 = 100;
PrintLine("100 >> 2 {}", value >> 2);
```
100 / 4 = 25. For a negative number, `>>` rounds **down**, towards minus infinity, where `/` rounds towards zero: `-7 >> 1` is `-4`, while `-7 / 2` is `-3`.
## Two right shifts
Shifting right empties the top bits, and something has to fill them. For a signed number there are two sensible answers, so Rux has two operators:
| Operator | Name | Fills the top with | On a negative number |
| -------- | ---------- | ---------------------- | -------------------- |
| `>>` | arithmetic | copies of the sign bit | stays negative |
| `>>>` | logical | zeros, always | becomes positive |
The program shows both on −16. `{:b}` would print a signed value with a minus sign, so each one is shown through `as uint32` to see the bits themselves:
```rux
let negative: int32 = -16;
let arithmetic = negative >> 2;
let logical = negative >>> 2;
PrintLine("-16 {:032b}", negative as uint32);
PrintLine("-16 >> 2 {:032b} {}", arithmetic as uint32, arithmetic);
PrintLine("-16 >>> 2 {:032b} {}", logical as uint32, logical);
```
```text
-16 11111111111111111111111111110000
-16 >> 2 11111111111111111111111111111100 -4
-16 >>> 2 00111111111111111111111111111100 1073741820
```
`>>` kept the sign and divided: −16 / 4 = −4. `>>>` treated the value as plain bits, and the two zeros it brought in made a large positive number.
An unsigned type has no sign bit to copy, so its `>>` already brings in zeros. That is why `>>>` exists only for signed types: on a `uint8` or `uint32`, use `>>`.
## Packing fields into one integer
Shifts and masks together store several small fields in one number. A colour is the classic case: one byte each of red, green and blue. Each channel is shifted into its own byte, then the three are combined with `|`:
```rux
let red: uint32 = 0xFF;
let green: uint32 = 0x80;
let blue: uint32 = 0x20;
let color = (red << 16) | (green << 8) | blue;
```
```mermaid
flowchart LR
r["red 0xFF"] -- "shifted left 16" --> c["0x FF 80 20"]
g["green 0x80"] -- "shifted left 8" --> c
b["blue 0x20"] -- "as is" --> c
```
Unpacking runs the other way: shift the field down to the bottom, then mask away everything above it with `& 0xFF`:
```rux
PrintLine("red {:#x}", (color >> 16) & 0xFF);
PrintLine("green {:#x}", (color >> 8) & 0xFF);
PrintLine("blue {:#x}", color & 0xFF);
```
`{:#x}` prints hexadecimal with a `0x` in front, so the packed value shows as `0xff8020`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/Shift){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// A shift slides every bit of an integer along by a number of places. `x << n` moves them towards
// the high end and brings zeros in at the bottom, which multiplies by 2 to the n as long as nothing
// falls off the top. Shifting the other way divides, and Rux has two right shifts because a signed
// number gives the question of what to bring in at the top two answers:
//
// >> arithmetic: copies the sign bit, so a negative number stays negative
// >>> logical: always brings in zeros, treating the value as plain bits
//
// On an unsigned type there is no sign to copy, so `>>` already brings in zeros and `>>>` is
// refused: it needs a signed left operand. On a signed type, `-16 >> 2` is -4, while `-16 >>> 2`
// is a large positive number.
//
// Shifts and masks together pack several small fields into one integer, the way a color is stored
// as one byte each of red, green and blue.
import Io::PrintLine;
func Main() -> int {
// Left shifts multiply by powers of two.
let one: uint32 = 1;
PrintLine("1 << 4 {}", one << 4);
PrintLine("5 << 3 {}", 5 << 3);
// Right shifts divide, rounding down.
let value: int32 = 100;
PrintLine("100 >> 2 {}", value >> 2);
// The two right shifts part ways on a negative number. `{:b}` prints a signed value with a
// minus sign, so each one is shown through `as uint32` to see the bits themselves.
let negative: int32 = -16;
let arithmetic = negative >> 2;
let logical = negative >>> 2;
PrintLine("-16 {:032b}", negative as uint32);
PrintLine("-16 >> 2 {:032b} {}", arithmetic as uint32, arithmetic);
PrintLine("-16 >>> 2 {:032b} {}", logical as uint32, logical);
PrintLine("");
// Packing: each channel shifted into its own byte, then combined with `|`.
let red: uint32 = 0xFF;
let green: uint32 = 0x80;
let blue: uint32 = 0x20;
let color = (red << 16) | (green << 8) | blue;
PrintLine("packed {:#08x}", color);
// Unpacking: shift the field down to the bottom, then mask away everything above it.
PrintLine("red {:#x}", (color >> 16) & 0xFF);
PrintLine("green {:#x}", (color >> 8) & 0xFF);
PrintLine("blue {:#x}", color & 0xFF);
return 0;
}
```
## Run it
```sh
cd Examples/Numbers/Shift
rux run
```
```text
1 << 4 16
5 << 3 40
100 >> 2 25
-16 11111111111111111111111111110000
-16 >> 2 11111111111111111111111111111100 -4
-16 >>> 2 00111111111111111111111111111100 1073741820
packed 0xff8020
red 0xff
green 0x80
blue 0x20
```
## Common mistakes
::warning
**Using `>>>` on an unsigned value.**:br`b >>> 1` with a `uint8` called `b` fails with `error: operator '>>>' requires a signed integer left operand, but found 'uint8'`. Unsigned `>>` already fills with zeros — use it.
::
::warning
**Expecting `>>` to divide exactly like `/`.**:br
On a negative odd number they differ: `-7 >> 1` is `-4`, `-7 / 2` is `-3`. Use `/` when you mean division, and `>>` when you mean bits.
::
::warning
**Shifting bits off the top.**:br
A left shift multiplies only while the result fits: a `uint8` 200 `<< 1` is `144`, with no warning. Shift a wider type when the result needs the room, and keep every shift count below the width of the left operand.
::
## Try it yourself
1. Build a mask for bit 5 with `1 << 5`, and use it to set, test and clear that bit in a `uint8`.
2. Pack an hour (0–23), a minute and a second into one `uint32`, eight bits each, then unpack them.
3. Add an alpha channel to the colour: four bytes, alpha highest. Print it with `{:#010x}`.
4. Print `-1 >> 1` and `-1 >>> 1` for an `int32`. Explain both answers from the bits.
## Learn more
- [Shift operations](https://rux-lang.dev/docs/lang/expressions/shift) in the Rux Reference
- [Bitwise](https://rux-lang.dev/docs/learn/bitwise) — the masks shifts are combined with
- [Bit operation](https://rux-lang.dev/docs/learn/bit-operation) — rotations, where no bit falls off
- [Endian](https://rux-lang.dev/docs/learn/endian) — the order of a number's bytes in memory
# Checked arithmetic
::note
**You'll need**: [Out parameter](https://rux-lang.dev/docs/learn/out-parameter), [Optional](https://rux-lang.dev/docs/learn/optional), [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Number limit](https://rux-lang.dev/docs/learn/number-limit)
::
Ordinary `+`, `-` and `*` never complain. A result too big for its type wraps around, and the program carries on with a wrong number: `uint8` 255 + 1 is 0. Often that is harmless — the values are known to be small. But in a size, a price, a count or an index, a silently wrong number is a bug waiting to happen.
When it would matter, ask `Core` to **check** instead. `AddChecked`, `SubChecked` and `MulChecked` do the same arithmetic and also tell you whether it overflowed. They work at every integer width, signed or unsigned.
## Two answers from one call
A checked operation has two things to hand back: the result, and whether it is trustworthy. So it uses an [out-parameter](https://rux-lang.dev/docs/learn/out-parameter). The result is written through a pointer, and the return value is a `bool`:
```rux
var sum: uint8 = 0;
let first = AddChecked(uint8::Max - 5, 5, @sum);
```
`@sum` is a pointer to `sum`, and the call writes 255 into it. The returned `bool` answers the question **"did it overflow?"** — so here it is `false`.
That reads backwards the first time, since `true` means failure. Name the flag after the problem rather than the success — `overflowed`, `short`, `deep` — and the `if` that follows reads correctly.
```mermaid
flowchart LR
call["AddChecked(a, b, @result)"] --> w["writes a + b
through @result"]
call --> q{"returns:
did it overflow?"}
q -- "false" --> ok["result is the true answer"]
q -- "true" --> bad["result is the wrapped value:
do not use it"]
```
## When it overflows
One step too far, and the flag is raised:
```rux
let second = AddChecked(uint8::Max, 1, @sum);
```
`second` is `true`. The pointer is written either way, with the value `+` would have wrapped to — here 0 — so check the flag before you use the result.
An unsigned type has nothing below zero, so subtraction overflows downwards:
```rux
var stock: uint32 = 0;
let short = SubChecked(3, 5, @stock);
```
`short` is `true` and `stock` holds the wrapped 4294967294. Signed types overflow at both ends — one below the minimum wraps to the maximum:
```rux
var level: int32 = 0;
let deep = SubChecked(int32::Min, 1, @level);
```
| Call | Overflow | Written to the pointer |
| ------------------------------ | -------- | ---------------------- |
| `AddChecked(250u8, 5, …)` | `false` | 255 |
| `AddChecked(255u8, 1, …)` | `true` | 0 |
| `SubChecked(3u32, 5, …)` | `true` | 4294967294 |
| `SubChecked(int32::Min, 1, …)` | `true` | 2147483647 |
All three arguments share one type, which the compiler works out from the call — from a typed operand, or from the pointer when both operands are plain literals, as in `SubChecked(3, 5, @stock)`. Every argument has to agree with it.
## Reporting overflow in a type
A `bool` and an out-parameter are awkward to pass around. The usual move is to hide them inside a function whose return type says what can go wrong — here an [optional](https://rux-lang.dev/docs/learn/optional):
```rux
func Area(width: uint32, height: uint32) -> uint32? {
var area: uint32 = 0;
let overflowed = MulChecked(width, height, @area);
if overflowed {
return none;
}
return area;
}
```
A caller can no longer use a wrapped area by accident. It gets `none`, and has to decide — with `??`, or with a `match`:
```rux
PrintLine("area {}", Area(1920, 1080) ?? 0);
match Area(100000, 100000) {
area? => PrintLine("area {}", area),
none => PrintLine("area too large")
}
```
100 000 × 100 000 is ten billion, more than a `uint32` holds, so the second call prints `too large`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/CheckedArithmetic){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Ordinary `+`, `-` and `*` never complain: a result too big for its type wraps around and the
// program carries on with a wrong number. When a wrong number would matter, as in a size, a price
// or a count, ask `Core` to check instead. `AddChecked`, `SubChecked` and `MulChecked` work at
// every integer width, signed or unsigned.
//
// Each one hands back two things, so it uses an out-parameter: the result is written through the
// pointer, and the return value is a `bool` that answers "did it overflow?". That reads backwards
// the first time, since `true` means failure, so name the flag after the problem rather than the
// success. The pointer is written either way, with the wrapped value when the answer is `true`.
//
// A function that does the checking can then report overflow in a type, here as an optional.
import Core::{ AddChecked, MulChecked, SubChecked, int32, uint8 };
import Io::PrintLine;
// The area of a rectangle, or `none` when it does not fit in 32 bits.
func Area(width: uint32, height: uint32) -> uint32? {
var area: uint32 = 0;
let overflowed = MulChecked(width, height, @area);
if overflowed {
return none;
}
return area;
}
func Main() -> int {
var sum: uint8 = 0;
// Within range: no overflow, and the result is the true sum.
let first = AddChecked(uint8::Max - 5, 5, @sum);
PrintLine("250 + 5 overflow {:5} result {}", first, sum);
// One step too far: the flag is raised, and the result is what `+` would have wrapped to.
let second = AddChecked(uint8::Max, 1, @sum);
PrintLine("255 + 1 overflow {:5} result {}", second, sum);
// An unsigned type has nothing below zero.
var stock: uint32 = 0;
let short = SubChecked(3, 5, @stock);
PrintLine("3 - 5 overflow {:5} result {}", short, stock);
// Signed types overflow at both ends.
var level: int32 = 0;
let deep = SubChecked(int32::Min, 1, @level);
PrintLine("Min - 1 overflow {:5} result {}", deep, level);
// The check, hidden behind a function that says what went wrong in its type.
PrintLine("area {}", Area(1920, 1080) ?? 0);
match Area(100000, 100000) {
area? => PrintLine("area {}", area),
none => PrintLine("area too large")
}
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/CheckedArithmetic
rux run
```
```text
250 + 5 overflow false result 255
255 + 1 overflow true result 0
3 - 5 overflow true result 4294967294
Min - 1 overflow true result 2147483647
area 2073600
area too large
```
## Common mistakes
::warning
**Reading the flag the wrong way round.**:br
The `bool` means "overflowed", so `true` is the bad case. `if AddChecked(a, b, @sum) { use(sum) }` uses exactly the results it should reject. Name the flag after the problem — `let overflowed = …` — and test that.
::
::warning
**Ignoring the flag.**:br`AddChecked(a, 5, @sum);` on its own compiles: the `bool` may be dropped. But then the result is no safer than `a + 5`. Keep the flag and act on it.
::
::warning
**Mixing types in one call.**:br
Both operands must have one type. With a `uint8` called `a` and an `int32` called `b`, `AddChecked(a, b, @sum)` fails with `error: argument 2 to 'AddChecked' has type 'int32', but parameter 'right' requires 'uint8'`. Convert one of them first — checking that it fits.
::
::warning
**A result pointer of another type.**:br`AddChecked(a, 5, @sum)` with a `uint8` called `a` and an `int32` called `sum` fails with `error: argument 1 to 'AddChecked' has type 'uint8', but parameter 'left' requires 'T'`. The message names the first argument, but the fix is the third: the result must have the operands' type.
::
## Try it yourself
1. Write `func Total(prices: uint32[..]) -> uint32?` that adds the prices with `AddChecked` and returns `none` at the first overflow.
2. Use `MulChecked` on two `int8` values, −128 and −1. Does it overflow? Why?
3. Change `Area` to return `uint32 ! AreaError`, with an error variant `TooLarge`, and handle it in `Main` with `catch`.
## Learn more
- [Arithmetic operations](https://rux-lang.dev/docs/lang/expressions/arithmetic) in the Rux Reference
- [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) — when overflow is the plan, not the problem
- [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) — the same idea for conversions between types
- [Out-parameter](https://rux-lang.dev/docs/learn/out-parameter) — returning a second answer through a pointer
# Wrapping arithmetic
::note
**You'll need**: [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic), [String literal](https://rux-lang.dev/docs/learn/string-literal)
::
[Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) treats overflow as a problem to report. Sometimes it is not a problem but the plan, and the only question is what the result should be. `Core` names the two useful answers, at every integer width:
- **Wrapping** — go round, like a car's odometer rolling from 999999 back to 000000.
- **Saturating** — stop at the type's limit and stay there, like a volume knob turned all the way up.
## Wrapping, on purpose
`AddWrapping`, `SubWrapping` and `MulWrapping` compute exactly what `+`, `-` and `*` already do:
```rux
let high: uint8 = 250;
let low: uint8 = 5;
let middle: uint8 = 200;
PrintLine("wrapping 250 + 10 = {}", AddWrapping(high, 10));
PrintLine("wrapping 5 - 10 = {}", SubWrapping(low, 10));
PrintLine("wrapping 200 * 2 = {}", MulWrapping(middle, 2));
```
A `uint8` counts 0 to 255 and then starts again, so 250 + 10 is 260 − 256 = `4`, and 5 − 10 goes below zero and comes round to `251`.
If the answer is the same as `+`, why have the function? For the **reader**. `AddWrapping` says "this wraps on purpose", so nobody mistakes the overflow for a bug — or "fixes" it. Hashes, checksums, random number generators and clock counters all want it.
The program ends with one: a tiny multiplicative hash, where overflow is what mixes the bits together:
```rux
let text = "Hello, World!";
var hash: uint32 = 17;
for i in 0..text.length {
hash = AddWrapping(MulWrapping(hash, 31), text[i] as uint32);
}
```
Before the text is half done, the product outgrows 32 bits and wraps — as it is meant to. The final value, `1494227876`, is a fingerprint of the text: change a letter and the fingerprint changes.
## Saturating, at the limit
`AddSaturating`, `SubSaturating` and `MulSaturating` stop at the nearest limit instead:
```rux
PrintLine("saturating 250 + 10 = {}", AddSaturating(high, 10));
PrintLine("saturating 5 - 10 = {}", SubSaturating(low, 10));
PrintLine("saturating 200 * 2 = {}", MulSaturating(middle, 2));
```
They give `255`, `0` and `255`. That suits a quantity with a natural ceiling and floor — a volume, a brightness, a health bar. Adding to the maximum keeps it at the maximum, and taking from zero leaves zero, instead of jumping to the other end of the range.
A signed type saturates at whichever end it ran past:
```rux
let cold: int8 = -100;
let warm: int8 = 100;
```
| Operation on `int8` | Wrapping | Saturating |
| ------------------- | -------- | ---------- |
| −100 − 100 | 56 | −128 |
| 100 + 100 | −56 | 127 |
## Choosing a policy
Every integer operation that might overflow has four ways to go. Pick the one that says what you mean:
```mermaid
flowchart LR
q{"Can this result
overflow?"} -- "no, the values are small" --> plain["+ - *"]
q -- "yes, and that is a bug" --> checked["AddChecked …
report it"]
q -- "yes, it should go round" --> wrap["AddWrapping …"]
q -- "yes, it should stop
at the limit" --> sat["AddSaturating …"]
```
## Literals take the operand's type
The functions are generic: the type comes from the arguments. In `AddWrapping(high, 10)` the `10` takes the type of the `uint8` beside it, so the sum is worked out in eight bits. With two plain literals the call would be made at `int` — and `AddWrapping(250, 10)` is a plain `260`.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/WrappingArithmetic){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// Sometimes overflow is not a mistake but the plan, and the only question is what it should
// produce. `Core` names the two useful answers, at every integer width:
//
// AddWrapping, SubWrapping, MulWrapping wrap around, like a car's odometer
// AddSaturating, SubSaturating, MulSaturating stop at the type's limit and stay there
//
// Wrapping computes exactly what `+`, `-` and `*` already do. The point of the name is the reader:
// `AddWrapping` says "this wraps on purpose", so nobody mistakes it for a bug or "fixes" it.
// Hashes, checksums and clock arithmetic all want it.
//
// Saturating suits quantities with a natural ceiling and floor, such as a volume control, a
// brightness or a health bar: adding to the maximum keeps it at the maximum, and taking from zero
// leaves zero, instead of jumping to the other end of the range.
import Core::{ AddSaturating, AddWrapping, MulSaturating, MulWrapping, SubSaturating, SubWrapping };
import Io::PrintLine;
func Main() -> int {
// The same three uint8 sums, answered both ways. Unsuffixed literals take the type of the
// `uint8` beside them; with two plain literals the call would be made at `int`.
let high: uint8 = 250;
let low: uint8 = 5;
let middle: uint8 = 200;
PrintLine("wrapping 250 + 10 = {}", AddWrapping(high, 10));
PrintLine("wrapping 5 - 10 = {}", SubWrapping(low, 10));
PrintLine("wrapping 200 * 2 = {}", MulWrapping(middle, 2));
PrintLine("saturating 250 + 10 = {}", AddSaturating(high, 10));
PrintLine("saturating 5 - 10 = {}", SubSaturating(low, 10));
PrintLine("saturating 200 * 2 = {}", MulSaturating(middle, 2));
PrintLine("");
// A signed type saturates at whichever end it ran past.
let cold: int8 = -100;
let warm: int8 = 100;
PrintLine("int8 -100 - 100 wraps to {}, saturates at {}", SubWrapping(cold, 100),
SubSaturating(cold, 100));
PrintLine("int8 100 + 100 wraps to {}, saturates at {}", AddWrapping(warm, 100),
AddSaturating(warm, 100));
PrintLine("");
// Wrapping on purpose: a tiny multiplicative hash, where overflow mixes the bits.
let text = "Hello, World!";
var hash: uint32 = 17;
for i in 0..text.length {
hash = AddWrapping(MulWrapping(hash, 31), text[i] as uint32);
}
PrintLine("hash of {} {}", text, hash);
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/WrappingArithmetic
rux run
```
```text
wrapping 250 + 10 = 4
wrapping 5 - 10 = 251
wrapping 200 * 2 = 144
saturating 250 + 10 = 255
saturating 5 - 10 = 0
saturating 200 * 2 = 255
int8 -100 - 100 wraps to 56, saturates at -128
int8 100 + 100 wraps to -56, saturates at 127
hash of Hello, World! 1494227876
```
## Common mistakes
::warning
**Calling with two plain literals.**:br`AddWrapping(250, 10)` is made at `int`, where nothing wraps, and returns `260`. Give one operand the type you mean, as the program does with `high`, `low` and `middle`.
::
::warning
**Expecting your own limit.**:br
Saturation stops at the **type's** limits, not at the range your program has in mind. A `uint8` volume that should stop at 100 still goes up to 255 under `AddSaturating`. Use it for the floor at zero, and compare with your own maximum before adding.
::
::warning
**Wrapping where you meant modulo.**:br
Wrapping goes round at the width of the type — 256 for a `uint8`, not 24 or 60. For clock hours, use `%`: `(hours + 5) % 24`.
::
## Try it yourself
1. A health bar is a `uint8`. Take 30 damage from 20 health with `SubWrapping` and then with `SubSaturating`. Which result would a game want?
2. Change the hash to start at 0 instead of 17, and hash `"ab"` and `"ba"`. Do they still differ?
3. Find the first `n` for which `MulWrapping` and `MulSaturating` disagree on `n * n` in a `uint8`.
## Learn more
- [Arithmetic operations](https://rux-lang.dev/docs/lang/expressions/arithmetic) in the Rux Reference
- [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) — when overflow must be noticed
- [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) — `ConvertWrapping` and `ConvertSaturating` for conversions
- [Hash](https://rux-lang.dev/docs/learn/hash) — real hash functions from the standard packages
# Checked convert
::note
**You'll need**: [Convert](https://rux-lang.dev/docs/learn/convert), [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic), [Optional](https://rux-lang.dev/docs/learn/optional), [Presence](https://rux-lang.dev/docs/learn/presence)
::
`as` always produces a value of the new type, and says nothing when the old value did not fit: 300 as a `uint8` is quietly 44, and −1 is quietly 255. That is fine where the value is known to fit — and a hidden bug everywhere else. A count read from a file, a size passed in by a caller, a result of float arithmetic: none of these is known to fit.
`Core::ConvertChecked` converts the same way **and** tells you whether anything was lost.
## Converting and checking
It follows the out-parameter shape of [checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic). The converted value is written through a pointer — which also names the destination type — and the `bool` it returns is `true` when the value did not survive:
```rux
var small: uint8 = 0;
let fits = ConvertChecked(200, @small);
let tooLarge = ConvertChecked(300, @small);
let negative = ConvertChecked(-1, @small);
```
There is no type argument to write: the source type comes from the value, the destination type from the pointer. `@small` points at a `uint8`, so each call converts to `uint8`.
| Source | Destination | Lost | Written |
| ------ | ----------- | ------- | ------- |
| `200` | `uint8` | `false` | 200 |
| `300` | `uint8` | `true` | 44 |
| `-1` | `uint8` | `true` | 255 |
| `42.0` | `int32` | `false` | 42 |
| `42.7` | `int32` | `true` | 42 |
| `NaN` | `int32` | `true` | 0 |
As with checked arithmetic, the pointer is written either way: the value written is what `as` would have produced. Check the flag before you trust it.
## Every way a value can be lost
"Did not survive" covers every way a conversion can go wrong:
- **Too large** for the destination: 300 into a `uint8`.
- **Negative** into an unsigned type: −1 into a `uint8`.
- **A fraction cut off**: 42.7 into an `int32` becomes 42, and the 0.7 counts as lost. 42.0 has no fraction, so it converts cleanly.
- **NaN or an infinity**, which no integer can represent.
```rux
var whole: int32 = 0;
let exact = ConvertChecked(42.0, @whole);
let fraction = ConvertChecked(42.7, @whole);
let nan = ConvertChecked(float64::NaN, @whole);
```
## Two relatives that decide for you
Sometimes the right response to a value that does not fit is not to report it but to pick something. Two functions with the same shape do that, without returning a flag:
- `ConvertWrapping` is `as` with a name that says the loss is intended — the counterpart of [wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic).
- `ConvertSaturating` clamps to the nearest value the destination can hold.
```rux
ConvertSaturating(300, @small);
ConvertSaturating(-1, @small);
```
300 becomes `255`, the largest `uint8`, and −1 becomes `0`, the smallest.
```mermaid
flowchart LR
v["A value that might
not fit the destination"] --> q{"What should happen
if it does not?"}
q -- "it always fits" --> as["as"]
q -- "tell me" --> c["ConvertChecked"]
q -- "keep the low bits,
on purpose" --> w["ConvertWrapping"]
q -- "use the nearest
value that fits" --> s["ConvertSaturating"]
```
## Reporting it in a type
As with `Area` in checked arithmetic, the flag and the pointer are best kept inside a function whose return type tells the caller what can happen:
```rux
func ToByte(count: int32) -> uint8? {
var result: uint8 = 0;
let lost = ConvertChecked(count, @result);
if lost {
return none;
}
return result;
}
```
`ToByte(99)` is `99`; `ToByte(999)` is `none`, and the caller cannot use a wrong byte without first deciding what `none` means.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/CheckedConvert){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// `as` always produces a value of the new type, and says nothing when the old value did not fit:
// 300 as a `uint8` is quietly 44, and -1 is quietly 255. That is fine where the value is known to
// fit, and a hidden bug everywhere else. `Core::ConvertChecked` converts the same way and also
// tells you whether anything was lost.
//
// It follows the out-parameter shape of the checked arithmetic: the converted value is written
// through a pointer, which also names the destination type, and the `bool` it returns is `true`
// when the value did not survive. "Did not survive" covers every way a conversion can go wrong:
// too large, negative into an unsigned type, a fraction cut off a float, and a NaN or infinity.
//
// Two relatives decide what to do with a value that does not fit, without reporting it.
// `ConvertWrapping` is `as` with a name that says the loss is intended, and `ConvertSaturating`
// clamps to the nearest value the destination can hold.
import Core::{ ConvertChecked, ConvertSaturating, float64 };
import Io::PrintLine;
// Turns a count into a byte, or `none` when it does not fit in one.
func ToByte(count: int32) -> uint8? {
var result: uint8 = 0;
let lost = ConvertChecked(count, @result);
if lost {
return none;
}
return result;
}
func Main() -> int {
var small: uint8 = 0;
// Each source value, converted to `uint8` and checked.
let fits = ConvertChecked(200, @small);
PrintLine("200 -> uint8 lost {:5} value {}", fits, small);
let tooLarge = ConvertChecked(300, @small);
PrintLine("300 -> uint8 lost {:5} value {}", tooLarge, small);
let negative = ConvertChecked(-1, @small);
PrintLine("-1 -> uint8 lost {:5} value {}", negative, small);
// From a float, the fraction counts as part of the value.
var whole: int32 = 0;
let exact = ConvertChecked(42.0, @whole);
PrintLine("42.0 -> int32 lost {:5} value {}", exact, whole);
let fraction = ConvertChecked(42.7, @whole);
PrintLine("42.7 -> int32 lost {:5} value {}", fraction, whole);
let nan = ConvertChecked(float64::NaN, @whole);
PrintLine("NaN -> int32 lost {}", nan);
PrintLine("");
// Saturating keeps the nearest value instead.
ConvertSaturating(300, @small);
PrintLine("300 saturated {}", small);
ConvertSaturating(-1, @small);
PrintLine("-1 saturated {}", small);
PrintLine("");
// A checking function turns the report into a type.
PrintLine("ToByte(99) {}", ToByte(99) ?? 0);
match ToByte(999) {
value? => PrintLine("ToByte(999) {}", value),
none => PrintLine("ToByte(999) does not fit")
}
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/CheckedConvert
rux run
```
```text
200 -> uint8 lost false value 200
300 -> uint8 lost true value 44
-1 -> uint8 lost true value 255
42.0 -> int32 lost false value 42
42.7 -> int32 lost true value 42
NaN -> int32 lost true
300 saturated 255
-1 saturated 0
ToByte(99) 99
ToByte(999) does not fit
```
## Common mistakes
::warning
**Reading the flag the wrong way round.**:br`true` means the value was **lost**. Name it after the problem — `lost`, `tooLarge` — so that `if lost { … }` reads correctly.
::
::warning
**A destination you cannot write.**:br
The pointer must be writable. With `let small: uint8 = 0;`, `ConvertChecked(300, @small)` fails with `error: argument 1 to 'ConvertChecked' has type 'int', but parameter 'value' requires 'From'` — the message names the value, but the fix is to declare the destination with `var`.
::
::warning
**Expecting a float to round.**:br`ConvertChecked(42.7, @whole)` writes `42` and reports the value lost — it truncates, like `as`. If rounding is what you want, round first with `Round` from [Math](https://rux-lang.dev/docs/learn/math), then convert.
::
## Try it yourself
1. Convert `float64::Infinity` to an `int32` with `ConvertChecked`, and then with `ConvertSaturating`. What does each give?
2. Compare `ConvertWrapping(300, @small)` with `300 as uint8`.
3. Convert the integer 16777217 to a `float32` with `ConvertChecked`. Is anything lost? Look up why in [Float](https://rux-lang.dev/docs/learn/float).
4. Write `func ToPercent(ratio: float64) -> uint8?` that multiplies by 100 and returns `none` unless the result is a whole number from 0 to 100.
## Learn more
- [Type casts](https://rux-lang.dev/docs/lang/expressions/casts) in the Rux Reference
- [Convert](https://rux-lang.dev/docs/learn/convert) — what `as` does with a value that does not fit
- [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) — the same out-parameter shape for `+`, `-` and `*`
- [Float special](https://rux-lang.dev/docs/learn/float-special) — NaN and infinity
# Bit operation
::note
**You'll need**: [Bitwise](https://rux-lang.dev/docs/learn/bitwise), [Shift](https://rux-lang.dev/docs/learn/shift)
::
The bitwise operators **change** bits. A second family of functions, in `Core`, **answers questions** about them: how many are set, where the first and last set bit are, and what the value looks like turned around. They sound like curiosities and turn up everywhere — counting the flags set in a mask, finding how many bits a number needs, choosing the lowest free slot, mixing bits in a hash function.
## Counting bits
| Function | Answers |
| --------------- | --------------------------------------- |
| `CountOnes` | How many bits are 1 |
| `CountZeros` | How many bits are 0 |
| `LeadingZeros` | How many 0 bits sit above the highest 1 |
| `TrailingZeros` | How many 0 bits sit below the lowest 1 |
| `IsPowerOfTwo` | Whether exactly one bit is set |
The program asks all four counting questions about one byte:
```rux
let value: uint8 = 0b0010_1100;
PrintLine("CountOnes {}", CountOnes(value));
PrintLine("CountZeros {}", CountZeros(value));
PrintLine("LeadingZeros {}", LeadingZeros(value));
PrintLine("TrailingZeros {}", TrailingZeros(value));
```
```text
bit: 7 6 5 4 3 2 1 0
0 0 1 0 1 1 0 0
└┬┘ └┬┘
2 leading zeros 2 trailing zeros
```
Three ones, five zeros, two zeros above the highest one and two below the lowest. Counting from bit 0, `TrailingZeros` is also the **position** of the lowest 1 — here bit 2.
## The width matters
Each function works across the full width of its argument's type, so the same number gives different answers in different types:
```rux
let wide: uint32 = 44;
PrintLine("as uint32 LeadingZeros {}", LeadingZeros(wide));
```
44 is the same `101100` either way, but a `uint32` has 24 more bits above it, so `LeadingZeros` is `26`, not `2`. `CountOnes` would not change; the zeros-counting functions do.
That gives a neat way to ask how many bits a number really needs — its width minus the zeros above it:
```rux
PrintLine("bits needed {}", uint8::Bits - LeadingZeros(value) as uint);
```
44 needs `6` bits: 101100.
## Powers of two
A power of two has exactly one bit set — 4096 is `1` followed by twelve zeros. `IsPowerOfTwo` checks that:
```rux
let block: uint32 = 4096;
PrintLine("4096 is power {}", IsPowerOfTwo(block));
PrintLine("44 is power {}", IsPowerOfTwo(wide));
```
It matters because memory sizes, alignments and hash-table capacities are usually powers of two, and code that relies on one should check it.
## Rotating instead of shifting
A shift drops the bits it pushes off the end. A **rotation** brings them round to the other side, so nothing is lost:
```rux
let pattern: uint8 = 0b1001_0110;
PrintLine("<< 3 {:08b}", pattern << 3);
PrintLine("RotateLeft 3 {:08b}", RotateLeft(pattern, 3));
PrintLine("RotateRight 3 {:08b}", RotateRight(pattern, 3));
```
```mermaid
flowchart LR
p["10010110"] -- "shift left 3:
top 3 bits dropped" --> s["10110000"]
p -- "rotate left 3:
top 3 bits wrap round" --> r["10110100"]
```
The shift lost the top three bits, `100`, and brought in zeros. The rotation moved those same three bits to the bottom. Every bit survives a rotation, so `CountOnes` is `4` before and after — which is what makes rotations useful for mixing bits in hashes without throwing any away.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/BitOperation){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The bitwise operators change bits. A second family, in `Core`, answers questions about them:
// how many are set, where the first and last set bit are, and what the value looks like turned
// around. Each takes any integer type and works across its full width, so the same call gives a
// different answer on a `uint8` than on a `uint32` holding the same number.
//
// CountOnes, CountZeros how many bits are 1, or 0
// LeadingZeros, TrailingZeros how many 0 bits sit above the highest 1, or below the
// lowest 1
// RotateLeft, RotateRight a shift where the bits that fall off one end come back at
// the other, so nothing is lost
//
// These sound like curiosities and turn up everywhere: counting the flags set in a mask, finding
// how many bits a number needs, choosing the lowest free slot, and mixing bits in hash functions.
import Core::{ CountOnes, CountZeros, IsPowerOfTwo, LeadingZeros, RotateLeft, RotateRight,
TrailingZeros, uint8 };
import Io::PrintLine;
func Main() -> int {
let value: uint8 = 0b0010_1100;
PrintLine("value {:08b} ({})", value, value);
PrintLine("CountOnes {}", CountOnes(value));
PrintLine("CountZeros {}", CountZeros(value));
PrintLine("LeadingZeros {}", LeadingZeros(value));
// Counting from bit 0, TrailingZeros is also the position of the lowest 1.
PrintLine("TrailingZeros {}", TrailingZeros(value));
PrintLine("");
// The width matters: the same number in 32 bits has 24 more leading zeros.
let wide: uint32 = 44;
PrintLine("as uint32 LeadingZeros {}", LeadingZeros(wide));
// How many bits a number needs: its width minus the zeros above it.
PrintLine("bits needed {}", uint8::Bits - LeadingZeros(value) as uint);
// A power of two has exactly one bit set.
let block: uint32 = 4096;
PrintLine("4096 is power {}", IsPowerOfTwo(block));
PrintLine("44 is power {}", IsPowerOfTwo(wide));
PrintLine("");
// A shift drops the bits it pushes off the end; a rotation brings them round to the other side.
let pattern: uint8 = 0b1001_0110;
PrintLine("pattern {:08b}", pattern);
PrintLine("<< 3 {:08b}", pattern << 3);
PrintLine("RotateLeft 3 {:08b}", RotateLeft(pattern, 3));
PrintLine("RotateRight 3 {:08b}", RotateRight(pattern, 3));
PrintLine("ones survive {} and {}", CountOnes(pattern), CountOnes(RotateLeft(pattern, 3)));
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/BitOperation
rux run
```
```text
value 00101100 (44)
CountOnes 3
CountZeros 5
LeadingZeros 2
TrailingZeros 2
as uint32 LeadingZeros 26
bits needed 6
4096 is power true
44 is power false
pattern 10010110
<< 3 10110000
RotateLeft 3 10110100
RotateRight 3 11010010
ones survive 4 and 4
```
## Common mistakes
::warning
**Passing a plain literal.**:br
An unsuffixed literal is an `int`, 64 bits wide. `LeadingZeros(44)` is therefore `58`, not `2`. Give the value the type whose width you mean: `let value: uint8 = 44;`.
::
::warning
**Forgetting zero.**:br
Zero has no 1 bit, so it has no "lowest 1" to find. `TrailingZeros` and `LeadingZeros` of a `uint8` zero are both `8`, the full width, and `IsPowerOfTwo` of zero is `false`. Check for zero first when the answer is used as a position.
::
## Try it yourself
1. A `uint8` mask records which of eight seats are taken. Find the lowest free seat with `TrailingZeros(~taken)`. What happens when every seat is taken?
2. How many bits does 1000 need? Work it out with a `uint16` and `LeadingZeros`.
3. Rotate a `uint8` left by 8. Why is the result the value you started with?
4. Import `ReverseBits` from `Core` and print `0b0010_1100` reversed.
## Learn more
- [Bitwise operations](https://rux-lang.dev/docs/lang/expressions/bitwise) and [shift operations](https://rux-lang.dev/docs/lang/expressions/shift) in the Rux Reference
- [Bitwise](https://rux-lang.dev/docs/learn/bitwise) and [Shift](https://rux-lang.dev/docs/learn/shift) — the operators these functions complement
- [Number limit](https://rux-lang.dev/docs/learn/number-limit) — `Bits` and the other constants every type carries
- [Hash](https://rux-lang.dev/docs/learn/hash) — where rotations earn their keep
# Endian
::note
**You'll need**: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Out parameter](https://rux-lang.dev/docs/learn/out-parameter), [Slice](https://rux-lang.dev/docs/learn/slice), [Shift](https://rux-lang.dev/docs/learn/shift)
::
An integer wider than one byte is stored as several bytes, and something has to decide their order. There are two conventions:
- **Little endian** puts the least significant byte first.
- **Big endian** puts the most significant byte first — the order the number is written in.
Today's desktop processors are little endian, while network protocols and many file formats are big endian. So a program that reads or writes bytes — to a file, a socket, a packet — has to say which order it means, rather than take whatever the machine happens to do. Otherwise the same file means different numbers on different machines.
## One value, two orders
The program picks a value whose four bytes are all different, so the order is easy to see:
```rux
let value: uint32 = 0x12345678;
```
| Order | Byte 0 | Byte 1 | Byte 2 | Byte 3 |
| ------------- | ------ | ------ | ------ | ------ |
| Big endian | `12` | `34` | `56` | `78` |
| Little endian | `78` | `56` | `34` | `12` |
Big endian reads like the hexadecimal you wrote; little endian starts at the small end.
## Writing bytes in a chosen order
`Core` has a function for each direction and each order:
| Function | Does |
| ------------------- | ------------------------------------------------------ |
| `StoreBigEndian` | Write a value into bytes, most significant first |
| `StoreLittleEndian` | Write a value into bytes, least significant first |
| `LoadBigEndian` | Read a value back out of big-endian bytes |
| `LoadLittleEndian` | Read a value back out of little-endian bytes |
| `ReverseBytes` | Swap a value's bytes, turning one order into the other |
They work through [pointers](https://rux-lang.dev/docs/learn/pointer). A store writes from a pointer to the **first byte** onwards, as many bytes as the value's type has:
```rux
var little: uint8[4];
var big: uint8[4];
StoreLittleEndian(value, @little[0]);
StoreBigEndian(value, @big[0]);
```
`@little[0]` is a pointer to the first element. A `uint32` is four bytes, so each store fills the whole array.
```mermaid
flowchart LR
v["uint32 0x12345678"] -- "StoreBigEndian" --> b["12 34 56 78"]
v -- "StoreLittleEndian" --> l["78 56 34 12"]
```
`TargetIsLittleEndian()` tells you which order this machine uses itself — the program prints `true` on an ordinary desktop. You rarely need it: the store and load functions give the same bytes on every machine, which is the point.
## Reading bytes back
A load reads from a pointer to the first byte and hands the value back through an [out-parameter](https://rux-lang.dev/docs/learn/out-parameter). Here are two bytes from a network packet, where a port number is big endian:
```rux
let packet: uint8[2] = [0x01, 0xBB];
var port: uint16 = 0;
LoadBigEndian(@packet[0], @port);
```
`0x01BB` is `443`, the HTTPS port. The type of `port` decides how many bytes are read — two for a `uint16`.
## The order is part of the data
Bytes written big endian must be read big endian, on every machine. Read the same two bytes in the wrong order and you get a different number entirely:
```rux
PrintLine("wrong order {}", ReverseBytes(port));
```
`0xBB01` is `47873`. Nothing fails — the bytes are just misread. That is why a file format or protocol always says which order it uses, and why code that reads one should name that order explicitly.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/Endian){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// An integer wider than one byte is stored as several bytes, and something has to decide their
// order. Little endian puts the least significant byte first; big endian puts the most significant
// first, the order the number is written in. Today's desktop processors are little endian, while
// network protocols and many file formats are big endian, so a program that reads or writes bytes
// has to say which order it means rather than take whatever the machine does.
//
// `Core` provides both directions for any integer type:
//
// StoreBigEndian, StoreLittleEndian write a value into bytes, in the order named
// LoadBigEndian, LoadLittleEndian read a value back out of bytes
// ReverseBytes swap a value's bytes, turning one order into the other
//
// They work through pointers, as in the Memory part: a store writes from a pointer to the first
// byte onwards, and a load reads from one and hands the value back through an out-parameter.
// The order is part of the data: bytes written big endian must be read big endian, on every
// machine.
import Core::{ LoadBigEndian, ReverseBytes, StoreBigEndian, StoreLittleEndian,
TargetIsLittleEndian };
import Io::{ Print, PrintLine };
func ShowBytes(label: char8[..], bytes: uint8[..]) {
Print("{}", label);
for i in 0..bytes.length {
Print(" {:02x}", bytes[i]);
}
PrintLine();
}
func Main() -> int {
PrintLine("this machine is little endian: {}", TargetIsLittleEndian());
// One value with four distinct bytes, so the order is easy to see.
let value: uint32 = 0x12345678;
PrintLine("value {:#x}", value);
var little: uint8[4];
var big: uint8[4];
StoreLittleEndian(value, @little[0]);
StoreBigEndian(value, @big[0]);
ShowBytes("little endian ", little[..]);
ShowBytes("big endian ", big[..]);
// Reversing the bytes turns one order into the other.
PrintLine("ReverseBytes {:#x}", ReverseBytes(value));
PrintLine("");
// Reading: two bytes from a network packet, where a port number is big endian.
let packet: uint8[2] = [0x01, 0xBB];
var port: uint16 = 0;
LoadBigEndian(@packet[0], @port);
PrintLine("port {}", port);
// The same bytes read in the wrong order give a different number entirely.
PrintLine("wrong order {}", ReverseBytes(port));
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/Endian
rux run
```
```text
this machine is little endian: true
value 0x12345678
little endian 78 56 34 12
big endian 12 34 56 78
ReverseBytes 0x78563412
port 443
wrong order 47873
```
## Common mistakes
::warning
**Pointing at the whole array.**:br
The functions want a pointer to the first byte, not to the array. `StoreBigEndian(value, @big)` fails with `error: argument 1 to 'StoreBigEndian' has type 'uint32', but parameter 'value' requires 'T'` — the message names the value, but the fix is the pointer: `@big[0]`.
::
::warning
**Storing an untyped literal.**:br
The value's type decides how many bytes are written. `StoreBigEndian(0x12345678, @x[0])` stores an `int` — eight bytes, not four — and a four-byte array has no room for them. Store a typed value, such as the program's `let value: uint32`.
::
::warning
**Relying on the machine's order.**:br
Writing a number's memory straight to a file works until the file is read on a machine of the other order. Choose an order for the data and use the matching `Store…` and `Load…` functions, whatever `TargetIsLittleEndian()` says.
::
## Try it yourself
1. Store the `uint16` `0xBEEF` both ways and print the bytes with `ShowBytes`.
2. Store `value` big endian, then read it back with `LoadLittleEndian`. Which number do you get — and which function gives the same answer without any bytes?
3. Store the `uint64` `1` big endian into a `uint8[8]` and print the bytes. Where did the `01` go?
## Learn more
- [Pointer](https://rux-lang.dev/docs/learn/pointer) and [Out-parameter](https://rux-lang.dev/docs/learn/out-parameter) — the pointers these functions work through
- [Shift](https://rux-lang.dev/docs/learn/shift) — taking a number apart byte by byte by hand
- [Binary](https://rux-lang.dev/docs/learn/binary) — reading and writing binary files
- [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) in the Rux Reference
# Math
::note
**You'll need**: [Float](https://rux-lang.dev/docs/learn/float), [Float special](https://rux-lang.dev/docs/learn/float-special), [Function](https://rux-lang.dev/docs/learn/function)
::
The arithmetic operators stop at the four operations. Square roots, powers, logarithms, angles and rounding live in the `Math` package, as functions on `float64` — with `float32` versions of each, chosen by the type of the argument.
Two things are true of all of them, and both matter more than any single function. Every one has a **domain** — the inputs it has an answer for — and outside it the answer is a special value, not an error. And every answer is a float: the nearest float to the true answer, not the answer itself.
The program imports what it uses, and lists `Math` as a dependency:
```rux
import Math::{ Abs, Cbrt, Ceil, Cos, DegToRad, Exp, Floor, Log, Log10, Log2, Pi, Pow, Round, Sin,
Sqrt, Trunc };
```
## Roots and powers
```rux
PrintLine("Sqrt(2) {}", Sqrt(2.0));
PrintLine("Cbrt(-8) {}", Cbrt(-8.0));
PrintLine("Pow(2, 10) {}", Pow(2.0, 10.0));
PrintLine("Sqrt(-1) {}", Sqrt(-1.0));
```
`Sqrt` wants a number that is not negative; `Cbrt`, the cube root, takes any sign, so `Cbrt(-8.0)` is `-2.0`. `Pow(x, y)` is x to the power y. And `Sqrt(-1.0)` is outside the square root's domain: the answer is `NaN`, as in [Float special](https://rux-lang.dev/docs/learn/float-special), and the program carries on.
Notice the arguments are written `2.0`, not `2`. The functions take floats, and an integer is not converted on its own.
## Logarithms
```rux
PrintLine("Log(Exp(1)) {}", Log(Exp(1.0)));
PrintLine("Log2(1024) {}", Log2(1024.0));
PrintLine("Log10(0.001) {}", Log10(0.001));
PrintLine("Log(0) {}", Log(0.0));
```
`Log` is the natural logarithm, base *e*, and the inverse of `Exp`; the others say their base in their name. `Log2(1024.0)` is `10.0` because 2¹⁰ = 1024. Logarithms want a number greater than zero, and `Log(0.0)` is `-Inf`.
| Function | Wants | Outside the domain |
| ---------------------- | ----- | ------------------------ |
| `Sqrt` | x ≥ 0 | `NaN` |
| `Cbrt` | any x | — |
| `Log`, `Log2`, `Log10` | x > 0 | `-Inf` at 0, `NaN` below |
Check the input first when it might fall outside — a `NaN` produced deep inside a calculation is much harder to trace than an `if` before it.
## Angles
Trigonometry works in **radians**, where a half turn is `Pi`. `DegToRad` converts from degrees:
```rux
PrintLine("Sin(30 deg) {}", Sin(DegToRad(30.0)));
PrintLine("Cos(60 deg) {}", Cos(DegToRad(60.0)));
PrintLine("Sin(Pi) {}", Sin(Pi));
```
On paper, sin 30° and cos 60° are both exactly ½, and sin π is exactly 0. The program prints `0.49999999999999994`, `0.5000000000000001` and `1.2246467991473532e-16`. Nothing is wrong: `Pi` is the nearest `float64` to π, not π itself, and every step rounds to the nearest float.
So a float result is compared **within a tolerance**, never with `==`:
```rux
let tolerance = 1e-9;
PrintLine("Sin(Pi) is 0? {}", Abs(Sin(Pi)) < tolerance);
```
## Rounding, in four senses
"Round" can mean four different things. They agree on most positive numbers and part ways on negative ones — which is where a program usually finds out it chose the wrong one:
```rux
let value = -2.5;
PrintLine("Floor(-2.5) {} towards minus infinity", Floor(value));
PrintLine("Ceil(-2.5) {} towards plus infinity", Ceil(value));
PrintLine("Trunc(-2.5) {} towards zero", Trunc(value));
PrintLine("Round(-2.5) {} to nearest, halves away from zero", Round(value));
```
| Function | Rounds | 2.5 | −2.5 |
| -------- | --------------------------------- | --- | ---- |
| `Floor` | down, towards minus infinity | 2.0 | −3.0 |
| `Ceil` | up, towards plus infinity | 3.0 | −2.0 |
| `Trunc` | towards zero | 2.0 | −2.0 |
| `Round` | to nearest, halves away from zero | 3.0 | −3.0 |
All four return a float. `Trunc` is what `as` does when it converts a float to an integer; to round to the nearest whole number, `Round` first and convert after.
## The program
The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Numbers/Math){rel=""nofollow""}. Its comments explain every step.
```rux [Src/Main.rux]
// The arithmetic operators stop at the four operations. Roots, powers, logarithms, angles and
// rounding live in the `Math` package, as functions on `float64` (with `float32` versions too).
//
// Every one of them has a domain, the inputs it has an answer for, and outside it the answer is
// one of the special values from FloatSpecial rather than an error: `Sqrt(-1)` is NaN, `Log(0)` is
// minus infinity. Check the input first when it might fall outside.
//
// And every answer is a float, so it is the nearest float to the true answer, not the answer
// itself. `Sin(Pi)` is not 0, because `Pi` is not exactly pi. Compare results with a tolerance,
// never with `==`.
import Io::PrintLine;
import Math::{ Abs, Cbrt, Ceil, Cos, DegToRad, Exp, Floor, Log, Log10, Log2, Pi, Pow, Round, Sin,
Sqrt, Trunc };
func Main() -> int {
// Roots and powers. Sqrt wants x >= 0; Cbrt takes any sign.
PrintLine("Sqrt(2) {}", Sqrt(2.0));
PrintLine("Cbrt(-8) {}", Cbrt(-8.0));
PrintLine("Pow(2, 10) {}", Pow(2.0, 10.0));
PrintLine("Sqrt(-1) {}", Sqrt(-1.0));
PrintLine("");
// Logarithms want x > 0. `Log` is the natural one; the others say their base.
PrintLine("Log(Exp(1)) {}", Log(Exp(1.0)));
PrintLine("Log2(1024) {}", Log2(1024.0));
PrintLine("Log10(0.001) {}", Log10(0.001));
PrintLine("Log(0) {}", Log(0.0));
PrintLine("");
// Trigonometry works in radians, where a half turn is Pi. DegToRad converts from degrees.
PrintLine("Sin(30 deg) {}", Sin(DegToRad(30.0)));
PrintLine("Cos(60 deg) {}", Cos(DegToRad(60.0)));
PrintLine("Sin(Pi) {}", Sin(Pi));
// So a float result is compared within a tolerance.
let tolerance = 1e-9;
PrintLine("Sin(Pi) is 0? {}", Abs(Sin(Pi)) < tolerance);
PrintLine("");
// Rounding, in its four senses. They differ on negative numbers, which is where a program
// usually finds out it chose the wrong one.
let value = -2.5;
PrintLine("Floor(-2.5) {} towards minus infinity", Floor(value));
PrintLine("Ceil(-2.5) {} towards plus infinity", Ceil(value));
PrintLine("Trunc(-2.5) {} towards zero", Trunc(value));
PrintLine("Round(-2.5) {} to nearest, halves away from zero", Round(value));
return 0;
}
```
Besides `Io`, its `Rux.toml` lists `Math` under `[Dependencies]`.
## Run it
```sh
cd Examples/Numbers/Math
rux run
```
```text
Sqrt(2) 1.4142135623730951
Cbrt(-8) -2.0
Pow(2, 10) 1024.0
Sqrt(-1) NaN
Log(Exp(1)) 1.0
Log2(1024) 10.0
Log10(0.001) -3.0
Log(0) -Inf
Sin(30 deg) 0.49999999999999994
Cos(60 deg) 0.5000000000000001
Sin(Pi) 1.2246467991473532e-16
Sin(Pi) is 0? true
Floor(-2.5) -3.0 towards minus infinity
Ceil(-2.5) -2.0 towards plus infinity
Trunc(-2.5) -2.0 towards zero
Round(-2.5) -3.0 to nearest, halves away from zero
```
## Common mistakes
::warning
**Passing an integer.**:br`Sqrt(2)` fails with `error: no matching overload for 'Sqrt' with argument types (int)`, and the notes list the two candidates, `Sqrt(x: float32)` and `Sqrt(x: float64)`. Write `2.0`, or convert a variable with `as float64`.
::
::warning
**Giving degrees to a function that wants radians.**:br`Sin(30.0)` is the sine of 30 radians, about `-0.988`, not ½. Convert first: `Sin(DegToRad(30.0))`.
::
::warning
**Comparing a result with `==`.**:br`Cos(DegToRad(60.0)) == 0.5` is `false`, because the result is `0.5000000000000001`. Compare the difference with a tolerance: `Abs(result - 0.5) < 1e-9`.
::
## Try it yourself
1. Import `Hypot` and work out the hypotenuse of a 3–4–5 triangle.
2. Convert `Pi` back to degrees with `RadToDeg`.
3. Round −2.4 and −2.6 to the nearest whole number and convert them to `int32`. Then do the same with `as` alone and compare.
4. Call `Sqrt(2.0f32)`. How many digits does the `float32` version print?
## Learn more
- The [Math API](https://rux-lang.dev/docs/api/math) — every function, including [Sqrt](https://rux-lang.dev/docs/api/math/sqrt), [Pow](https://rux-lang.dev/docs/api/math/pow) and [Round](https://rux-lang.dev/docs/api/math/round)
- [Float special](https://rux-lang.dev/docs/learn/float-special) — the NaN and infinities these functions return outside their domain
- [Convert](https://rux-lang.dev/docs/learn/convert) — why `as` truncates instead of rounding
- [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic) — checkpoint projects that put these functions to work
# Part 17: Collections
Until now every sequence had a length fixed while compiling. Real data rarely does: lines read from a file, orders placed today, words in a text. This part introduces the containers of the `Collections` package, which hold as many values as arrive and manage their own memory through an allocator. By the end you will know seven of them, what each is quick and slow at, and which to reach for.
## What you will learn
- `Vector`, the growable sequence, and the difference between its length and its capacity.
- `Array`, a sequence whose length is chosen at run time and then fixed.
- `Deque`, quick at both ends — the natural queue.
- `HashMap` and `HashSet`: lookup by key and membership tests in about constant time, with no order.
- `TreeMap` and `TreeSet`: the same, kept sorted, with ordered questions such as `Floor`, `Ceiling` and `Range`.
- The pattern every collection shares: made from an allocator, fallible where it needs memory, optional where a value may be missing, and freed by its destructor.
## Which collection?
```mermaid
flowchart LR
q{"How do you find
a value again?"} -- "by position" --> pos{"How does the
length change?"}
pos -- "never — known
while compiling" --> inline["Inline array
T[n]"]
pos -- "never — known
only at run time" --> arr["Array"]
pos -- "values come and go
at the end" --> vec["Vector"]
pos -- "values come and go
at both ends" --> dq["Deque"]
q -- "by key" --> korder{"Do the keys need
to stay in order?"}
korder -- "no" --> hm["HashMap"]
korder -- "yes" --> tm["TreeMap"]
q -- "only: is it here?" --> sorder{"Do the members need
to stay in order?"}
sorder -- "no" --> hs["HashSet"]
sorder -- "yes" --> ts["TreeSet"]
```
When in doubt, start with a `Vector` for a list and a `HashMap` for lookups. Switch to a `Deque` when values join at the front, and to a tree when the order of the keys is part of the answer.
## Lessons
| | Lesson | What you will learn |
| ---- | -------------------------------------------------------------- | -------------------------------------------------------------- |
| 17.1 | [Vector](https://rux-lang.dev/docs/learn/vector) | a growable array that manages its own memory and capacity |
| 17.2 | [Dynamic array](https://rux-lang.dev/docs/learn/dynamic-array) | `Collections::Array`, and how it differs from an inline array |
| 17.3 | [Deque](https://rux-lang.dev/docs/learn/deque) | add and remove at both ends, and see where that beats a vector |
| 17.4 | [Hash map](https://rux-lang.dev/docs/learn/hash-map) | look values up by key |
| 17.5 | [Hash set](https://rux-lang.dev/docs/learn/hash-set) | test membership with a hash set |
| 17.6 | [Tree map](https://rux-lang.dev/docs/learn/tree-map) | keep keys in order, and walk them in order |
| 17.7 | [Tree set](https://rux-lang.dev/docs/learn/tree-set) | keep a set of values in order |
## Before you start
Every collection takes an [allocator](https://rux-lang.dev/docs/learn/allocator), so finish [Part 15: Memory](https://rux-lang.dev/docs/learn/memory) first. The lessons also lean on [generic types](https://rux-lang.dev/docs/learn/generic-type) from Part 13, a [fallible `Main`](https://rux-lang.dev/docs/learn/fallible-main) and [`?`](https://rux-lang.dev/docs/learn/propagate) from Part 9, [optionals](https://rux-lang.dev/docs/learn/optionals) from Part 8, [moves](https://rux-lang.dev/docs/learn/move) from Part 11 and [Comparable](https://rux-lang.dev/docs/learn/comparable) from Part 12. Each lesson's package is in the Examples repository's `Collections/` folder:
```sh
cd Examples/Collections/Vector
rux run
```
## After this part
[Part 18: Algorithms](https://rux-lang.dev/docs/learn/algorithms) sorts, searches and folds the values you can now collect. Parts 18 to 24 tour the standard packages and can be read in any order. Before moving on, try the checkpoint projects [Word count](https://rux-lang.dev/docs/learn/word-count), which counts words in a hash map and prints them in order through a tree map, and [Inventory](https://rux-lang.dev/docs/learn/inventory), a shop's stock kept as structs in a vector.
For the rules behind the inline sequences these containers are compared with, see [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) and [Slices](https://rux-lang.dev/docs/lang/slices/overview) in the Rux Reference.
# Vector
::note
**You'll need**: [Allocator](https://rux-lang.dev/docs/learn/allocator), [Generic type](https://rux-lang.dev/docs/learn/generic-type), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main), [Coalesce](https://rux-lang.dev/docs/learn/coalesce)
::
An inline array has the length its type says, fixed while compiling. Most real data is not like that: lines read from a file, results that pass a test, orders placed today. You do not know how many there will be until they arrive.
The Memory lessons solved that by hand — ask an allocator for a block, keep count, ask for a larger block when it is full. A `Vector`, from the `Collections` package, does that bookkeeping for you. It owns a block, hands out room as values are pushed, and moves to a larger block when the current one is full. It is the container you will reach for most often.
## Making a vector
A vector keeps its values somewhere, so it is given an [allocator](https://rux-lang.dev/docs/learn/allocator) when it is made:
```rux
var system = SystemAllocator();
let allocator: Allocator = system;
var numbers = Vector(allocator);
```
`Vector