# 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` is a [generic type](https://rux-lang.dev/docs/learn/generic-type): a vector of `int`. Making one asks the allocator for nothing yet — an empty vector has no block, and a capacity of zero. The binding is a `var`, because pushing changes the vector. ## Length and capacity Two numbers describe a vector, and they are worth keeping apart: | Number | Method | Meaning | | -------- | ------------ | ------------------------------------------------- | | Length | `Length()` | How many values it holds | | Capacity | `Capacity()` | How many would fit before it needs a larger block | Pushing raises the length by one every time; the capacity only jumps now and then. The program pushes nine values and prints a line whenever the capacity changes: ```rux for i in 1..=9 { let before = numbers.Capacity(); numbers.Push(i * 10)?; if numbers.Capacity() != before { PrintLine("push {} length {} capacity {} (grew)", i, numbers.Length(), numbers.Capacity()); } } ``` ```text push 1 length 1 capacity 4 (grew) push 5 length 5 capacity 8 (grew) push 9 length 9 capacity 16 (grew) ``` Nine pushes, three moves. The capacity grows **ahead** of the length on purpose: each time the vector moves it takes twice the room, so the pushes that follow cost nothing. The vector moves house a handful of times, not once per value. ```mermaid flowchart LR push["Push(value)"] --> room{"Is length below
capacity?"} room -- "yes" --> write["Write the value
into the next slot"] room -- "no" --> grow["Ask the allocator
for a larger block"] grow --> copy["Move the values across,
give the old block back"] copy --> write grow -- "no memory" --> fail["Push fails with
CollectionError"] ``` ## Pushing can fail That last arrow is why `Push` returns `! CollectionError`: a push may need a larger block, and the allocator may have none to give. The `?` hands that failure on to `Main`, which is declared [fallible](https://rux-lang.dev/docs/learn/fallible-main) for exactly this reason: ```rux func Main() -> ! CollectionError { ``` A failed push leaves the vector exactly as it was — no value half-added. ## Reserving room up front When the final size is known, `Reserve` makes the room in one step: ```rux numbers.Reserve(100)?; ``` At least 100 more values will now fit, so the pushes that follow never need to grow. The program prints a capacity of `128`: the vector rounded the request up. ## Reading the values A vector is a container a `for` loop can walk, in the order the values went in: ```rux for value in numbers { Print(" {}", value); } ``` To read one value, use `Get`. It is bounds-checked: it returns `int?`, an [optional](https://rux-lang.dev/docs/learn/optional), with `none` past the end: ```rux PrintLine("index 2 {}", numbers.Get(2) ?? -1); PrintLine("index 50 {}", numbers.Get(50) ?? -1); ``` Index 2 is `30`; index 50 does not exist, and the [`??`](https://rux-lang.dev/docs/learn/coalesce) fallback gives `-1`. Nothing in the program frees anything. The vector's destructor gives its block back to the allocator when `numbers` goes out of scope. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Collections/Vector){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // The Memory lessons asked for a block of a fixed size and kept count by hand. A `Vector` 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. // // Two numbers describe a vector, and they are worth keeping apart. Its length is how many values // it holds. Its capacity is how many would fit before it needs a larger block. Pushing raises the // length by one every time; the capacity only jumps now and then. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, Vector }; import Io::{ Print, PrintLine }; func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; // A vector keeps its storage somewhere, so it is given an allocator. Making one asks for // nothing yet: an empty vector has a capacity of zero. var numbers = Vector(allocator); PrintLine("empty length {} capacity {}", numbers.Length(), numbers.Capacity()); // A push may need a larger block, and the allocator may have none to give, so `Push` // returns `! CollectionError`. The `?` hands that failure to `Main`. for i in 1..=9 { let before = numbers.Capacity(); numbers.Push(i * 10)?; if numbers.Capacity() != before { PrintLine("push {} length {} capacity {} (grew)", i, numbers.Length(), numbers.Capacity()); } } PrintLine("after 9 length {} capacity {}", numbers.Length(), numbers.Capacity()); // The capacity grows ahead of the length on purpose. Taking more room than needed means the // next pushes cost nothing, so the vector moves house a handful of times, not once a value. // When the final size is known up front, `Reserve` makes the room in one step: at least 100 // more values will now fit, so the pushes that follow never need to grow. numbers.Reserve(100)?; PrintLine("reserved length {} capacity {}", numbers.Length(), numbers.Capacity()); // A vector is a container a `for` loop can walk, in the order the values went in. Print("contents "); for value in numbers { Print(" {}", value); } PrintLine(); // `Get` is bounds-checked: it returns `int?`, and `none` past the end. PrintLine("index 2 {}", numbers.Get(2) ?? -1); PrintLine("index 50 {}", numbers.Get(50) ?? -1); // Nothing here frees anything. The vector's destructor gives its block back to the allocator // when `numbers` goes out of scope. } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Collections/Vector rux run ``` ```text empty length 0 capacity 0 push 1 length 1 capacity 4 (grew) push 5 length 5 capacity 8 (grew) push 9 length 9 capacity 16 (grew) after 9 length 9 capacity 16 reserved length 9 capacity 128 contents 10 20 30 40 50 60 70 80 90 index 2 30 index 50 -1 ``` ## Common mistakes ::warning **Leaving out the `?`.**:br`numbers.Push(10);` fails with `error: fallible result of type '! CollectionError' is discarded`, and the help suggests `?`, `catch` or a `match`. A push that ran out of memory must be handled somehow. :: ::warning **Using `?` in a `Main` that returns `int`.**:br`?` hands the failure to the enclosing function, so that function has to be able to fail. In `func Main() -> int`, `numbers.Push(10)?` fails with `error: '?' propagates native fallible '! CollectionError', but the enclosing function returns 'int'`. Declare `func Main() -> ! CollectionError`. :: ::warning **A vector bound with `let`.**:br`Push` changes the vector. With `let numbers = …`, it fails with `error: cannot call 'Push' on immutable 'numbers'`. Bind it with `var`. :: ::warning **Indexing with square brackets.**:br`numbers[0]` fails with `error: type 'Vector' cannot be indexed`. Use `numbers.Get(0)`, which returns an optional — or `numbers.AsSlice()` to get a slice you can index. :: ## Try it yourself 1. Push 1000 values and count how many times the capacity grew. 2. Make the vector with `Vector::WithCapacity(allocator, 100)?` instead. What are its length and capacity before the first push? 3. Remove the first value with `RemoveAt(0)`, which returns an optional, and print what is left. 4. Add up the values with a `for` loop, then again by indexing `numbers.AsSlice()`. ## Learn more - [Allocator](https://rux-lang.dev/docs/learn/allocator) — where a vector's block comes from - [Dynamic array](https://rux-lang.dev/docs/learn/dynamic-array) — a run whose length is set at run time and never changes - [Deque](https://rux-lang.dev/docs/learn/deque) — a vector that is quick at both ends - [Slices](https://rux-lang.dev/docs/lang/slices/overview) in the Rux Reference # Dynamic array ::note **You'll need**: [Array](https://rux-lang.dev/docs/learn/array), [Slice](https://rux-lang.dev/docs/learn/slice), [Move](https://rux-lang.dev/docs/learn/move), [Catch](https://rux-lang.dev/docs/learn/catch), [Allocator](https://rux-lang.dev/docs/learn/allocator), [Vector](https://rux-lang.dev/docs/learn/vector) :: An inline array, `int32[4]`, carries its length in its type. The compiler fixes it, and the elements live wherever the array itself lives — inside a local, a struct, another array. That is fast and simple, and it needs the length while compiling. Sometimes the length is only known once the program runs: a count read from a file, a size passed in by a caller. `Collections::Array` is for that case. It asks an allocator for a block of the length you choose at run time, and once made, that length never changes. It is **not** a vector: there is no `Push`. A fixed run whose size is decided late is the whole idea. ## A length chosen at run time The length arrives as an ordinary parameter: ```rux func Squares(allocator: Allocator, count: uint) -> Array ! CollectionError { var squares = Array::Filled(allocator, count, 0)?; for i in 0..count { squares.Set(i, (i * i) as int32)?; } return <-squares; } ``` `Array::Filled` makes `count` copies of one value — here `count` zeros — and `Set` replaces them one by one. Asking for memory can fail, so `Filled` returns a fallible and needs `?`; so does `Set`, for a reason you will see below. An inline array could not do this. `int32[count]` is rejected, because an inline array's length must be a constant. The array owns its block, so returning it **moves** the block out to the caller — the `<-` of [Move](https://rux-lang.dev/docs/learn/move). `Main` receives it and walks it like any container: ```rux var squares = Squares(allocator, 6)?; for value in squares { Print(" {}", value); } ``` ## Copying an inline array onto the heap `Array::FromSlice` copies existing values into a new dynamic array: ```rux let primes: int32[4] = [2, 3, 5, 7]; var copy = Array::FromSlice(allocator, primes)?; copy.Set(0, 99)?; ``` The copy is independent: changing it leaves the original alone, so the copy starts with `99` while `primes[0]` is still `2`. ## What a wrong index costs Both kinds of array check every index. They differ in **what happens** when one is wrong: | Wrong index on… | What happens | | ---------------------- | ---------------------------------------------- | | inline array, constant | does not build: `index 7 is out of range…` | | inline array, computed | the program stops: `Panic: index out of range` | | dynamic array, `Get` | returns `none` | | dynamic array, `Set` | fails with `IndexOutOfRange` | A dynamic array reports the mistake in its types, so the program decides what to do with it: ```rux PrintLine("copy[9] {}", copy.Get(9) ?? -1); copy.Set(9, 1) catch { e => { PrintLine("copy.Set(9) failed: {}", e); } }; ``` `Get(9)` is `none`, and the `??` turns it into `-1`. `Set(9, 1)` fails, and the [`catch`](https://rux-lang.dev/docs/learn/catch) prints the error: `index the collection does not have`. ## Owned, not copied One more difference. An inline array is a plain value, copied by `=`. A dynamic array owns its block, and two arrays must never think they own the same one — so it cannot be copied. You move it with `<-` instead, and the old name can no longer be used. When `squares` and `copy` go out of scope, each gives its block back to the allocator. | | Inline `int32[4]` | `Array` | `Vector` | | -------------- | -------------------- | ---------------------------- | ---------------------------- | | Length decided | while compiling | at run time | at run time | | Length changes | never | never | with every push | | Elements live | in the array itself | in a block from an allocator | in a block from an allocator | | `=` | copies | refused — move with `<-` | refused — move with `<-` | | Wrong index | build error or panic | `none` or a failure | `none` or a failure | ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Collections/DynamicArray){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // An inline array, `int32[4]`, carries its length in its type. The compiler fixes it, and the // elements live wherever the array itself lives — inside a local, a struct, another array. // // Sometimes the length is only known once the program runs: a count read from a file, a size // passed in by a caller. `Collections::Array` is for that case. It asks an allocator for a // block of the length you choose at run time, and once made, that length never changes. It is // not a vector: there is no `Push`. A fixed run whose size is decided late is the whole idea. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ Array, CollectionError }; import Io::{ Print, PrintLine }; // The length arrives as an ordinary parameter. `int32[count]` would be rejected, because an // inline array's length must be known while compiling; a dynamic array has no such limit. func Squares(allocator: Allocator, count: uint) -> Array ! CollectionError { // `Filled` makes `count` copies of one value. Asking for memory can fail, hence `?`. var squares = Array::Filled(allocator, count, 0)?; for i in 0..count { squares.Set(i, (i * i) as int32)?; } // The array owns its block, so returning it moves the block out to the caller. return <-squares; } func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; let primes: int32[4] = [2, 3, 5, 7]; PrintLine("inline length {}", primes.length); var squares = Squares(allocator, 6)?; Print("squares "); for value in squares { Print(" {}", value); } PrintLine(" (length {})", squares.Length()); // `FromSlice` copies an inline array onto the heap. The copy is independent: changing it // leaves the original alone. var copy = Array::FromSlice(allocator, primes)?; copy.Set(0, 99)?; PrintLine("copy[0] {}, primes[0] still {}", copy.Get(0) ?? -1, primes[0]); // Both kinds check an index, but they differ in what a wrong one costs. On an inline array, // a constant index past the end does not build, `primes[7]` being "index 7 is out of range // for an array of 4 elements", and a computed one stops the program with "Panic: index out // of range". A dynamic array's `Get` and `Set` report it in their types instead: `Get` // returns `none`, and `Set` fails with `IndexOutOfRange`, so the program decides what to do. PrintLine("copy[9] {}", copy.Get(9) ?? -1); copy.Set(9, 1) catch { e => { PrintLine("copy.Set(9) failed: {}", e); } }; // One more difference: an inline array is copied by `=`, but a dynamic array owns its block // and cannot be copied. Writing `let other = copy;` is rejected; `let other <- copy;` moves it. // When `squares` and `copy` go out of scope, each gives its block back to the allocator. } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Collections/DynamicArray rux run ``` ```text inline length 4 squares 0 1 4 9 16 25 (length 6) copy[0] 99, primes[0] still 2 copy[9] -1 copy.Set(9) failed: index the collection does not have ``` ## Common mistakes ::warning **An inline array with a run-time length.**:br`var a: int32[count];` with a parameter `count` fails with `error: array length must be a non-negative compile-time integer`. Use `Array::Filled(allocator, count, 0)?`. :: ::warning **Copying with `=`.**:br`let other = copy;` fails with `error: move-only value 'copy' requires an explicit '<-' in initialization`, and a note explains that `'Array' prohibits copying`. Write `let other <- copy;` to move it — or `copy.Clone()?` when you really want two arrays. :: ::warning **Using the array after moving it.**:br After `let other <- copy;`, the name `copy` is empty: `copy.Length()` fails with `error: value 'copy' is used after it was moved`. :: ::warning **Expecting a dynamic array to grow.**:br`copy.Push(1)?` fails with `error: struct 'Array' has no field 'Push'`. The length is fixed once the array is made — use a [Vector](https://rux-lang.dev/docs/learn/vector) for a sequence that grows. :: ## Try it yourself 1. Change `Squares` to `Cubes`, returning an `Array`. 2. Make an array of 10 copies of `-1` with `Filled`, and set every third element to its index. 3. Write `let other = copy;`, read the error, then fix it with `<-`. What happens if you use `copy` afterwards? 4. Push a few values into a `Vector`, then freeze them into an `Array` with `ToArray()?`. ## Learn more - [Array](https://rux-lang.dev/docs/learn/array) — inline arrays, whose length is part of the type - [Vector](https://rux-lang.dev/docs/learn/vector) — the growable sequence - [Move](https://rux-lang.dev/docs/learn/move) — handing ownership on with `<-` - [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) in the Rux Reference # Deque ::note **You'll need**: [Vector](https://rux-lang.dev/docs/learn/vector), [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce) :: A [vector](https://rux-lang.dev/docs/learn/vector) is quick at its end and slow at its front. Pushing at the end writes into the next free slot; putting a value at position zero means shifting every other value along by one to make room — a thousand moves for a thousand values. A `Deque` — a **d**ouble-**e**nded **que**ue, said "deck" — is quick at both ends. It remembers where its contents start as well as where they end, so adding at the front moves a marker, not the values. Its everyday use is a queue: work joins at the back and is served from the front, which is exactly the pair of operations a vector is bad at. ## Four operations, two ends ```mermaid flowchart LR f["PushFront
PopFront"] <--> front["front"] front --- mid["…"] mid --- back["back"] back <--> b["PushBack
PopBack"] ``` | Operation | Vector | Deque | | ------------------- | ------------------------ | ------------------- | | Add at the end | quick (`Push`) | quick (`PushBack`) | | Remove at the end | quick | quick (`PopBack`) | | Add at the front | slow: shifts every value | quick (`PushFront`) | | Remove at the front | slow: shifts every value | quick (`PopFront`) | | Read by position | quick (`Get`) | quick (`Get`) | A deque is made like a vector, from an allocator: ```rux var queue = Deque(allocator); ``` ## A queue of tickets Tickets join at the back, in the order they arrive. Each push may need more room, and so can fail; `?` hands that failure to `Main`: ```rux queue.PushBack(101)?; queue.PushBack(102)?; queue.PushBack(103)?; ``` An urgent ticket jumps the line by going in at the front: ```rux queue.PushFront(900)?; ``` The queue is now `900 101 102 103`. Serving takes from the front: ```rux PrintLine("served {}", queue.PopFront() ?? -1); PrintLine("served {}", queue.PopFront() ?? -1); ``` The urgent ticket, `900`, is served first, then `101` — first in, first out. Both pops return an optional, `int?`, since an empty deque has nothing to give; `?? -1` supplies a stand-in that never prints here. The newest arrival changes their mind and leaves from the back: ```rux PrintLine("left {}", queue.PopBack() ?? -1); ``` That is `103`, leaving only `102` waiting. ## When the queue runs dry After one more pop the deque is empty, and the next `PopFront` returns `none`. A `match` says so properly rather than printing a stand-in: ```rux match queue.PopFront() { ticket? => PrintLine("served {}", ticket), none => PrintLine("empty nobody is waiting") } ``` ## Looking without taking `Show` prints the queue without changing it. It borrows the deque as `&Deque` and reads each position with `Get`, which, like a vector's, returns an optional: ```rux func Show(label: char8[..], queue: &Deque) { Print("{:8}", label); for i in 0..queue.Length() { Print(" {}", queue.Get(i) ?? -1); } PrintLine(); } ``` Position 0 is always the front, whichever end the values came in by. ## Which to reach for A vector when values only come and go at the end — a list you build and then read. A deque when both ends are in play — a queue, a window of the latest readings, a list of jobs where some jump the line. The deque's bookkeeping costs a little on every operation, which is why it is not simply the default. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Collections/Deque){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A vector is quick at its end and slow at its front: putting a value at position zero means // shifting every other value along to make room. // // A `Deque` — a double-ended queue, said "deck" — is quick at both ends. It remembers where its // contents start as well as where they end, so adding at the front moves a marker, not the // values. The everyday use is a queue: work joins at the back and is served from the front, // which is exactly the pair of operations a vector is bad at. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, Deque }; import Io::{ Print, PrintLine }; func Show(label: char8[..], queue: &Deque) { Print("{:8}", label); for i in 0..queue.Length() { Print(" {}", queue.Get(i) ?? -1); } PrintLine(); } func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; var queue = Deque(allocator); // Tickets join at the back, in the order they arrive. Each push may need more room, and so // can fail; `?` hands that failure to `Main`. queue.PushBack(101)?; queue.PushBack(102)?; queue.PushBack(103)?; Show("arrived", queue); // An urgent ticket jumps the line by going in at the front. queue.PushFront(900)?; Show("urgent", queue); // Serving takes from the front. Both pops return an optional, `int?`, since an empty deque // has nothing to give. PrintLine("served {}", queue.PopFront() ?? -1); PrintLine("served {}", queue.PopFront() ?? -1); // The newest arrival changes their mind and leaves from the back. PrintLine("left {}", queue.PopBack() ?? -1); Show("waiting", queue); PrintLine("served {}", queue.PopFront() ?? -1); match queue.PopFront() { ticket? => PrintLine("served {}", ticket), none => PrintLine("empty nobody is waiting") } // Which to reach for: a vector when values only come and go at the end, a deque when both // ends are in play. The deque's bookkeeping costs a little per operation, which is why it // is not simply the default. } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Collections/Deque rux run ``` ```text arrived 101 102 103 urgent 900 101 102 103 served 900 served 101 left 103 waiting 102 served 102 empty nobody is waiting ``` ## Common mistakes ::warning **Treating a pop as a value.**:br A pop may find nothing, so its type is `int?`. `let t: int = queue.PopFront();` fails with `error: cannot assign 'int?' to 'int'`. Unwrap it with `??`, a `match`, or [`?? return`](https://rux-lang.dev/docs/learn/coalesce-exit). :: ::warning **Printing the optional directly.**:br`PrintLine("{}", queue.PopFront())` fails with `error: argument 2 to 'PrintLine' has type 'int?', but variadic parameter 'args' requires 'Display'`. An optional cannot be printed as it is — decide what `none` should look like first. :: ::warning **Serving from the wrong end.**:br`PushBack` with `PopBack` is last in, first out — a stack, not a queue. A queue pairs `PushBack` with `PopFront`. :: ## Try it yourself 1. Use the deque as a stack: push three values with `PushBack` and pop them with `PopBack`. In which order do they come out? 2. Make a round robin: pop a ticket from the front, print it, and push it on the back, six times over three tickets. 3. Keep only the latest three readings: push each new reading at the back, and pop from the front whenever the length passes three. ## Learn more - [Vector](https://rux-lang.dev/docs/learn/vector) — the container a deque is compared with - [Presence](https://rux-lang.dev/docs/learn/presence) and [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — unwrapping the optional a pop returns - [Reference](https://rux-lang.dev/docs/learn/reference) — borrowing the deque in `Show` - [Hash map](https://rux-lang.dev/docs/learn/hash-map) — finding values by key instead of by position # Hash map ::note **You'll need**: [Callback](https://rux-lang.dev/docs/learn/callback), [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [String literal](https://rux-lang.dev/docs/learn/string-literal), [Vector](https://rux-lang.dev/docs/learn/vector) :: A vector answers "what is at position 3?". Often that is the wrong question. A shop wants to know how many pears are in stock; a phone book, which number belongs to a name; a cache, whether this request was answered before. Each looks a value up by a **key** — whatever suits: a number, an id, a name. A `HashMap` stores values under keys of type `K` and finds them again fast. It works out a number from the key — its **hash** — and goes straight to the slot that number points at. So a lookup costs about the same whether the map holds ten entries or ten million. ## Making a map Along with the allocator, a hash map is given two functions: ```rux var stock = HashMap(allocator, HashSlice, EqualsSlice); ``` | Argument | Does | | ------------- | ------------------------------------------------------ | | `allocator` | Provides the memory for the entries | | `HashSlice` | Turns a key into a number, which picks the likely slot | | `EqualsSlice` | Tells whether two keys are equal | Hashing finds the likely slot; equality confirms that the key found there really is the one wanted. Both are needed because two different keys can hash to the same number. The `Collections` package provides the pair for text keys, and others — `HashInt32` and `EqualsInt32`, `HashChar8` and `EqualsChar8` and more — for the common key types. They are passed as [callbacks](https://rux-lang.dev/docs/learn/callback): plain function names, not calls. ```mermaid flowchart LR k["key: pears"] --> h["HashSlice"] h --> slot["the slot that
hash points at"] slot --> eq{"EqualsSlice:
is it this key?"} eq -- "yes" --> v["its value: 4"] eq -- "no entry there" --> none["none"] ``` ## Inserting and looking up `Insert` adds an entry. It may need more room, and so can fail; `?` hands that failure to `Main`: ```rux stock.Insert("apples", 12)?; stock.Insert("pears", 4)?; stock.Insert("plums", 30)?; ``` `Get` returns an optional, `int32?`, because the key may not be there. A `match` handles both answers: ```rux match stock.Get("pears") { count? => PrintLine("pears {}", count), none => PrintLine("pears not stocked") } ``` Pears are in stock, so this prints `4`; the same `match` on `"kiwis"` prints `not stocked`. ## One entry per key A key appears at most once. Inserting it again **replaces** its value, and the map does not grow. `Replace` does the same and also hands back the value it displaced: ```rux stock.Insert("apples", 15)?; let previous = stock.Replace("apples", 20)?; ``` Apples are now `20`, `previous` holds the `15` they replaced, and the map still has three entries. `previous` is an optional — a key that was not there yet displaced nothing. ## Removing and checking `Remove` takes the entry out and returns its value, or `none` if there was no such key. `ContainsKey` asks without changing anything: ```rux PrintLine("removed plums {}", stock.Remove("plums") ?? 0); PrintLine("has plums {}", stock.ContainsKey("plums")); ``` | Method | Returns | Changes the map | | ------------- | ---------------------- | ---------------- | | `Insert` | `! CollectionError` | adds or replaces | | `Replace` | `V? ! CollectionError` | adds or replaces | | `Get` | `V?` | no | | `ContainsKey` | `bool` | no | | `Remove` | `V?` | removes | | `Length` | `uint` | no | ## No order What a hash map does **not** keep is order. A `for` loop over it visits the entries in whatever arrangement the hashing produced, which may differ from one run to the next. When order matters — an alphabetical listing, the earliest entry — the [tree map](https://rux-lang.dev/docs/learn/tree-map) keeps it. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Collections/HashMap){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A vector answers "what is at position 3". A hash map answers "what is stored under this key", // where the key is whatever suits — a number, an id, a name. // // It finds an entry by computing a number from the key, its hash, and going straight to the slot // that number points at. So a lookup costs about the same whether the map holds ten entries or // ten million. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, EqualsSlice, HashMap, HashSlice }; import Io::PrintLine; func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; // Along with the allocator come two functions: one to hash a key, one to tell whether two // keys are equal. Hashing finds the likely slot; equality confirms that the key found there // really is the one wanted, since two different keys can hash to the same number. The // package provides both for text keys. var stock = HashMap(allocator, HashSlice, EqualsSlice); // Inserting may need more room, and so can fail; `?` hands that failure to `Main`. stock.Insert("apples", 12)?; stock.Insert("pears", 4)?; stock.Insert("plums", 30)?; PrintLine("entries {}", stock.Length()); // `Get` returns an optional, `int32?`, because the key may not be there. match stock.Get("pears") { count? => PrintLine("pears {}", count), none => PrintLine("pears not stocked") } match stock.Get("kiwis") { count? => PrintLine("kiwis {}", count), none => PrintLine("kiwis not stocked") } // A key appears at most once. Inserting it again replaces its value, and the map does not // grow. `Replace` does the same and also hands back the value it displaced. stock.Insert("apples", 15)?; let previous = stock.Replace("apples", 20)?; PrintLine("apples {} (was {}), {} entries", stock.Get("apples") ?? 0, previous ?? 0, stock.Length()); // `Remove` takes the entry out and returns its value, or `none` if there was no such key. PrintLine("removed plums {}", stock.Remove("plums") ?? 0); PrintLine("has plums {}", stock.ContainsKey("plums")); PrintLine("entries {}", stock.Length()); // What a hash map does not keep is order. A `for` loop over it visits the entries in // whatever arrangement the hashing produced, which may differ from one run to the next. // When order matters, the TreeMap lesson's map keeps it. } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Collections/HashMap rux run ``` ```text entries 3 pears 4 kiwis not stocked apples 20 (was 15), 3 entries removed plums 30 has plums false entries 2 ``` ## Common mistakes ::warning **Functions for the wrong key type.**:br The hash and equality functions must take the key type. `HashMap(allocator, HashInt32, EqualsInt32)` fails with `error: argument 2 to 'HashMap' has type 'func(int32, uint64, uint64) -> uint64', but parameter 'hash' requires 'func(char8[..], uint64, uint64) -> uint64'`. Text keys take `HashSlice` and `EqualsSlice`. :: ::warning **Treating `Get` as a value.**:br`let n: int32 = stock.Get("pears");` fails with `error: cannot assign 'int32?' to 'int32'`. The key might be missing; say what to do then, with `??` or a `match`. :: ::warning **Overwriting by accident.**:br`Insert` on an existing key replaces its value without a word. When a second entry for the same key is a mistake, ask `ContainsKey` first — or use `Replace` and look at what it displaced. :: ::warning **Relying on the order of a walk.**:br A `for` loop over a hash map may visit the entries in a different order from one run to the next. Sort them, or keep them in a [tree map](https://rux-lang.dev/docs/learn/tree-map). :: ## Try it yourself 1. Add a fourth fruit, then print every entry with `for entry in stock`, using `entry.key` and `entry.value`. 2. Count the letters of `"banana"` in a `HashMap`, made with `HashChar8` and `EqualsChar8`: for each letter, insert `(counts.Get(c) ?? 0) + 1`. 3. Sell three apples: read the count, check there are enough, and store the new count. ## Learn more - [Hash set](https://rux-lang.dev/docs/learn/hash-set) — a hash map with only the keys - [Tree map](https://rux-lang.dev/docs/learn/tree-map) — a map that keeps its keys in order - [Callback](https://rux-lang.dev/docs/learn/callback) — passing a function as an argument - [Word count](https://rux-lang.dev/docs/learn/word-count) — a checkpoint project that counts words in a hash map # Hash set ::note **You'll need**: [Hash map](https://rux-lang.dev/docs/learn/hash-map), [Propagate](https://rux-lang.dev/docs/learn/propagate), [Presence](https://rux-lang.dev/docs/learn/presence), [Array](https://rux-lang.dev/docs/learn/array) :: Often the only question is "have I seen this one before?" — a visitor already counted, a file already processed, a word already in the dictionary. A [hash map](https://rux-lang.dev/docs/learn/hash-map) could answer it, but it would store a value under every key that nobody ever reads. A `HashSet` is that map with the values left out. It holds each element **at most once**, and answers membership in about the same time however large it grows. It is built the same way as a hash map — a hash function and an equality test — and, like a hash map, keeps no order. | | `HashMap` | `HashSet` | | ---------- | ---------------------------------- | ------------------------------- | | Stores | a value under each key | the elements alone | | Asks | "what is stored under this key?" | "is this one here?" | | Duplicates | a second insert replaces the value | a second insert changes nothing | | Order | none | none | ## Making a set The same three arguments as a hash map, for the element type instead of a key type: ```rux var visited = HashSet(allocator, HashSlice, EqualsSlice); ``` ## Inserting answers a question `Insert` answers as it works: `true` when the element was new, `false` when the set already had it. It may also need more room, so its type is `bool ! CollectionError` — a `bool`, or a failure — and `?` unwraps the answer or hands the failure to `Main`: ```rux let route: char8[..][6] = ["Lyon", "Paris", "Lille", "Paris", "Nice", "Lyon"]; for city in route { let isNew = visited.Insert(city)?; if isNew { PrintLine("{:6} first visit", city); } else { PrintLine("{:6} been here before", city); } } ``` `route` is an [array](https://rux-lang.dev/docs/learn/array) of six text slices. The second Paris and the second Lyon get `false`, so the set ends up with four cities from six stops: inserting and testing were one step, not two. When you only want the element added and do not care whether it was new, `visited.Insert(city)?;` on its own line is fine — the `bool` may be dropped once `?` has dealt with the failure. ## Asking and removing `Contains` asks without changing anything: ```rux PrintLine("visited Nice? {}", visited.Contains("Nice")); PrintLine("visited Brest? {}", visited.Contains("Brest")); ``` `Remove` hands the element back as an optional, `none` when it was not there: ```rux match visited.Remove("Lille") { city? => PrintLine("forgot {}", city), none => PrintLine("Lille was never visited") } ``` | Method | Returns | Changes the set | | ---------- | -------------------------------------- | --------------- | | `Insert` | `bool ! CollectionError` — was it new? | adds | | `Contains` | `bool` | no | | `Remove` | `T?` | removes | | `Length` | `uint` | no | ## Combining sets Two sets can be compared and combined as a whole. `IntersectWith` keeps only the elements both sets have, `Subtract` removes the other set's elements, `UnionWith` adds them — the last can need memory, so it is fallible — and `IsSubsetOf` asks whether every element of one is in the other. They are the classic set operations, done in one call each. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Collections/HashSet){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Often the only question is "have I seen this one before?". A hash map could answer it, but it // would store a value under every key that nobody ever reads. // // A `HashSet` is that map with the values left out. It holds each element at most once and // answers membership in about the same time however large it grows. It is built the same way as // a hash map — a hash function and an equality test — and, like a hash map, keeps no order. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, EqualsSlice, HashSet, HashSlice }; import Io::PrintLine; func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; var visited = HashSet(allocator, HashSlice, EqualsSlice); // `Insert` answers a question as it works: `true` when the element was new, `false` when the // set already had it. It may also need more room, so its type is `bool ! CollectionError`, // and `?` unwraps the answer or hands the failure to `Main`. let route: char8[..][6] = ["Lyon", "Paris", "Lille", "Paris", "Nice", "Lyon"]; for city in route { let isNew = visited.Insert(city)?; if isNew { PrintLine("{:6} first visit", city); } else { PrintLine("{:6} been here before", city); } } PrintLine("{} stops, {} different cities", route.length, visited.Length()); // `Contains` asks without changing anything. PrintLine("visited Nice? {}", visited.Contains("Nice")); PrintLine("visited Brest? {}", visited.Contains("Brest")); // `Remove` hands the element back as an optional, `none` when it was not there. match visited.Remove("Lille") { city? => PrintLine("forgot {}", city), none => PrintLine("Lille was never visited") } PrintLine("visited Lille? {}", visited.Contains("Lille")); } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Collections/HashSet rux run ``` ```text Lyon first visit Paris first visit Lille first visit Paris been here before Nice first visit Lyon been here before 6 stops, 4 different cities visited Nice? true visited Brest? false forgot Lille visited Lille? false ``` ## Common mistakes ::warning **Using `Insert` directly as a condition.**:br`if visited.Insert("Lyon") { … }` fails with `error: condition for 'if' must have type 'bool', but found 'bool8 ! CollectionError'`. The answer comes wrapped in a fallible: unwrap it first, with `let isNew = visited.Insert("Lyon")?;`. :: ::warning **Leaving out the `?`.**:br`visited.Insert("Lyon");` fails with `error: fallible result of type 'bool8 ! CollectionError' is discarded`. The `bool` may be dropped, but the possible failure may not. :: ::warning **Expecting the elements back in order.**:br A `for` loop over a hash set visits its elements in no particular order. When order matters, use a [tree set](https://rux-lang.dev/docs/learn/tree-set). :: ## Try it yourself 1. Print every city in the set with `for city in visited`. Run it twice — is the order the one in `route`? 2. Make a second set of cities a friend visited and keep only the cities you both saw, with `visited.IntersectWith(friend)`. 3. Find the first repeated word in a sentence: insert each word, and stop at the first `false`. ## Learn more - [Hash map](https://rux-lang.dev/docs/learn/hash-map) — the same structure, with a value under each key - [Tree set](https://rux-lang.dev/docs/learn/tree-set) — a set that keeps its elements in order - [Propagate](https://rux-lang.dev/docs/learn/propagate) — what `?` does with the failure - [Word count](https://rux-lang.dev/docs/learn/word-count) and [Inventory](https://rux-lang.dev/docs/learn/inventory) — checkpoint projects built on collections # Tree map ::note **You'll need**: [Hash map](https://rux-lang.dev/docs/learn/hash-map), [Comparable](https://rux-lang.dev/docs/learn/comparable), [Iterable](https://rux-lang.dev/docs/learn/iterable), [Presence](https://rux-lang.dev/docs/learn/presence) :: A [hash map](https://rux-lang.dev/docs/learn/hash-map) finds an entry fast and keeps no order: walk it, and the entries come out however the hashing scattered them. For a stock list that is fine. For temperatures taken through the day, a timetable or a leaderboard, the order **is** the information. A `TreeMap` keeps its keys sorted. However they were inserted, a walk visits them smallest first, and questions about order become cheap: the first key, the last, the nearest one below a bound, everything between two bounds. A lookup costs a little more than a hash map's — the map is searched rather than jumped into — and that is the trade. ## Making a tree map A hash map was told how to hash and compare keys for equality. A tree map is told how to put two keys **in order**: ```rux var readings = TreeMap(allocator, CompareInt32); ``` `CompareInt32` takes two `int32` keys and returns an `Ordering` — less, equal or greater — as in the [Comparable](https://rux-lang.dev/docs/learn/comparable) lesson. `Collections` has the same for other key types, such as `CompareSlice` for text. ## Keys arrive in any order Temperatures, keyed by the hour they were taken, arrive out of order: ```rux readings.Insert(15, 21)?; readings.Insert(6, 9)?; readings.Insert(12, 19)?; readings.Insert(9, 14)?; readings.Insert(18, 16)?; readings.Insert(3, 7)?; ``` A `for` loop walks the map in key order anyway. Each entry is a `KeyValue`, with `key` and `value` fields: ```rux for entry in readings { PrintLine(" {:2}:00 {} C", entry.key, entry.value); } ``` The hours come out `3, 6, 9, 12, 15, 18`. `{:2}` pads each hour to two characters, so the colons line up. ## Walking part of the map `Range(start, end)` walks only the keys from `start` up to, but not including, `end` — the same half-open shape as `a..b`. It starts at `start` rather than at the smallest key, so it does not visit the entries it skips: ```rux for entry in readings.Range(9, 17) { PrintLine(" {:2}:00 {} C", entry.key, entry.value); } ``` ```mermaid flowchart LR subgraph range ["Range(9, 17)"] k9["9"] --> k12["12"] --> k15["15"] end k3["3"] --> k6["6"] --> k9 k15 --> k18["18"] ``` ## Ordered questions A tree map can answer questions a hash map cannot: | Method | Returns the entry with… | Here | | ------------ | ------------------------------------ | ---------------------- | | `First()` | the smallest key | 3:00 | | `Last()` | the largest key | 18:00 | | `Floor(k)` | the largest key at or **below** `k` | `Floor(14)` is 12:00 | | `Ceiling(k)` | the smallest key at or **above** `k` | `Ceiling(14)` is 15:00 | Each returns an optional `KeyValue`, since an empty map has no first key and a bound may have nothing below it. A `match` unwraps it: ```rux match readings.Floor(14) { entry? => PrintLine("latest by 14:00 was {}:00, {} C", entry.key, entry.value), none => PrintLine("nothing by 14:00") } ``` No reading was taken at 14:00, so `Floor` finds the latest before it — 12:00, 19 °C. ## Looking up a key Lookup reads the same as a hash map's. `Get` returns `int32?`, and `ContainsKey` a `bool`: ```rux PrintLine("at 12:00 it was {} C", readings.Get(12) ?? 0); PrintLine("any reading at 13:00? {}", readings.ContainsKey(13)); ``` `Insert`, `Replace`, `Remove`, `Get` and `ContainsKey` all work as they do on a hash map; only the order, and the questions it makes possible, are new. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Collections/TreeMap){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A hash map finds an entry fast and keeps no order: walk it, and the entries come out however // the hashing scattered them. // // A `TreeMap` keeps its keys sorted instead. However they were inserted, a walk visits // them smallest first, and questions about order become cheap: the first key, the last, the // nearest one below a bound, everything between two bounds. A lookup costs a little more than a // hash map's — the map is searched rather than jumped into — and that is the trade. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, CompareInt32, TreeMap }; import Io::PrintLine; func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; // A tree map is told how to put two keys in order, where a hash map was told how to hash and // compare them. `CompareInt32` returns an `Ordering`, as in the Comparable lesson. var readings = TreeMap(allocator, CompareInt32); // Temperatures, keyed by the hour they were taken, arriving out of order. readings.Insert(15, 21)?; readings.Insert(6, 9)?; readings.Insert(12, 19)?; readings.Insert(9, 14)?; readings.Insert(18, 16)?; readings.Insert(3, 7)?; // A `for` loop walks the map in key order. Each entry is a `KeyValue`, with `key` and `value`. PrintLine("all readings"); for entry in readings { PrintLine(" {:2}:00 {} C", entry.key, entry.value); } // `Range(start, end)` walks only the keys from `start` up to, but not including, `end` — the // same half-open shape as `a..b`. It starts at `start` rather than at the smallest key. PrintLine("working hours, 9 to 17"); for entry in readings.Range(9, 17) { PrintLine(" {:2}:00 {} C", entry.key, entry.value); } // The ordered questions return optionals, since an empty map has no first key and a bound // may have nothing below it. `Floor` finds the largest key at or below the one asked for. match readings.Floor(14) { entry? => PrintLine("latest by 14:00 was {}:00, {} C", entry.key, entry.value), none => PrintLine("nothing by 14:00") } match readings.First() { entry? => PrintLine("first reading at {}:00", entry.key), none => PrintLine("no readings") } // Lookup reads the same as a hash map's: `Get` returns `int32?`. PrintLine("at 12:00 it was {} C", readings.Get(12) ?? 0); PrintLine("any reading at 13:00? {}", readings.ContainsKey(13)); } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Collections/TreeMap rux run ``` ```text all readings 3:00 7 C 6:00 9 C 9:00 14 C 12:00 19 C 15:00 21 C 18:00 16 C working hours, 9 to 17 9:00 14 C 12:00 19 C 15:00 21 C latest by 14:00 was 12:00, 19 C first reading at 3:00 at 12:00 it was 19 C any reading at 13:00? false ``` ## Common mistakes ::warning **Expecting `Range` to include its end.**:br`Range(9, 17)` stops **before** 17: a reading taken at 17:00 would not be visited. Pass the first key you do **not** want as the end, as with `a..b`. :: ::warning **Reading a field through the optional.**:br`readings.Floor(16).key` fails with `error: type 'KeyValue?' has no field 'key'`. The entry may not exist; unwrap it with a `match` or `?? return` before reading `key` and `value`. :: ::warning **Choosing a tree map by habit.**:br A tree map pays for its order on every insert and lookup. When nothing ever asks for order — no sorted walk, no `Floor`, no `Range` — a [hash map](https://rux-lang.dev/docs/learn/hash-map) does the same job faster. :: ## Try it yourself 1. Print the reading nearest **after** 10:00 with `Ceiling(10)`, and the last reading of the day with `Last()`. 2. Walk the morning readings, from midnight up to but not including noon. 3. Remove the 3:00 reading with `Remove(3)`, and check that `First()` now answers 6:00. 4. Make a `TreeMap` with `CompareSlice`, insert a few names with ages, and print them in alphabetical order. ## Learn more - [Hash map](https://rux-lang.dev/docs/learn/hash-map) — the unordered map, and the methods both share - [Tree set](https://rux-lang.dev/docs/learn/tree-set) — the ordered set, built the same way - [Comparable](https://rux-lang.dev/docs/learn/comparable) — `Ordering`, and how two values are put in order - [Range](https://rux-lang.dev/docs/learn/range) — the half-open `a..b` that `Range(start, end)` mirrors # Tree set ::note **You'll need**: [Hash set](https://rux-lang.dev/docs/learn/hash-set), [Tree map](https://rux-lang.dev/docs/learn/tree-map), [Coalesce](https://rux-lang.dev/docs/learn/coalesce) :: A [hash set](https://rux-lang.dev/docs/learn/hash-set) answers "is it here?" and nothing about order. A `TreeSet` answers the same question and also keeps its elements sorted, the way a [tree map](https://rux-lang.dev/docs/learn/tree-map) keeps its keys. That makes it the set to reach for when the members should come out in order — a word list in alphabetical order, free seat numbers lowest first — or when the question is "which member comes next after this one?". ## Making a tree set Like a tree map, it is told how to order two elements: ```rux var tags = TreeSet(allocator, CompareSlice); ``` `CompareSlice` orders text by its bytes. For lowercase English words that is alphabetical order; the Common mistakes below show where it is not. ## Membership, as in a hash set `Insert` reports whether the element was new, and a duplicate is not added a second time: ```rux let incoming: char8[..][7] = ["rust", "memory", "arena", "rux", "memory", "pool", "arena"]; for tag in incoming { let isNew = tags.Insert(tag)?; if !isNew { PrintLine("duplicate {}", tag); } } ``` Seven tags go in; `memory` and `arena` arrive twice, so five are kept. `Contains` asks without changing anything, just as on a hash set. ## Walking in order A `for` loop walks the set in order, whatever order the elements arrived in: ```rux for tag in tags { Print(" {}", tag); } ``` They arrived as `rust memory arena rux pool`; they come out as `arena memory pool rust rux`. ## Ordered questions The same questions a tree map answers about its keys, a tree set answers about its elements. Each returns an optional, because the answer may not exist — so the program supplies a stand-in with `??`: ```rux PrintLine("first {}", tags.First() ?? "(none)"); PrintLine("last {}", tags.Last() ?? "(none)"); PrintLine("from n on {}", tags.Ceiling("n") ?? "(none)"); PrintLine("from z on {}", tags.Ceiling("z") ?? "(none)"); ``` `Ceiling` is the smallest element at or above the one asked for. `"n"` is not a tag, so `Ceiling("n")` finds the first tag from `n` on: `pool`. Nothing comes at or after `"z"`, so that one prints `(none)`. ```mermaid flowchart LR a["arena"] --> m["memory"] --> p["pool"] --> r["rust"] --> x["rux"] n(["Ceiling of n"]) -. "smallest element
at or above n" .-> p f(["Floor of n"]) -. "largest element
at or below n" .-> m ``` | Method | Returns | | ------------- | ----------------------------------------- | | `First()` | the smallest element | | `Last()` | the largest element | | `Floor(x)` | the largest element at or below `x` | | `Ceiling(x)` | the smallest element at or above `x` | | `Range(a, b)` | a walk from `a` up to, not including, `b` | ## A range of elements `Range(start, end)` walks the elements from `start` up to, but not including, `end`: ```rux for tag in tags.Range("r", "s") { Print(" {}", tag); } ``` Every word that starts with `r` sorts at or after `"r"` and before `"s"`, so this prints `rust rux` — a prefix search in one call. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Collections/TreeSet){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A hash set answers "is it here?" and nothing about order. A `TreeSet` answers the same // question and also keeps its elements sorted, the way a tree map keeps its keys. // // That makes it the set to reach for when the members should come out in order — a word list // in alphabetical order, a list of free seat numbers lowest first — or when the question is // "which member comes next after this one?". import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, CompareSlice, TreeSet }; import Io::{ Print, PrintLine }; func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; // Like a tree map, it is told how to order two elements. `CompareSlice` orders text by its // bytes, which is alphabetical for lowercase English words. var tags = TreeSet(allocator, CompareSlice); // Membership works as in a hash set: `Insert` reports whether the element was new, and a // duplicate is not added a second time. let incoming: char8[..][7] = ["rust", "memory", "arena", "rux", "memory", "pool", "arena"]; for tag in incoming { let isNew = tags.Insert(tag)?; if !isNew { PrintLine("duplicate {}", tag); } } PrintLine("{} tags in, {} kept", incoming.length, tags.Length()); PrintLine("has pool? {}", tags.Contains("pool")); // A `for` loop walks the set in order, whatever order the elements arrived in. Print("in order "); for tag in tags { Print(" {}", tag); } PrintLine(); // Ordered questions return optionals, because the answer may not exist. `Ceiling` is the // smallest element at or above the one asked for, so it finds the first tag from "n" on. PrintLine("first {}", tags.First() ?? "(none)"); PrintLine("last {}", tags.Last() ?? "(none)"); PrintLine("from n on {}", tags.Ceiling("n") ?? "(none)"); PrintLine("from z on {}", tags.Ceiling("z") ?? "(none)"); // `Range(start, end)` walks the elements from `start` up to, but not including, `end`. Print("r to s "); for tag in tags.Range("r", "s") { Print(" {}", tag); } PrintLine(); } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Collections/TreeSet rux run ``` ```text duplicate memory duplicate arena 7 tags in, 5 kept has pool? true in order arena memory pool rust rux first arena last rux from n on pool from z on (none) r to s rust rux ``` ## Common mistakes ::warning **Expecting dictionary order from `CompareSlice`.**:br It compares bytes, and every uppercase letter's byte is smaller than every lowercase one's. Insert `"Zebra"`, `"apple"` and `"mango"`, and the walk gives `Zebra apple mango`. Store words in one case when you want alphabetical order. :: ::warning **Printing an ordered answer directly.**:br`First`, `Last`, `Floor` and `Ceiling` return optionals, which cannot be printed as they are: `PrintLine("{}", tags.First())` fails with `error: argument 2 to 'PrintLine' has type 'char8[..]?', but variadic parameter 'args' requires 'Display'`. Supply a stand-in with `??`, as the program does, or `match` on the result. :: ::warning **Expecting `Remove` to return the element.**:br A hash set's `Remove` hands the element back as an optional; a tree set's returns a `bool` — whether it was there. Write `if tags.Remove("pool") { … }`. :: ## Try it yourself 1. Print `tags.Floor("n")`, the largest tag at or below `"n"`. Then `Floor("a")` — what does it give, and why? 2. Insert `"Zebra"` and see where it lands in the walk. Then insert it as `"zebra"`. 3. Keep free seat numbers in a `TreeSet` made with `CompareInt32`. Take the lowest free seat with `First()`, and remove it so it is no longer free. 4. Print every tag from `"a"` up to, but not including, `"n"` with `Range`. ## Learn more - [Hash set](https://rux-lang.dev/docs/learn/hash-set) — the unordered set, and the methods both share - [Tree map](https://rux-lang.dev/docs/learn/tree-map) — the same ordered questions, for keys with values - [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — the `??` that supplies `(none)` - [Sort](https://rux-lang.dev/docs/learn/sort) — sorting a slice once, when the values do not change afterwards # Part 18: Algorithms From here on the course tours the standard packages, and it starts with the one every program reaches for sooner or later. The `Algorithms` package is a set of generic functions over slices: sort them, search them, find their extremes, fold them into one answer. Because every function takes a slice, one function serves an array of any length, part of an array, or the contents of a collection — and none of them ever allocates memory. ## What you will learn - Sorting a slice in place through a writable view, by the element's own `<` or by an order function of yours. - Searching any slice from the front, and handling the `none` that "not found" is. - Searching sorted data in logarithmic time, and the promise that comes with it. - The smaller of two values, and the position of the smallest in a slice — which may not exist. - Collapsing a slice into one answer with `Fold` and a step function. ## Which function? ```mermaid flowchart LR q(["What do you want
from a slice?"]) --> order["to put it in order"] q --> find["to find a value"] q --> ext["its smallest or
largest element"] q --> one["one answer
from every element"] order --> sort["Sort, SortDescending, SortBy
(18.1)"] find --> sorted{"is it sorted?"} sorted -- "no, or not sure" --> lin["IndexOf, LastIndexOf, Contains
(18.2)"] sorted -- "yes" --> bin["BinarySearch, LowerBound, UpperBound
(18.3)"] ext --> mm["MinIndex, MaxIndex
(18.4)"] one --> fold["Fold, FoldRight
(18.5)"] ``` The functions share a few habits, and knowing them makes the rest of the package predictable: | Habit | Functions in this part | | --------------------------------------------------- | ---------------------------------------------------------------- | | Take a writable view, `var T[..]`, and change it | `Sort`, `SortDescending`, `SortBy` | | Answer with `uint?`, `none` when there is no answer | `IndexOf`, `LastIndexOf`, `BinarySearch`, `MinIndex`, `MaxIndex` | | Always have an answer | `Contains`, `LowerBound`, `UpperBound`, `Min`, `Max`, `Fold` | | Take a named function for the part that varies | `SortBy`, `Fold`, `FoldRight` | ## Lessons | | Lesson | What you will learn | | ---- | -------------------------------------------------------------- | --------------------------------------------------- | | 18.1 | [Sort](https://rux-lang.dev/docs/learn/sort) | sort a slice in place | | 18.2 | [Search](https://rux-lang.dev/docs/learn/search) | find a value in a slice, and handle not finding it | | 18.3 | [Binary search](https://rux-lang.dev/docs/learn/binary-search) | find a value in sorted data quickly | | 18.4 | [Min and max](https://rux-lang.dev/docs/learn/min-max) | the smallest and largest values, and the empty case | | 18.5 | [Fold](https://rux-lang.dev/docs/learn/fold) | combine all elements into one value with a function | ## Before you start The lessons lean on [Slice](https://rux-lang.dev/docs/learn/slice) and [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) from Part 5, [Callback](https://rux-lang.dev/docs/learn/callback) and [Generic](https://rux-lang.dev/docs/learn/generic) from Part 4, [Presence](https://rux-lang.dev/docs/learn/presence) and [Coalesce](https://rux-lang.dev/docs/learn/coalesce) from [Part 8: Optionals](https://rux-lang.dev/docs/learn/optionals), and [Comparable](https://rux-lang.dev/docs/learn/comparable) from Part 12. Each lesson's package is in the Examples repository's `Algorithms/` folder: ```sh cd Examples/Algorithms/Sort rux run ``` ## After this part The checkpoint project [Statistics](https://rux-lang.dev/docs/learn/statistics) puts the whole part to work: it sorts, finds extremes and folds slices of numbers into a count, a mean, a variance and a median, including the awkward cases of one value and none. Then [Part 19: Files](https://rux-lang.dev/docs/learn/files) leaves memory behind and reads and writes the disk. For the rules underneath this part, see [Slices](https://rux-lang.dev/docs/lang/slices/overview), [Function types](https://rux-lang.dev/docs/lang/functions/function-types) and [Generic functions](https://rux-lang.dev/docs/lang/generics/overview) in the Rux Reference. # Sort ::note **You'll need**: [Writable slice](https://rux-lang.dev/docs/learn/writable-slice), [Callback](https://rux-lang.dev/docs/learn/callback), [Comparable](https://rux-lang.dev/docs/learn/comparable) :: Sorting puts a sequence in order, smallest first. It is the first job of the `Algorithms` package, the standard package this part tours: a set of generic functions that work on slices of any element type. `Sort` rearranges the elements **in place** — where they already are, with nothing copied and nothing allocated — so the array itself comes out in order. Like every standard package, `Algorithms` is a dependency you name in `Rux.toml`, next to `Io`: ```toml [Dependencies] Algorithms = { Namespace = "Rux", Version = "*" } ``` ## Sorting needs a writable view In place means `Sort` writes into your array, so it asks for the kind of slice that may write: `var T[..]`, from [Writable slice](https://rux-lang.dev/docs/learn/writable-slice). ```rux var numbers: int[7] = [5, -8, 3, -1, 6, 0, -4]; Show("start ", numbers); Sort(numbers[..]); Show("sorted ", numbers); ``` `numbers[..]` is a writable view of the whole array. Plain `numbers` would not do: an array turns into a slice on its own, but only into a read-only one, and a sort through a read-only view could not move anything. `Show` takes `int[..]` because it only reads, so plain `numbers` is fine there. Because `Sort` takes a slice, not an array, one function serves every length, any part of an array, and the contents of a [Vector](https://rux-lang.dev/docs/learn/vector), whose `AsMutableSlice` hands out the same kind of writable view. | Argument | `numbers` declared with | Gives | `Sort` accepts it? | | --------------- | ----------------------- | --------------------------------- | ---------------------- | | `numbers` | `var` | a read-only view | no | | `numbers[..]` | `var` | a writable view of every element | yes | | `numbers[1..6]` | `var` | a writable view of indexes 1 to 5 | yes — sorts only those | | `numbers[..]` | `let` | a read-only view | no | ## Three ways to order `Sort` uses the element's own `<`, so the result runs from smallest to largest. `SortDescending` turns that round: ```rux SortDescending(numbers[..]); Show("descending ", numbers); ``` When the order you want is not the type's own, `SortBy` takes it as a function. Given two elements, the function answers with an `Ordering` — `Less`, `Equal` or `Greater` — the same three answers [Comparable](https://rux-lang.dev/docs/learn/comparable) introduced. This one orders numbers by their distance from zero, whatever the sign: ```rux func ByMagnitude(left: int, right: int) -> Ordering { if Magnitude(left) < Magnitude(right) { return Ordering::Less; } if Magnitude(right) < Magnitude(left) { return Ordering::Greater; } return Ordering::Equal; } ``` The function is passed by name, the way [Callback](https://rux-lang.dev/docs/learn/callback) passed one — not called: ```rux SortBy(numbers[..], ByMagnitude); ``` ```mermaid flowchart LR s["SortBy(numbers[..], ByMagnitude)"] --> pick["picks a pair,
such as 6 and -8"] pick --> ask["asks ByMagnitude(6, -8)"] ask --> ans{"the answer"} ans -- "Less" --> a["6 belongs first"] ans -- "Greater" --> b["-8 belongs first"] ans -- "Equal" --> c["either order will do"] ``` `SortBy` repeats that with other pairs until the slice is in order. It decides which pairs to ask about and how often; your function only ever answers for one pair. `Ordering` lives in `Core`, which is why the lesson imports `Core::Ordering` and lists `Core` as a dependency. ## Sorting part of an array A range picks out part of the array, and only that part is sorted: ```rux var part: int[7] = [9, 7, 5, 3, 1, 8, 6]; Sort(part[1..6]); Show("middle only ", part); ``` `1..6` covers indexes 1 to 5. The 9 at index 0 and the 6 at index 6 are outside the view, so they stay exactly where they were, and the output reads `9 1 3 5 7 8 6`. ## Equal elements may swap `Sort` and `SortBy` are not **stable**: elements the order calls equal may come out in either order. With `ByMagnitude`, `-1` and `1` are equal, so a slice holding both may put either one first. The sort is deterministic — the same input gives the same output every time — but it promises nothing about ties. Usually that does not matter. When it does — sort a table by one column, then by another, and you want the first order kept within each group — `StableSort` and `StableSortBy` keep equal elements in the order they arrived. The price is storage: they ask you for a `scratch` slice as long as the one being sorted, because the `Algorithms` package never allocates memory on its own. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Algorithms/Sort){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Sorting puts a sequence in order, smallest first. `Sort`, from the Algorithms package, sorts in // place: it rearranges the elements where they already are instead of building a sorted copy, so // nothing is allocated and the array itself comes out in order. // // That is why it asks for a writable slice, `var T[..]`, the kind the WritableSlice lesson // introduced. Because it takes a slice rather than an array, the same function sorts an array of // any length, a part of one, or the contents of a vector. // // `Sort` uses the element's own `<`. `SortBy` takes the order as a function instead: given two // elements it answers `Less`, `Equal` or `Greater`, the `Ordering` from the Comparable lesson. // Neither sort is stable, so elements the order calls equal may end up in either order. import Algorithms::{ Sort, SortBy, SortDescending }; import Core::Ordering; import Io::{ Print, PrintLine }; func Show(label: char8[..], items: int[..]) { Print("{}", label); for item in items { Print(" {}", item); } PrintLine(); } func Magnitude(value: int) -> int { return value < 0 ? -value : value; } // A custom order: closest to zero first, whatever the sign. func ByMagnitude(left: int, right: int) -> Ordering { if Magnitude(left) < Magnitude(right) { return Ordering::Less; } if Magnitude(right) < Magnitude(left) { return Ordering::Greater; } return Ordering::Equal; } func Main() -> int { var numbers: int[7] = [5, -8, 3, -1, 6, 0, -4]; Show("start ", numbers); // `numbers[..]` asks for a writable view of the whole array. Writing just `Sort(numbers)` is // rejected: an array becomes a slice on its own, but only a read-only one. Sort(numbers[..]); Show("sorted ", numbers); SortDescending(numbers[..]); Show("descending ", numbers); // The function is passed by name, not called. `SortBy` calls it whenever it compares a pair. SortBy(numbers[..], ByMagnitude); Show("by magnitude", numbers); // A range sorts just that part. The two ends are left where they were. var part: int[7] = [9, 7, 5, 3, 1, 8, 6]; Sort(part[1..6]); Show("middle only ", part); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Algorithms` and `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/Algorithms/Sort rux run ``` ```text start 5 -8 3 -1 6 0 -4 sorted -8 -4 -1 0 3 5 6 descending 6 5 3 0 -1 -4 -8 by magnitude 0 -1 3 -4 5 6 -8 middle only 9 1 3 5 7 8 6 ``` ## Common mistakes ::warning **Passing the array itself.**:br`Sort(numbers)` fails with `error: argument 1 to 'Sort' has type 'int[7]', but parameter 'items' requires 'var T[..]'`. An array becomes a read-only view on its own; write `Sort(numbers[..])` to hand over a writable one. :: ::warning **Sorting a `let` array.**:br Declare the array with `let` and even `Sort(numbers[..])` fails, this time naming `'int[..]'` — a read-only view. A view can never grant more than its array allows, so an array you mean to sort must be `var`. :: ::warning **Sorting a type that has no `<`.**:br Sort an array of a struct that declares no `<` and the error appears inside the package: `error: operator '<' is not defined for 'P'`, with a note naming the call that instantiated the sort and `help: declare '<' on 'P'`. Either give the struct a `<`, as in [Operator overload](https://rux-lang.dev/docs/learn/operator-overload), or sort it with `SortBy` and an order function. :: ::warning **Calling the order function instead of passing it.**:br`SortBy(numbers[..], ByMagnitude(1, 2))` is rejected. `ByMagnitude(1, 2)` is one `Ordering` value, not a way to compare; pass the name alone and let `SortBy` do the calling. :: ::warning **A range that runs past the end.**:br`Sort(part[1..9])` on a seven-element array compiles, and stops the program with `Panic: index out of range` when it runs. The view is checked when it is made, before anything is sorted. :: ## Try it yourself 1. Sort an array of `float64` values, and then an array of `char8` letters. Nothing about `Sort` changes. 2. Sort only the first three elements of `numbers` with `numbers[..3]`, and predict the output before you run it. 3. Write an order function `ByLastDigit` for non-negative numbers that compares `value % 10`, and sort `[42, 17, 5, 30, 21]` with it. 4. Sort `[3, -1, -3, 1, 2, -2]` with `SortBy` and `ByMagnitude`. Then sort it with `StableSortBy(mixed[..], scratch[..], ByMagnitude)`, where `scratch` is a `var int[6]`, and check that each pair of equals keeps its original order. ## Learn more - [Slicing](https://rux-lang.dev/docs/lang/arrays/overview#arrays-as-slices) and [Function types](https://rux-lang.dev/docs/lang/functions/function-types) in the Rux Reference - [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) — the `var T[..]` view a sort writes through - [Comparable](https://rux-lang.dev/docs/learn/comparable) — `Ordering`, and giving your own types an order - [Binary search](https://rux-lang.dev/docs/learn/binary-search) — what sorted data lets you do quickly # Search ::note **You'll need**: [Slice](https://rux-lang.dev/docs/learn/slice), [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce) :: A **linear search** looks at the elements one by one, from the front, until it finds what it wants. It works on any slice, sorted or not, and it takes time in proportion to the length: twice the elements, twice the looking. The interesting part is not the looking but the answer, because the value you want may not be there at all. ## "Where is it?" may have no answer `IndexOf` returns the position of the first element equal to the value. No position could honestly say "nowhere": 0 is a real position, and a made-up one such as the length is easy to use by mistake. So the answer is an optional index, `uint?`, and it is `none` when nothing matched — the [Optional](https://rux-lang.dev/docs/learn/optional) idea from Part 8. `Report` takes the answer apart with a [presence](https://rux-lang.dev/docs/learn/presence) match before it uses it: ```rux func Report(rolls: int[..], value: int) { match IndexOf(rolls, value) { at? => PrintLine("{} first appears at {}", value, at), none => PrintLine("{} was never rolled", value) } } ``` Inside the `at?` arm, `at` is a plain `uint` you can print or index with. Outside the match there is only the `uint?`, and the compiler will not let you treat it as a position until you have looked. ```rux let rolls: int[8] = [4, 2, 6, 6, 1, 3, 6, 5]; Report(rolls, 1); Report(rolls, 6); Report(rolls, 7); ``` 1 appears once, at index 4. 6 appears three times; `IndexOf` reports the earliest, index 2. 7 is not there, so the `none` arm runs. ## Three questions, three functions | Function | Question | Answer | | --------------------------- | --------------------------- | ------------------------- | | `IndexOf(items, value)` | where does it first appear? | `uint?` — `none` if never | | `LastIndexOf(items, value)` | where does it last appear? | `uint?` — `none` if never | | `Contains(items, value)` | is it there at all? | `bool` | `LastIndexOf` searches from the back, which matters only when the value appears more than once: for 6 it reports index 6, not 2. `Contains` answers the plainer question with a plain `bool`, so there is nothing to unwrap: ```rux PrintLine("contains 5 {}", Contains(rolls, 5)); PrintLine("contains 0 {}", Contains(rolls, 0)); ``` The `Algorithms` package has more in the same family: `Count` says how many elements equal a value, and `IndexWhere` finds the first element a function of yours accepts, for searches that are not about equality. ## A stand-in with `??` When a missing value has an obvious stand-in, [`??`](https://rux-lang.dev/docs/learn/coalesce) supplies it: ```rux let at = IndexOf(rolls, 7) ?? rolls.length; PrintLine("7 at {} of {}", at, rolls.length); ``` `at` is now a plain `uint`. The length works as "past the end", but notice what has happened: the type no longer says "maybe absent", so the code that reads `at` has to remember that 8 means "not found". Use the stand-in when that meaning is obvious and local; keep the `uint?` when it travels further. ## Positions are relative to the slice A search of part of an array reports positions within that part: ```rux let tail = rolls[4..]; PrintLine("6 in the tail at {}", IndexOf(tail, 6) ?? tail.length); ``` `tail` starts at index 4 of `rolls`, so its own index 2 is index 6 of the array. `IndexOf` only ever sees the slice it is given, and has no idea where that slice came from. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Algorithms/Search){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A linear search looks at the elements one by one, from the front, until it finds what it wants. // It works on any slice, sorted or not, and takes time in proportion to the length. // // `IndexOf` answers "where is it?". The value might not be there at all, and no index could // honestly say so: 0 is a real position, and a made-up one such as the length is easy to use by // mistake. So the answer is an optional index, `uint?`, which is `none` when nothing matched, and // the program has to look before it can use the position. // // `Contains` answers the plainer question "is it there?" with a `bool`. `LastIndexOf` searches // from the back, which matters only when the value appears more than once. import Algorithms::{ Contains, IndexOf, LastIndexOf }; import Io::PrintLine; func Report(rolls: int[..], value: int) { match IndexOf(rolls, value) { at? => PrintLine("{} first appears at {}", value, at), none => PrintLine("{} was never rolled", value) } } func Main() -> int { let rolls: int[8] = [4, 2, 6, 6, 1, 3, 6, 5]; // Present once, present three times, and absent. With several matches, `IndexOf` reports the // earliest. Report(rolls, 1); Report(rolls, 6); Report(rolls, 7); match LastIndexOf(rolls, 6) { at? => PrintLine("6 last appears at {}", at), none => PrintLine("6 was never rolled") } PrintLine("contains 5 {}", Contains(rolls, 5)); PrintLine("contains 0 {}", Contains(rolls, 0)); // When a missing value has an obvious stand-in, `??` supplies it. Here the length works as // "past the end", but now the code has to remember that it means "not found". let at = IndexOf(rolls, 7) ?? rolls.length; PrintLine("7 at {} of {}", at, rolls.length); // A search of part of the array reports positions within that part, not within the array. let tail = rolls[4..]; PrintLine("6 in the tail at {}", IndexOf(tail, 6) ?? tail.length); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Algorithms` under `[Dependencies]`. ## Run it ```sh cd Examples/Algorithms/Search rux run ``` ```text 1 first appears at 4 6 first appears at 2 7 was never rolled 6 last appears at 6 contains 5 true contains 0 false 7 at 8 of 8 6 in the tail at 2 ``` ## Common mistakes ::warning **Using the answer as an index before looking.**:br`rolls[IndexOf(rolls, 6)]` fails with `error: index for type 'int[8]' must be an integer or range, but has type 'uint?'`. Match it, or supply a stand-in with `??`, and index with the plain `uint` you get out. :: ::warning **Doing arithmetic on the optional.**:br`IndexOf(rolls, 6) + 1` fails with `error: operator '+' cannot combine left operand 'uint?' with right operand 'int'`. "One past nowhere" means nothing, so the absent case has to be dealt with first. :: ::warning **Forgetting where a slice starts.**:br`IndexOf(rolls[4..], 6)` reports 2, not 6. Add the slice's starting index back when you need a position in the whole array. :: ::warning **Searching for a value of another type.**:br`Contains(rolls, 2.5)` is rejected: `T` cannot be both the `int` of the elements and the `float64` of the value. The value must have the element type. :: ## Try it yourself 1. Use `Count(rolls, 6)` to print how many sixes were rolled. Remember to add `Count` to the import. 2. Write `func IsHigh(value: int) -> bool` that accepts rolls above 4, and print the result of `IndexWhere(rolls, IsHigh) ?? rolls.length`. 3. Change `Report` to also print the last position, using `LastIndexOf`, but only when it differs from the first. 4. Search a string. Declare `let text: char8[..] = "rux-lang";` and `let dash: char8 = '-';`, then print `IndexOf(text, dash) ?? text.length`. Then try `IndexOf(text, '-')` and work out why it is rejected: a character literal with no typed partner is a `char`, not a `char8`. ## Learn more - [Slices](https://rux-lang.dev/docs/lang/slices/overview) in the Rux Reference - [Presence](https://rux-lang.dev/docs/learn/presence) and [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — the two ways to use an optional answer - [Binary search](https://rux-lang.dev/docs/learn/binary-search) — a much faster search, when the data is sorted - [Min and max](https://rux-lang.dev/docs/learn/min-max) — another search whose answer may be `none` # Binary search ::note **You'll need**: [Search](https://rux-lang.dev/docs/learn/search), [Sort](https://rux-lang.dev/docs/learn/sort) :: [Search](https://rux-lang.dev/docs/learn/search) looked at every element in turn. When the slice is already **sorted**, that is wasted effort. A **binary search** checks the middle element, throws away the half that cannot hold the value, and repeats on the half that is left. Every step halves the work, so a million elements take about twenty steps instead of a million. ## Halving, step by step `BinarySearch` answers like `IndexOf`, with a `uint?` that is `none` when the value is absent: ```rux func Report(sorted: int[..], value: int) { match BinarySearch(sorted, value) { at? => PrintLine("{} found at {}", value, at), none => PrintLine("{} not found, would go at {}", value, LowerBound(sorted, value)) } } ``` Here is how it finds 8 in `[2, 3, 5, 5, 5, 8, 13, 21]`. It keeps a range of positions that may still hold the answer and looks at the middle one: | Step | Still in play | Middle | Element there | So the answer is… | | ---- | ------------- | ------ | ------------- | -------------------- | | 1 | 0 to 7 | 4 | 5 | after 4: keep 5 to 7 | | 2 | 5 to 7 | 6 | 13 | before 6: keep 5 | | 3 | 5 | 5 | 8 | found at 5 | ```mermaid flowchart LR start["the whole slice
is in play"] --> mid["look at the middle element"] mid --> q{"middle < value?"} q -- "yes" --> right["keep the half
after the middle"] q -- "no" --> left["keep the middle
and the half before it"] right --> more{"anything
left in play?"} left --> more more -- "yes" --> mid more -- "no" --> done["the position is found:
is the value there?"] ``` Three looks instead of six. The search never reads 2, 3 or 21 at all — and it can only skip them because the order promises what they hold. ## Where a value would go Notice what the search actually narrows down to: a **position**, the first one whose element is not less than the value. `LowerBound` returns exactly that, and `BinarySearch` is `LowerBound` plus one check — is the element at that position equal to the value? So `LowerBound` is useful even when the value is absent. It says where the value *would* go to keep the slice sorted: ```rux Report(sorted, 4); Report(sorted, 99); ``` 4 belongs at index 2, between the 3 and the first 5. 99 is larger than everything, so its place is 8, the length — one past the end. A position always exists, so `LowerBound` returns a plain `uint`, never `none`. ## Bracketing duplicates `UpperBound` is the last position where the value could go: one past the last equal element. Between the two bounds lie all the equal elements: ```rux let first = LowerBound(sorted, 5); let after = UpperBound(sorted, 5); PrintLine("5 runs from {} to {}, {} of them", first, after, after - first); ``` The 5s occupy indexes 2, 3 and 4, so the bounds are 2 and 5, and `after - first` counts them without a scan. With duplicates, `BinarySearch` reports the first of them — index 2 — because it is built on `LowerBound`. | Function | Returns | Means | | ---------------------------- | ------- | ------------------------------------------- | | `BinarySearch(items, value)` | `uint?` | where an equal element is; `none` if absent | | `LowerBound(items, value)` | `uint` | the first place the value could go | | `UpperBound(items, value)` | `uint` | the last place the value could go | ## The promise nothing checks A binary search is only correct on sorted data, and nothing checks that the data is sorted. Checking would mean reading every element — the very scan a binary search exists to avoid. On unsorted data it still answers, and the answer is meaningless: ```rux let shuffled: int[3] = [9, 1, 5]; Report(shuffled, 9); ``` 9 is right there at index 0. But the first look lands on the 1 in the middle; in sorted data, everything before a 1 would be smaller than 9, so the search drops that half and never looks left again. It reports 9 as not found. Sort first, with [Sort](https://rux-lang.dev/docs/learn/sort), or keep the data sorted as you add to it — `LowerBound` tells you where each new value goes. When in doubt, `IsSorted(items)` answers with a `bool`, at the cost of the full scan. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Algorithms/BinarySearch){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // When a slice is already sorted, a search does not have to look at every element. A binary // search checks the middle, discards the half that cannot hold the value, and repeats, so a // million elements take about twenty steps instead of a million. // // `BinarySearch` answers like `IndexOf`, with a `uint?`. With duplicates it reports the first. // `LowerBound` answers a different question: the first position where the value could be // inserted without breaking the order. That is a position, not a match, so it is a plain `uint` // and never `none`. `UpperBound` is the last such position, and the equal elements lie between. // // The surprise is the promise that comes with it. The slice must be sorted, and nothing checks: // checking would cost the very scan a binary search exists to avoid. On unsorted data the search // still answers, and the answer is meaningless. import Algorithms::{ BinarySearch, LowerBound, UpperBound }; import Io::PrintLine; func Report(sorted: int[..], value: int) { match BinarySearch(sorted, value) { at? => PrintLine("{} found at {}", value, at), none => PrintLine("{} not found, would go at {}", value, LowerBound(sorted, value)) } } func Main() -> int { let sorted: int[8] = [2, 3, 5, 5, 5, 8, 13, 21]; Report(sorted, 8); Report(sorted, 4); Report(sorted, 99); // Three 5s. The bounds bracket them, and their difference counts them without a scan. let first = LowerBound(sorted, 5); let after = UpperBound(sorted, 5); PrintLine("5 runs from {} to {}, {} of them", first, after, after - first); Report(sorted, 5); // 9 is present, but the data is not sorted. The first step looks at the 1 in the middle, // concludes 9 must be to its right, and never looks left again. let shuffled: int[3] = [9, 1, 5]; Report(shuffled, 9); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Algorithms` under `[Dependencies]`. ## Run it ```sh cd Examples/Algorithms/BinarySearch rux run ``` ```text 8 found at 5 4 not found, would go at 2 99 not found, would go at 8 5 runs from 2 to 5, 3 of them 5 found at 2 9 not found, would go at 3 ``` ## Common mistakes ::warning **Searching data that is not sorted.**:br Nothing stops `BinarySearch` on unsorted data, and nothing reports it either: the program compiles, runs and prints a wrong answer, as the `[9, 1, 5]` example shows. The only cure is to sort first, or to know the data arrives sorted. :: ::warning **Matching a bound as if it were optional.**:br A bound is always a position. `match LowerBound(sorted, 4) { at? => …, none => … }` fails with `error: a presence suffix needs an optional subject, but the matched value has type 'uint'`, and `LowerBound(sorted, 4) ?? 0` with `error: operator '??' requires an optional left operand, but found 'uint'`. :: ::warning **Reading the lower bound as a match.**:br`LowerBound(sorted, 4)` is 2, and the element at index 2 is a 5, not a 4. A bound says where a value would go, not that it is there. Compare the element with the value, or use `BinarySearch`, before treating the position as a hit. :: ## Try it yourself 1. Search for 2 and for 21, the two ends. How many steps does each take? Trace them as the table does. 2. Count the 4s in `sorted` with `UpperBound` and `LowerBound`. What do the two bounds tell you when the count is zero? 3. Call `EqualRange(sorted, 5)`. It returns a range; print its `start` and `end` and compare them with the bounds above. 4. Print `IsSorted(shuffled)`, then sort `shuffled` with `Sort` (it must become `var`) and search for 9 again. ## Learn more - [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) in the Rux Reference - [Search](https://rux-lang.dev/docs/learn/search) — the linear search that works on anything - [Sort](https://rux-lang.dev/docs/learn/sort) — getting the data into the order a binary search needs - [Tree map](https://rux-lang.dev/docs/learn/tree-map) — a collection that keeps its keys sorted for you # Min and max ::note **You'll need**: [Search](https://rux-lang.dev/docs/learn/search), [Presence](https://rux-lang.dev/docs/learn/presence) :: "Which is smaller?" is a question about two values, and it always has an answer. "Which is the smallest?" is a question about a slice, and it has none when the slice is empty. The `Algorithms` package answers the two questions with different functions, and the difference is in their return types. ## The smaller of two `Min` and `Max` take two values and return one of them, unchanged: ```rux PrintLine("Min(3, 8) = {}", Min(3, 8)); PrintLine("Max(3, 8) = {}", Max(3, 8)); PrintLine("Max(-2.5, -7.0) = {}", Max(-2.5, -7.0)); ``` They are generic, so they work for any type with `<` — integers, floats, characters, and your own types once they declare `<`. Both arguments must have the same type, which is why the last line writes `-7.0` and not `-7`: a lone `-7` is an `int`, and `Max` cannot take an `int` and a `float64` at once. A third function in the family, `Clamp(value, low, high)`, keeps a value inside a range: it returns `low` for anything below it, `high` for anything above, and the value itself otherwise. ## The smallest of many Over a slice there may be nothing to compare. Rather than invent an answer for an empty slice, `MinIndex` and `MaxIndex` return an optional index, `uint?`, which is `none` when the slice is empty: ```rux func Report(temperatures: int[..]) { match MinIndex(temperatures) { day? => PrintLine("coldest: day {} at {}", day, temperatures[day]), none => PrintLine("coldest: no readings") } match MaxIndex(temperatures) { day? => PrintLine("warmest: day {} at {}", day, temperatures[day]), none => PrintLine("warmest: no readings") } } ``` They return the **position**, not the value. The position is the more useful answer — it says *which* day was coldest, not just how cold — and the value is one index away: `temperatures[day]`. ```mermaid flowchart LR s["MinIndex(temperatures)"] --> q{"is the slice empty?"} q -- "yes" --> n["none:
no readings"] q -- "no" --> w["walk the elements,
keeping the first smallest"] w --> i["day?
its index, a uint"] i --> v["temperatures[day]
is the value"] ``` | Function | Takes | Returns | Empty input | | ----------------- | ---------- | ------- | ------------- | | `Min(a, b)` | two values | `T` | cannot happen | | `Max(a, b)` | two values | `T` | cannot happen | | `MinIndex(items)` | a slice | `uint?` | `none` | | `MaxIndex(items)` | a slice | `uint?` | `none` | ## Ties go to the earliest ```rux let week: int[7] = [-3, 4, 11, 0, 11, 6, -3]; Report(week); ``` Day 4 is as warm as day 2, and day 6 as cold as day 0. Both functions report the **earlier** day: 0 for the coldest and 2 for the warmest. That is a promise, not an accident, so you can rely on it — the first coldest day is always the one you get. ## The empty case ```rux Report(week[..0]); ``` `week[..0]` is a view of no elements at all — the same array, cut down to nothing. There is nothing to compare, so both matches take their `none` arm. Without the optional, the function would have to return something for this case: index 0, which does not exist here, or a made-up number the caller could mistake for a real day. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Algorithms/MinMax){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `Min` and `Max` pick the smaller or larger of two values. They work for any type with `<`, // numbers included, and return one of the two unchanged. // // Over a whole slice the question changes, because a slice may be empty, and an empty slice has // no smallest element. Rather than invent one, `MinIndex` and `MaxIndex` answer with an optional // index, `uint?`, which is `none` for an empty slice. They give the position, not the value: the // position is the more useful answer (it says *which* day, not just how cold), and the value is // one index away. // // When several elements tie, both report the earliest. That is a promise, not an accident. import Algorithms::{ Max, MaxIndex, Min, MinIndex }; import Io::PrintLine; func Report(temperatures: int[..]) { match MinIndex(temperatures) { day? => PrintLine("coldest: day {} at {}", day, temperatures[day]), none => PrintLine("coldest: no readings") } match MaxIndex(temperatures) { day? => PrintLine("warmest: day {} at {}", day, temperatures[day]), none => PrintLine("warmest: no readings") } } func Main() -> int { PrintLine("Min(3, 8) = {}", Min(3, 8)); PrintLine("Max(3, 8) = {}", Max(3, 8)); PrintLine("Max(-2.5, -7.0) = {}", Max(-2.5, -7.0)); // Day 4 is as warm as day 2, and day 6 as cold as day 0: the earlier day is reported. let week: int[7] = [-3, 4, 11, 0, 11, 6, -3]; Report(week); // An empty view of the same array, `[..0]`. Nothing to compare, so `none` both times. Report(week[..0]); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Algorithms` under `[Dependencies]`. ## Run it ```sh cd Examples/Algorithms/MinMax rux run ``` ```text Min(3, 8) = 3 Max(3, 8) = 8 Max(-2.5, -7.0) = -2.5 coldest: day 0 at -3 warmest: day 2 at 11 coldest: no readings warmest: no readings ``` ## Common mistakes ::warning **Mixing an integer literal with a float.**:br`Max(-2.5, -7)` fails with `error: argument 1 to 'Max' has type 'float64', but parameter 'left' requires 'T'`. With no typed partner, `-7` is an `int` and `-2.5` a `float64`, so no single `T` fits both. Write `-7.0`. :: ::warning **Treating the index as the value.**:br`let cold: int = MinIndex(week);` fails with `error: cannot assign 'uint?' to 'int'`. The answer is an optional position: match it, then read `week[day]`. :: ::warning **Passing a slice to `Min`.**:br`Min(week)` fails with `error: call to 'Min' expects 2 arguments, but 1 was provided`. `Min` compares two values; for the smallest element of a slice, use `MinIndex`. :: ## Try it yourself 1. Print `Clamp(15, 0, 10)`, `Clamp(-3, 0, 10)` and `Clamp(7, 0, 10)`. Remember to import `Clamp`. 2. Add a second week of readings and print the coldest day of each, then the colder of the two coldest values with `Min`. 3. Report only the first three days with `week[..3]`. Which day is the warmest now? 4. Use `Min` and `Max` on two `char8` values. Which letter is "smaller"? ## Learn more - [Search](https://rux-lang.dev/docs/learn/search) — the other search whose answer may be `none` - [Presence](https://rux-lang.dev/docs/learn/presence) — matching `day?` and `none` - [Fold](https://rux-lang.dev/docs/learn/fold) — combining every element into one answer, of which the minimum is one example - [Number limit](https://rux-lang.dev/docs/learn/number-limit) — the smallest and largest values a type can hold, which is a different question # Fold ::note **You'll need**: [Callback](https://rux-lang.dev/docs/learn/callback), [Slice](https://rux-lang.dev/docs/learn/slice), [Literal](https://rux-lang.dev/docs/learn/literal) :: Many loops over a slice share one shape: start with an answer, walk the elements, and update the answer from each one. A total, a product, a count. `Fold` is that loop written once, so you only write the part that differs — the update. ## The step function You give `Fold` three things: the slice, the starting answer, and a **step** function. The step takes the answer so far and one element, and returns the new answer: ```rux func Add(total: int, value: int) -> int { return total + value; } ``` ```rux let values: int[5] = [3, -1, 4, -1, 5]; PrintLine("sum {}", Fold(values, 0, Add)); PrintLine("product {}", Fold(values, 1, Multiply)); ``` `Fold` calls `Add` once per element, feeding each answer into the next call: ```mermaid flowchart LR s(["0"]) -- "Add(0, 3)" --> a["3"] a -- "Add(3, -1)" --> b["2"] b -- "Add(2, 4)" --> c["6"] c -- "Add(6, -1)" --> d["5"] d -- "Add(5, 5)" --> e(["10"]) ``` Written as one expression, `Fold(items, initial, step)` computes `step(…step(step(initial, a), b)…, z)`. The product works the same way from a start of 1 — the value that leaves a product unchanged, as 0 leaves a sum unchanged. The step is a named function, passed by name as in [Callback](https://rux-lang.dev/docs/learn/callback). Rux has no closures, so a step cannot reach into the caller's local variables: everything it needs arrives as its two arguments. ## The starting answer The starting answer is also what an empty slice gives back, untouched: ```rux PrintLine("empty sum {}", Fold(values[..0], 0, Add)); ``` There are no elements, so `Add` is never called, and the result is the 0 that went in. That is why the start matters: a sum must start at 0 and a product at 1, or every answer is off by the starting value — and the empty case shows it most plainly. ## Left or right When the step is like `+`, the order of the elements does not change the answer. When it is not, it does. `AppendDigit` shifts a number one decimal place left and puts the digit in the gap: ```rux func AppendDigit(number: int, digit: int) -> int { return number * 10 + digit; } ``` `Fold` walks from the front; `FoldRight` walks from the back, with the same step: ```rux let digits: int[3] = [4, 0, 7]; PrintLine("left {}", Fold(digits, 0, AppendDigit)); PrintLine("right {}", FoldRight(digits, 0, AppendDigit)); ``` | Call | Steps | Result | | ----------------------------------- | ---------------- | ------ | | `Fold(digits, 0, AppendDigit)` | 0 → 4 → 40 → 407 | 407 | | `FoldRight(digits, 0, AppendDigit)` | 0 → 7 → 70 → 704 | 704 | ## The answer need not be an element Nothing says the answer has the element's type. `Fold` has two type parameters: `T` for the elements and `A` for the answer, and the step is a `func(A, T) -> A`. This step counts `int` elements into a `uint`: ```rux func CountNegative(count: uint, value: int) -> uint { return value < 0 ? count + 1 : count; } ``` ```rux PrintLine("negatives {}", Fold(values, 0, CountNegative)); ``` The unsuffixed `0` takes its type from the step function — `A` is `uint` — just as a literal takes the type of its partner in [Literal](https://rux-lang.dev/docs/learn/literal). For the two most common folds, the package has them ready-made: `Sum(items, initial)` adds with `+` and `Product(items, initial)` multiplies with `*`, so `Sum(values, 0)` is 10 with no step function to write. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Algorithms/Fold){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Many loops over a slice share one shape: start with an answer, walk the elements, and update // the answer from each one. A total, a product, a count. `Fold` is that loop written once. You // give it the slice, the starting answer, and a *step* function, which takes the answer so far and // one element and returns the new answer: // // Fold(items, initial, step) computes step(...step(step(initial, a), b)..., z) // // The step must be a named function, passed by name as in the Callback lesson. Rux has no // closures, so a step cannot reach into the caller's locals; everything it needs arrives as its // two arguments. Note also that the answer need not have the element's type: the last step below // counts `int` elements into a `uint`. import Algorithms::{ Fold, FoldRight }; import Io::PrintLine; func Add(total: int, value: int) -> int { return total + value; } func Multiply(product: int, value: int) -> int { return product * value; } // Shifts the number one decimal place left and puts the digit in the gap. func AppendDigit(number: int, digit: int) -> int { return number * 10 + digit; } func CountNegative(count: uint, value: int) -> uint { return value < 0 ? count + 1 : count; } func Main() -> int { let values: int[5] = [3, -1, 4, -1, 5]; PrintLine("sum {}", Fold(values, 0, Add)); PrintLine("product {}", Fold(values, 1, Multiply)); // The starting answer is what an empty slice gives back, untouched. PrintLine("empty sum {}", Fold(values[..0], 0, Add)); // Order matters when the step is not like `+`. `FoldRight` walks from the back. let digits: int[3] = [4, 0, 7]; PrintLine("left {}", Fold(digits, 0, AppendDigit)); PrintLine("right {}", FoldRight(digits, 0, AppendDigit)); // The answer here is a `uint`. The unsuffixed `0` takes that type from the step function. PrintLine("negatives {}", Fold(values, 0, CountNegative)); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Algorithms` under `[Dependencies]`. ## Run it ```sh cd Examples/Algorithms/Fold rux run ``` ```text sum 10 product 60 empty sum 0 left 407 right 704 negatives 2 ``` ## Common mistakes ::warning **Giving the start a type of its own.**:br With `let start = 0;`, the call `Fold(values, start, CountNegative)` fails with `error: argument 2 to 'Fold' has type 'int', but parameter 'initial' requires 'uint'`. A bare literal adapts to the step; a variable already has its type. Declare it `let start: uint = 0;`. :: ::warning **Writing the step inline.**:br`Fold(values, 0, func(c: uint, v: int) -> uint { … })` fails to parse: `error: expected an expression after ',' in the argument list before 'func'`. Rux has no closures or inline functions. Declare the step as a named function and pass its name. :: ::warning **Parameters in the wrong order.**:br The step takes the answer first and the element second. A `CountNegative(value: int, count: uint)` does not fit `func(A, T) -> A`, and the call is rejected. :: ::warning **Calling the step instead of passing it.**:br`Fold(values, 0, Add(0, 1))` passes the number 1, not a function, and is rejected. Write `Add` alone. :: ## Try it yourself 1. Write `func Larger(best: int, value: int) -> int` and fold `values` with it to find the largest value. What should the starting answer be? Why is 0 wrong when every value is negative, and why does `values[0]` work? 2. Count the even numbers in `values` into a `uint`. 3. Print `Sum(values, 0)` and `Product(values, 1)`, and check them against the folds above. 4. Fold `digits` with `AppendDigit` starting from 9 instead of 0. Predict both results first. ## Learn more - [Function types](https://rux-lang.dev/docs/lang/functions/function-types) and [Generic functions](https://rux-lang.dev/docs/lang/generics/overview) in the Rux Reference - [Callback](https://rux-lang.dev/docs/learn/callback) — passing a function by name - [Generic](https://rux-lang.dev/docs/learn/generic) — functions with type parameters, such as `T` and `A` here - [Min and max](https://rux-lang.dev/docs/learn/min-max) — ready-made answers to two common folds # Part 19: Files Everything so far has lived in memory and vanished when the program ended. A file is how data outlives the program — and it is also where a program meets a world it does not control: names that are not valid text, disks that fill up, files that someone else deletes or reads half-written. This part tours the `Path` package, which names files, and the `FileSystem` package, which reads, writes and manages them, and it handles every failure along the way. ## What you will learn - Why a path is not a string, and how `OsString`, `Path` and `PathBuffer` take one apart, build one and tidy one. - Opening, writing, reading and closing a file, with `IoError` handled at every step — including the end of the file, which arrives as a failure. - Making, listing and removing directories, and asking the filesystem what a name refers to. - Writing fixed-width binary records, and refusing one that the file cut short. - Gathering small writes in a buffer, and knowing when the bytes really reach the file. - Replacing a file so that no reader ever sees it half-written, and scratch files that clean up after themselves. ## A file's life Every lesson here covers a piece of one sequence: turn text into a path, open the file, move bytes, close it — and check each step, because each one asks the operating system for something it may refuse. ```mermaid sequenceDiagram participant P as Program participant F as File participant OS as Operating system Note over P: OsString::FromText(text)?
Path::FromView(…) P->>F: File::Open(path, Writing())? F->>OS: create the file, or empty it P->>F: WriteAll(file, bytes)? F->>OS: Write, again and again until every byte is out P->>F: Close()? OS-->>P: success, or a failure delayed until now P->>F: File::Open(path, Reading())? loop until the end of the file P->>F: Read(buffer[..]) F-->>P: a count of bytes, or an IoError end Note over P: IsEnd() is true: the file is read P->>F: Close()? P->>OS: DeleteFile(path)? ``` | You want to… | Use | Lesson | | -------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------- | | take a path apart | `Components`, `FileName`, `Parent` | [Path](https://rux-lang.dev/docs/learn/path) | | build a path from pieces | `Join`, `PathBuffer::Push` | [Path join](https://rux-lang.dev/docs/learn/path-join) | | compare or display paths | `Normalize` | [Path normalize](https://rux-lang.dev/docs/learn/path-normalize) | | read or write a text file | `File::Open`, `WriteAll`, `Read` | [File](https://rux-lang.dev/docs/learn/file) | | list a directory | `ReadDirectory`, `Next` | [Directory](https://rux-lang.dev/docs/learn/directory) | | ask whether a file exists, and what it is | `MetadataOf` | [Metadata](https://rux-lang.dev/docs/learn/metadata) | | store numbers as bytes | `WriteUint32`, `ReadUint32`, … | [Binary](https://rux-lang.dev/docs/learn/binary) | | make many small writes cheaply | `BufferedWriter` | [Buffered I/O](https://rux-lang.dev/docs/learn/buffered-io) | | replace a file without a half-written moment | `WriteAtomically`, `AtomicWrite` | [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) | | scratch space that removes itself | `TemporaryFile` | [Temporary file](https://rux-lang.dev/docs/learn/temporary-file) | ## Lessons | | Lesson | What you will learn | | ----- | ---------------------------------------------------------------- | --------------------------------------------------------------------- | | 19.1 | [Path](https://rux-lang.dev/docs/learn/path) | split a path into its parts, and see why a path is not a string | | 19.2 | [Path join](https://rux-lang.dev/docs/learn/path-join) | build a path from parts | | 19.3 | [Path normalize](https://rux-lang.dev/docs/learn/path-normalize) | tidy a path by removing `.` and `..` | | 19.4 | [File](https://rux-lang.dev/docs/learn/file) | write text to a file and read it back, handling failure at every step | | 19.5 | [Directory](https://rux-lang.dev/docs/learn/directory) | create, list, and remove directories | | 19.6 | [Metadata](https://rux-lang.dev/docs/learn/metadata) | ask a file for its size and kind | | 19.7 | [Binary](https://rux-lang.dev/docs/learn/binary) | read and write fixed-width values and raw bytes | | 19.8 | [Buffered I/O](https://rux-lang.dev/docs/learn/buffered-io) | buffer writes and flush them | | 19.9 | [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) | replace a file's contents all at once or not at all | | 19.10 | [Temporary file](https://rux-lang.dev/docs/learn/temporary-file) | a scratch file that cleans up after itself | ## Before you start This part leans hard on [Part 9: Errors](https://rux-lang.dev/docs/learn/errors) — every lesson's `Main` is a [fallible main](https://rux-lang.dev/docs/learn/fallible-main) with an [error sum](https://rux-lang.dev/docs/learn/error-sum) on its failure side, and the lessons use [Catch](https://rux-lang.dev/docs/learn/catch), [Guard](https://rux-lang.dev/docs/learn/guard), [Nested fallible](https://rux-lang.dev/docs/learn/nested-fallible) and [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit). It also uses [Move](https://rux-lang.dev/docs/learn/move) and [Destructor](https://rux-lang.dev/docs/learn/destructor) from Part 11, [Interface value](https://rux-lang.dev/docs/learn/interface-value) and [Iterator](https://rux-lang.dev/docs/learn/iterator) from Part 12, [Allocator](https://rux-lang.dev/docs/learn/allocator) from Part 15 and [Endian](https://rux-lang.dev/docs/learn/endian) from Part 16. Each lesson's package is in the Examples repository's `Files/` folder: ```sh cd Examples/Files/File rux run ``` The programs create their files inside their own package's `Bin/` folder and delete them before they finish. If a run stops part-way, a leftover file or `Bin/scratch` directory may need deleting by hand. ## After this part [Part 20: Utilities](https://rux-lang.dev/docs/learn/utilities) covers time, randomness, hashing and UUIDs — including the `Timestamp` that [Metadata](https://rux-lang.dev/docs/learn/metadata) reported. [Part 21: Data formats](https://rux-lang.dev/docs/learn/data-formats) then gives your files a structure, and its checkpoint project, [Notes](https://rux-lang.dev/docs/learn/notes), keeps a to-do list as JSON in a file — saved with `WriteAtomically`, loaded back, and recovered when the file is missing or damaged. The Rux Reference describes the language features these packages are built from: [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview), the mechanism behind `Reader` and `Writer`, and [Enumerations](https://rux-lang.dev/docs/lang/enums/overview), the shape of `IoErrorKind` and `FileKind`. # Path ::note **You'll need**: [Fallible main](https://rux-lang.dev/docs/learn/fallible-main), [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Iterator](https://rux-lang.dev/docs/learn/iterator), [Allocator](https://rux-lang.dev/docs/learn/allocator) :: Every file a program touches is named by a **path**: `Bin/reports/summary.txt`. A path looks like a string, and the first lesson of this part is why it is not one. The `Path` package gives paths their own types, and this lesson takes one apart — into its components, its file name, stem and extension, and its parent — without ever asking the disk whether the file exists. ## A path is not text The operating system decides what a file name may hold, and its answer is not "valid text". On Windows a name is a run of 16-bit units that need not form valid UTF-16; on Linux and macOS it is bytes that need not form valid UTF-8. A type that insisted on text could not name every file that exists. So the `Path` package has its own types, built on the units the system uses: | Type | What it is | | -------------- | --------------------------------------------------------------------------------------------------- | | `OsString` | owns a name in the system's own units; needs an allocator | | `OsStringView` | a borrowed, read-only look at such units | | `Path` | a borrowed view with path meaning attached: components, parent, extension | | `PathBuffer` | an owned path that can grow — the subject of [Path join](https://rux-lang.dev/docs/learn/path-join) | Text goes in through `OsString::FromText`, which converts it to the system's units and so needs memory — an `Allocator`, the interface from [Allocator](https://rux-lang.dev/docs/learn/allocator): ```rux var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin//reports/summary.txt")?; let path = Path::FromView(holder.View()); ``` `FromText` returns `OsString ! TextError`, so the `?` passes a failure on, and `Main` is fallible — `func Main() -> ! TextError`, as in [Fallible main](https://rux-lang.dev/docs/learn/fallible-main). The `OsString` is the **holder**: it owns the units. `path` is only a view of them, so the holder must stay alive for as long as the path is used. ```mermaid flowchart LR t["text
char8[..]"] -- "OsString::FromText(…)?" --> h["OsString
owns the units"] h -- ".View()" --> v["OsStringView"] v -- "Path::FromView" --> p["Path
a view with path meaning"] p -- "Components, FileName,
Stem, Extension, Parent" --> parts["more views
of the same units"] p -- ".AsView().ToText(…)?" --> s["String
text again"] ``` ## Components A path is a sequence of components, and the separators between them belong to the platform: Windows accepts `/` and `\`, the others only `/`. `Components` returns an [iterator](https://rux-lang.dev/docs/learn/iterator), so `for` walks it: ```rux for part in path.Components() { Print(" [{}]", part); } ``` The output is `[Bin] [reports] [summary.txt]`. A run of separators counts as one, so the doubled slash yields no empty component — which is exactly what splitting the text on `"/"` by hand would get wrong. ## Named parts may be missing `FileName`, `Stem` and `Extension` each return an `OsStringView?`, because each can be absent: a root such as `/` has no file name, and `Makefile` has no extension. `ShowPart` matches the optional before printing: ```rux func ShowPart(label: char8[..], part: OsStringView?) { match part { name? => PrintLine("{:10} {}", label, name), none => PrintLine("{:10} (none)", label) } } ``` | Call | For `Bin//reports/summary.txt` | | ------------- | ------------------------------ | | `FileName()` | `summary.txt` | | `Stem()` | `summary` | | `Extension()` | `txt` — without the dot | | `Parent()` | `Bin//reports` | `Parent` drops the last component and returns a `Path?`, absent once there is nothing left to drop. Here [`??`](https://rux-lang.dev/docs/learn/coalesce) supplies an empty `Path()` in that case: ```rux let parent = path.Parent() ?? Path(); ``` The parent keeps the doubled slash: these calls only pick out part of the units, and never rewrite them. Tidying a path is the job of [Path normalize](https://rux-lang.dev/docs/learn/path-normalize). ## Back to text Printing with `{}` always works: any unit that is not text is swapped for the replacement character U+FFFD. That is fine for a message and useless for reopening the file. The exact way back is `ToText`, and it is fallible, because a name from the system may not be text at all: ```rux let text = path.AsView().ToText(allocator)?; ``` Here it succeeds, because this name began as text. A name read from a directory listing comes with no such guarantee. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/Path){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A path looks like a string and is not one. // // The operating system decides what a file name may hold, and its answer is not "valid text". // On Windows a name is 16-bit units that need not form valid UTF-16; on Linux and macOS it is // bytes that need not form valid UTF-8. A type that insisted on text could not name every file // that exists. So the `Path` package has `OsString`, which holds the units the system holds, // and `Path`, a borrowed view of them with path meaning attached. // // That meaning is structure. A path is a sequence of components, and the separators between // them belong to the platform: Windows accepts `/` and `\`, the others only `/`. Runs of // separators count as one, so splitting the text on "/" by hand would invent empty pieces. // Nothing here asks the filesystem anything: a path need not name a file that exists. // // Text goes in through `OsString::FromText` and comes back out through `ToText`. Both return // `T ! TextError`, and the way out is the one that can really fail: a name from the system may // not be text at all. `{}` shows a path anyway, swapping any unit that is not text for U+FFFD, // which is fine for a message and useless for reopening the file. import Allocator::{ Allocator, SystemAllocator }; import Io::{ Print, PrintLine }; import Path::{ OsString, OsStringView, Path }; import Text::TextError; // The named parts are optional: a root has no file name, and `Makefile` has no extension. func ShowPart(label: char8[..], part: OsStringView?) { match part { name? => PrintLine("{:10} {}", label, name), none => PrintLine("{:10} (none)", label) } } func Main() -> ! TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin//reports/summary.txt")?; let path = Path::FromView(holder.View()); PrintLine("{:10} {}", "path", path); // `Components` is an iterator, so `for` walks it. The doubled slash yields no empty part. Print("{:10}", "components"); for part in path.Components() { Print(" [{}]", part); } PrintLine(); ShowPart("file name", path.FileName()); ShowPart("stem", path.Stem()); ShowPart("extension", path.Extension()); // `Parent` drops the last component, and is absent once there is nothing left to drop. let parent = path.Parent() ?? Path(); PrintLine("{:10} {}", "parent", parent); // The exact conversion back to text. It succeeds here because the name began as text. let text = path.AsView().ToText(allocator)?; PrintLine("{:10} {}", "as text", text); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/Path rux run ``` ```text path Bin//reports/summary.txt components [Bin] [reports] [summary.txt] file name summary.txt stem summary extension txt parent Bin//reports as text Bin//reports/summary.txt ``` ## Common mistakes ::warning **Forgetting the `?` after `FromText`.**:br Without it, `holder` is the whole `OsString ! TextError`, not the string, and `holder.View()` fails with `error: type 'OsString ! TextError' has no field 'View'`. Unwrap the fallible first. :: ::warning **Using `?` in a `Main` that returns `int`.**:br`?` needs somewhere to send the failure. In `func Main() -> int`, it fails with `error: '?' propagates native fallible 'OsString ! TextError', but the enclosing function returns 'int'`, and the help suggests the fix: declare the error channel, as `Main` here does with `-> ! TextError`. :: ::warning **Passing text where a path is expected.**:br`Path::FromView("Bin/notes.txt")` fails with `error: argument 1 to 'Path::FromView' has type 'char8[..]', but parameter 'view' requires 'OsStringView'`. Text never becomes a path on its own; it goes through `OsString::FromText` first. :: ::warning **Printing a part without unwrapping it.**:br`PrintLine("{}", path.Parent())` fails with `variadic parameter 'args' requires 'Display'`, naming `'Path?'`. An optional has no text of its own; match it or supply a fallback with `??`. :: ::warning **Backslashes in a string literal.**:br`"C:\Users\me"` fails to compile with `error: escape sequence '\U' is not recognized`, because `\` starts an escape. Write `"C:\\Users\\me"`, or simply use `/`, which Windows accepts too. :: ## Try it yourself 1. Change the path to `archive.tar.gz` and predict the stem and extension before you run it. 2. Try `.gitignore` and `Makefile`. Which parts are `none`, and which are just short? 3. Walk up the tree: call `Parent` in a loop, printing each path, until it returns `none` (`current = current.Parent() ?? break;`). What is the last path printed before that? 4. Print the number of components by counting them in the `for` loop. ## Learn more - [Path join](https://rux-lang.dev/docs/learn/path-join) — building a path from parts with `PathBuffer` - [Allocator](https://rux-lang.dev/docs/learn/allocator) — where an `OsString`'s memory comes from - [Encoding](https://rux-lang.dev/docs/learn/encoding) and [UTF-8](https://rux-lang.dev/docs/learn/utf8) — why a name that is not valid text is a real possibility - [Iterator](https://rux-lang.dev/docs/learn/iterator) — what makes `for part in path.Components()` work # Path join ::note **You'll need**: [Path](https://rux-lang.dev/docs/learn/path), [Move](https://rux-lang.dev/docs/learn/move) :: Programs build paths all the time: a folder from the settings plus a file name, a base directory plus a date. It is tempting to glue two strings with a `/` between them, and gluing goes wrong in three ways. It doubles the separator when the base already ends in one; it writes `/` where Windows prefers `\`; and it does something odd when the second piece is itself a full path. `Path::Join` knows the rules. ## Join ```rux func Join(allocator, base: Path, segment: OsStringView) -> PathBuffer ! TextError ``` The base is a `Path` and the segment an `OsStringView`, so text goes through `OsString::FromText` first, as in [Path](https://rux-lang.dev/docs/learn/path). The lesson wraps that in a helper so each example is one line: ```rux func JoinText(allocator: Allocator, base: char8[..], segment: char8[..]) -> PathBuffer ! TextError { var baseHolder = OsString::FromText(allocator, base)?; var segmentHolder = OsString::FromText(allocator, segment)?; return Join(allocator, Path::FromView(baseHolder.View()), segmentHolder.View()); } ``` Joining is fallible only because the new path needs memory. It never looks at the disk: neither piece has to exist. ## The rules ```mermaid flowchart LR j["Join(base, segment)"] --> e{"segment empty?"} e -- "yes" --> same["the base, unchanged"] e -- "no" --> abs{"segment starts
with a separator?"} abs -- "yes" --> rep["the segment alone:
it replaces the base"] abs -- "no" --> tail{"base ends
with a separator?"} tail -- "yes" --> app["base + segment"] tail -- "no" --> sep["base + preferred
separator + segment"] ``` The program runs all four cases. The output on the page is from Windows, where the preferred separator is `\`; on Linux and macOS it is `/`: | Base | Segment | Windows | Linux and macOS | Why | | ------ | ------------- | ------------- | --------------- | ------------------------------------- | | `Bin` | `reports` | `Bin\reports` | `Bin/reports` | a separator is added, the platform's | | `Bin/` | `reports` | `Bin/reports` | `Bin/reports` | the base already ends in one | | `Bin` | (empty) | `Bin` | `Bin` | an empty segment changes nothing | | `Bin` | `/etc/passwd` | `/etc/passwd` | `/etc/passwd` | an absolute segment replaces the base | The last rule is what joining means in most languages and shells, and it is a trap. If the segment comes from outside — a file name typed by a user, a name inside an archive — joining it onto a safe base can escape the base entirely. Check untrusted input before you join it. ## PathBuffer: a path that owns its units `Join` returns a `PathBuffer`, not a `Path`. A `Path` only borrows units someone else holds; a `PathBuffer` owns its own, so it can be returned from `JoinText` and outlive the holders inside it. Owning has a consequence from [Move](https://rux-lang.dev/docs/learn/move): a `PathBuffer` is move-only. Anything that reads a path — printing it, passing it to another `Path` function — borrows it as a `Path` through `AsPath`: ```rux let plain = JoinText(allocator, "Bin", "reports")?; PrintLine("Bin + reports {}", plain.AsPath()); ``` | Type | Owns its units? | Can grow? | Copyable? | Use it to | | ------------ | --------------- | --------- | ----------------- | ---------------- | | `Path` | no — a view | no | yes, it is a view | read and pass on | | `PathBuffer` | yes | `Push` | no — move-only | build and keep | ## Growing one buffer with Push `Push` adds a segment to the end of a buffer in place, by the same rules as `Join`. It starts from an empty `PathBuffer(allocator)`, which must be `var` because `Push` changes it: ```rux var built = PathBuffer(allocator); let pieces = ["Bin", "reports", "2026", "summary.txt"]; for piece in pieces { var holder = OsString::FromText(allocator, piece)?; built.Push(holder.View())?; } ``` Four pushes, three separators added, none doubled: `Bin\reports\2026\summary.txt` on Windows. `Push` is fallible for the same reason `Join` is — growing needs memory. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/PathJoin){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Joining two paths is not gluing two strings with a `/` between them. Gluing doubles the // separator when the base already ends in one, writes `/` where Windows prefers `\`, and does // something odd when the second piece is itself a full path. `Path::Join` knows the rules: // // func Join(allocator, base: Path, segment: OsStringView) -> PathBuffer ! TextError // // - a separator is added only where one is missing, and it is the platform's preferred one; // - a segment that starts with a separator is absolute, and replaces the base instead of // extending it, which is what joining means in most languages and shells. // // The result is a `PathBuffer`: a path that owns its units and can grow with `Push`, which // follows the same rules. A buffer is move-only, so to print it or pass it to anything that // reads a path, lend it out as a `Path` through `AsPath`. // // Joining is fallible only because the new buffer needs memory; it never looks at the disk. // On Windows the separators it adds are `\`; on Linux and macOS they are `/`. import Allocator::{ Allocator, SystemAllocator }; import Io::PrintLine; import Path::{ Join, OsString, Path, PathBuffer }; import Text::TextError; // Joins two pieces of text, both converted to native names first. func JoinText(allocator: Allocator, base: char8[..], segment: char8[..]) -> PathBuffer ! TextError { var baseHolder = OsString::FromText(allocator, base)?; var segmentHolder = OsString::FromText(allocator, segment)?; return Join(allocator, Path::FromView(baseHolder.View()), segmentHolder.View()); } func Main() -> ! TextError { var system = SystemAllocator(); let allocator: Allocator = system; let plain = JoinText(allocator, "Bin", "reports")?; PrintLine("Bin + reports {}", plain.AsPath()); // The base already ends in a separator, so none is added. let trailing = JoinText(allocator, "Bin/", "reports")?; PrintLine("Bin/ + reports {}", trailing.AsPath()); // An empty segment changes nothing. let empty = JoinText(allocator, "Bin", "")?; PrintLine("Bin + (empty) {}", empty.AsPath()); // An absolute segment wins. Joining untrusted input onto a safe base can escape it. let absolute = JoinText(allocator, "Bin", "/etc/passwd")?; PrintLine("Bin + /etc/passwd {}", absolute.AsPath()); // Growing one buffer in place, a segment at a time. var built = PathBuffer(allocator); let pieces = ["Bin", "reports", "2026", "summary.txt"]; for piece in pieces { var holder = OsString::FromText(allocator, piece)?; built.Push(holder.View())?; } PrintLine("pushed four {}", built.AsPath()); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/PathJoin rux run ``` ```text Bin + reports Bin\reports Bin/ + reports Bin/reports Bin + (empty) Bin Bin + /etc/passwd /etc/passwd pushed four Bin\reports\2026\summary.txt ``` ## Common mistakes ::warning **Printing a buffer directly.**:br`PrintLine("{}", plain)` fails with `error: move-only value 'plain' requires an explicit '<-' in argument`. Printing would move the buffer away. Lend it instead: `plain.AsPath()`. :: ::warning **Copying a buffer.**:br`let copy = plain;` fails with `error: move-only value 'plain' requires an explicit '<-' in initialization`, and a note that `'PathBuffer' prohibits copying`. Move it with `<-plain` if you mean to hand it over, or make a new one with `PathBuffer::FromPath`. :: ::warning **Passing a buffer where a `Path` is expected.**:br`Normalize(allocator, plain)` fails with `has type 'PathBuffer', but parameter 'path' requires 'Path'`. A buffer is not a path; `plain.AsPath()` is. :: ::warning **Joining text directly.**:br`Join(allocator, base, "reports")` fails with `error: argument 3 to 'Join' has type 'char8[..]', but parameter 'segment' requires 'OsStringView'`. Convert the text with `OsString::FromText` first, as `JoinText` does. :: ::warning **Pushing onto a `let` buffer.**:br`let built = PathBuffer(allocator);` and then `built.Push(…)` fails with `error: cannot call 'Push' on immutable 'built'`, and a note that `Push` declares a writable receiver. :: ## Try it yourself 1. Join `Bin` and `reports/2026` in one call. Is the `/` inside the segment rewritten? 2. Join `Bin` and `../secrets.txt`. `Join` keeps the `..`; what would [Path normalize](https://rux-lang.dev/docs/learn/path-normalize) make of it? 3. Write `func IsSafeSegment(text: char8[..]) -> bool` that refuses a segment starting with `/` or `\`, and use it before joining. 4. Build `Bin/reports/2026/summary.txt` with `Join` calls instead of `Push`. How many buffers do you end up with? ## Learn more - [Path](https://rux-lang.dev/docs/learn/path) — `OsString`, `Path` and the holder that keeps a view alive - [Move](https://rux-lang.dev/docs/learn/move) — why a `PathBuffer` is passed with `<-` and lent with `AsPath` - [Path normalize](https://rux-lang.dev/docs/learn/path-normalize) — tidying the result of a join - [File](https://rux-lang.dev/docs/learn/file) — opening the path you have built # Path normalize ::note **You'll need**: [Path join](https://rux-lang.dev/docs/learn/path-join) :: The same place can be spelled many ways: `Bin/reports`, `Bin//reports`, `Bin/./reports`, `Bin/old/../reports`. That makes paths awkward to compare, to show to a person, or to use as keys. `Path::Normalize` rewrites a path into one tidy spelling — and the most important thing to learn about it is what it *cannot* know. ## Three rewrites ```rux func Normalize(allocator, path: Path) -> PathBuffer ! TextError ``` Like `Join`, it builds a new `PathBuffer` and is fallible only because that needs memory. It applies three rules: - every run of separators becomes one preferred separator — `\` on Windows, `/` elsewhere; - `.` segments go, because `.` means "right here"; - `..` removes the segment before it. The lesson's `Tidy` helper converts text and normalizes it, and `Show` prints the before and after side by side: ```rux func Tidy(allocator: Allocator, text: char8[..]) -> PathBuffer ! TextError { var holder = OsString::FromText(allocator, text)?; return Normalize(allocator, Path::FromView(holder.View())); } ``` The output on the page is from Windows; on Linux and macOS the separators are `/`. | Written | Normalized (Windows) | What happened | | ----------------------------- | --------------------- | --------------------------------------- | | `Bin//reports/./2026` | `Bin\reports\2026` | `//` became one separator, `.` went | | `Bin/old/../reports` | `Bin\reports` | `..` cancelled `old` | | `Bin/reports/2026/../../logs` | `Bin\logs` | two `..` cancelled two segments | | `../shared/./notes.txt` | `..\shared\notes.txt` | a leading `..` has nothing to cancel | | `/../etc` | `\etc` | at the root there is nowhere further up | | `Bin/reports/` | `Bin\reports` | the trailing separator went | ## Lexical, not real ```mermaid flowchart LR p["a Path"] --> n["Normalize"] n --> t["looks only at the text"] t --> ok["right for display,
comparison and deduplication"] t -.-> blind["never asks the disk:
links, existence, case"] blind -.-> fs["FileSystem::Canonicalize
asks the disk,
for paths that exist"] ``` The work is **lexical**: it looks only at the text, and never asks the filesystem. That is why it is fast and cannot fail for lack of a file — and it is also the source of its limits: - `link/..` is dropped even when `link` is a symbolic link to somewhere else entirely, where the real parent is a different directory. The text cannot know. - The result is not made absolute, and not checked to exist. - On Windows it is not case-folded, so `bin` and `Bin` still differ although they name one folder. Use `Normalize` to display, compare or deduplicate paths. When the true answer matters — "are these the same file?" — ask the filesystem: `FileSystem::Canonicalize` resolves links, but only for paths that exist. ## Comparing paths `Equals` compares two paths unit by unit, exactly as written, so two spellings of one place differ until both are normalized: ```rux PrintLine("equal as written {}", one.Equals(two)); let tidyOne = Normalize(allocator, one)?; let tidyTwo = Normalize(allocator, two)?; PrintLine("equal normalized {}", tidyOne.AsPath().Equals(tidyTwo.AsPath())); ``` `Bin//reports` and `Bin/old/../reports` are not equal as written, and are equal once tidied. `Equals` is a method of `Path`, so the two buffers are lent out with `AsPath` first. ## A helper that can fail `Show` itself is fallible — it calls `Tidy`, which can fail — so every call in `Main` ends in `?`: ```rux Show(allocator, "Bin//reports/./2026")?; ``` A fallible call that stands alone as a statement must be dealt with, even when its success carries no value. That is the rule from [Discard](https://rux-lang.dev/docs/learn/discard), and the next lesson leans on it at every step. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/PathNormalize){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // The same place can be spelled many ways: `Bin/reports`, `Bin//reports`, `Bin/./reports` and // `Bin/old/../reports`. `Path::Normalize` rewrites a path into one tidy spelling: // // func Normalize(allocator, path: Path) -> PathBuffer ! TextError // // - every run of separators becomes one preferred separator: `\` on Windows, `/` elsewhere; // - `.` segments go, because `.` means "right here"; // - `..` removes the segment before it. // // The work is lexical: it looks only at the text and never asks the disk. That is the source of // its limits, and they are the point of this lesson. // // - A leading `..` in a relative path has nothing to cancel, so it stays. // - At the root there is nowhere further up, so `/..` simply vanishes. // - `link/..` is dropped even when `link` is a symbolic link to somewhere else entirely, where // the real parent is not the same directory. The text cannot know. // - The result is not made absolute, not checked to exist, and on Windows not case-folded, so // `bin` and `Bin` still differ although they name one folder. // // Use it to display, compare or deduplicate paths. When the true answer matters, ask the // filesystem: `FileSystem::Canonicalize` resolves links, but only for paths that exist. import Allocator::{ Allocator, SystemAllocator }; import Io::PrintLine; import Path::{ Normalize, OsString, Path, PathBuffer }; import Text::TextError; func Tidy(allocator: Allocator, text: char8[..]) -> PathBuffer ! TextError { var holder = OsString::FromText(allocator, text)?; return Normalize(allocator, Path::FromView(holder.View())); } func Show(allocator: Allocator, text: char8[..]) -> ! TextError { let tidy = Tidy(allocator, text)?; PrintLine("{:28} {}", text, tidy.AsPath()); } func Main() -> ! TextError { var system = SystemAllocator(); let allocator: Allocator = system; Show(allocator, "Bin//reports/./2026")?; Show(allocator, "Bin/old/../reports")?; Show(allocator, "Bin/reports/2026/../../logs")?; Show(allocator, "../shared/./notes.txt")?; Show(allocator, "/../etc")?; Show(allocator, "Bin/reports/")?; PrintLine(); // `Equals` compares units exactly, so two spellings of one place differ until both are // normalized. var oneHolder = OsString::FromText(allocator, "Bin//reports")?; var twoHolder = OsString::FromText(allocator, "Bin/old/../reports")?; let one = Path::FromView(oneHolder.View()); let two = Path::FromView(twoHolder.View()); PrintLine("equal as written {}", one.Equals(two)); let tidyOne = Normalize(allocator, one)?; let tidyTwo = Normalize(allocator, two)?; PrintLine("equal normalized {}", tidyOne.AsPath().Equals(tidyTwo.AsPath())); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/PathNormalize rux run ``` ```text Bin//reports/./2026 Bin\reports\2026 Bin/old/../reports Bin\reports Bin/reports/2026/../../logs Bin\logs ../shared/./notes.txt ..\shared\notes.txt /../etc \etc Bin/reports/ Bin\reports equal as written false equal normalized true ``` ## Common mistakes ::warning **Comparing paths with `==`.**:br`one == two` fails with `error: structural equality for 'Path' is unavailable because element type …` — a path holds the system's units, which have no `==` here. Use `one.Equals(two)`, after normalizing both if the spelling may differ. :: ::warning **Calling `Equals` on a buffer.**:br`tidyOne.Equals(tidyTwo)` fails with `error: struct 'PathBuffer' has no field 'Equals'`. Compare the paths the buffers lend: `tidyOne.AsPath().Equals(tidyTwo.AsPath())`. :: ::warning **Dropping the `?` on a fallible helper.**:br`Show(allocator, "/../etc");` without the `?` fails with `error: fallible result of type '! TextError' is discarded`, and a note: `a failure that nothing handles is lost`. :: ::warning **Trusting a normalized path to be safe.**:br`../shared/notes.txt` stays `..\shared\notes.txt`: normalizing does not stop a path from leaving a directory. And `link/..` is tidied away without looking at where `link` points. For security decisions, ask the filesystem. :: ## Try it yourself 1. Normalize `./a/./b/.` and `a/b/../../..`. Predict both before running. 2. Write `func SamePlace(allocator: Allocator, left: char8[..], right: char8[..]) -> bool ! TextError` that normalizes both and compares them. 3. Normalize `Bin` and `bin`, and compare the results. Then ask yourself whether they name the same folder on your system. 4. Join `Bin` and `../secrets.txt` as in [Path join](https://rux-lang.dev/docs/learn/path-join), then normalize the result. ## Learn more - [Path join](https://rux-lang.dev/docs/learn/path-join) — `PathBuffer`, which `Normalize` returns - [Path](https://rux-lang.dev/docs/learn/path) — components, which are what `Normalize` rewrites - [Discard](https://rux-lang.dev/docs/learn/discard) — why a fallible statement cannot simply be left alone - [Metadata](https://rux-lang.dev/docs/learn/metadata) — asking the filesystem about a path, instead of reading its text # File ::note **You'll need**: [Path](https://rux-lang.dev/docs/learn/path), [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Catch](https://rux-lang.dev/docs/learn/catch), [Guard](https://rux-lang.dev/docs/learn/guard) :: A file is where a program's data outlives the program. Writing one and reading it back takes only a handful of calls, but every one of them can fail for reasons outside the program: the disk is full, the name is taken, permission is refused, the file is not there. So every step returns a fallible, `T ! IoError`, and this lesson accounts for each one. `Main` is fallible too: `func Main() -> ! IoError | TextError`. Its failure side is an [error sum](https://rux-lang.dev/docs/learn/error-sum) of two errors — `IoError` from the file calls and `TextError` from turning text into a path. A `?` anywhere passes a failure on, and the program ends with exit status 1. ## Opening a file `File::Open` takes an allocator, a [path](https://rux-lang.dev/docs/learn/path) and an `OpenOptions` saying what you mean to do: ```rux var writing = File::Open(allocator, path, OpenOptions::Writing())?; ``` The options decide both what the handle may do and what happens to the file itself: | Options | The handle may | If the file is missing | If it exists | | -------------------------- | ---------------- | ---------------------- | ------------------------ | | `OpenOptions::Reading()` | read | fails with `NotFound` | opened as it is | | `OpenOptions::Writing()` | write | created | emptied first | | `OpenOptions::Appending()` | write at the end | created | kept; writes go after it | The open file is a `File`, and it must be `var`: writing, reading and closing all change it. ## Writing, and closing ```rux WriteAll(writing, "first line\nsecond line\n")?; writing.Close()?; ``` `File::Write` is a single attempt: it may move fewer bytes than asked, and the count it returns says how many. `Io::WriteAll` keeps calling `Write` until every byte is out, which is almost always what you want. `Close` is fallible as well. The system may have delayed reporting a failed write until now, so a close that is not checked can lose an error that happened earlier. ## Reading until the end `File::Read` fills as much of a buffer as it can in one attempt and returns how many bytes it put there. The lesson's buffer is only eight bytes, so the 23 bytes of the file take several reads: ```rux var buffer: char8[8]; var reads = 0; var total: uint = 0; loop { let filled = reading.Read(buffer[..8]) catch { error if error.IsEnd() => break, error => fail error }; Print("{}", buffer[..filled]); reads += 1; total += filled; } ``` The surprise is how the end arrives. It is not a count of zero: it comes on the **failure** channel, as an `IoError` whose `IsEnd()` is true. So the [`catch`](https://rux-lang.dev/docs/learn/catch) has two arms, separated by a [guard](https://rux-lang.dev/docs/learn/guard): the end of the file breaks out of the loop, and any other error is a real failure, passed on with `fail`. ```mermaid flowchart LR r["reading.Read(buffer[..8])"] --> q{"what came back?"} q -- "a count" --> use["print buffer[..filled],
add it up, read again"] use --> r q -- "an IoError,
IsEnd() true" --> done["break: the file is read"] q -- "any other
IoError" --> fail["fail error:
Main ends with status 1"] ``` Only the first `filled` bytes are new. The rest of the buffer still holds whatever the previous read left there, which is why the loop prints `buffer[..filled]`, never the whole buffer. | Read | Bytes | Buffer holds | | ---- | ----- | ---------------------------------------- | | 1 | 8 | `first li` | | 2 | 8 | `ne`, a newline, `secon` | | 3 | 7 | `d line` and a newline, plus a stale `n` | | 4 | — | the end of the file: `break` | ## Handling a failure instead of passing it on Not every failure should end the program. After `DeleteFile` the file is gone, so opening it again must fail — and here that is the expected outcome. A `match` on the fallible takes it apart: ```rux match File::Open(allocator, path, OpenOptions::Reading()) { .Success(_) => PrintLine("still there?"), .Failure(error) if error.kind == IoErrorKind::NotFound => PrintLine("deleted, as expected"), .Failure(error) => fail error } ``` `error.kind` is an `IoErrorKind`, an enum that names what went wrong in the same way on every platform: `NotFound`, `PermissionDenied`, `AlreadyExists`, `StorageFull` and more. The guard picks out the one failure this code expects; anything else is still passed on. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/File){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Writing a text file and reading it back, with every failure accounted for. // // Each step can fail for reasons outside the program: the disk is full, the name is taken, // permission is refused. So each one returns `T ! IoError`, and `Main` is fallible: `?` passes a // failure on and the program ends with exit status 1. Its failure side also names `TextError`, // which turning text into a path can produce. // // Two surprises are worth knowing before the code. // // - `File::Write` and `File::Read` are single attempts. Each may move fewer bytes than asked, // and the count it returns says how many. `Io::WriteAll` keeps calling `Write` until every // byte is out, and a reader loops in the same way. // - The end of a file is not a count of zero. It arrives on the failure channel, as an // `IoError` whose `IsEnd()` is true, so the read loop's `catch` treats it as "done" and any // other error as a real failure. // // The file lives in this package's `Bin/` folder, and the program deletes it at the end. import Allocator::{ Allocator, SystemAllocator }; import FileSystem::{ DeleteFile, File, OpenOptions }; import Io::{ IoError, IoErrorKind, Print, PrintLine, WriteAll }; import Path::{ OsString, Path }; import Text::TextError; func Main() -> ! IoError | TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin/notes.txt")?; let path = Path::FromView(holder.View()); // `Writing` creates the file, or empties it if it already exists. var writing = File::Open(allocator, path, OpenOptions::Writing())?; WriteAll(writing, "first line\nsecond line\n")?; // Closing can report a failure that was delayed until now, so it is fallible too. writing.Close()?; PrintLine("wrote {}", path); // `Reading` requires the file to exist. A small buffer makes the loop take several reads. var reading = File::Open(allocator, path, OpenOptions::Reading())?; var buffer: char8[8]; var reads = 0; var total: uint = 0; loop { let filled = reading.Read(buffer[..8]) catch { error if error.IsEnd() => break, error => fail error }; // Only the first `filled` bytes are new. The rest of the buffer is left over. Print("{}", buffer[..filled]); reads += 1; total += filled; } reading.Close()?; PrintLine("read {} bytes in {} reads", total, reads); DeleteFile(allocator, path)?; // Handling a failure instead of passing it on: the file is gone, so opening it fails. match File::Open(allocator, path, OpenOptions::Reading()) { .Success(_) => PrintLine("still there?"), .Failure(error) if error.kind == IoErrorKind::NotFound => PrintLine("deleted, as expected"), .Failure(error) => fail error } } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `FileSystem`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/File rux run ``` ```text wrote Bin/notes.txt first line second line read 23 bytes in 3 reads deleted, as expected ``` ## Common mistakes ::warning **Leaving `Close` unchecked.**:br`writing.Close();` fails with `error: fallible result of type '! IoError' is discarded`. Close can report a failure that was delayed until then, so handle it like any other step. :: ::warning **Treating the end of the file as an error.**:br Replace the `catch` with `?` and the program compiles, prints the file — and then ends with exit status 1 and no message, because the end arrives as an `IoError` and `?` passes it on. It never reaches `DeleteFile`, so `Bin/notes.txt` is left behind. Catch the end with `IsEnd()`. :: ::warning **Printing the whole buffer.**:br`Print("{}", buffer[..])` prints the stale bytes too: the output gains a stray `n` after `second line`, left over from the second read. Print `buffer[..filled]`. :: ::warning **Opening a file with `let`.**:br`let writing = File::Open(…)?;` makes the later calls fail: `error: argument 1 to 'WriteAll' cannot borrow immutable 'writing' as '&var Writer'`, and `error: cannot call 'Close' on immutable 'writing'`. An open file changes as you use it; declare it `var`. :: ::warning **Passing text as the path.**:br`File::Open(allocator, "Bin/notes.txt", …)` fails with `error: argument 2 to 'File::Open' has type 'char8[..]', but parameter 'path' requires 'Path'`. Convert the text with `OsString::FromText` and take a `Path` of it, as at the top of `Main`. :: ## Try it yourself 1. Change the buffer to 4 bytes, and then to 64. How many reads does the file take each time? 2. Open the file a second time with `OpenOptions::Appending()`, add a third line, and read the whole file back. 3. Open a file that does not exist with `OpenOptions::Reading()` and let `?` pass the failure on. Check the exit status with `echo $?`. 4. Count the lines as you read, by counting the newline characters in each `buffer[..filled]`. ## Learn more - [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Catch](https://rux-lang.dev/docs/learn/catch) and [Guard](https://rux-lang.dev/docs/learn/guard) — the tools this lesson combines - [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) — what happens when `?` reaches `Main` - [Buffered I/O](https://rux-lang.dev/docs/learn/buffered-io) — fewer, larger reads and writes - [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) — replacing a file so that no reader ever sees it half-written - [`loop`](https://rux-lang.dev/docs/lang/statements/loops#loop) and [`match`](https://rux-lang.dev/docs/lang/patterns/match) in the Rux Reference # Directory ::note **You'll need**: [File](https://rux-lang.dev/docs/learn/file), [Path join](https://rux-lang.dev/docs/learn/path-join), [Nested fallible](https://rux-lang.dev/docs/learn/nested-fallible), [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) :: A directory holds names: files, and other directories. The `FileSystem` package makes one, lists what is in it and removes it, and this program does all three to a scratch directory of its own, `Bin/scratch`, inside the package's build folder. The interesting part is the listing, because a directory can hold a great many names, and reading it can fail part-way through. ## Four calls | Call | Does | Fails when | | ---------------------------------- | ------------------------------- | ---------------------------- | | `MakeDirectory(allocator, path)` | creates one directory | the name is already taken | | `ReadDirectory(allocator, path)` | starts a listing | the directory cannot be read | | `DeleteFile(allocator, path)` | removes one file | it is missing | | `DeleteDirectory(allocator, path)` | removes one **empty** directory | anything is still inside | All four return fallibles with `IoError` on the failure side, and none of them touches more than the one name it is given. The files go in with two small helpers. Each converts a name to an `OsString`, joins it onto the directory with [`Join`](https://rux-lang.dev/docs/learn/path-join), and acts on the result: ```rux func MakeFile(allocator: Allocator, directory: Path, name: char8[..]) -> ! IoError | TextError { var holder = OsString::FromText(allocator, name)?; let path = Join(allocator, directory, holder.View())?; var file = File::Open(allocator, path.AsPath(), OpenOptions::Writing())?; WriteAll(file, name)?; file.Close()?; } ``` ## Listing, one name at a time `ReadDirectory` does not return every name at once — that would mean holding them all at once. It returns a cursor, a `DirectoryIterator`, whose `Next` hands out one name per call: ```rux func Next(self: &var DirectoryIterator) -> OsString? ! IoError ``` That is the nested shape from [Nested fallible](https://rux-lang.dev/docs/learn/nested-fallible): two questions in one return type. The outer one is "could the directory be read?"; the inner one is "is there another name?". The loop answers both in one line: ```rux var listing = ReadDirectory(allocator, directory)?; var count = 0; loop { let name = listing.Next()? ?? break; PrintLine(" {}", name); count += 1; } ``` ```mermaid flowchart LR n["listing.Next()"] --> f{"failed?"} f -- "yes" --> p["? passes the IoError on:
Main ends with status 1"] f -- "no" --> o{"a name, or none?"} o -- "none" --> b["?? break:
every name handed out"] o -- "a name" --> use["bind it to name,
print it, loop again"] use --> n ``` `?` strips the outer fallible, and [`?? break`](https://rux-lang.dev/docs/learn/coalesce-exit) strips the inner optional — leaving the loop when it is `none`. What is left is an `OsString`, a name in the system's own units, for the reason [Path](https://rux-lang.dev/docs/learn/path) gave. Three more things about a listing: - `.` and `..` are skipped; you get only real entries. - The order is whatever the filesystem keeps. This program happens to print `first.txt` before `second.txt`; do not rely on it. Sort the names if order matters. - The cursor holds a system handle. `listing.Release()` gives it back as soon as you are done, rather than at the end of the scope. ## Removal is strict `DeleteDirectory` refuses a directory that still has anything in it, so removing the scratch directory too early fails. This program expects that, and handles the failure rather than passing it on: ```rux DeleteDirectory(allocator, directory) catch { else => { PrintLine("refused: the directory is not empty"); } }; ``` `IoErrorKind` has no case for "not empty", so the failure arrives as `Other`, with the system's own code beside it. That is why this `catch` takes every failure with `else` rather than matching one kind. Then the files go first, and the empty directory after them: ```rux RemoveFile(allocator, directory, "first.txt")?; RemoveFile(allocator, directory, "second.txt")?; DeleteDirectory(allocator, directory)?; ``` Nothing here deletes a whole tree in one call. Removing a directory and everything below it means listing it, deleting each file, and descending into each subdirectory — which is exactly why it is not a single innocent-looking function. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/Directory){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A directory is made, listed and removed through the `FileSystem` package, and this program does // all three to a scratch directory it owns: `Bin/scratch`, inside this package's build folder. // // Listing is the interesting part. `ReadDirectory` returns a cursor, and its `Next` hands out one // name at a time, because a directory can hold a great many and returning them all at once would // mean holding them all at once. `Next` has the nested shape from the Errors part: // // func Next(self: &var DirectoryIterator) -> OsString? ! IoError // // The outer failure means the directory could not be read. A success holds the next name, or // `none` once every name has been handed out. So `listing.Next()? ?? break` passes a failure on, // stops at the end, and otherwise binds a name. The names are `OsString`s, not text, for the // reason the Path lesson gave; `.` and `..` are skipped. The order is whatever the filesystem // keeps, so do not rely on it. // // Removal is strict. `MakeDirectory` fails if the name is taken, and `DeleteDirectory` refuses a // directory that still has anything in it, so the files go first. import Allocator::{ Allocator, SystemAllocator }; import FileSystem::{ DeleteDirectory, DeleteFile, File, MakeDirectory, OpenOptions, ReadDirectory }; import Io::{ IoError, PrintLine, WriteAll }; import Path::{ Join, OsString, Path }; import Text::TextError; // Writes a small file called `name` inside `directory`. func MakeFile(allocator: Allocator, directory: Path, name: char8[..]) -> ! IoError | TextError { var holder = OsString::FromText(allocator, name)?; let path = Join(allocator, directory, holder.View())?; var file = File::Open(allocator, path.AsPath(), OpenOptions::Writing())?; WriteAll(file, name)?; file.Close()?; } // Deletes the file called `name` inside `directory`. func RemoveFile(allocator: Allocator, directory: Path, name: char8[..]) -> ! IoError | TextError { var holder = OsString::FromText(allocator, name)?; let path = Join(allocator, directory, holder.View())?; DeleteFile(allocator, path.AsPath())?; } func Main() -> ! IoError | TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin/scratch")?; let directory = Path::FromView(holder.View()); MakeDirectory(allocator, directory)?; MakeFile(allocator, directory, "first.txt")?; MakeFile(allocator, directory, "second.txt")?; PrintLine("made {} with two files", directory); var listing = ReadDirectory(allocator, directory)?; var count = 0; loop { let name = listing.Next()? ?? break; PrintLine(" {}", name); count += 1; } // The cursor holds a system handle; `Release` gives it back now rather than at scope end. listing.Release(); PrintLine("listed {} entries", count); // Removing it too early is refused, and this failure is handled rather than passed on. DeleteDirectory(allocator, directory) catch { else => { PrintLine("refused: the directory is not empty"); } }; RemoveFile(allocator, directory, "first.txt")?; RemoveFile(allocator, directory, "second.txt")?; DeleteDirectory(allocator, directory)?; PrintLine("removed {}", directory); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `FileSystem`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/Directory rux run ``` ```text made Bin/scratch with two files first.txt second.txt listed 2 entries refused: the directory is not empty removed Bin/scratch ``` ## Common mistakes ::warning **Coalescing without the `?`.**:br`listing.Next() ?? break` fails with `error: operator '??' cannot take 'OsString? ! IoError'`, and the note explains why: `coalescing tests one optional level and would silently discard an error`. Deal with the failure first: `listing.Next()? ?? break`. :: ::warning **Forgetting the `?? break`.**:br`let name = listing.Next()?;` leaves `name` an `OsString?`, and printing it fails with `variadic parameter 'args' requires 'Display'`. Without the `??`, nothing ends the loop either. :: ::warning **A scratch directory left behind.**:br If a run stops part-way — a failure passed on with `?`, or a change of yours — `Bin/scratch` may still exist, and the next run fails at once at `MakeDirectory`, with exit status 1 and no output. Delete the folder by hand and run again. :: ::warning **Relying on the listing order.**:br The names come back in whatever order the filesystem keeps them, which differs between systems and can change. Sort them when the order matters. :: ## Try it yourself 1. Add a third file, and a subdirectory made with `MakeDirectory`. What does the listing show for the subdirectory, and what must change before the scratch directory can be removed? 2. Call `MakeDirectory` twice on the same path and handle the second failure with a `match` that checks for `IoErrorKind::AlreadyExists`. 3. Convert each name to text with `name.View().ToText(allocator)?` before printing it. When could that conversion fail, when printing with `{}` never does? 4. List the package's own `Bin` folder instead of the scratch directory. ## Learn more - [Nested fallible](https://rux-lang.dev/docs/learn/nested-fallible) and [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit) — the two halves of `Next()? ?? break` - [Path join](https://rux-lang.dev/docs/learn/path-join) — building each file's path inside the directory - [Metadata](https://rux-lang.dev/docs/learn/metadata) — asking whether a name is a file or a directory - [Temporary file](https://rux-lang.dev/docs/learn/temporary-file) — scratch space that removes itself # Metadata ::note **You'll need**: [File](https://rux-lang.dev/docs/learn/file), [Enum](https://rux-lang.dev/docs/learn/enum), [Match expression](https://rux-lang.dev/docs/learn/match-expression) :: **Metadata** is what the filesystem knows about a name without reading the contents: what kind of thing it is, how long it is, whether it may be written and when it last changed. One call asks all of it, and the same call is how a program answers the most common question about a path — does it exist? ## One call ```rux func MetadataOf(allocator, path: Path, followLinks: bool) -> Metadata ! IoError ``` `followLinks` decides what a symbolic link at the end of the path means. `true` describes the file the link points to — usually what you want — and `false` describes the link itself, which is the only way you will ever see a `SymbolicLink` reported. An open `File` answers the same question through its own `Metadata` method. That answer is about the file the handle holds, even if the name has since been moved or replaced. ## The answer is a plain struct | Field | Type | Holds | | ------------- | ------------- | --------------------------------------------------- | | `kind` | `FileKind` | `File`, `Directory`, `SymbolicLink` or `Other` | | `size` | `uint64` | the length in bytes; zero for a directory | | `permissions` | `Permissions` | whether the owner may write: `IsWritable()` | | `modified` | `Timestamp` | when the contents last changed | | `accessed` | `Timestamp` | when the contents were last read | | `linkCount` | `uint64` | how many names the contents have; one on most files | A directory's `size` is zero because what it would mean differs from system to system. `Timestamp` comes from the `Time` package, which has its own lesson in [Date and time](https://rux-lang.dev/docs/learn/date-time). `kind` is an [enum](https://rux-lang.dev/docs/learn/enum), and a [match expression](https://rux-lang.dev/docs/learn/match-expression) turns it into words. Every case is covered, so the match needs no `else`: ```rux func KindName(kind: FileKind) -> char8[..] { return match kind { FileKind::File => "file", FileKind::Directory => "directory", FileKind::SymbolicLink => "symbolic link", FileKind::Other => "other" }; } ``` `Other` is a device, a socket, a pipe — anything the filesystem holds that is none of the other three. ## A time that changes on every run The file is written a moment before it is described, so its modification time is "just now" — a different number on every run. The program prints only whether that was within the last minute, so the output stays the same: ```rux let age = Timestamp::Now().UnixSeconds() - info.modified.UnixSeconds(); PrintLine(" recent {}", age < 60); ``` `UnixSeconds` counts seconds since the start of 1970, so subtracting two of them gives the age in seconds. ## "Not found" is an answer Asking about a name that is not there is not a crash, and not a struct full of zeros. It is a failure whose kind is `NotFound`, which is what makes `MetadataOf` the way to ask "does this exist?". `Describe` matches all three outcomes: ```rux match MetadataOf(allocator, path, true) { .Success(info) => { PrintLine("{}", path); PrintLine(" kind {}", KindName(info.kind)); // … }, .Failure(error) if error.kind == IoErrorKind::NotFound => PrintLine("{}: not found", path), .Failure(error) => fail error } ``` ```mermaid flowchart LR m["MetadataOf(path)"] --> s{"outcome"} s -- ".Success(info)" --> yes["it exists:
read info.kind, info.size …"] s -- ".Failure, kind NotFound" --> no["it does not exist"] s -- "any other .Failure" --> unk["we cannot tell:
pass the error on"] ``` The third arm matters. A `PermissionDenied` does not mean the file is missing — only that this program may not look. Treating every failure as "not there" would turn a permissions problem into a wrong answer. The program describes the file, then the `Bin` directory it lives in, then deletes the file and asks again, which takes the `NotFound` arm. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/Metadata){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Metadata is what the filesystem knows about a name without reading the contents: what kind of // thing it is, how long it is, who may write it and when it last changed. One call asks: // // func MetadataOf(allocator, path: Path, followLinks: bool) -> Metadata ! IoError // // `followLinks` decides what a symbolic link at the end of the path means: `true` describes the // file the link points to, `false` the link itself. An open `File` answers the same question // through its `Metadata` method, about the file it holds even if the name has since moved. // // The answer is a plain struct. `kind` is a `FileKind` enum, `size` is in bytes (and zero for a // directory, where it means nothing portable), `permissions` says whether the owner may write, // and `modified` is a `Timestamp` from the `Time` package, which has its own lesson later on. // // Asking about a name that is not there is not a crash and not a zero-filled struct: it is a // failure whose kind is `NotFound`, which makes `MetadataOf` the way to ask "does this exist?". import Allocator::{ Allocator, SystemAllocator }; import FileSystem::{ DeleteFile, File, FileKind, MetadataOf, OpenOptions }; import Io::{ IoError, IoErrorKind, PrintLine, WriteAll }; import Path::{ OsString, Path }; import Text::TextError; import Time::Timestamp; func KindName(kind: FileKind) -> char8[..] { return match kind { FileKind::File => "file", FileKind::Directory => "directory", FileKind::SymbolicLink => "symbolic link", FileKind::Other => "other" }; } func Describe(allocator: Allocator, text: char8[..]) -> ! IoError | TextError { var holder = OsString::FromText(allocator, text)?; let path = Path::FromView(holder.View()); match MetadataOf(allocator, path, true) { .Success(info) => { PrintLine("{}", path); PrintLine(" kind {}", KindName(info.kind)); PrintLine(" size {} bytes", info.size); PrintLine(" writable {}", info.permissions.IsWritable()); // The seconds since the last change differ on every run, so only whether that was // within the last minute is shown. let age = Timestamp::Now().UnixSeconds() - info.modified.UnixSeconds(); PrintLine(" recent {}", age < 60); }, .Failure(error) if error.kind == IoErrorKind::NotFound => PrintLine("{}: not found", path), .Failure(error) => fail error } } func Main() -> ! IoError | TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin/sample.txt")?; let path = Path::FromView(holder.View()); var file = File::Open(allocator, path, OpenOptions::Writing())?; WriteAll(file, "0123456789")?; file.Close()?; Describe(allocator, "Bin/sample.txt")?; Describe(allocator, "Bin")?; DeleteFile(allocator, path)?; Describe(allocator, "Bin/sample.txt")?; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `FileSystem`, `Path`, `Text` and `Time` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/Metadata rux run ``` ```text Bin/sample.txt kind file size 10 bytes writable true recent true Bin kind directory size 0 bytes writable true recent true Bin/sample.txt: not found ``` ## Common mistakes ::warning **Leaving out a kind.**:br Drop the `FileKind::Other` arm and the match fails with `error: match on 'FileKind' is not exhaustive; missing FileKind::Other`. A file kind you have not thought of is still a file kind. :: ::warning **Forgetting `followLinks`.**:br`MetadataOf(allocator, path)` fails with `error: call to 'MetadataOf' expects 3 arguments, but 2 were provided`. There is no default: say whether a link at the end of the path should be followed. :: ::warning **Asking "does it exist?" with `?`.**:br`let info = MetadataOf(allocator, path, true)?;` works while the file is there, and ends the whole program with exit status 1 the moment it is not. When absence is an answer you expect, match the failure, as `Describe` does. :: ::warning **Reading every failure as "missing".**:br A `.Failure(_) => PrintLine("not found")` arm also catches `PermissionDenied` and every other failure. Check `error.kind == IoErrorKind::NotFound`, and pass the rest on. :: ## Try it yourself 1. Print `info.linkCount` and `info.accessed.UnixSeconds()` for both paths. 2. Describe a path that does not exist at all, such as `Bin/nothing/here.txt`. Which arm runs? 3. Open the file and call its `Metadata` method instead of `MetadataOf`. What does it report for `size`? 4. Write `func Exists(allocator: Allocator, path: Path) -> bool ! IoError` that returns `false` for `NotFound`, `true` for success, and passes any other failure on. ## Learn more - [Enum](https://rux-lang.dev/docs/learn/enum) and [Match expression](https://rux-lang.dev/docs/learn/match-expression) — turning a `FileKind` into words - [Guard](https://rux-lang.dev/docs/learn/guard) — the `if` on a match arm that picks out `NotFound` - [Temporary file](https://rux-lang.dev/docs/learn/temporary-file) — which uses `MetadataOf` to check that a file is gone - [Date and time](https://rux-lang.dev/docs/learn/date-time) — `Timestamp` and what else it can do # Binary ::note **You'll need**: [File](https://rux-lang.dev/docs/learn/file), [Endian](https://rux-lang.dev/docs/learn/endian) :: A text file stores numbers as digits: 1200000 is seven characters, 3 is one. A **binary** file stores the bytes of the values themselves, so a `uint16` is always two bytes, a `uint32` four and a `float64` eight, however large the value. Every record has the same size, and a reader knows exactly how many bytes to expect — which also means it can tell when some are missing. ## A fixed-width record The lesson's record is three fields, fourteen bytes whatever the values are: | Bytes | Field | Type | Written with | Read with | | ----- | --------- | --------- | -------------- | ------------- | | 0–1 | `version` | `uint16` | `WriteUint16` | `ReadUint16` | | 2–5 | `count` | `uint32` | `WriteUint32` | `ReadUint32` | | 6–13 | `average` | `float64` | `WriteFloat64` | `ReadFloat64` | The `Io` package has a pair of functions like these for every fixed-width type, from `uint8` up to `int512` and both floats. Each takes an `Endian` naming the byte order — [Endian](https://rux-lang.dev/docs/learn/endian) explains why that must be stated. This format simply says little endian, and both sides must agree: ```rux var writing = File::Open(allocator, path, OpenOptions::Writing())?; WriteUint16(writing, Endian::Little, 3)?; WriteUint32(writing, Endian::Little, 1200000)?; WriteFloat64(writing, Endian::Little, 2.75)?; writing.Close()?; ``` Each literal takes its parameter's type — `3` becomes a `uint16`, `1200000` a `uint32` — the [Literal](https://rux-lang.dev/docs/learn/literal) rule at work. Reading is the same three calls in the same order: ```rux func ReadRecord(file: &var File) -> ! IoError { let version = ReadUint16(file, Endian::Little)?; let count = ReadUint32(file, Endian::Little)?; let average = ReadFloat64(file, Endian::Little)?; PrintLine(" version {}, count {}, average {}", version, count, average); } ``` Nothing in the file says where one field ends and the next begins. The format is the agreement between writer and reader, and the code on each side is its only record. ## A value is all of its bytes, or nothing [File](https://rux-lang.dev/docs/learn/file) showed that `File::Read` is one attempt that may return fewer bytes than asked. A number cannot be assembled from part of its bytes, so the fixed-width readers are built on `Io::ReadExact`, which keeps reading until every byte of the value has arrived — and the writers on `WriteAll`: ```mermaid flowchart LR r["ReadUint32(file, …)"] --> e["ReadExact:
4 bytes wanted"] e --> rd["file.Read(…)"] rd --> q{"what came back?"} q -- "some bytes,
more still needed" --> rd q -- "the last
bytes needed" --> v["assemble the uint32
in the stated byte order"] q -- "the end of the file" --> f["fail with an IoError
whose IsEnd() is true"] ``` If the stream ends part-way through a value, `ReadUint32` fails instead of building a number from the bytes it got plus whatever happened to be in memory. A short file is an error to report, never a value to guess. ## A record cut short The second half of the program simulates a crash part-way through writing. It opens the file with plain write permission — `OpenOptions()` with only `write` set, so the file is not emptied the way `Writing` would — and cuts it to ten bytes: ```rux var options = OpenOptions(); options.write = true; var cutting = File::Open(allocator, path, options)?; cutting.SetLength(10)?; cutting.Close()?; ``` Ten bytes hold the whole `version` and `count`, and four of the eight bytes of `average`. `ShowRecord` reads the record again and catches the end-of-file failure rather than passing it on: ```rux ReadRecord(reading) catch { error if error.IsEnd() => { PrintLine(" the file ends inside a field: record refused"); }, error => fail error }; ``` The first two fields read perfectly well, but the record as a whole is refused: one field that cannot be read whole fails `ReadRecord`, and the half-read values are never printed. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/Binary){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A text file stores numbers as digits. A binary file stores the bytes of the values themselves: // a `uint16` is always two bytes, a `uint32` four and a `float64` eight, however large the // value. Every record has the same size, so a reader knows exactly how many bytes to expect. // // `Io` has a function pair for each fixed-width type, such as `WriteUint32` and `ReadUint32`. // Each takes an `Endian` naming the byte order; the Endian lesson explains why that must be // stated. Here the format simply says little endian, on both sides. // // The real lesson is about bytes that do not arrive. `File::Read` is one attempt and may return // fewer bytes than asked; at the end of a file it fails with `EndOfStream`. The fixed-width // functions are built on `ReadExact` and `WriteAll`, which keep calling until every byte of the // value has moved. If the stream ends part-way through a value, `ReadUint32` fails instead of // assembling a number from the bytes it got plus whatever was in memory. A short file is an // error to report, never a value to guess. // // The file lives in this package's `Bin/` folder, and the program deletes it at the end. import Allocator::{ Allocator, SystemAllocator }; import FileSystem::{ DeleteFile, File, OpenOptions }; import Io::{ Endian, IoError, PrintLine, ReadFloat64, ReadUint16, ReadUint32, WriteFloat64, WriteUint16, WriteUint32 }; import Path::{ OsString, Path }; import Text::TextError; // Reads one record. Any field that cannot be read whole fails the whole record. func ReadRecord(file: &var File) -> ! IoError { let version = ReadUint16(file, Endian::Little)?; let count = ReadUint32(file, Endian::Little)?; let average = ReadFloat64(file, Endian::Little)?; PrintLine(" version {}, count {}, average {}", version, count, average); } // Opens the file, reads a record from it, and reports a short file instead of passing it on. func ShowRecord(allocator: Allocator, path: Path) -> ! IoError { var reading = File::Open(allocator, path, OpenOptions::Reading())?; PrintLine("{} holds {} bytes", path, reading.Size()?); ReadRecord(reading) catch { error if error.IsEnd() => { PrintLine(" the file ends inside a field: record refused"); }, error => fail error }; reading.Close()?; } func Main() -> ! IoError | TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin/record.bin")?; let path = Path::FromView(holder.View()); // 2 + 4 + 8 bytes: fourteen, whatever the values are. var writing = File::Open(allocator, path, OpenOptions::Writing())?; WriteUint16(writing, Endian::Little, 3)?; WriteUint32(writing, Endian::Little, 1200000)?; WriteFloat64(writing, Endian::Little, 2.75)?; writing.Close()?; ShowRecord(allocator, path)?; // Cut the file in the middle of the last field, as a crash part-way through writing might // have left it. Plain `write` permission, without `Writing`'s emptying of the file. var options = OpenOptions(); options.write = true; var cutting = File::Open(allocator, path, options)?; cutting.SetLength(10)?; cutting.Close()?; ShowRecord(allocator, path)?; DeleteFile(allocator, path)?; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `FileSystem`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/Binary rux run ``` ```text Bin/record.bin holds 14 bytes version 3, count 1200000, average 2.75 Bin/record.bin holds 10 bytes the file ends inside a field: record refused ``` ## Common mistakes ::warning **A value that does not fit the field.**:br`WriteUint16(writing, Endian::Little, 70000)` fails with `error: argument 3 to 'WriteUint16' has type 'int', but parameter 'value' requires 'uint16'`. 70000 needs more than sixteen bits, so the literal cannot become a `uint16`. :: ::warning **Writing an `int` variable.**:br With `let count = 1200000;`, `WriteUint32(writing, Endian::Little, count)` fails with `has type 'int', but parameter 'value' requires 'uint32'`. A variable already has its type. Declare it `let count: uint32 = 1200000;`, or convert it with `as uint32`. :: ::warning **Reading the fields in a different order.**:br Swap the first two reads and the program still runs, printing `version 18, count 1333788675`: the bytes are read, just as the wrong fields. Nothing in a binary file can catch this; only the agreement between writer and reader can. :: ::warning **Reading with the other byte order.**:br Read `count` with `Endian::Big` and it comes out as 2152665600 instead of 1200000 — the same four bytes, assembled backwards. The byte order is part of the format, and both sides must state the same one. :: ## Try it yourself 1. Add a fourth field, an `int8` temperature, to both the writer and `ReadRecord`. How many bytes is the record now? 2. Cut the file to 6 bytes instead of 10. Which field is the first one that cannot be read? 3. Write two records one after the other, and read them back in a loop until the end of the file. 4. Write the `uint32` 1200000 with `Endian::Big` and read it with `Endian::Big`. Is the file any different in size? ## Learn more - [Endian](https://rux-lang.dev/docs/learn/endian) — byte order, and why a format must state it - [File](https://rux-lang.dev/docs/learn/file) — `Read` as one attempt, and the end of a file as a failure - [Integer](https://rux-lang.dev/docs/learn/integer) and [Float](https://rux-lang.dev/docs/learn/float) — the widths behind each field's size - [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) — how to avoid leaving a record cut short in the first place # Buffered I/O ::note **You'll need**: [File](https://rux-lang.dev/docs/learn/file), [Interface value](https://rux-lang.dev/docs/learn/interface-value), [Destructor](https://rux-lang.dev/docs/learn/destructor) :: Every `File::Write` is a request to the operating system, and a request costs about the same for ten bytes as for ten thousand. A program that writes a log line at a time pays that cost per line. `Io::BufferedWriter` stands in between: it gathers small writes in memory and sends them to the file in one large write when its buffer would overflow. The price is a new question — has this byte actually reached the file yet? — and this lesson makes the answer visible. ## A writer in front of the file ```rux var file = File::Open(allocator, path, OpenOptions::Writing())?; let sink: Writer = file.Stream(); var buffered = BufferedWriter::New(allocator, sink, 32)?; ``` A buffered writer keeps its destination for as long as it lives, so it needs a stored stream rather than a borrow. `file.Stream()` gives one: a `Writer` [interface value](https://rux-lang.dev/docs/learn/interface-value) that refers to `file`. That makes an ordering rule — **the file must outlive the writer**. The last argument is the buffer's size in bytes. The lesson uses a tiny 32 so it fills quickly; the usual choice is 0, which means the default of 8 KiB. The buffer is memory, so `New` takes an allocator and is fallible. ## Pending until the buffer fills Each entry is ten bytes. After every write the program prints how many bytes are **pending** in the buffer and how big the file really is: ```rux for i in 1..=6 { buffered.Write("entry ...\n")?; PrintLine("{:5} {:7} {:7}", i, buffered.Pending(), file.Size()?); } ``` | Write | Pending | In the file | What happened | | ----- | ------- | ----------- | ------------------------------------------- | | 1 | 10 | 0 | gathered | | 2 | 20 | 0 | gathered | | 3 | 30 | 0 | gathered | | 4 | 10 | 30 | 40 would not fit in 32: the 30 go out first | | 5 | 20 | 30 | gathered | | 6 | 30 | 30 | gathered | | flush | 0 | 60 | `Flush` sends the rest | Six writes reached the operating system as two. With a real 8 KiB buffer and short lines, it is hundreds to one. ```mermaid sequenceDiagram participant P as Program participant B as BufferedWriter participant F as File P->>B: Write (entries 1, 2, 3) Note over B: 30 bytes pending P->>B: Write (entry 4) B->>F: one Write of 30 bytes Note over B: entry 4 pending P->>B: Write (entries 5, 6) P->>B: Flush() B->>F: one Write of 30 bytes B-->>P: success, or the IoError P->>F: Close() ``` A single write as large as the whole buffer skips it: the writer sends what it holds, then passes the big write straight through, since gathering it would gain nothing. ## Flush, and where its failure goes A byte that is pending is not in the file, and nothing reading the file can see it. So call `Flush` when the data must be there: before closing the file, before telling anyone it is written, and before reading it back. ```rux buffered.Flush()?; PrintLine("flush {:7} {:7}", buffered.Pending(), file.Size()?); file.Close()?; ``` `Flush` returns `! IoError`, and that is where a failed send is reported. The writer's [destructor](https://rux-lang.dev/docs/learn/destructor) flushes too, as a safety net — but a destructor has nowhere to report a failure, so there it is discarded. The safety net catches forgetfulness; only an explicit `Flush` tells you whether the bytes arrived. Reading has a mirror image, `BufferedReader`: it fills its buffer with one large read and serves small reads from it, the same trade the other way round. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/BufferedIo){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Every `File::Write` is a request to the operating system, and a request costs about the same // for ten bytes as for ten thousand. A program that writes many small pieces pays that cost per // piece. `Io::BufferedWriter` stands in between: it gathers small writes in memory and sends // them to the file in one large write when the buffer would overflow. `BufferedReader` does the // same for reading, filling its buffer with one large read and serving small reads from it. // // The price is that a written byte is not in the file yet. It is pending in the buffer until // the buffer fills or `Flush` sends it, and until then nothing reading the file can see it. So: // // - call `Flush` when the data must be in the file: before closing it, before telling anyone it // is written, and before reading it back; // - `Flush` returns `! IoError`, and that is where a failed send is reported. The writer's // destructor flushes too, as a safety net, but a destructor has nowhere to report a failure, // so it is discarded there. // // A buffered writer keeps its destination for as long as it lives, so it needs a stored stream // rather than a borrow: `file.Stream()` gives one. The file must outlive the writer. import Allocator::{ Allocator, SystemAllocator }; import FileSystem::{ DeleteFile, File, OpenOptions }; import Io::{ BufferedWriter, IoError, PrintLine, Writer }; import Path::{ OsString, Path }; import Text::TextError; func Main() -> ! IoError | TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin/log.txt")?; let path = Path::FromView(holder.View()); var file = File::Open(allocator, path, OpenOptions::Writing())?; let sink: Writer = file.Stream(); // A tiny 32-byte buffer, so it fills quickly. The usual choice is 0, meaning 8 KiB. var buffered = BufferedWriter::New(allocator, sink, 32)?; PrintLine("write pending in file"); for i in 1..=6 { // Each entry is ten bytes. The fourth no longer fits, so the first three go out first. buffered.Write("entry ...\n")?; PrintLine("{:5} {:7} {:7}", i, buffered.Pending(), file.Size()?); } buffered.Flush()?; PrintLine("flush {:7} {:7}", buffered.Pending(), file.Size()?); file.Close()?; DeleteFile(allocator, path)?; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `FileSystem`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/BufferedIo rux run ``` ```text write pending in file 1 10 0 2 20 0 3 30 0 4 10 30 5 20 30 6 30 30 flush 0 60 ``` ## Common mistakes ::warning **Handing over the file instead of its stream.**:br`BufferedWriter::New(allocator, file, 32)` fails with `error: move-only value 'file' requires an explicit '<-' in argument`, and a note that `'File' prohibits copying`. The writer wants a `Writer` that refers to the file, not the file itself: pass `file.Stream()`. :: ::warning **Closing the file before flushing.**:br Swap `Flush` and `Close` and the last 30 bytes never arrive: the file ends at 30 bytes, not 60. The explicit `Flush` after `Close` fails and ends the program with status 1; leave it out and the destructor's flush fails silently instead. Flush first, then close. :: ::warning **Checking the file too early.**:br`file.Size()` reports what the operating system has, not what you have written. Until the buffer fills or is flushed, the file looks shorter than your program thinks it is. :: ## Try it yourself 1. Change the buffer size to 25, then to 0. Predict the "in file" column each time before you run it. 2. Write one entry of 40 bytes into the 32-byte buffer. What does `Pending` say straight afterwards? 3. Remove the `Flush` call and the `DeleteFile` at the end, run, and look at `Bin/log.txt`. Did the destructor's flush deliver the last entries? Think about when the destructor runs, and when the file was closed. 4. Replace `buffered.Write(…)` with `WriteAll(buffered, …)` and read the error. `BufferedWriter` has a `Write` and a `Flush` — so why is it not a `Writer`? (Hint: look back at how a type implements an interface in [Interface](https://rux-lang.dev/docs/learn/interface).) ## Learn more - [Interface value](https://rux-lang.dev/docs/learn/interface-value) — what `let sink: Writer = file.Stream();` holds - [Destructor](https://rux-lang.dev/docs/learn/destructor) — the safety-net flush, and why it cannot report - [File](https://rux-lang.dev/docs/learn/file) — `Write` as one attempt, and `Close` as a fallible step - [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) — a different promise: that a reader never sees a file half-written # Atomic file ::note **You'll need**: [File](https://rux-lang.dev/docs/learn/file), [Binary](https://rux-lang.dev/docs/learn/binary) :: Overwriting a file in place has a dangerous moment. Opening it with `OpenOptions::Writing()` empties it at once, and until the last byte is written the file holds only part of the new contents. A crash, a full disk, or a reader arriving at the wrong time finds a broken file — and the old one is already gone. Settings files, saved games and databases all need a way round that moment, and the `FileSystem` package has one. ## Write elsewhere, then rename ```rux func WriteAtomically(allocator, target: Path, contents: char8[..]) -> ! IoError ``` `WriteAtomically` never touches the target until the new contents are complete. It writes them to a new temporary file in the **same directory** as the target, forces them to the disk, and then renames the temporary file onto the target's name in one step the filesystem cannot interrupt: ```mermaid flowchart LR b["create a temporary file
beside the target"] --> w["write the new
contents into it"] w --> s["sync it
to the disk"] s --> r["rename it onto
the target's name"] r --> n["from here on, readers
see the new contents"] ``` Until the last step the target is never opened, so anyone reading it meanwhile finds the complete old contents. The same directory matters: a rename is only atomic within one volume, and the system's temporary folder is often on another, where a "rename" silently becomes a copy and a delete. | | Writing in place | `WriteAtomically` | | ------------------------------- | -------------------------- | -------------------------------------- | | While writing, the target holds | part of the new contents | the complete old contents | | After a crash part-way | a broken file | the old file, untouched | | If writing fails | a broken file | the old file; the temporary is deleted | | A reader may see | old, empty, partial or new | old or new, never anything else | For a file whose whole contents are in hand, one call does it: ```rux WriteAtomically(allocator, path, "volume = 3")?; ``` ## The same steps by hand `WriteAtomically` is built on `AtomicWrite`, and the second half of the program uses it directly to look inside. `Begin` creates the temporary file; the value is a `Writer`, so `WriteAll` writes into it: ```rux var replacement = AtomicWrite::Begin(allocator, path)?; WriteAll(replacement, "volume = 11")?; Show(allocator, "while writing", path)?; ``` At this point the new contents exist — in the temporary file. `Show` opens the target and still finds `volume = 3`. Only `Commit` makes the switch: ```rux replacement.Commit(allocator)?; Show(allocator, "after commit", path)?; ``` `Commit` syncs the temporary file and renames it onto the target, and from then on the target reads `volume = 11`. ## A replacement you do not commit If you change your mind, `Abandon` deletes the temporary file and leaves the target alone. Simply dropping the value does the same: its destructor throws the partial file away. So an early return, or a `?` that passes a failure on part-way through writing, leaves the old contents exactly where they were — which is the behaviour that makes the type worth using. ## What it does not promise Atomic is not the same as durable. After `Commit`, the new contents are on the disk, but the rename itself — the directory entry — may not be: making that survive a power cut needs the directory synced too, which Windows does not offer. So after a crash at the wrong moment the target may still hold the old version. Old or new, but always whole. The `Show` helper reads the whole file into one buffer. Its 64 bytes are plenty for these settings; a file of unknown size would need a reading loop like the one in [File](https://rux-lang.dev/docs/learn/file). ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/AtomicFile){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Overwriting a file in place has a dangerous moment. Opening it with `Writing` empties it, and // until the last byte is written the file holds part of the new contents. A crash, a full disk // or a reader arriving at the wrong time sees a broken file, and the old one is already gone. // // `FileSystem::WriteAtomically` avoids that moment: // // func WriteAtomically(allocator, target: Path, contents: char8[..]) -> ! IoError // // It writes the contents to a new temporary file in the same directory as the target, forces // them to the disk, and then renames the temporary file onto the target's name in one step. The // same directory matters, because a rename is only atomic within one volume. // // What it promises: anyone opening the target at any moment finds the complete old contents or // the complete new ones, never a mix, never an empty file and never a missing one. If it fails, // the target still holds the old contents, and the temporary file is deleted. // // What it does not promise: that the rename itself survives a power cut. That would need the // directory synced to disk too, which Windows does not offer, so after a crash at the wrong // moment the target may still hold the old version. Old or new, but always whole. // // The work is done by `AtomicWrite`, which the second half uses directly to look inside. import Allocator::{ Allocator, SystemAllocator }; import FileSystem::{ AtomicWrite, DeleteFile, File, OpenOptions, WriteAtomically }; import Io::{ IoError, PrintLine, ReadExact, WriteAll }; import Path::{ OsString, Path }; import Text::TextError; // Prints what the file holds right now. The files here are short, so one buffer is enough. func Show(allocator: Allocator, label: char8[..], path: Path) -> ! IoError { var file = File::Open(allocator, path, OpenOptions::Reading())?; let size = file.Size()? as uint; var buffer: char8[64]; ReadExact(file, buffer[..size])?; file.Close()?; PrintLine("{:16} {}", label, buffer[..size]); } func Main() -> ! IoError | TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin/settings.txt")?; let path = Path::FromView(holder.View()); WriteAtomically(allocator, path, "volume = 3")?; Show(allocator, "first write", path)?; // The same steps by hand. While the new contents are being written they live in the // temporary file, and the target still holds the old ones. var replacement = AtomicWrite::Begin(allocator, path)?; WriteAll(replacement, "volume = 11")?; Show(allocator, "while writing", path)?; // `Commit` syncs the temporary file and renames it onto the target. replacement.Commit(allocator)?; Show(allocator, "after commit", path)?; DeleteFile(allocator, path)?; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `FileSystem`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/AtomicFile rux run ``` ```text first write volume = 3 while writing volume = 3 after commit volume = 11 ``` ## Common mistakes ::warning **Forgetting `Commit`.**:br Leave out `replacement.Commit(allocator)?;` and the program still compiles and runs, but "after commit" prints `volume = 3`: the new contents went into the temporary file, and the destructor throws them away at the end of `Main`. Nothing reaches the target without `Commit`. :: ::warning **Leaving `Commit` unchecked.**:br`replacement.Commit(allocator);` fails with `error: fallible result of type '! IoError' is discarded`. A commit that failed means the target still holds the old contents, and the program must know that. :: ::warning **Declaring the replacement with `let`.**:br`let replacement = AtomicWrite::Begin(…)?;` makes `WriteAll(replacement, …)` fail with `error: argument 1 to 'WriteAll' cannot borrow immutable 'replacement' as '&var Writer'`. Writing changes it; declare it `var`. :: ::warning **Building your own with a temporary folder elsewhere.**:br Writing to `FileSystem::TemporaryDirectory` and renaming onto the target looks the same, but when that folder is on another volume the rename is no longer atomic. `AtomicWrite` always creates its temporary file beside the target. :: ## Try it yourself 1. Replace `Commit` with `replacement.Abandon(allocator)?;` and check that the target still says `volume = 3`. 2. Write a `volume = 11` setting in place instead, with `File::Open` and `OpenOptions::Writing()`, and call `Show` between opening and writing. What does a reader see at that moment? 3. Use `AtomicWrite` to write the contents in three `WriteAll` calls — `volume`, `=`, `11` — and commit once. 4. Call `Show` on the target from a second `Show` call placed after `Begin` but before `WriteAll`. Is the target empty, as it would be with `Writing`? ## Learn more - [File](https://rux-lang.dev/docs/learn/file) — opening, writing and closing, and why `Close` is fallible - [Temporary file](https://rux-lang.dev/docs/learn/temporary-file) — the scratch file `AtomicWrite` is built on - [Destructor](https://rux-lang.dev/docs/learn/destructor) — how dropping an uncommitted replacement cleans up - [Binary](https://rux-lang.dev/docs/learn/binary) — the cut-short record that writing atomically prevents # Temporary file ::note **You'll need**: [Metadata](https://rux-lang.dev/docs/learn/metadata), [Destructor](https://rux-lang.dev/docs/learn/destructor), [Move](https://rux-lang.dev/docs/learn/move) :: A temporary file is scratch space with a name: somewhere to put data too big for memory, or a file to hand to another program. Two things make it harder than opening any file. The name must not clash with another file, and must not be guessable. And the file must not be left behind when the program is done with it — not even when the program leaves early through a failure. ## A name nobody can predict If another program could predict the name, it could create that name first — perhaps as a link to some other file — and this program would write straight through it. So `TemporaryFile::Create` builds the name from a prefix plus 16 random hex digits, and creates the file only if nothing by that name exists yet; a collision just means the next random name is tried. ```rux func Create(allocator, directory: Path, prefix: char8[..]) -> TemporaryFile ! IoError ``` ```rux var scratch = TemporaryFile::Create(allocator, directory, "draft-")?; WriteAll(scratch.file, "work in progress")?; PrintLine("created {}", scratch.AsPath().FileName() ?? OsStringView()); ``` The name comes out as `draft-` and sixteen hex digits, different on every run. `scratch.file` is an ordinary open `File`, opened for both reading and writing, and `AsPath` lends the full path. The directory is up to you. `FileSystem::TemporaryDirectory` returns the system's own folder for such files; this program uses the package's `Bin/` folder instead, so you can watch what happens there. ## Three ways to finish A `TemporaryFile` owns both the open file and its name, so it decides what happens to them: ```mermaid flowchart LR c["TemporaryFile::Create"] --> use["write, read,
hand on the name"] use --> close["Close(allocator)?"] use --> drop["dropped without Close:
an early return, a failure"] use --> keep["Keep()?"] close --> gone1["closed and deleted;
a failure is reported"] drop --> gone2["the destructor deletes it;
a failure goes unreported"] keep --> stays["closed, and the file stays;
the value gives up its name"] ``` | Ending | The file afterwards | A failed delete is… | | ------------------- | ------------------- | ------------------------- | | `Close(allocator)?` | deleted | reported, as an `IoError` | | dropped | deleted | silently ignored | | `Keep()?` | kept | — nothing is deleted | `Exists` checks the outcome. It asks for the file's [metadata](https://rux-lang.dev/docs/learn/metadata) and turns the fallible into a `bool` with `Core::Succeeded` — true for any success — met in [Generic outcome](https://rux-lang.dev/docs/learn/generic-outcome): ```rux func Exists(allocator: Allocator, path: Path) -> bool { return Succeeded(MetadataOf(allocator, path, true)); } ``` ## Closing: remember the name first After `Close`, the value no longer has a name — its `AsPath` is empty. To check that the file is gone, the program copies the name into a `PathBuffer` of its own before closing: ```rux var name = PathBuffer::FromPath(allocator, scratch.AsPath())?; scratch.Close(allocator)?; PrintLine("exists after Close {}", Exists(allocator, name.AsPath())); ``` `Close` closes the file, deletes it, and reports whether the deletion worked. ## Dropping: the destructor cleans up `Forgotten` creates a temporary file, writes to it, and returns without closing it: ```rux func Forgotten(allocator: Allocator, directory: Path) -> PathBuffer ! IoError | TextError { var scratch = TemporaryFile::Create(allocator, directory, "draft-")?; WriteAll(scratch.file, "never finished")?; var name = PathBuffer::FromPath(allocator, scratch.AsPath())?; return <-name; } ``` When `Forgotten` returns, `scratch` goes out of scope and its [destructor](https://rux-lang.dev/docs/learn/destructor) deletes the file. The same happens if any `?` in the function passes a failure on, which is the point: no path out of the function leaves the file behind. The name comes back as a `PathBuffer` moved out with `<-`, as in [Move](https://rux-lang.dev/docs/learn/move), and `Main` confirms the file no longer exists. The safety net has the same limit as the one in [Buffered I/O](https://rux-lang.dev/docs/learn/buffered-io): a destructor cannot report a failure, so a delete that fails there goes unnoticed. Call `Close` when you want to know. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Files/TemporaryFile){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A temporary file is scratch space with a name: somewhere to put data that is too big for // memory, or a name to hand to another program. Two things make that harder than it sounds. // // The name must not clash, and must not be guessable. If another program could predict it, it // could create that name first, perhaps as a link to some other file, and this program would // write through it. `TemporaryFile::Create` builds the name from a prefix plus 16 random hex // digits, and creates the file only if nothing by that name exists yet. // // func Create(allocator, directory: Path, prefix: char8[..]) -> TemporaryFile ! IoError // // And the file must not be left behind. A `TemporaryFile` owns both the open file and its name. // `Close` closes and deletes it, and reports whether the deletion worked. If the value is // dropped without `Close`, its destructor deletes the file anyway, on early returns and failure // paths too, but a destructor cannot report a failure, so it stays quiet about one. `Keep` // closes the file and gives up the name, for a file that should outlive the value. // // `FileSystem::TemporaryDirectory` returns the system's folder for such files. This program // uses this package's own `Bin/` folder instead. The name is random, so it differs on every run. import Allocator::{ Allocator, SystemAllocator }; import Core::Succeeded; import FileSystem::{ MetadataOf, TemporaryFile }; import Io::{ IoError, PrintLine, WriteAll }; import Path::{ OsString, OsStringView, Path, PathBuffer }; import Text::TextError; func Exists(allocator: Allocator, path: Path) -> bool { return Succeeded(MetadataOf(allocator, path, true)); } // Creates a temporary file, writes to it, and returns without closing it. func Forgotten(allocator: Allocator, directory: Path) -> PathBuffer ! IoError | TextError { var scratch = TemporaryFile::Create(allocator, directory, "draft-")?; WriteAll(scratch.file, "never finished")?; var name = PathBuffer::FromPath(allocator, scratch.AsPath())?; return <-name; } func Main() -> ! IoError | TextError { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin")?; let directory = Path::FromView(holder.View()); var scratch = TemporaryFile::Create(allocator, directory, "draft-")?; // `file` is an ordinary open `File`, opened for reading and writing. WriteAll(scratch.file, "work in progress")?; PrintLine("created {}", scratch.AsPath().FileName() ?? OsStringView()); PrintLine("exists while held {}", Exists(allocator, scratch.AsPath())); // Remember the name, because after `Close` the value no longer has one. var name = PathBuffer::FromPath(allocator, scratch.AsPath())?; scratch.Close(allocator)?; PrintLine("exists after Close {}", Exists(allocator, name.AsPath())); let forgotten = Forgotten(allocator, directory)?; PrintLine("exists after drop {}", Exists(allocator, forgotten.AsPath())); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Core`, `FileSystem`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Files/TemporaryFile rux run ``` ```text created draft-08bf264c6b30a0cc exists while held true exists after Close false exists after drop false ``` ## Common mistakes ::warning **Returning the borrowed path.**:br If `Forgotten` returned `scratch.AsPath()` as a `Path`, it would compile — and crash when `Main` used it. A `Path` only borrows its units, and these belong to `scratch`, which is destroyed as the function returns. Copy the name into a `PathBuffer` that owns its units, as the lesson does. :: ::warning **Returning a buffer without `<-`.**:br`return name;` fails with `error: move-only value 'name' requires an explicit '<-' in return`, and a note that `'PathBuffer' prohibits copying`. Write `return <-name;`. :: ::warning **Asking for the name after `Close`.**:br Once closed, the value has given its name up: `scratch.AsPath()` is an empty path. Copy the name first if you still need it. :: ::warning **Declaring it with `let`.**:br`let scratch = TemporaryFile::Create(…)?;` makes writing fail with `error: argument 1 to 'WriteAll' cannot borrow a part of immutable 'scratch' as '&var Writer'`, and `Close` with `cannot call 'Close' on immutable 'scratch'`. :: ## Try it yourself 1. Replace `scratch.Close(allocator)?` with `scratch.Keep()?`. Does the file survive the program? Look in `Bin/`, and delete it by hand afterwards. 2. Create the file in the system's folder instead: `var temporary = TemporaryDirectory(allocator)?;` gives a `PathBuffer`, and `temporary.AsPath()` is the directory. 3. Make `Forgotten` fail on purpose after writing — for example with `fail IoError::Of(IoErrorKind::Other);` — so that the program ends with status 1. Is a `draft-` file left in `Bin/` afterwards? 4. Create two temporary files with the same prefix and print both names. ## Learn more - [Destructor](https://rux-lang.dev/docs/learn/destructor) — what runs when `scratch` goes out of scope - [Move](https://rux-lang.dev/docs/learn/move) — returning the `PathBuffer` with `<-` - [Metadata](https://rux-lang.dev/docs/learn/metadata) — how `Exists` asks whether a file is there - [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) — a temporary file put to work: replacing another file whole # Part 20: Utilities Four small standard packages that almost every real program reaches for sooner or later: `Time` for spans, clocks and calendar dates, `Random` and `Entropy` for numbers you cannot predict — or deliberately can — `Hash` for fingerprints, and `Uuid` for names nobody else will pick. Each lesson is short and stands on its own, and together they show how the standard library speaks about failure: a step that may not fit returns an optional, and a request the system may refuse returns a fallible. ## What you will learn - Spans of time with `Duration`, where every step that could overflow says so in its type. - Measuring elapsed time with `Instant`, the clock that never goes backwards. - Calendar dates and the leap-year rule, and reading dates with errors that point at the faulty byte. - Moments in time: a `DateTime` with a `UtcOffset`, RFC 3339 text, and comparing moments through `Timestamp`. - Seeded generators that replay the same numbers, and distributions that shape them into dice, shuffles and bell curves. - Unpredictable bytes from the operating system, and why a failure there must never be papered over. - Fast, non-cryptographic hashes and checksums, all at once or piece by piece. - Creating, printing and strictly parsing UUIDs. ## The part at a glance ```mermaid flowchart LR u(["Utilities"]) --> t["Time"] u --> r["Randomness"] u --> id["Fingerprints and names"] t --> span["How long?
Duration, Stopwatch"] t --> when["When?
Date, Date and time"] r --> rep["Repeatable
Random, Distribution"] r --> unp["Unguessable
Entropy"] id --> h["From data
Hash"] id --> uu["From nothing
UUID"] unp -. "seeds" .-> rep unp -. "random bits" .-> uu ``` ## Lessons ## Lessons | | Lesson | What you will learn | | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 20.1 | [Duration](https://rux-lang.dev/docs/learn/duration) | lengths of time, and arithmetic on them | | 20.2 | [Stopwatch](https://rux-lang.dev/docs/learn/stopwatch) | measure how long something takes | | 20.3 | [Date](https://rux-lang.dev/docs/learn/date) | calendar dates, parsing them, and leap years | | 20.4 | [Date and time](https://rux-lang.dev/docs/learn/date-time) | a date with a time and an offset, in RFC 3339 | | 20.5 | [Random](https://rux-lang.dev/docs/learn/random) | a reproducible random number generator | | 20.6 | [Distribution](https://rux-lang.dev/docs/learn/distribution) | draw from a range, shuffle, and sample a normal distribution | | 20.7 | [Entropy](https://rux-lang.dev/docs/learn/entropy) | unpredictable bytes from the operating system | | 20.8 | [Hash](https://rux-lang.dev/docs/learn/hash) | fast non-cryptographic hashes and checksums | | 20.9 | [UUID](https://rux-lang.dev/docs/learn/uuid) | create, print and parse UUIDs | ## Before you start This part leans on the native outcomes: [Part 8: Optionals](https://rux-lang.dev/docs/learn/optionals) for every `Duration?` and `Date?`, and [Part 9: Errors](https://rux-lang.dev/docs/learn/errors) for parsing and for requests the system may refuse. The randomness lessons pass generators as [mutable references](https://rux-lang.dev/docs/learn/mutable-reference) and call [generic](https://rux-lang.dev/docs/learn/generic) functions, and Entropy and Hash hand bytes over through [pointers](https://rux-lang.dev/docs/learn/pointer) from [Part 15: Memory](https://rux-lang.dev/docs/learn/memory). Each lesson's package is in the Examples repository's `Utilities/` folder: ```sh cd Examples/Utilities/Duration rux run ``` A few lessons print something different on every run — a random key, a die roll, a fresh UUID. Their pages say which lines to expect to change. ## After this part [Part 21: Data formats](https://rux-lang.dev/docs/learn/data-formats) reads and writes JSON and TOML, the formats programs use to exchange and configure data. Before moving on, try the checkpoint projects. [Guess](https://rux-lang.dev/docs/learn/guess) is a number-guessing game whose secret comes from a generator seeded with entropy; [Age](https://rux-lang.dev/docs/learn/age) works out an age in years, months and days from two dates; [Password](https://rux-lang.dev/docs/learn/password) draws a password straight from the system's entropy without favouring any character; and [Launch](https://rux-lang.dev/docs/learn/launch) counts down to a rocket launch with `SleepFor`. For the language rules this part relies on, see [Structs](https://rux-lang.dev/docs/lang/structs/overview), [Enums](https://rux-lang.dev/docs/lang/enums/overview) and [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) in the Rux Reference. # Duration ::note **You'll need**: [Presence](https://rux-lang.dev/docs/learn/presence), [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) :: A *duration* is a span of time — how long, not when. A lap of 83.4 seconds, a timeout of five minutes and a working week are all durations, and the `Time` package gives them one type, `Duration`, so they add, subtract and print the same way whatever unit they started in. This lesson also puts [optionals](https://rux-lang.dev/docs/learn/optional) to work. Some steps on a duration can overflow, and the package says so in their types: they return `Duration?`, and the program decides what to do when the answer is `none`. The program imports the type and lists `Time` as a dependency: ```rux import Time::Duration; ``` ## Building a duration A `Duration` keeps whole seconds plus a nanosecond remainder. Small units are converted exactly, and `{}` prints the result as decimal seconds: ```rux let lap = Duration::FromMilliseconds(83400); PrintLine("one lap {}", lap); PrintLine("in parts {} s and {} ns", lap.WholeSeconds(), lap.SubsecondNanoseconds()); ``` 83 400 ms prints as `83.4s`; underneath it is 83 whole seconds and 400 000 000 nanoseconds. Every count of seconds, milliseconds, microseconds or nanoseconds fits, so those constructors always succeed. Minutes and hours are different: an `int64` count of hours, times 3600, can be too large for an `int64` count of seconds. | Constructor | Returns | Why | | ------------------------------------------------------------------------ | ----------- | -------------------------------------- | | `FromSeconds`, `FromMilliseconds`, `FromMicroseconds`, `FromNanoseconds` | `Duration` | every count of these fits | | `FromMinutes`, `FromHours` | `Duration?` | multiplying up to seconds can overflow | | `Zero()` | `Duration` | the empty span | ## Arithmetic that can overflow `Plus`, `Minus` and `Times` can overflow the same way, so they answer `Duration?` too. Where an overflow would be a surprise, [`??`](https://rux-lang.dev/docs/learn/coalesce) supplies a fallback and the program carries on with a plain `Duration`: ```rux let race = lap.Times(12) ?? Duration::Zero(); PrintLine("twelve laps {}", race); PrintLine("in ms {}", race.TotalMilliseconds() ?? -1); ``` Asking for the total in milliseconds is a question that can overflow as well — a huge duration has more milliseconds than an `int64` holds — so `TotalMilliseconds` returns `int64?`. Going below zero is **not** an overflow. A duration may be negative, and `IsNegative` tells you so: ```rux let limit = Duration::FromSeconds(1000); match limit.Minus(race) { margin? => PrintLine("limit - race {} negative: {}", margin, margin.IsNegative()), none => PrintLine("too far apart to subtract") } ``` The race took 1000.8 s against a limit of 1000 s, so the margin is `-0.8s`. ## Several steps, one question A clock reading such as 1 h 30 min 15 s takes four steps, and three of them may overflow. Inside a function that itself returns `Duration?`, [`?`](https://rux-lang.dev/docs/learn/optional-propagate) handles each one: the first `none` ends the function with `none`, and otherwise the value is unwrapped and the next step runs. ```rux func Span(hours: int64, minutes: int64, seconds: int64) -> Duration? { let fromHours = Duration::FromHours(hours)?; let fromMinutes = Duration::FromMinutes(minutes)?; let both = fromHours.Plus(fromMinutes)?; return both.Plus(Duration::FromSeconds(seconds)); } ``` ```mermaid flowchart LR h["FromHours"] -- "value" --> m["FromMinutes"] m -- "value" --> p1["Plus"] p1 -- "value" --> p2["Plus seconds"] p2 --> r(["Duration?"]) h -- "none" --> n(["none"]) m -- "none" --> n p1 -- "none" --> n ``` The last line needs no `?`: `Plus` already returns `Duration?`, which is exactly what `Span` returns. ## When it does not fit The largest `int64` is about 9.2 × 10¹⁸. As a count of hours that is around a million billion years, and converted to seconds it does not fit, so `FromHours` answers `none` rather than a wrong number: ```rux match Duration::FromHours(9223372036854775807) { huge? => PrintLine("huge = {}", huge), none => PrintLine("9223372036854775807 hours does not fit, so FromHours gave none") } ``` ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Duration){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A duration is a span of time: how long, not when. The Time package keeps one as whole seconds // plus a nanosecond remainder, so a millisecond and a working week are the same type, add // exactly, and print with `{}` as decimal seconds. // // Building one from seconds, milliseconds or nanoseconds always works, because every count of // those fits. Building one from minutes or hours does not: an `int64` count of hours times 3600 // can overflow an `int64` count of seconds. So `FromMinutes` and `FromHours` return `Duration?`, // and so do `Plus`, `Minus` and `Times`, which can overflow the same way. A step that might not // fit says so in its type, and the program decides what to do about it. import Io::PrintLine; import Time::Duration; // A clock reading such as 1 h 30 min 15 s as one duration. Each step may overflow, so each is // followed by `?`, and the first one that does makes the whole answer `none`. func Span(hours: int64, minutes: int64, seconds: int64) -> Duration? { let fromHours = Duration::FromHours(hours)?; let fromMinutes = Duration::FromMinutes(minutes)?; let both = fromHours.Plus(fromMinutes)?; return both.Plus(Duration::FromSeconds(seconds)); } func Main() -> int { // Small units never fail, so they return a plain `Duration`. let lap = Duration::FromMilliseconds(83400); PrintLine("one lap {}", lap); PrintLine("in parts {} s and {} ns", lap.WholeSeconds(), lap.SubsecondNanoseconds()); // Arithmetic answers an optional; `??` supplies a fallback for the overflow nobody expects. let race = lap.Times(12) ?? Duration::Zero(); PrintLine("twelve laps {}", race); PrintLine("in ms {}", race.TotalMilliseconds() ?? -1); // Going below zero is not an overflow: a duration may be negative. let limit = Duration::FromSeconds(1000); match limit.Minus(race) { margin? => PrintLine("limit - race {} negative: {}", margin, margin.IsNegative()), none => PrintLine("too far apart to subtract") } // Building from hours, the case the lesson is about. match Span(1, 30, 15) { span? => PrintLine("1 h 30 min 15 s = {}", span), none => PrintLine("1 h 30 min 15 s does not fit") } // As a count of hours, the largest `int64` is about a million billion years, and as seconds it // does not fit. match Duration::FromHours(9223372036854775807) { huge? => PrintLine("huge = {}", huge), none => PrintLine("9223372036854775807 hours does not fit, so FromHours gave none") } return 0; } ``` Besides `Io`, its `Rux.toml` lists `Time` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Duration rux run ``` ```text one lap 83.4s in parts 83 s and 400000000 ns twelve laps 1000.8s in ms 1000800 limit - race -0.8s negative: true 1 h 30 min 15 s = 5415s 9223372036854775807 hours does not fit, so FromHours gave none ``` ## Common mistakes ::warning **Using the optional as a duration.**:br`FromHours` returns `Duration?`, not `Duration`. Writing `let fromHours: Duration = Duration::FromHours(hours);` fails with `error: cannot assign 'Duration?' to 'Duration'`. Unwrap it first — with `?`, `??` or a `match`. :: ::warning **Printing an answer that may be missing.**:br Without the `??`, `race` is a `Duration?`, and printing it fails with `error: argument 2 to 'PrintLine' has type 'Duration?', but variadic parameter 'args' requires 'Display'`. Methods are refused too: `type 'Duration?' has no field 'TotalMilliseconds'`. :: ::warning **Using `?` in a function that cannot pass `none` on.**:br`?` hands the absence to the caller, so the enclosing function must return an optional. In `Main`, which returns `int`, `Duration::FromMinutes(17)?` fails with `error: '?' propagates the absence of 'Duration?', but the enclosing function returns 'int'`. Use `??` there, or move the steps into a function like `Span`. :: ::warning **Fractional seconds.**:br The constructors count whole units, so `Duration::FromSeconds(1.5)` fails with `error: argument 1 to 'Duration::FromSeconds' has type 'float64', but parameter 'seconds' requires 'int64'`. Pick a smaller unit instead: `Duration::FromMilliseconds(1500)`. :: ## Try it yourself 1. Print `race` with `{:.3}` and then with `{:.0}`. A precision names how many digits of the fraction to write — does it round or cut off? 2. Call `Span(0, 90, 0)`. How many seconds are 90 minutes? 3. Import `ParseDuration`, read the text `"1000.8s"`, and check with `Equals` that it is the same duration as `race`. 4. Turn the negative `margin` round with `Negated()`, which also returns `Duration?`. ## Learn more - [Optional](https://rux-lang.dev/docs/learn/optional), [Coalesce](https://rux-lang.dev/docs/learn/coalesce) and [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate) — the three tools this lesson uses on every `Duration?` - [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic) — the same idea, overflow reported instead of wrapped, on plain integers - [Stopwatch](https://rux-lang.dev/docs/learn/stopwatch) — measuring a duration with a clock - [Launch](https://rux-lang.dev/docs/learn/launch) — a checkpoint project that counts down with durations # Stopwatch ::note **You'll need**: [Duration](https://rux-lang.dev/docs/learn/duration), [Catch](https://rux-lang.dev/docs/learn/catch) :: To find out how long something takes, read a clock before it, read it again after, and subtract. The interesting question is *which* clock. This lesson uses `Instant`, a clock made for exactly this job, and shows the one way the subtraction can go wrong. Timing is also the first thing in this course whose output is different on every run. The program deals with that honestly: it prints only facts that hold every time, never the measured numbers themselves. ```rux import Time::{ Duration, Instant, SleepFor }; ``` ## Two kinds of clock A computer has two clocks, and they answer different questions. | | `Instant` (monotonic) | `Timestamp` (wall clock) | | ------------------------- | ------------------------------------------- | ----------------------------------------- | | Answers | how much time passed | what time it is | | Can go backwards | never | yes — when someone or the network sets it | | Means anything on its own | no: it counts from an origin picked at boot | yes: seconds since 1970-01-01 UTC | | Printable with `{}` | no | yes, as RFC 3339 text | The wall clock is the wrong tool for timing: if it is set back an hour in the middle of a measurement, the measurement comes out an hour short. An `Instant` only ever moves forward, so the difference between two readings is always the time that passed. You will meet `Timestamp` properly in [Date and time](https://rux-lang.dev/docs/learn/date-time). ## Elapsed time `Instant::Now()` takes a reading, and `Elapsed()` is the time from that reading until now. Here it times a 50 ms sleep: ```rux let nap = Duration::FromMilliseconds(50); let started = Instant::Now(); ``` `SleepFor` returns `! TimeError`, because the system may refuse to wait. A bare call would be an error, so the program [catches](https://rux-lang.dev/docs/learn/catch) the failure and gives up — with nothing slept, there is nothing to measure: ```rux SleepFor(nap) catch { else => { PrintLine("the system refused to sleep"); return 1; } }; let napped = started.Elapsed(); ``` A sleep lasts *at least* as long as asked, plus whatever it took the system to wake the program up again. So `napped` is a little over 50 ms, by an amount that changes every run. The program prints the fact that holds every time instead of the number: ```rux let oversleep = napped.Minus(nap) ?? Duration::Zero(); PrintLine("asked to sleep for {}", nap); PrintLine("slept at least that long: {}", !oversleep.IsNegative()); ``` ## Timing a piece of work The usual pattern is two readings around the work: ```rux let before = Instant::Now(); var sum: uint64 = 0; for i in 0..1000000 { sum += i as uint64; } let after = Instant::Now(); ``` `later.Since(earlier)` gives the `Duration` between them. It answers `Duration?`, and that is the safety catch: given the readings the wrong way round, it returns `none` rather than a huge or negative number. ```rux match after.Since(before) { took? => PrintLine("the loop took more than no time: {}", !took.IsZero()), none => PrintLine("the clock went backwards") } match before.Since(after) { took? => PrintLine("reversed: {}", took), none => PrintLine("reversed: none, because the argument was the later reading") } ``` ```mermaid flowchart LR b(["before = Instant::Now()"]) --> w["the work"] --> a(["after = Instant::Now()"]) a --> s1["after.Since(before)"] --> d["Duration"] a --> s2["before.Since(after)"] --> n["none"] ``` `start.Elapsed()` is shorthand for `Instant::Now().Since(start)` that can never be the wrong way round, which is why it returns a plain `Duration`. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Stopwatch){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // To time something, read a clock before and after and subtract. The clock to read is `Instant`: // a monotonic clock, which only ever moves forward. The wall clock is the wrong tool, because // someone (or the network) can set it back an hour in the middle of the measurement. // // An `Instant` has no meaning on its own. It counts from an origin the system picked at boot, // so it cannot be printed as a time of day; it is only good for comparing with another reading // from the same run. `later.Since(earlier)` gives the `Duration` between two readings, and // `start.Elapsed()` is shorthand for "since start, until now". // // How long anything takes depends on the machine and on what else it is doing, so a measured // number changes from run to run. This program prints only what is true on every run: that a // sleep of 50 ms took at least 50 ms, and that readings never go backwards. import Io::PrintLine; import Time::{ Duration, Instant, SleepFor }; func Main() -> int { let nap = Duration::FromMilliseconds(50); let started = Instant::Now(); // Sleeping can fail if the system refuses to wait. Then there is nothing to measure. SleepFor(nap) catch { else => { PrintLine("the system refused to sleep"); return 1; } }; let napped = started.Elapsed(); // `napped` is a little over 50 ms, by an amount that differs every run, so compare it rather // than print it. Subtracting the request leaves the oversleep, which is never negative. let oversleep = napped.Minus(nap) ?? Duration::Zero(); PrintLine("asked to sleep for {}", nap); PrintLine("slept at least that long: {}", !oversleep.IsNegative()); // Timing some work: take a reading, do the work, take another. let before = Instant::Now(); var sum: uint64 = 0; for i in 0..1000000 { sum += i as uint64; } let after = Instant::Now(); PrintLine("sum of 0..1000000 = {}", sum); // `Since` wants the earlier reading as its argument. Asked the wrong way round it gives // `none` instead of a huge or negative duration, so a swapped pair cannot go unnoticed. match after.Since(before) { took? => PrintLine("the loop took more than no time: {}", !took.IsZero()), none => PrintLine("the clock went backwards") } match before.Since(after) { took? => PrintLine("reversed: {}", took), none => PrintLine("reversed: none, because the argument was the later reading") } return 0; } ``` Besides `Io`, its `Rux.toml` lists `Time` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Stopwatch rux run ``` ```text asked to sleep for 0.05s slept at least that long: true sum of 0..1000000 = 499999500000 the loop took more than no time: true reversed: none, because the argument was the later reading ``` The measured times differ on every run, so the program prints only facts about them that hold every time. Every line is the same on every run, because none of them prints a measured time. ## Common mistakes ::warning **Printing an `Instant`.**:br An instant has no meaning outside the run that took it, so it cannot be printed at all. `PrintLine("started at {}", started)` fails with `error: argument 2 to 'PrintLine' has type 'Instant', but variadic parameter 'args' requires 'Display'`. Print the `Duration` between two instants instead. :: ::warning **Ignoring a failed sleep.**:br Calling `SleepFor(nap);` on its own fails with `error: fallible result of type '! TimeError' is discarded`. Decide what a refused sleep means: `catch` it, as the program does, or pass it on with `?`. :: ::warning **Treating `Since` as a duration.**:br`let took: Duration = after.Since(before);` fails with `error: cannot assign 'Duration?' to 'Duration'`. The `none` case is the swapped pair, and a `match` or `??` has to say what to do about it. :: ::warning **Timing with the wall clock.**:br`Timestamp` has a `Since` method too, so timing with two wall-clock readings compiles and usually gives a sensible answer — until the clock is adjusted mid-measurement. Use `Instant` for anything that measures. :: ## Try it yourself 1. In the first `match`, print `took` with `{:.6}`. Run the program several times and watch the number change. 2. Change the loop to count to 10 000 000. Does the time grow by about ten times? 3. Print `oversleep` itself, in milliseconds, with `TotalMilliseconds`. How much does your system oversleep? 4. Import `Timestamp` and print `Timestamp::Now()` — the wall clock. Its text is in UTC; how far is it from your own clock? ## Learn more - [Duration](https://rux-lang.dev/docs/learn/duration) — the type every measurement here produces - [Catch](https://rux-lang.dev/docs/learn/catch) — handling the failure of `SleepFor` - [Date and time](https://rux-lang.dev/docs/learn/date-time) — the wall clock, and moments that mean something on their own - [Launch](https://rux-lang.dev/docs/learn/launch) — a checkpoint project that counts down with `SleepFor` # Date ::note **You'll need**: [Stopwatch](https://rux-lang.dev/docs/learn/stopwatch), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Catch](https://rux-lang.dev/docs/learn/catch), [Variant match](https://rux-lang.dev/docs/learn/variant-match) :: A `Date` is a day on the calendar: a year, a month and a day, with no time of day and no time zone. Unlike a duration it is not just a number, because the calendar is irregular — months have 28 to 31 days, and February has a 29th only in a leap year. That irregularity is what makes dates interesting to read. The text `2023-02-29` is perfectly well-formed, and still wrong: 2023 had no 29 February. A program that reads dates has to tell those cases apart and say which one it found. ```rux import Time::{ DaysInMonth, IsLeapYear, ParseDate, TimeParseError }; ``` ## Reading a date `ParseDate` reads the ISO 8601 form `YYYY-MM-DD` and returns `Date ! TimeParseError`. A [`match`](https://rux-lang.dev/docs/learn/outcome) on the outcome handles both sides: ```rux func Show(text: char8[..]) { match ParseDate(text) { .Success(date) => PrintLine("{} ok, day {} of the year", date, date.DayOfYear()), .Failure(error) => PrintLine("{} {}, at byte {}", text, Reason(error), error.Offset()) } } ``` A `Date` prints with `{}` in the same `YYYY-MM-DD` form, and `DayOfYear` counts from 1 on 1 January — so 29 February is day 60. ## What went wrong, and where `TimeParseError` is a [variant](https://rux-lang.dev/docs/learn/variant-match): each case names a different mistake, and each carries the byte of the text where the parser found it. `Offset()` reads that byte whatever the case, and `Reason` turns the case into words: ```rux func Reason(error: TimeParseError) -> char8[..] { return match error { .InvalidSyntax(_) => "not shaped like YYYY-MM-DD", .InvalidMonth(_) => "no such month", .NonexistentDay(_) => "no such day in that month", .InvalidTime(_) => "time out of range", .ExcessiveFraction(_) => "too many fraction digits", .InvalidOffset(_) => "offset out of range" }; } ``` | Text | Case | Byte | Points at | | ------------ | ---------------- | ---- | ------------------------------------------ | | `2023-02-29` | `NonexistentDay` | 8 | the day, which February 2023 does not have | | `2024-13-01` | `InvalidMonth` | 5 | the month | | `2024/02/01` | `InvalidSyntax` | 4 | the `/` where a `-` belongs | | `2024-2-1` | `InvalidSyntax` | 6 | the `-` where a second month digit belongs | The last three cases never happen for a date alone. The whole `Time` package shares one error type — the next lesson reads times and offsets with it — so the `match` still covers them all. A program that knows the byte can do better than "invalid date": it can point at the character that is wrong. ## The leap-year rule ```mermaid flowchart LR y(["A year"]) --> d4{"Divisible by 4?"} d4 -- "no" --> c["Common year
February has 28 days"] d4 -- "yes" --> d100{"Divisible by 100?"} d100 -- "no" --> l["Leap year
February has 29 days"] d100 -- "yes" --> d400{"Divisible by 400?"} d400 -- "yes" --> l d400 -- "no" --> c ``` `IsLeapYear` applies the rule, and `DaysInMonth` uses it. The program tries one year of each kind — an ordinary year, a fourth year, and the two kinds of century: ```rux let years: int32[4] = [2023, 2024, 1900, 2000]; for year in years { PrintLine("{} leap: {}, February has {} days", year, IsLeapYear(year), DaysInMonth(year, 2)); } ``` The array is written `int32[4]` on purpose: both functions take the year as an `int32`, and an array of plain literals would be an array of `int`. ## Calendar arithmetic Date arithmetic follows the calendar, so the day after 28 February depends on the year: ```rux for text in ["2023-02-28", "2024-02-28"] { let date = ParseDate(text) catch { else => continue }; match date.PlusDays(1) { next? => PrintLine("the day after {} is {}", date, next), none => PrintLine("the day after {} is out of range", date) } } ``` `PlusDays` returns `Date?`, because a date far enough out leaves the range the package supports: years that four digits can write, up to 9999. The `catch { else => continue }` skips any text that is not a date; here both are. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Date){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A `Date` is a day on the calendar: a year, a month and a day, with no time and no time zone. // Unlike a duration it is not just a number, because the calendar is irregular. Months have 28 // to 31 days, and February has a 29th only in a leap year: every fourth year, except a century // year, except a century divisible by 400. So 2000 was a leap year and 1900 was not. // // That makes text such as "2023-02-29" well-formed but wrong. `ParseDate` reads the ISO 8601 // form `YYYY-MM-DD` and returns `Date ! TimeParseError`, and the error says which mistake it // was (a bad shape, a month that does not exist, a day the month does not have) and at which // byte of the text, so a program can point at the problem rather than just reject it. import Io::PrintLine; import Time::{ DaysInMonth, IsLeapYear, ParseDate, TimeParseError }; func Reason(error: TimeParseError) -> char8[..] { return match error { .InvalidSyntax(_) => "not shaped like YYYY-MM-DD", .InvalidMonth(_) => "no such month", .NonexistentDay(_) => "no such day in that month", .InvalidTime(_) => "time out of range", .ExcessiveFraction(_) => "too many fraction digits", .InvalidOffset(_) => "offset out of range" }; } func Show(text: char8[..]) { match ParseDate(text) { .Success(date) => PrintLine("{} ok, day {} of the year", date, date.DayOfYear()), .Failure(error) => PrintLine("{} {}, at byte {}", text, Reason(error), error.Offset()) } } func Main() -> int { Show("2024-02-29"); Show("2023-02-29"); Show("2024-13-01"); Show("2024/02/01"); Show("2024-2-1"); PrintLine(""); // The leap-year rule: an ordinary year, a fourth year, and the two kinds of century. let years: int32[4] = [2023, 2024, 1900, 2000]; for year in years { PrintLine("{} leap: {}, February has {} days", year, IsLeapYear(year), DaysInMonth(year, 2)); } PrintLine(""); // Date arithmetic follows the calendar, so the day after 28 February depends on the year. // `PlusDays` returns `Date?`, because a date far enough out leaves the supported range. for text in ["2023-02-28", "2024-02-28"] { let date = ParseDate(text) catch { else => continue }; match date.PlusDays(1) { next? => PrintLine("the day after {} is {}", date, next), none => PrintLine("the day after {} is out of range", date) } } return 0; } ``` Besides `Io`, its `Rux.toml` lists `Time` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Date rux run ``` ```text 2024-02-29 ok, day 60 of the year 2023-02-29 no such day in that month, at byte 8 2024-13-01 no such month, at byte 5 2024/02/01 not shaped like YYYY-MM-DD, at byte 4 2024-2-1 not shaped like YYYY-MM-DD, at byte 6 2023 leap: false, February has 28 days 2024 leap: true, February has 29 days 1900 leap: false, February has 28 days 2000 leap: true, February has 29 days the day after 2023-02-28 is 2023-03-01 the day after 2024-02-28 is 2024-02-29 ``` ## Common mistakes ::warning **Leaving out an error case.**:br A `match` on `TimeParseError` must be exhaustive, even for the cases a date cannot produce. Drop the `.InvalidOffset` arm and the compiler says `error: match on 'TimeParseError' is not exhaustive; missing TimeParseError::InvalidOffset`. :: ::warning **Using the outcome as a date.**:br`ParseDate` returns `Date ! TimeParseError`, which has no date methods. `let leap = ParseDate("2024-02-29");` followed by `leap.DayOfYear()` fails with `error: type 'Date ! TimeParseError' has no field 'DayOfYear'`. Take the date out with `match` or `catch` first. :: ::warning **Years as plain integers.**:br`let years = [2023, 2024, 1900, 2000];` makes an array of `int`, and passing one on fails with `error: argument 1 to 'IsLeapYear' has type 'int', but parameter 'year' requires 'int32'`. Give the array its type, as the program does. :: ## Try it yourself 1. Add `Show("2100-02-29")` and `Show("2400-02-29")`. Work out the answers with the diagram before you run them. 2. Parse `2024-01-31` and call `PlusMonths(1)`. 31 February does not exist — what does the package do instead? 3. Print `DayOfWeek()` for a date you know. It counts from 0 for Sunday to 6 for Saturday. 4. Parse two dates and print how many days apart they are with `later.DaysSince(earlier)`. ## Learn more - [Outcome](https://rux-lang.dev/docs/learn/outcome) and [Catch](https://rux-lang.dev/docs/learn/catch) — the two ways the program handles `Date ! TimeParseError` - [Variant match](https://rux-lang.dev/docs/learn/variant-match) — matching the cases of a variant and their payloads - [Date and time](https://rux-lang.dev/docs/learn/date-time) — adding a time of day and an offset - [Age](https://rux-lang.dev/docs/learn/age) — a checkpoint project that counts years, months and days between two dates # Date and time ::note **You'll need**: [Date](https://rux-lang.dev/docs/learn/date), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Coalesce](https://rux-lang.dev/docs/learn/coalesce) :: A date and a time of day together make a `DateTime`, such as 2026-10-04T09:30:00. It looks like a moment, but it is not one yet: 09:30 in Kyiv and 09:30 in New York are seven hours apart. What it lacks is an **offset** — how far the local clock was ahead of or behind UTC, the world's reference clock. This lesson adds the offset, writes the result in RFC 3339 — the date-and-time format of logs, JSON and HTTP — and shows how to tell whether two texts name the same moment. ```rux import Time::{ DateTime, OffsetDateTime, ParseDateTime, ParseRfc3339, UtcOffset }; ``` ## Four types for four questions | Type | Holds | Example | Is it one moment? | | ---------------- | ------------------------------ | --------------------------- | --------------------------- | | `Date` | year, month, day | `2026-10-04` | no — a whole day, somewhere | | `DateTime` | a date and a time of day | `2026-10-04T09:30:00` | no — 09:30 where? | | `OffsetDateTime` | a `DateTime` and a `UtcOffset` | `2026-10-04T09:30:00+03:00` | yes | | `Timestamp` | seconds since 1970-01-01 UTC | `1791095400` | yes, with one spelling | ## A local date and time `ParseDateTime` reads a date, a `T` and a time. Like `ParseDate` in the [previous lesson](https://rux-lang.dev/docs/learn/date), it returns an outcome with a `TimeParseError`, and here a [`catch`](https://rux-lang.dev/docs/learn/catch) ends the program if the text is not a date and time: ```rux let meeting = ParseDateTime("2026-10-04T09:30:00") catch { else => { PrintLine("not a date and time"); return 1; } }; PrintLine("local {}", meeting); ``` There is no offset in the text and none in the value: it prints back as `2026-10-04T09:30:00`. ## Adding an offset `OffsetDateTime(dateTime, offset)` pairs a reading with the offset it was taken in. `UtcOffset::Of(hours, minutes)` makes the offset, and returns `UtcOffset?` because not every pair is one — no clock is 30 hours ahead of UTC: ```rux let kyiv = OffsetDateTime(meeting, UtcOffset::Of(3, 0) ?? UtcOffset::Utc()); let newYork = OffsetDateTime(meeting, UtcOffset::Of(-4, 0) ?? UtcOffset::Utc()); ``` They print as `2026-10-04T09:30:00+03:00` and `2026-10-04T09:30:00-04:00`: the same clock reading, in two places, so two different moments. To measure between moments, turn each into a `Timestamp` with `ToTimestamp()`. A timestamp counts seconds since the start of 1970 in UTC, so it has no offset left to disagree about: ```rux let apart = newYork.ToTimestamp().UnixSeconds() - kyiv.ToTimestamp().UnixSeconds(); PrintLine("apart by {} s", apart); ``` New York is seven hours behind Kyiv, so its 09:30 comes 25 200 seconds later. ```mermaid flowchart LR dt["DateTime
09:30:00"] --> odt["OffsetDateTime
09:30:00+03:00"] off["UtcOffset
+03:00"] --> odt odt -- "ToTimestamp()" --> ts["Timestamp
one moment, in UTC"] ts -- "DateTime::FromTimestamp(ts, offset)" --> local["DateTime
the reading at another offset"] ``` ## RFC 3339 RFC 3339 is an `OffsetDateTime` written as text: the date, a `T`, the time, and then either `Z` for UTC or a signed `+HH:MM`. `ParseRfc3339` reads it and returns `OffsetDateTime ! TimeParseError`; printing an `OffsetDateTime` with `{}` writes it back. This text is the Kyiv meeting again, written in UTC — different text, same moment: ```rux match ParseRfc3339("2026-10-04T06:30:00Z") { .Success(utc) => { PrintLine("parsed {}", utc); let same = utc.ToTimestamp().UnixSeconds() == kyiv.ToTimestamp().UnixSeconds(); PrintLine("same moment as the Kyiv meeting: {}", same); // Converting the moment to another offset gives the local reading there. let tokyo = UtcOffset::Of(9, 0) ?? UtcOffset::Utc(); let there = DateTime::FromTimestamp(utc.ToTimestamp(), tokyo); PrintLine("in Tokyo {}", OffsetDateTime(there, tokyo)); }, .Failure(error) => PrintLine("rejected at byte {}", error.Offset()) } ``` The comparison is between timestamps, which have one spelling per moment, so `same` is `true`. Going the other way, `DateTime::FromTimestamp` takes a moment and an offset and gives the local reading there: the meeting is at 15:30 in Tokyo. RFC 3339 requires the offset. Without it the text is only a local reading, so `ParseRfc3339("2026-10-04T09:30:00")` is refused at byte 19 — the end of the text, where the offset should start. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/DateTime){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A date and a time of day together make a `DateTime`, such as 2026-10-04T09:30:00. On its own // that is not yet a moment: 09:30 in Kyiv and 09:30 in New York are seven hours apart. What it // lacks is an offset, how far the clock was ahead of or behind UTC, the world's reference clock. // // `OffsetDateTime` is a `DateTime` plus a `UtcOffset`, and that is enough to name one moment. Its // text form is RFC 3339, the format of logs, JSON and HTTP: the date, a `T`, the time, then `Z` // for UTC or a signed `+HH:MM`. `ParseRfc3339` reads it and returns // `OffsetDateTime ! TimeParseError`, and printing an `OffsetDateTime` with `{}` writes it back. // // Two texts can name the same moment in different offsets. To compare moments, turn each into a // `Timestamp`, a count of seconds since 1970-01-01 in UTC, which has one spelling per moment. import Io::PrintLine; import Time::{ DateTime, OffsetDateTime, ParseDateTime, ParseRfc3339, UtcOffset }; func Main() -> int { // A local date and time. There is no offset in it, and none in its text either. let meeting = ParseDateTime("2026-10-04T09:30:00") catch { else => { PrintLine("not a date and time"); return 1; } }; PrintLine("local {}", meeting); // The same reading in two places. `UtcOffset::Of` returns `UtcOffset?`, since 30 hours ahead // is not an offset any clock can have. let kyiv = OffsetDateTime(meeting, UtcOffset::Of(3, 0) ?? UtcOffset::Utc()); let newYork = OffsetDateTime(meeting, UtcOffset::Of(-4, 0) ?? UtcOffset::Utc()); PrintLine("in Kyiv {}", kyiv); PrintLine("in New York {}", newYork); // Two different moments, 7 hours (25200 seconds) apart. let apart = newYork.ToTimestamp().UnixSeconds() - kyiv.ToTimestamp().UnixSeconds(); PrintLine("apart by {} s", apart); // RFC 3339 from text. This one is the Kyiv meeting written in UTC: other text, same moment. match ParseRfc3339("2026-10-04T06:30:00Z") { .Success(utc) => { PrintLine("parsed {}", utc); let same = utc.ToTimestamp().UnixSeconds() == kyiv.ToTimestamp().UnixSeconds(); PrintLine("same moment as the Kyiv meeting: {}", same); // Converting the moment to another offset gives the local reading there. let tokyo = UtcOffset::Of(9, 0) ?? UtcOffset::Utc(); let there = DateTime::FromTimestamp(utc.ToTimestamp(), tokyo); PrintLine("in Tokyo {}", OffsetDateTime(there, tokyo)); }, .Failure(error) => PrintLine("rejected at byte {}", error.Offset()) } // RFC 3339 requires the offset. Without it, the text stops being a moment. match ParseRfc3339("2026-10-04T09:30:00") { .Success(moment) => PrintLine("parsed {}", moment), .Failure(error) => PrintLine("no offset rejected at byte {}", error.Offset()) } return 0; } ``` Besides `Io`, its `Rux.toml` lists `Time` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/DateTime rux run ``` ```text local 2026-10-04T09:30:00 in Kyiv 2026-10-04T09:30:00+03:00 in New York 2026-10-04T09:30:00-04:00 apart by 25200 s parsed 2026-10-04T06:30:00Z same moment as the Kyiv meeting: true in Tokyo 2026-10-04T15:30:00+09:00 no offset rejected at byte 19 ``` ## Common mistakes ::warning **Passing the optional offset straight in.**:br`UtcOffset::Of` returns `UtcOffset?`. Writing `OffsetDateTime(meeting, UtcOffset::Of(3, 0))` fails with `error: argument 2 to 'OffsetDateTime' has type 'UtcOffset?', but parameter 'offset' requires 'UtcOffset'`. Supply a fallback with `??`, or `match` the `none`. :: ::warning **Comparing moments with `==`.**:br`utc == kyiv` compiles, and is `false`. It compares the two values field by field — the clock reading and the offset — and those differ even though the moment is the same. Compare their timestamps instead. :: ::warning **Offsets with mixed signs.**:br Both parts of an offset carry its sign, so −03:30 is `UtcOffset::Of(-3, -30)`. `UtcOffset::Of(-3, 30)` is `none`, and behind a `?? UtcOffset::Utc()` fallback that quietly becomes UTC. :: ::warning **RFC 3339 text without an offset.**:br`ParseRfc3339` refuses it. If the text really is a local reading, read it with `ParseDateTime`, and decide which offset it is in yourself. :: ## Try it yourself 1. Add a line for Mumbai, five and a half hours ahead of UTC: `UtcOffset::Of(5, 30)`. 2. Parse `2026-10-04T09:30:00.250+03:00`. Is it the same moment as `kyiv`? How does it print? 3. Import `Timestamp`, then print the current time in Kyiv: `DateTime::FromTimestamp(Timestamp::Now(), offset)` paired with that offset. 4. Change the Kyiv offset to `UtcOffset::Of(30, 0)`. What does the program print, and why is a fallback to UTC risky in a real program? ## Learn more - [Date](https://rux-lang.dev/docs/learn/date) — the calendar half of a `DateTime`, and `TimeParseError` - [Coalesce](https://rux-lang.dev/docs/learn/coalesce) — the `??` that supplies each offset's fallback - [Structural equality](https://rux-lang.dev/docs/learn/structural-equality) — what `==` compares on a struct - [Stopwatch](https://rux-lang.dev/docs/learn/stopwatch) — why measuring time uses a different clock from this one # Random ::note **You'll need**: [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), [Copy](https://rux-lang.dev/docs/learn/copy), [Generic](https://rux-lang.dev/docs/learn/generic), [Format](https://rux-lang.dev/docs/learn/format) :: Arithmetic alone cannot produce randomness. What a random number generator produces is a sequence that *looks* random but follows entirely from where it started — its **seed**. Give it the same seed and you get the same numbers, in the same order, on every machine and every run. That sounds like a weakness, and it is the opposite. A simulation can be rerun exactly, a test that draws random data can be repeated, and a bug that only showed up for one particular draw can be reproduced — all by remembering one number. The program imports the `Random` package's general-purpose generator and one way of drawing from it: ```rux import Random::{ Pcg64Dxsm, UniformBelow }; ``` ## A generator is a struct `Pcg64Dxsm(seed)` builds a generator. It is an ordinary [struct](https://rux-lang.dev/docs/learn/struct) whose fields are its state, and every draw advances that state. `UniformBelow(generator, bound)` draws a number from 0 up to, but not including, `bound` — so `100` gives 0 to 99: ```rux func Draw(label: char8[..], generator: &var Pcg64Dxsm) { Print("{}", label); for i in 0..6 { Print(" {:2}", UniformBelow(generator, 100)); } PrintLine(); } ``` Because a draw changes the generator, `Draw` takes it as a [mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), `&var Pcg64Dxsm`: the six draws advance the caller's generator, not a copy of it. The `{:2}` pads each number to two columns, as in [Format](https://rux-lang.dev/docs/learn/format). `UniformBelow` is [generic](https://rux-lang.dev/docs/learn/generic) over any generator, which is why the call names the type in angle brackets. The compiler can also work it out from the argument; the program spells it out so you can see which generator is drawing. ## Same seed, same numbers ```rux var first = Pcg64Dxsm(2026); Draw("seed 2026 ", first); var second = Pcg64Dxsm(2026); Draw("seed 2026 again", second); var other = Pcg64Dxsm(2027); Draw("seed 2027 ", other); ``` The first two lines of output are identical: `23 73 75 87 37 28`, twice. The third has nothing in common with them, even though 2027 is right next to 2026 — a seed is a label for a sequence, not a starting value that nearby seeds start close to. ## Drawing continues Calling `Draw` on `first` again does not start over. It carries on from where the first six draws left the state, and gives six new numbers. ## A copy replays the future Copying a generator copies its state, as [copying](https://rux-lang.dev/docs/learn/copy) any struct copies its fields. The copy then produces exactly what the original is about to produce: ```rux var copy = first; Draw("2026 continued ", first); Draw("its copy ", copy); ``` ```mermaid flowchart LR seed(["seed 2026"]) --> s0["state after
12 draws"] s0 -- "var copy = first" --> c["copy:
same state"] s0 -- "Draw(first)" --> f["16 12 33 48 2 29"] c -- "Draw(copy)" --> g["16 12 33 48 2 29"] ``` That is useful on purpose — saving a generator and rewinding to it — and a trap by accident, when two parts of a program each hold a copy and draw "different" numbers that are the same. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Random){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Arithmetic alone cannot produce randomness. What a random number generator produces is a // sequence that *looks* random but follows entirely from where it started, its seed. Give it // the same seed and you get the same numbers, in the same order, on every machine, every run. // // That is a feature. A simulation can be rerun exactly, a test that draws random data can be // repeated, and a bug that depended on the draw can be reproduced, all by remembering one number. // // The Random package's general-purpose generator is `Pcg64Dxsm`. `Pcg64Dxsm(seed)` builds one, // and `UniformBelow(generator, bound)` draws a number from 0 up to, not including, // `bound`. The generator is an ordinary struct whose fields are its state: each draw advances // it, which is why it is passed as `&var`. And like any struct, copying it copies the state, // so a copy replays exactly what the original is about to produce. import Io::{ Print, PrintLine }; import Random::{ Pcg64Dxsm, UniformBelow }; func Draw(label: char8[..], generator: &var Pcg64Dxsm) { Print("{}", label); for i in 0..6 { Print(" {:2}", UniformBelow(generator, 100)); } PrintLine(); } func Main() -> int { var first = Pcg64Dxsm(2026); Draw("seed 2026 ", first); // A second generator from the same seed: the same run, exactly. var second = Pcg64Dxsm(2026); Draw("seed 2026 again", second); // A neighbouring seed gives an unrelated run. Seeds are labels, not starting values. var other = Pcg64Dxsm(2027); Draw("seed 2027 ", other); // Drawing again continues where the generator stopped; it does not start over. Draw("2026 continued ", first); // A copy holds the same state, so it produces the same next numbers as the original. var copy = first; Draw("2026 continued ", first); Draw("its copy ", copy); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Random` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Random rux run ``` ```text seed 2026 23 73 75 87 37 28 seed 2026 again 23 73 75 87 37 28 seed 2027 73 1 34 5 0 38 2026 continued 67 36 72 16 94 29 2026 continued 16 12 33 48 2 29 its copy 16 12 33 48 2 29 ``` ## Common mistakes ::warning **A generator declared with `let`.**:br Every draw changes the generator, so it must be mutable. With `let first = Pcg64Dxsm(2026);` the call `Draw("seed 2026 ", first)` fails with `error: argument 2 to 'Draw' cannot borrow immutable 'first' as '&var Pcg64Dxsm'`, and the help says `declare 'first' with 'var' to make it mutable`. :: ::warning **Taking the generator by value.**:br Declare the parameter as `generator: Pcg64Dxsm` and the draw inside fails with `error: argument 1 to 'UniformBelow' cannot borrow immutable 'generator' as '&var Pcg64Dxsm'`. Even if the function copied it into a `var` local, every call would draw from a copy, and the caller's generator would never move on. Pass it as `&var`. :: ::warning **Seeding again for every draw.**:br A function that builds `Pcg64Dxsm(2026)` each time it is called returns the same numbers on every call. Make one generator, seed it once, and pass it to everything that draws. :: ## Try it yourself 1. Change the seed to your birth year. Do the first two lines still match each other? 2. `Pcg64Dxsm::WithStream(2026, 1)` builds a generator with the same seed on a different *stream*. Draw from it and compare with `seed 2026`. 3. Import `Xoshiro256`, a second generator, and draw from `Xoshiro256(2026)` with `UniformBelow`. Same seed, different generator — same numbers? 4. Import `Bernoulli` and flip ten coins with `Bernoulli(generator, 0.5)`, which returns a `bool`. ## Learn more - [Distribution](https://rux-lang.dev/docs/learn/distribution) — shaping raw draws into dice, shuffles and bell curves - [Entropy](https://rux-lang.dev/docs/learn/entropy) — seeds that nobody can guess - [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) and [Copy](https://rux-lang.dev/docs/learn/copy) — the two ideas that explain how a generator behaves - [Generic](https://rux-lang.dev/docs/learn/generic) — why `UniformBelow` works with any generator # Distribution ::note **You'll need**: [Random](https://rux-lang.dev/docs/learn/random), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice), [Format number](https://rux-lang.dev/docs/learn/format-number) :: A generator hands out raw 64-bit numbers. What a program wants is almost never that: it wants a die roll from 1 to 6, a deck in a random order, or a height that is usually near the average and only rarely far from it. A **distribution** turns raw numbers into those values, and decides how likely each one is. Each of these is easy to get subtly wrong by hand, and the mistakes do not show in a quick test. The `Random` package has exact versions, and this lesson tours three of them: ```rux import Random::{ NormalWith, Pcg64Dxsm, Shuffle, UniformInRange }; ``` The generator has a fixed seed, `Pcg64Dxsm(7)`, so — as in [Random](https://rux-lang.dev/docs/learn/random) — every run prints the same results. ## Uniform: every value equally likely `UniformInRange(generator, low, high)` makes every value from `low` to `high` equally likely, **both ends included**. A die is therefore `1, 6`: ```rux var faces: uint[6] = [0; 6]; for i in 0..6000 { let face = UniformInRange(generator, 1, 6); faces[face - 1] += 1; } ``` Six thousand rolls land close to 1000 per face — `1013 956 939 1048 1026 1018` — and never exactly. That is what fair looks like: equally likely is not equally often. Why not take a raw number and write `raw % 6`? Because 2⁶⁴ is not a multiple of 6. The raw numbers split into six groups that are almost, but not quite, the same size, and the low faces come up very slightly more often. `UniformInRange` corrects for that; `%` does not. | Function | Draws | Ends | | ----------------------------------- | -------------------------------- | ---------------- | | `UniformBelow(g, bound)` | `0` to `bound - 1` | `bound` excluded | | `UniformInRange(g, low, high)` | `low` to `high` | both included | | `UniformFloatInRange(g, low, high)` | a `float64` from `low` to `high` | `high` excluded | ## Shuffle: every order equally likely `Shuffle` puts a slice in a random order, in place, where every one of the possible arrangements is equally likely: ```rux var cards: char8[..][8] = ["A", "2", "3", "4", "5", "6", "7", "8"]; Shuffle(generator, cards[..]); ``` The cards are rearranged where they are, so `Shuffle` needs a [writable slice](https://rux-lang.dev/docs/learn/writable-slice) of them: `cards` is a `var` array and `cards[..]` views all of it. The second type argument, `char8[..]`, is the type of one element. The tempting home-made shuffle — swap each position with a random one anywhere in the slice — does not make every order equally likely: some arrangements come up more often than others. `Shuffle` uses Fisher and Yates' method, which swaps each position only with one at or before it, and is exactly uniform. ## Normal: the bell curve Many measured quantities — heights, errors, reaction times — cluster around an average and thin out on either side. `NormalWith(generator, mean, deviation)` draws from that bell curve. About 68% of its values fall within one deviation of the mean: ```rux let draws = 10000; var total = 0.0; var withinOne = 0; for i in 0..draws { let height = NormalWith(generator, 170.0, 10.0); total += height; if height >= 160.0 && height <= 180.0 { withinOne += 1; } } ``` The average of ten thousand draws is `170.1` — close to the mean of 170, not equal to it — and `68.3%` of them land between 160 and 180. `{:.1}` prints both to one decimal place, as in [Format number](https://rux-lang.dev/docs/learn/format-number). ```mermaid flowchart LR g(["Pcg64Dxsm
raw 64-bit numbers"]) --> u["UniformInRange
1, 2, 3, 4, 5, 6 —
all equally likely"] g --> s["Shuffle
every order
equally likely"] g --> n["NormalWith
near the mean likely,
far from it rare"] ``` ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Distribution){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A generator hands out raw 64-bit numbers. A distribution turns them into the numbers a program // actually wants, and decides how likely each one is. // // `UniformInRange(generator, low, high)` makes every value from `low` to `high` equally likely, // both ends included, so a die is `1, 6`. `Shuffle` puts a slice in a random order in which // every arrangement is equally likely. `NormalWith(generator, mean, deviation)` draws from the // bell curve: values cluster around the mean, about 68% of them within one deviation of it. // // Each of these is easy to get subtly wrong by hand. `raw % 6` favours the low faces a little, // and swapping random pairs a few times does not make every order equally likely. The library // versions are exact, so use them. The generator here has a fixed seed, so every run prints // the same results. import Io::{ Print, PrintLine }; import Random::{ NormalWith, Pcg64Dxsm, Shuffle, UniformInRange }; func Main() -> int { var generator = Pcg64Dxsm(7); // Uniform: 6000 rolls of a die land close to 1000 per face, never exactly. var faces: uint[6] = [0; 6]; for i in 0..6000 { let face = UniformInRange(generator, 1, 6); faces[face - 1] += 1; } Print("6000 die rolls "); for count in faces { Print(" {}", count); } PrintLine(); // Shuffle: the slice is rearranged in place, so it needs a writable view. var cards: char8[..][8] = ["A", "2", "3", "4", "5", "6", "7", "8"]; Shuffle(generator, cards[..]); Print("shuffled cards "); for card in cards { Print(" {}", card); } PrintLine(); // Normal: heights with a mean of 170 cm and a deviation of 10 cm. let draws = 10000; var total = 0.0; var withinOne = 0; for i in 0..draws { let height = NormalWith(generator, 170.0, 10.0); total += height; if height >= 160.0 && height <= 180.0 { withinOne += 1; } } PrintLine("average height {:.1} cm", total / (draws as float64)); PrintLine("within 160..180 {:.1}%", (withinOne as float64) * 100.0 / (draws as float64)); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Random` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Distribution rux run ``` ```text 6000 die rolls 1013 956 939 1048 1026 1018 shuffled cards 2 7 3 8 4 A 6 5 average height 170.1 cm within 160..180 68.3% ``` ## Common mistakes ::warning **Forgetting that both ends are included.**:br`UniformInRange(generator, 1, 6)` can return 6. Counting into `faces[face]` instead of `faces[face - 1]` compiles, and then stops the program with `Panic: index out of range` the first time a 6 is rolled. :: ::warning **Shuffling something that cannot change.**:br Declare the array with `let` and the call fails with `error: argument 2 to 'Shuffle' has type 'char8[..][..]', but parameter 'items' requires 'var char8[..][..]'`. Passing the array itself rather than a slice of it fails too: `error: no matching overload for 'Shuffle'`. Shuffle `cards[..]` of a `var` array. :: ::warning **Whole numbers for a float distribution.**:br`NormalWith` takes `float64` arguments, and an integer literal is not converted on its own: `NormalWith(generator, 170, 10)` fails with `error: argument 2 to 'NormalWith' has type 'int', but parameter 'mean' requires 'float64'`. Write `170.0` and `10.0`. :: ## Try it yourself 1. Roll two dice 6000 times and count each total from 2 to 12. Which total is most common, and why is this not a uniform distribution? 2. Import `UniformFloatInRange` and draw ten temperatures between −5.0 and 25.0. 3. Import `WeightedIndex` and make a loaded die: weights `[1.0, 1.0, 1.0, 1.0, 1.0, 5.0]` as a `float64` slice. How often does the last face come up now? 4. Change the deviation to 5.0. What share of heights land between 160 and 180 now? ## Learn more - [Random](https://rux-lang.dev/docs/learn/random) — the generator these functions draw from - [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) — the `var T[..]` that `Shuffle` rearranges - [Format number](https://rux-lang.dev/docs/learn/format-number) — the `{:.1}` used for the results - [Guess](https://rux-lang.dev/docs/learn/guess) — a checkpoint project that picks its secret number with `UniformInRange` # Entropy ::note **You'll need**: [Distribution](https://rux-lang.dev/docs/learn/distribution), [Catch](https://rux-lang.dev/docs/learn/catch), [Enum](https://rux-lang.dev/docs/learn/enum), [Pointer](https://rux-lang.dev/docs/learn/pointer) :: A seeded generator is predictable by design: anyone who learns the seed can replay every number it will ever produce. For a simulation that is a feature. For a session token, a password salt or an encryption key it is a disaster — those numbers must be ones nobody can guess, so they cannot come from arithmetic inside the program. They come from outside it. The operating system collects unpredictability — **entropy** — from hardware and from the timing of events, and the `Entropy` package asks it for some: ```rux import Entropy::{ EntropyError, Fill, NextUint64 }; import Random::{ Pcg64Dxsm, PcgFromEntropy, UniformBelow }; ``` The output is different on every run, and that is exactly what this lesson is about. ## Every request can fail Asking the system can fail: it may have no source, the request may be interrupted, or the system may simply refuse. So every function here is fallible, with an `EntropyError` [enum](https://rux-lang.dev/docs/learn/enum) saying why: | Function | Returns | Gives you | | ------------------ | -------------------------- | -------------------------------------------- | | `NextUint64()` | `uint64 ! EntropyError` | one unpredictable 64-bit number | | `Fill(buffer, n)` | `! EntropyError` | `n` unpredictable bytes, written to `buffer` | | `PcgFromEntropy()` | `Pcg64Dxsm ! EntropyError` | a generator with an unguessable seed | The whole point of these numbers is that nobody can guess them. A failure quietly replaced by some fixed value — a `catch` that answers `0` — would produce a "random" key that is the same on every machine. So the program checks every call and stops if one fails. `Describe` turns the reason into words, and `IsTransient` says whether asking again might help: ```rux func Describe(error: EntropyError) { let why = match error { EntropyError::Unsupported => "this system has no entropy source", EntropyError::Interrupted => "the request was interrupted", EntropyError::Failed => "the system refused", EntropyError::TooLarge => "too many bytes in one request" }; // `IsTransient` says whether asking again might help. Only an interruption is worth a retry. PrintLine("no entropy: {} (worth retrying: {})", why, error.IsTransient()); } ``` ## One number `NextUint64` is the simplest request. A `match` on its outcome prints the value, or describes the failure and ends the program with status 1: ```rux match NextUint64() { .Success(value) => PrintLine("a 64-bit value {:#018x}", value), .Failure(error) => { Describe(error); return 1; } } ``` `{:#018x}` prints it in hexadecimal with a `0x` prefix, padded with zeros to 18 characters — the prefix and all 16 digits. ## A buffer of bytes A key is usually a run of bytes rather than one number. `Fill` writes as many bytes as you ask for into memory you provide, so it takes a [pointer](https://rux-lang.dev/docs/learn/pointer) to the first byte and a count: ```rux var key: byte[16] = [0; 16]; Fill(@key[0] as *var opaque, 16) catch { error => { Describe(error); return 1; } }; ``` `@key[0]` is the address of the first byte. `Fill` takes the buffer as `*var opaque` — a writable pointer to bytes of any type — because it neither knows nor cares what the bytes will be used for. It also cannot see how big the buffer is: the count you pass is all it has, so it must match the array. The [`catch`](https://rux-lang.dev/docs/learn/catch) here names the error, `error => { … }`, so the arm can pass it to `Describe`. ## The common use: an unguessable seed Every request for entropy is a call into the operating system, while a generator makes a number in a few instructions — and a program rarely needs all its numbers to be unguessable. The usual pattern takes a little entropy once, as a seed, and then draws from a fast generator as in [Distribution](https://rux-lang.dev/docs/learn/distribution): ```rux var generator = PcgFromEntropy() catch { error => { Describe(error); return 1; } }; PrintLine("a die roll {}", UniformBelow(generator, 6) + 1); ``` ```mermaid flowchart LR os(["Operating system
hardware and timing"]) -- "NextUint64, Fill" --> v["Unguessable values:
keys, tokens, salts"] os -- "PcgFromEntropy" --> g["Pcg64Dxsm with an
unguessable seed"] g -- "UniformBelow, Shuffle, …" --> d["Fast draws that
differ every run"] ``` Every run now rolls differently — and the program has given up the ability to replay a run, because nobody knows the seed. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Entropy){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A seeded generator is predictable by design: anyone who learns the seed can replay it. When // the numbers must be unguessable, such as a session token, a password salt, or the seed for a // simulation that should differ every run, they have to come from outside the program. // // The operating system collects unpredictability from hardware and timing, and the Entropy // package asks it for some. `NextUint64()` returns `uint64 ! EntropyError` and `Fill` fills a // buffer, returning `! EntropyError`. Asking can fail (a system may have no source, or the // request may be interrupted), and since the whole point is that the bytes are unguessable, a // failure must never be quietly replaced by some fixed value. So every call here is checked. // // The output is different on every run, which is exactly what this lesson is about. import Entropy::{ EntropyError, Fill, NextUint64 }; import Io::{ Print, PrintLine }; import Random::{ Pcg64Dxsm, PcgFromEntropy, UniformBelow }; func Describe(error: EntropyError) { let why = match error { EntropyError::Unsupported => "this system has no entropy source", EntropyError::Interrupted => "the request was interrupted", EntropyError::Failed => "the system refused", EntropyError::TooLarge => "too many bytes in one request" }; // `IsTransient` says whether asking again might help. Only an interruption is worth a retry. PrintLine("no entropy: {} (worth retrying: {})", why, error.IsTransient()); } func Main() -> int { // One unpredictable 64-bit number. match NextUint64() { .Success(value) => PrintLine("a 64-bit value {:#018x}", value), .Failure(error) => { Describe(error); return 1; } } // Sixteen unpredictable bytes, written into a buffer through a pointer. var key: byte[16] = [0; 16]; Fill(@key[0] as *var opaque, 16) catch { error => { Describe(error); return 1; } }; Print("a 16-byte key "); for b in key { Print(" {:02x}", b); } PrintLine(); // The common use: seed a fast generator once from entropy, then draw from it as usual. // `PcgFromEntropy` returns `Pcg64Dxsm ! EntropyError`, so the seeding is checked too. var generator = PcgFromEntropy() catch { error => { Describe(error); return 1; } }; PrintLine("a die roll {}", UniformBelow(generator, 6) + 1); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Entropy` and `Random` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Entropy rux run ``` ```text a 64-bit value 0x0739b89787c9e4f2 a 16-byte key 2f ec 37 79 c3 31 0e 0f ba 9a a0 34 15 17 b9 b7 a die roll 6 ``` This is a sample: every value is different on each run. This is a sample: every value is different on each run, and so is the die roll. ## Common mistakes ::warning **Using the outcome as the number.**:br`NextUint64` returns `uint64 ! EntropyError`. Writing `let raw: uint64 = NextUint64();` fails with `error: cannot assign 'uint64 ! EntropyError' to 'uint64'`. Handle the failure first. :: ::warning **Passing the array instead of its address.**:br`Fill(key, 16)` fails with `error: argument 1 to 'Fill' has type 'uint8[16]', but parameter 'buffer' requires '*var opaque'`. Pass the address of the first byte: `@key[0]`. :: ::warning **Covering a failure with a fixed value.**:br A `catch` that answers a constant compiles, and turns a missing entropy source into a key everyone knows. When unpredictability is the point, stop, retry if `IsTransient()` says it may help, or report the failure — never invent the bytes. :: ::warning **A seeded generator where secrets are needed.**:br`Pcg64Dxsm` is fast and good for simulations, but it is not designed to resist someone who is trying to predict it, even with an unguessable seed. Draw passwords, tokens and keys straight from `Fill` or `NextUint64`, as the [Password](https://rux-lang.dev/docs/learn/password) project does. :: ## Try it yourself 1. Run the program three times. Which lines change? 2. Make the key 32 bytes long. What else has to change besides the array's size? 3. Replace `PcgFromEntropy()` with `Pcg64Dxsm(2026)` (no `catch` needed — it cannot fail) and run twice. What happens to the die roll? 4. Leave out one arm of the `match` in `Describe` and read the compiler's message. ## Learn more - [Random](https://rux-lang.dev/docs/learn/random) and [Distribution](https://rux-lang.dev/docs/learn/distribution) — the generator that `PcgFromEntropy` seeds - [Pointer](https://rux-lang.dev/docs/learn/pointer) — the address `Fill` writes through - [Catch](https://rux-lang.dev/docs/learn/catch) — recovering from a failure, and naming it with `error =>` - [Password](https://rux-lang.dev/docs/learn/password) — a checkpoint project that builds a password from entropy - [UUID](https://rux-lang.dev/docs/learn/uuid) — random identifiers made from the same source # Hash ::note **You'll need**: [Pointer](https://rux-lang.dev/docs/learn/pointer), [String literal](https://rux-lang.dev/docs/learn/string-literal), [Format number](https://rux-lang.dev/docs/learn/format-number) :: A **hash function** boils any amount of data down to one fixed-size number. The same bytes always give the same number, on every machine and every run, while bytes that differ even slightly almost always give a different one. That makes a hash a cheap fingerprint. A [hash map](https://rux-lang.dev/docs/learn/hash-map) files its keys by their hashes; a download site prints a checksum next to each file so you can tell whether your copy arrived intact; a program stores a hash beside its cache to notice when the data under it has changed. This lesson tours the `Hash` package's fast, everyday hashes: ```rux import Hash::{ Crc32Of, Fnv1a64, Fnv1a64Of, XxHash64Of }; ``` ## Three algorithms Each `…Of` function takes a pointer to the bytes and their count, and returns the hash in one call: | Function | Algorithm | Size | Good for | | ------------ | --------- | ------- | ---------------------------------------- | | `Fnv1a64Of` | FNV-1a | 64 bits | tiny and fast; short keys in hash tables | | `XxHash64Of` | xxHash | 64 bits | very fast on long inputs; fingerprints | | `Crc32Of` | CRC-32 | 32 bits | the checksum in ZIP, PNG and Ethernet | They work on **bytes**, not on text, so `Show` takes the address of the string's first character as a byte pointer: ```rux func Show(text: char8[..]) { let bytes = text.data as *byte; PrintLine("{:<12} fnv1a64 {:016x} xxhash64 {:016x} crc32 {:08x}", text, Fnv1a64Of(bytes, text.length), XxHash64Of(bytes, text.length), Crc32Of(bytes, text.length)); } ``` A [string literal](https://rux-lang.dev/docs/learn/string-literal) is a `char8[..]`: `text.data` points at its first character and `text.length` counts its bytes. `as *byte` reads the same memory as plain bytes, which is what the hash functions take. `{:016x}` prints the hash in hexadecimal padded to 16 digits, and `{:08x}` to 8, as in [Format number](https://rux-lang.dev/docs/learn/format-number) — the 64-bit and 32-bit sizes written out in full. ## Same bytes, same hash; one byte changed, a different hash ```rux Show("hello world"); Show("hello world"); Show("hello worle"); Show(""); ``` The first two lines are identical, as they must be. The third changes one letter, and every hash changes with it. xxHash and CRC-32 change throughout: `45ab6734b21e6968` becomes `06a208d921a7827a`. FNV-1a is simpler, and a change in the *last* byte leaves much of its old value standing — `779a65e7023cd2e7` against `779a64e7023cd134`. That is fine for a hash map, which only needs keys spread out, and a reason to prefer xxHash for fingerprints. Even no bytes at all have a hash. For FNV-1a it is the algorithm's starting value, `cbf29ce484222325`, and for CRC-32 it is zero. ## Hashing in pieces Data often arrives in pieces — read from a file a block at a time, or sent over a network. Each algorithm also has a **hasher** struct for that: make one, `Write` each piece, then `Finish`: ```rux let first = "hello "; let second = "world"; var hasher = Fnv1a64(); hasher.Write(first.data as *byte, first.length); hasher.Write(second.data as *byte, second.length); PrintLine("in two pieces fnv1a64 {:016x}", hasher.Finish()); ``` The answer, `779a65e7023cd2e7`, is the same as hashing `hello world` in one call: a hasher does not care where the pieces were split. ```mermaid flowchart LR new(["Fnv1a64()"]) --> w1["Write 'hello '"] --> w2["Write 'world'"] --> f["Finish()"] --> h(["779a65e7023cd2e7"]) one(["Fnv1a64Of('hello world')"]) --> h ``` ## Not for secrets None of these hashes is **cryptographic**. Someone who wants to can find two different inputs with the same hash on purpose. So they detect accidents — a flipped bit, a truncated download — but never tampering, and they must never be used to store or check passwords. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Hash){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A hash function boils any amount of data down to one fixed-size number. The same bytes always // give the same number, on every machine and every run, while bytes that differ even slightly // almost always give a different one. That makes a hash a cheap fingerprint: a hash map files // keys by it, and a checksum stored next to data shows whether the data changed. // // The Hash package has several well-known algorithms. Each `...Of` function takes a pointer to // the bytes and their count, and returns the hash: // // Fnv1a64Of FNV-1a, 64 bits: tiny and fast, good for hash tables // XxHash64Of xxHash, 64 bits: very fast on long inputs // Crc32Of CRC-32, 32 bits: the checksum in ZIP, PNG and Ethernet // // None of these is cryptographic. Someone who wants to can find two inputs with the same hash on // purpose, so they detect accidents, never tampering, and must not protect passwords. import Hash::{ Crc32Of, Fnv1a64, Fnv1a64Of, XxHash64Of }; import Io::PrintLine; func Show(text: char8[..]) { let bytes = text.data as *byte; PrintLine("{:<12} fnv1a64 {:016x} xxhash64 {:016x} crc32 {:08x}", text, Fnv1a64Of(bytes, text.length), XxHash64Of(bytes, text.length), Crc32Of(bytes, text.length)); } func Main() -> int { // The same bytes, the same hashes. Show("hello world"); Show("hello world"); // One letter changed. xxHash and CRC-32 change throughout. FNV-1a is simpler, and a change // in the last byte leaves much of its old value standing: fine for a hash map, but a reason // to prefer xxHash for fingerprints. Show("hello worle"); // Even no bytes at all have a hash. Show(""); // Data that arrives in pieces can be hashed piece by piece: make a hasher, `Write` each // piece, then `Finish`. The answer is the same as hashing all the bytes at once. let first = "hello "; let second = "world"; var hasher = Fnv1a64(); hasher.Write(first.data as *byte, first.length); hasher.Write(second.data as *byte, second.length); PrintLine("in two pieces fnv1a64 {:016x}", hasher.Finish()); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Hash` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Hash rux run ``` ```text hello world fnv1a64 779a65e7023cd2e7 xxhash64 45ab6734b21e6968 crc32 0d4a1185 hello world fnv1a64 779a65e7023cd2e7 xxhash64 45ab6734b21e6968 crc32 0d4a1185 hello worle fnv1a64 779a64e7023cd134 xxhash64 06a208d921a7827a crc32 7a4d2113 fnv1a64 cbf29ce484222325 xxhash64 ef46db3751d8e999 crc32 00000000 in two pieces fnv1a64 779a65e7023cd2e7 ``` ## Common mistakes ::warning **Passing the text instead of a pointer.**:br The hash functions take a pointer and a count. `Fnv1a64Of(text, text.length)` fails with `error: argument 1 to 'Fnv1a64Of' has type 'char8[..]', but parameter 'bytes' requires '*uint8'`. :: ::warning **A character pointer where a byte pointer is wanted.**:br`text.data` on its own is a `*char8`, and passing it fails with `error: argument 1 to 'Fnv1a64Of' has type '*char8', but parameter 'bytes' requires '*uint8'`. Convert it: `text.data as *byte`. :: ::warning **A hasher declared with `let`.**:br`Write` and `Finish` change the hasher, so with `let hasher = Fnv1a64();` the first call fails with `error: cannot call 'Write' on immutable 'hasher'`. Declare it with `var`. :: ::warning **Protecting passwords with a fast hash.**:br FNV-1a, xxHash and CRC-32 are built to be fast, and that is exactly what helps someone trying billions of guesses. Passwords need a deliberately slow, salted password hash, which is a different tool altogether. :: ## Try it yourself 1. Hash your name with all three functions. Then change one letter in the middle rather than at the end, and compare how much of the FNV-1a value survives this time. 2. Import `Fnv1a32Of` and `Crc32cOf` and add them to `Show`. Both are 32 bits, so print them with `{:08x}`. 3. Hash `hello world` in two pieces with an `XxHash64` hasher, and check the answer against `XxHash64Of`. 4. `XxHash64SeededOf(bytes, length, seed)` mixes a seed into the hash. Hash the same text with two different seeds. ## Learn more - [Pointer](https://rux-lang.dev/docs/learn/pointer) — the `*byte` every hash function takes - [String literal](https://rux-lang.dev/docs/learn/string-literal) — `data` and `length`, the two halves of a string - [Hash map](https://rux-lang.dev/docs/learn/hash-map) — where hashes do their everyday work - [UUID](https://rux-lang.dev/docs/learn/uuid) — the next lesson: identifiers that are unique rather than derived from data # UUID ::note **You'll need**: [Entropy](https://rux-lang.dev/docs/learn/entropy), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback), [Variant match](https://rux-lang.dev/docs/learn/variant-match) :: A **UUID** — universally unique identifier — is 16 bytes used as a name that nobody else will pick. Databases key rows by them, upload services name files with them, and logs tag every request with one, so the entries of a single request can be found among millions. What makes them useful is that nobody hands them out. Two programs that never talk to each other can each make UUIDs, and in practice they will never make the same one. This lesson makes one, reads and writes the text form, and compares two: ```rux import Uuid::{ Parse, Random, Uuid, UuidParseError }; ``` ## The text form A UUID is written as 32 hexadecimal digits in groups of 8, 4, 4, 4 and 12, separated by hyphens: ```text 123e4567-e89b-42d3-a456-426614174000 ^ ^ | variant: a = the standard layout version 4: random ``` The usual kind, **version 4**, is 122 random bits plus 6 fixed bits that record the version and the layout. 122 random bits is about 5 × 10³⁶ possibilities — enough that a collision between honestly made UUIDs is, in practice, never going to happen. ## Reading one `Parse` reads the text form and returns `Uuid ! UuidParseError`. Like `ParseDate` in [Date](https://rux-lang.dev/docs/learn/date), the error is a variant whose every case carries the byte where the text went wrong: ```rux func Reason(error: UuidParseError) -> char8[..] { return match error { .WrongLength(_) => "wrong length", .Misplaced(_) => "hyphen in the wrong place", .NotHexadecimal(_) => "not a hexadecimal digit", .BadPrefix(_) => "not a urn:uuid: prefix" }; } func Show(text: char8[..]) { match Parse(text) { .Success(id) => PrintLine("{:<45} -> {}", text, id), .Failure(error) => PrintLine("{:<45} -> {} at byte {}", text, Reason(error), error.Offset()) } } ``` `Parse` is strict. It accepts the hyphenated form, in either case, with or without the `urn:uuid:` prefix — and refuses everything else: | Text | Result | | ----------------------------------------------- | ---------------------------------------------- | | `123e4567-e89b-42d3-a456-426614174000` | accepted — the canonical form | | `123E4567-E89B-42D3-A456-426614174000` | accepted — upper case means the same | | `urn:uuid:123e4567-e89b-42d3-a456-426614174000` | accepted — the standard URN prefix | | `123e4567e89b42d3a456426614174000` | wrong length — the hyphens are not optional | | `123e4567-e89b-42d3-a456-42661417400g` | not a hexadecimal digit, at byte 35 | | `{123e4567-e89b-42d3-a456-426614174000}` | wrong length — braces are not part of the form | A strict reader is the safe default: text that is almost a UUID is more likely a mistake than a UUID, and refusing it with a position makes the mistake easy to find. ## Writing and comparing Printing with `{}` always writes the one canonical form, in lower case. So all three accepted spellings above print identically, and comparison is on the 16 bytes, not on the text: ```rux let lower = Parse("123e4567-e89b-42d3-a456-426614174000") catch { else => Uuid::Nil() }; let upper = Parse("123E4567-E89B-42D3-A456-426614174000") catch { else => Uuid::Nil() }; PrintLine("same identifier: {}", lower.Equals(upper)); PrintLine("the nil UUID: {}", Uuid::Nil()); ``` The [`catch` with a fallback](https://rux-lang.dev/docs/learn/catch-fallback) is safe here because both texts are known to be valid. The fallback, `Uuid::Nil()`, is the special UUID with every bit zero — useful as a "no identifier" placeholder, and never produced by `Random`. ## Making one `Random()` makes a version 4 UUID from the operating system's entropy, so like every request for entropy in [Entropy](https://rux-lang.dev/docs/learn/entropy) it can fail, and returns `Uuid ! EntropyError`: ```rux match Random() { .Success(id) => PrintLine("a new one: {} version {}", id, id.Version()), .Failure(_) => PrintLine("no entropy for a new UUID") } ``` `Version()` reads the version digit back: 4. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Utilities/Uuid){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A UUID (universally unique identifier) is 16 bytes used as a name that nobody else will pick: // a database row, an uploaded file, a request in a log. Its text form is 32 hexadecimal digits // in groups of 8-4-4-4-12, such as 123e4567-e89b-42d3-a456-426614174000. // // The usual kind, version 4, is 122 random bits plus 6 bits marking the version and layout. With // that many random bits, two programs that never talk to each other will still, in practice, // never produce the same one. `Random()` makes one from the operating system's entropy, so like // any request for entropy it returns `Uuid ! EntropyError`. // // `Parse` reads the text form and returns `Uuid ! UuidParseError`. It is strict: upper case is // accepted, since it means the same, but braces, missing hyphens or stray characters are // refused, with the byte where the text went wrong. Printing with `{}` always writes the one // canonical form, in lower case. import Io::PrintLine; import Uuid::{ Parse, Random, Uuid, UuidParseError }; func Reason(error: UuidParseError) -> char8[..] { return match error { .WrongLength(_) => "wrong length", .Misplaced(_) => "hyphen in the wrong place", .NotHexadecimal(_) => "not a hexadecimal digit", .BadPrefix(_) => "not a urn:uuid: prefix" }; } func Show(text: char8[..]) { match Parse(text) { .Success(id) => PrintLine("{:<45} -> {}", text, id), .Failure(error) => PrintLine("{:<45} -> {} at byte {}", text, Reason(error), error.Offset()) } } func Main() -> int { Show("123e4567-e89b-42d3-a456-426614174000"); Show("123E4567-E89B-42D3-A456-426614174000"); Show("urn:uuid:123e4567-e89b-42d3-a456-426614174000"); Show("123e4567e89b42d3a456426614174000"); Show("123e4567-e89b-42d3-a456-42661417400g"); Show("{123e4567-e89b-42d3-a456-426614174000}"); // Two spellings, one identifier: comparison is on the 16 bytes, not on the text. let lower = Parse("123e4567-e89b-42d3-a456-426614174000") catch { else => Uuid::Nil() }; let upper = Parse("123E4567-E89B-42D3-A456-426614174000") catch { else => Uuid::Nil() }; PrintLine("same identifier: {}", lower.Equals(upper)); PrintLine("the nil UUID: {}", Uuid::Nil()); // A fresh random one, different on every run. match Random() { .Success(id) => PrintLine("a new one: {} version {}", id, id.Version()), .Failure(_) => PrintLine("no entropy for a new UUID") } return 0; } ``` Besides `Io`, its `Rux.toml` lists `Uuid` under `[Dependencies]`. ## Run it ```sh cd Examples/Utilities/Uuid rux run ``` ```text 123e4567-e89b-42d3-a456-426614174000 -> 123e4567-e89b-42d3-a456-426614174000 123E4567-E89B-42D3-A456-426614174000 -> 123e4567-e89b-42d3-a456-426614174000 urn:uuid:123e4567-e89b-42d3-a456-426614174000 -> 123e4567-e89b-42d3-a456-426614174000 123e4567e89b42d3a456426614174000 -> wrong length at byte 32 123e4567-e89b-42d3-a456-42661417400g -> not a hexadecimal digit at byte 35 {123e4567-e89b-42d3-a456-426614174000} -> wrong length at byte 36 same identifier: true the nil UUID: 00000000-0000-0000-0000-000000000000 a new one: 12c16353-3376-4e49-a1ec-403586becf8d version 4 ``` Every line is the same on each run except the last, which is a sample: a new random UUID each time. Every line is the same on each run except the last, which is a sample: a new random UUID each time. ## Common mistakes ::warning **Using the outcome as an identifier.**:br`let id: Uuid = Random();` fails with `error: cannot assign 'Uuid ! EntropyError' to 'Uuid'`. Making a UUID can fail when the system has no entropy, so handle that first. :: ::warning **Comparing UUIDs as text.**:br`123e4567-…` and `123E4567-…` are different strings and the same identifier. Parse both and compare the values — with `Equals` or `==` — rather than the text. :: ::warning **A fallback on input you do not control.**:br`catch { else => Uuid::Nil() }` is fine for a literal the program knows is valid. On user input it turns every typo into the nil UUID, which then looks like a real value. Report the `UuidParseError` instead. :: ::warning **Leaving out an error case.**:br The `match` in `Reason` must name every case. Drop `.BadPrefix` and the compiler says `error: match on 'UuidParseError' is not exhaustive; missing UuidParseError::BadPrefix`. :: ## Try it yourself 1. Show a UUID with its last two digits missing, and one with a hyphen one place too far right. Which reasons and bytes do you get? 2. Print `Uuid::Max()`, the UUID with every bit set. 3. Import `TimeOrdered` and make a version 7 UUID, which starts with the current time. Make two and compare their first groups. 4. Make five random UUIDs in a loop and print them. Do any two share even their first group? ## Learn more - [Entropy](https://rux-lang.dev/docs/learn/entropy) — where the random bits come from, and why it can fail - [Variant match](https://rux-lang.dev/docs/learn/variant-match) — matching the cases of `UuidParseError` - [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) — `catch { else => … }` and when it is safe - [Hash](https://rux-lang.dev/docs/learn/hash) — fingerprints derived from data, the opposite of an identifier made from nothing # Part 21: Data formats Sooner or later a program has to talk to something that is not itself — a web service, a configuration file, another program written in another language. This part reads and writes the two text formats you will meet most: **JSON**, for exchanging data, and **TOML**, for configuration. Both packages work the same way: text becomes a tree of values, a program asks each value what it is before taking it apart, and a document that is wrong is refused with the place it went wrong. ## What you will learn - Parsing JSON into a tree of six kinds of value, and reading typed values out of it through pointers and out-parameters. - Building a JSON tree by hand, writing it compact or pretty, and checking a round trip. - Reading JSON as a stream of events, for documents too large to hold at once. - Reading TOML, whose values have real types: integers, floats, dates and tables. - Changing a TOML document and writing it back — and what a round trip keeps and loses. - Reporting a bad document by byte, or by line and column, so a person can find the mistake. ## The part at a glance ```mermaid flowchart LR jt(["JSON text"]) -- "Parse" --> jv["JsonValue tree"] jv -- "WriteValue" --> jt jt -- "JsonEventReader" --> ev["Events, one at a time"] tt(["TOML text"]) -- "TomlParse" --> tv["TomlValue tree"] tv -- "TomlWriteDocument" --> tt jt -- "malformed" --> err["Refused, with
where and why"] tt -- "malformed" --> err ``` ## Lessons ## Lessons | | Lesson | What you will learn | | ---- | ------------------------------------------------------------- | ----------------------------------------- | | 21.1 | [JSON](https://rux-lang.dev/docs/learn/json) | parse JSON into a value and walk it | | 21.2 | [Writing JSON](https://rux-lang.dev/docs/learn/json-write) | write a value out as JSON | | 21.3 | [Streaming JSON](https://rux-lang.dev/docs/learn/json-stream) | read JSON as a stream of events | | 21.4 | [TOML](https://rux-lang.dev/docs/learn/toml) | parse a TOML document and read its values | | 21.5 | [Writing TOML](https://rux-lang.dev/docs/learn/toml-write) | write a TOML document | ## Before you start Documents are held in memory from an [allocator](https://rux-lang.dev/docs/learn/allocator), and values are read through [pointers](https://rux-lang.dev/docs/learn/pointer) and [out-parameters](https://rux-lang.dev/docs/learn/out-parameter), all from [Part 15: Memory](https://rux-lang.dev/docs/learn/memory). Text is handled with the `String`, `StringView` and `StringBuilder` of [Part 14: Text](https://rux-lang.dev/docs/learn/text), trees are move-only values as in [Part 11: Ownership](https://rux-lang.dev/docs/learn/ownership), and failures come in [error sums](https://rux-lang.dev/docs/learn/error-sum) from Part 9. The TOML lesson also reads a `Date` from [Part 20: Utilities](https://rux-lang.dev/docs/learn/utilities). Each lesson's package is in the Examples repository's `DataFormats/` folder: ```sh cd Examples/DataFormats/Json rux run ``` ## After this part [Part 22: Packages](https://rux-lang.dev/docs/learn/packages) steps back from single programs to how code is organised and shared — modules, packages, libraries and the `Rux.toml` that describes them, which you can now read as the TOML it is. Before moving on, try the checkpoint project [Notes](https://rux-lang.dev/docs/learn/notes): a to-do list kept as JSON in a file, saved, loaded back, added to, and protected against a damaged or missing file. It puts this part together with [Part 19: Files](https://rux-lang.dev/docs/learn/files). For the language rules this part relies on, see [Pointers](https://rux-lang.dev/docs/lang/pointers/overview), [null pointers](https://rux-lang.dev/docs/lang/pointers/overview#null) and [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) in the Rux Reference, and the [manifest](https://rux-lang.dev/docs/packaging/manifest) for the TOML file every package has. # JSON ::note **You'll need**: [Allocator](https://rux-lang.dev/docs/learn/allocator), [Pointer](https://rux-lang.dev/docs/learn/pointer), [Out parameter](https://rux-lang.dev/docs/learn/out-parameter), [Outcome](https://rux-lang.dev/docs/learn/outcome), [String view](https://rux-lang.dev/docs/learn/string-view) :: JSON is how programs that share nothing else — not a language, not a machine, not an owner — agree on what a value is. A web API answers in it, a browser stores settings in it, and a log line often is one. This lesson reads a JSON document with the `Json` package, takes typed values out of it, and shows how a broken document is refused. ```rux import Allocator::{ Allocator, SystemAllocator }; import Json::{ JsonKind, JsonValue, Parse }; import Text::StringView; ``` ## Parsing a document `Parse` reads the whole text into a tree of values: ```text Parse(allocator, text) -> JsonValue ! JsonParseError ``` The tree needs memory — a document can be any size — so `Parse` takes an [allocator](https://rux-lang.dev/docs/learn/allocator) to get it from. `Main` makes the system allocator and hands out an `Allocator` view of it: ```rux var system = SystemAllocator(); let allocator: Allocator = system; ``` The result is an [outcome](https://rux-lang.dev/docs/learn/outcome): either the whole document, as its root value, or the reason there is none. There is never half a document. ```rux match Parse(allocator, document) { .Success(root) => Inspect(root), .Failure(reason) => PrintLine("refused at byte {}: {}", reason.Offset(), reason) } ``` The document is one long string literal, with every `"` inside it escaped as `\"`: ```json { "name": "Rux", "year": 2026, "tags": ["fast", "small"], "flags": { "beta": true } } ``` ## A tree of six kinds Every value in the tree is one of six kinds, and `Kind()` says which. Containers hold more values, so the document above parses into this tree: ```mermaid flowchart TD root["object"] -- "name" --> n["text: Rux"] root -- "year" --> y["number: 2026"] root -- "tags" --> t["array"] root -- "flags" --> f["object"] t -- "0" --> t0["text: fast"] t -- "1" --> t1["text: small"] f -- "beta" --> b["boolean: true"] ``` Reading the tree always means asking a value what it is before taking it apart: | Kind | Ask with | Answers | | --------- | ---------------------------- | ------------------------------------------------ | | `Object` | `Find(name)`, `Length()` | a pointer to the member, null when it is missing | | `Array` | `At(index)`, `Length()` | a pointer to the element, null past the end | | `Text` | `AsText()` | a `StringView`, empty for any other kind | | `Number` | `AsNumber()`, then `AsInt64` | `false` when the number is not that type | | `Boolean` | `AsBoolean(@flag)` | `false` when the value is not a boolean | | `Null` | `IsNull()` | `true` for `null` | `KindName` turns a kind into a word for printing, with `else` covering the last one, `Null`. ## Finding a member Member names are text, so `Find` takes a [`StringView`](https://rux-lang.dev/docs/learn/string-view). The small `Member` helper makes one from a literal: ```rux func Member(object: &JsonValue, name: char8[..]) -> *JsonValue { return object.Find(StringView::FromValidated(name)); } ``` `Find` answers a [pointer](https://rux-lang.dev/docs/learn/pointer) that is null when there is no such member. So the program checks before reading through it: ```rux let name = Member(root, "name"); if name != null { PrintLine("name {}", (*name).AsText()); } PrintLine("license present: {}", Member(root, "license") != null); ``` The document has no `license`, and the program says so instead of crashing. ## Numbers keep their text JSON has one kind of number, and it does not say how big or how precise a number may be. So a `JsonNumber` keeps the text it was written with, and converting it is a question that may be answered `false` — the text may have a fraction, or not fit the type asked for. The answer comes back through an [out-parameter](https://rux-lang.dev/docs/learn/out-parameter): ```rux var year: int64 = 0; let number = (*Member(root, "year")).AsNumber(); let fits = (*number).AsInt64(@year); PrintLine("year {} (text \"{}\", fits an int64: {})", year, (*number).Text(), fits); ``` `AsNumber` returns a pointer to the number — null if the value were some other kind — and `AsInt64(@year)` writes into `year` and reports whether it could. ## Arrays and nested objects An array is walked by index, and each element is asked its kind like any other value. A nested object is just another value with members of its own: ```rux let tags = Member(root, "tags"); for i in 0..(*tags).Length() { let tag = (*tags).At(i); PrintLine("tags[{}] {} {}", i, KindName((*tag).Kind()), (*tag).AsText()); } var beta = false; let flags = Member(root, "flags"); let isBoolean = (*Member(*flags, "beta")).AsBoolean(@beta); ``` Notice that `year`, `tags` and `flags` are read without the null check that `name` got. That is safe here only because the document is a literal in the program and they are known to be there. For a document from outside, check every pointer. ## Malformed input JSON is strict, and `Parse` refuses anything that is not exactly JSON. The failure says why, and at which byte the parser stopped: | Document | Refused at | Because | | ----------------- | ---------- | ----------------------------------------- | | `[1, 2, 3,]` | byte 10 | a trailing comma: `]` cannot follow `,` | | `{'name': 'Rux'}` | byte 1 | single quotes begin no token in JSON | | `{"open": [1, 2}` | byte 15 | `}` cannot close an array | | `{"a": 1} extra` | byte 9 | text after the end of the document | | *(empty)* | byte 0 | the document ended before any value began | A message that names the byte lets a person go straight to the mistake. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/DataFormats/Json){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // JSON is how programs that share nothing else agree on what a value is. The Json package reads // a document into a tree of `JsonValue`s, and every value in the tree is one of six kinds: text, // number, boolean, null, array or object. // // Parse(allocator, text) -> JsonValue ! JsonParseError // // Parsing hands back a whole document or the reason there is none, never half of one. Reading the // tree then means asking a value what it is before taking it apart: `Find` and `At` answer a // pointer that is null when the member or element is not there, and the conversions answer // `false`, through an out-parameter, when the value is some other kind. import Allocator::{ Allocator, SystemAllocator }; import Io::PrintLine; import Json::{ JsonKind, JsonValue, Parse }; import Text::StringView; func KindName(kind: JsonKind) -> char8[..] { return match kind { JsonKind::Text => "text", JsonKind::Number => "number", JsonKind::Boolean => "boolean", JsonKind::Array => "array", JsonKind::Object => "object", else => "null" }; } // Member names are compared as text, so `Find` takes a `StringView`. func Member(object: &JsonValue, name: char8[..]) -> *JsonValue { return object.Find(StringView::FromValidated(name)); } func Inspect(root: &JsonValue) { PrintLine("root {} of {} members", KindName(root.Kind()), root.Length()); // A member that is not there is a null pointer, so check before reading through it. let name = Member(root, "name"); if name != null { PrintLine("name {}", (*name).AsText()); } PrintLine("license present: {}", Member(root, "license") != null); // A number keeps the text it was written with, and converting it is a question that can // be answered `false`: the text may have a fraction, or not fit the type asked for. var year: int64 = 0; let number = (*Member(root, "year")).AsNumber(); let fits = (*number).AsInt64(@year); PrintLine("year {} (text \"{}\", fits an int64: {})", year, (*number).Text(), fits); // An array is walked by index, and each element is asked its kind like any other value. let tags = Member(root, "tags"); for i in 0..(*tags).Length() { let tag = (*tags).At(i); PrintLine("tags[{}] {} {}", i, KindName((*tag).Kind()), (*tag).AsText()); } // A nested object is just another value with members of its own. var beta = false; let flags = Member(root, "flags"); let isBoolean = (*Member(*flags, "beta")).AsBoolean(@beta); PrintLine("beta boolean: {}, value: {}", isBoolean, beta); } func Check(allocator: Allocator, document: char8[..]) { match Parse(allocator, document) { .Success(root) => PrintLine("{:18} parsed as {}", document, KindName(root.Kind())), .Failure(reason) => PrintLine("{:18} refused at byte {}: {}", document, reason.Offset(), reason) } } func Main() -> int { var system = SystemAllocator(); let allocator: Allocator = system; let document = "{\"name\":\"Rux\",\"year\":2026,\"tags\":[\"fast\",\"small\"],\"flags\":{\"beta\":true}}"; match Parse(allocator, document) { .Success(root) => Inspect(root), .Failure(reason) => PrintLine("refused at byte {}: {}", reason.Offset(), reason) } // Malformed input. The failure says why, and the byte where the parser stopped, so a message // to a person can point at the problem. JSON is strict: no trailing commas, no single quotes. PrintLine(""); Check(allocator, "[1, 2, 3]"); Check(allocator, "[1, 2, 3,]"); Check(allocator, "{'name': 'Rux'}"); Check(allocator, "{\"open\": [1, 2}"); Check(allocator, "{\"a\": 1} extra"); Check(allocator, ""); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Json` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/DataFormats/Json rux run ``` ```text root object of 4 members name Rux license present: false year 2026 (text "2026", fits an int64: true) tags[0] text fast tags[1] text small beta boolean: true, value: true [1, 2, 3] parsed as array [1, 2, 3,] refused at byte 10: token that cannot appear here {'name': 'Rux'} refused at byte 1: byte that begins no token {"open": [1, 2} refused at byte 15: token that cannot appear here {"a": 1} extra refused at byte 9: text after the end of the document refused at byte 0: document ended in the middle of a value ``` ## Common mistakes ::warning **Looking up a member with a plain literal.**:br`Find` compares names as text, and takes a `StringView`. `object.Find(name)` with a `char8[..]` fails with `error: argument 1 to 'Find' has type 'char8[..]', but parameter 'name' requires 'StringView'`. Wrap it: `StringView::FromValidated(name)`. :: ::warning **Passing the variable instead of its address.**:br The conversions write their answer through a pointer. `(*number).AsInt64(year)` fails with `error: argument 1 to 'AsInt64' has type 'int64', but parameter 'result' requires '*var int64'`. Write `@year`. :: ::warning **Reading through a missing member.**:br`Find` returns null for a name that is not there, and reading through null crashes the program. `(*Member(root, "license")).AsText()` compiles, and then stops the program dead. Compare the pointer with `null` first, as the program does for `name`. :: ::warning **Forgetting the allocator.**:br`Parse(document)` fails with `error: call to 'Parse' expects 2 arguments, but 1 was provided`. The tree's memory has to come from somewhere, and the allocator says where. :: ## Try it yourself 1. Add `"license": null` to the document. Check it with `IsNull()`. 2. Add `"version": 1.5`. What do `AsInt64` and `AsFloat64` each answer for it? 3. Pass `{"a": 1, "a": 2}` to `Check`. JSON does not forbid a repeated name — but what does this parser do with one? 4. List every member of the root object with `MemberAt(i)`, printing each one's `name` and the kind of its `value`. ## Learn more - [JSON write](https://rux-lang.dev/docs/learn/json-write) — building a tree and writing it back out as text - [JSON stream](https://rux-lang.dev/docs/learn/json-stream) — reading a document without building the tree - [Pointer](https://rux-lang.dev/docs/learn/pointer) and [Out-parameter](https://rux-lang.dev/docs/learn/out-parameter) — how `Find`, `At` and the conversions answer - [Allocator](https://rux-lang.dev/docs/learn/allocator) — where the tree's memory comes from - [Null pointers](https://rux-lang.dev/docs/lang/pointers/overview#null) in the Rux Reference # Writing JSON ::note **You'll need**: [JSON](https://rux-lang.dev/docs/learn/json), [String builder](https://rux-lang.dev/docs/learn/string-builder), [Display](https://rux-lang.dev/docs/learn/display), [Move](https://rux-lang.dev/docs/learn/move), [Error sum](https://rux-lang.dev/docs/learn/error-sum) :: Writing JSON is the [parse lesson](https://rux-lang.dev/docs/learn/json) run backwards: a tree of `JsonValue`s goes in, text comes out. The tree can come from `Parse`, or — as here — be built by hand, one value at a time. This lesson builds a small document, writes it two ways, and proves the writing is faithful by reading the output back. ```rux import Json::{ JsonStyle, JsonValue, Parse, WriteValue }; import Text::{ FormatError, String, StringBuilder, TextError, TextWriter }; ``` ## Building a tree by hand Each kind has a constructor: `JsonValue::Text`, `Number`, `Boolean`, `Null`, `Array` and `Object`. Containers start empty; `Push` appends to an array and `Insert` adds a member to an object: ```rux var tags = JsonValue::Array(allocator); tags.Push(JsonValue::Text(allocator, "fast")?); tags.Push(JsonValue::Number(allocator, "2.50")?); var root = JsonValue::Object(allocator); root.Insert(String::FromBytes(allocator, "name")?, JsonValue::Text(allocator, "Rux")?); root.Insert(String::FromBytes(allocator, "quote")?, JsonValue::Text(allocator, "say \"hi\"\n")?); root.Insert(String::FromBytes(allocator, "tags")?, <-tags); root.Insert(String::FromBytes(allocator, "license")?, JsonValue::Null(allocator)); ``` Three things to notice: - **Text is copied into the tree.** `Text`, `Number` and `String::FromBytes` copy their bytes into memory from the allocator, and that can fail with a `TextError` — hence the `?` after each. - **A number is given as text.** `"2.50"` stays `2.50`, exactly as written, the same way a parsed number keeps its text. - **Values are move-only.** A `JsonValue` owns everything under it, so it cannot be copied. `Push` and `Insert` take ownership of what they are given, and handing over the finished `tags` array is an explicit [move](https://rux-lang.dev/docs/learn/move): `<-tags`. After that line `tags` is gone, and the array lives on inside `root`. `Main` returns `! (TextError | FormatError)`, an [error sum](https://rux-lang.dev/docs/learn/error-sum), so `?` can pass on a failure of either kind from building or from writing. ## Writing it out `WriteValue` writes a tree into any `TextWriter`: ```text WriteValue(writer, value, style) -> ! FormatError ``` `Written` points it at a [`StringBuilder`](https://rux-lang.dev/docs/learn/string-builder), so the text can be kept, measured and parsed again, and hands the result back as a `String` the caller owns: ```rux func Written(allocator: Allocator, value: &JsonValue, style: JsonStyle) -> String ! FormatError { var builder = StringBuilder(allocator); let sink: &var TextWriter = builder; WriteValue(sink, value, style)?; return builder.IntoString(); } ``` `JsonStyle` chooses the layout: | Style | Layout | Size of this document | For | | ---------------------- | ------------------------------------ | --------------------- | -------------- | | `JsonStyle::Compact()` | no whitespace at all | 73 bytes | a wire, a file | | `JsonStyle::Pretty()` | one value per line, two-space indent | 103 bytes | a person | Either way the writer escapes exactly what JSON requires inside strings and nothing more: the quote's `"` and its newline come out as `\"` and `\n`. ## The round trip A writer is only trustworthy if what it writes reads back as the same thing. The program parses the pretty text and writes it compact again: ```rux match Parse(allocator, pretty.View().Bytes()) { .Success(again) => { let rewritten = Written(allocator, again, JsonStyle::Compact())?; let original = compact.View(); PrintLine("round trip gives the same text: {}", rewritten.View().Equals(original)); }, .Failure(reason) => PrintLine("the written text did not parse: {}", reason) } ``` ```mermaid flowchart LR tree["Tree built
by hand"] -- "Compact" --> c1["compact text"] tree -- "Pretty" --> p["pretty text"] p -- "Parse" --> tree2["Tree read back"] tree2 -- "Compact" --> c2["compact text"] c2 -. "the same bytes as" .-> c1 ``` The layout was only presentation, so the same 73 bytes come back — including `2.50`, which did not become `2.5`, because a number keeps the text it was written with. ## What JSON cannot say JSON has no spelling for NaN or infinity. A number built from the text `"NaN"` is accepted into the tree, but writing it is refused rather than invented: ```rux let notANumber = JsonValue::Number(allocator, "NaN")?; match Written(allocator, notANumber, JsonStyle::Compact()) { .Success(text) => PrintLine("wrote {}", text), .Failure(error) => PrintLine("NaN refused: {}", error) } ``` Printing the `FormatError` with `{}` gives its message, `value out of range for the form requested`. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/DataFormats/JsonWrite){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Writing JSON is the parse lesson run backwards: a tree of `JsonValue`s goes in, text comes // out. The tree can come from `Parse` or be built by hand, and `WriteValue` writes it into any // `TextWriter` — a StringBuilder here, so the text can be kept and parsed again. // // WriteValue(writer, value, style) -> ! FormatError // // `JsonStyle` chooses the layout. `Compact()` writes no whitespace at all, for a document going // over a wire; `Pretty()` indents by two spaces, for a person. Either way the writer escapes // exactly what JSON requires inside strings, and nothing more. import Allocator::{ Allocator, SystemAllocator }; import Io::PrintLine; import Json::{ JsonStyle, JsonValue, Parse, WriteValue }; import Text::{ FormatError, String, StringBuilder, TextError, TextWriter }; // Writes `value` in `style` and hands the text back as a String the caller owns. func Written(allocator: Allocator, value: &JsonValue, style: JsonStyle) -> String ! FormatError { var builder = StringBuilder(allocator); let sink: &var TextWriter = builder; WriteValue(sink, value, style)?; return builder.IntoString(); } func Main() -> ! (TextError | FormatError) { var system = SystemAllocator(); let allocator: Allocator = system; // Building by hand. Values are move-only: `Push` and `Insert` take ownership of what they // are given, and a member's name is an owned String. var tags = JsonValue::Array(allocator); tags.Push(JsonValue::Text(allocator, "fast")?); tags.Push(JsonValue::Number(allocator, "2.50")?); var root = JsonValue::Object(allocator); root.Insert(String::FromBytes(allocator, "name")?, JsonValue::Text(allocator, "Rux")?); root.Insert(String::FromBytes(allocator, "quote")?, JsonValue::Text(allocator, "say \"hi\"\n")?); root.Insert(String::FromBytes(allocator, "tags")?, <-tags); root.Insert(String::FromBytes(allocator, "license")?, JsonValue::Null(allocator)); let compact = Written(allocator, root, JsonStyle::Compact())?; let pretty = Written(allocator, root, JsonStyle::Pretty())?; PrintLine("compact, {} bytes:\n{}", compact.Length(), compact); PrintLine("pretty, {} bytes:\n{}", pretty.Length(), pretty); // The round trip: parse the pretty text and write it compact again. The layout was only // presentation, so the same bytes come back. A number keeps the text it was written with, // which is why `2.50` did not become `2.5`. match Parse(allocator, pretty.View().Bytes()) { .Success(again) => { let rewritten = Written(allocator, again, JsonStyle::Compact())?; let original = compact.View(); PrintLine("round trip gives the same text: {}", rewritten.View().Equals(original)); }, .Failure(reason) => PrintLine("the written text did not parse: {}", reason) } // JSON cannot spell NaN or infinity, so writing one is refused, not invented. let notANumber = JsonValue::Number(allocator, "NaN")?; match Written(allocator, notANumber, JsonStyle::Compact()) { .Success(text) => PrintLine("wrote {}", text), .Failure(error) => PrintLine("NaN refused: {}", error) } } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Json` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/DataFormats/JsonWrite rux run ``` ```text compact, 73 bytes: {"name":"Rux","quote":"say \"hi\"\n","tags":["fast",2.50],"license":null} pretty, 103 bytes: { "name": "Rux", "quote": "say \"hi\"\n", "tags": [ "fast", 2.50 ], "license": null } round trip gives the same text: true NaN refused: value out of range for the form requested ``` ## Common mistakes ::warning **Using a value after handing it over.**:br After `root.Insert(…, <-tags)`, the array belongs to `root`. A later `tags.Push(…)` fails with `error: value 'tags' is used after it was moved`. Finish filling a container before you insert it. :: ::warning **Inserting without `<-`.**:br`root.Insert(String::FromBytes(allocator, "tags")?, tags)` fails with `error: move-only value 'tags' requires an explicit '<-' in argument`, and a note explains that `'JsonValue' prohibits copying`. The `<-` makes the hand-over visible where it happens. :: ::warning **A literal as a member name.**:br`Insert` takes an owned `String`. `root.Insert("name", …)` fails with `error: argument 1 to 'Insert' has type 'char8[..]', but parameter 'name' requires 'String'`. Make one with `String::FromBytes(allocator, "name")?`. :: ::warning **Expecting `Insert` to replace.**:br`Insert` always appends, even when the object already has a member of that name — it returns `true` and the writer writes the name twice. Parsing that text back fails with `object with the same name twice`. Check with `Find` before inserting. :: ## Try it yourself 1. Add a boolean member, `"beta": true`, with `JsonValue::Boolean(allocator, true)`. How many bytes is the compact text now? 2. Build a nested object — say `"author": {"name": "Ada"}` — and look at how `Pretty()` indents it. 3. Try `"Infinity"` instead of `"NaN"`. Is it refused too? 4. Parse the document from the [JSON lesson](https://rux-lang.dev/docs/learn/json) and write it out with `Pretty()`. ## Learn more - [JSON](https://rux-lang.dev/docs/learn/json) — reading a document into the tree this lesson writes - [Move](https://rux-lang.dev/docs/learn/move) — what `<-` does, and why a `JsonValue` cannot be copied - [String builder](https://rux-lang.dev/docs/learn/string-builder) — the `TextWriter` that collects the text - [Error sum](https://rux-lang.dev/docs/learn/error-sum) — one fallible `Main` for two kinds of error - [Notes](https://rux-lang.dev/docs/learn/notes) — a checkpoint project that saves a to-do list as JSON and loads it back # Streaming JSON ::note **You'll need**: [JSON](https://rux-lang.dev/docs/learn/json), [Interface value](https://rux-lang.dev/docs/learn/interface-value), [Destructor](https://rux-lang.dev/docs/learn/destructor), [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern), [Coalesce](https://rux-lang.dev/docs/learn/coalesce) :: `Parse` builds the whole tree before your program sees any of it. For a settings file that is ideal. For a log of a million records it is not: the tree costs several times the document's size in memory, and a program that only wants one field from each record pays for all of them. `JsonEventReader` reads the same text as a **stream of events** instead — an object opened, a name, a text value, an array closed — pulling bytes from a source as it needs them and keeping only the token it is on. The program keeps whatever it wants and lets the rest go by. This lesson prints the title of every book in a catalogue without ever building a tree. ```rux import Io::{ IoError, IoErrorKind, PrintLine, Reader }; import Json::{ JsonEvent, JsonEventReader, JsonLimits, JsonParseError }; ``` ## A source of bytes The event reader reads from any `Reader` — the interface that files and network connections implement. The lesson stands one in with `TextSource`, which serves a string at most 8 bytes per read, so tokens arrive split across reads just as they would from a real stream: ```rux struct TextSource { text: char8[..]; offset: uint; } ``` Its `Read` method copies up to 8 bytes into the buffer it is given and reports how many, or fails with `EndOfStream` when nothing is left. Inside `Titles`, the source is handed over as an [interface value](https://rux-lang.dev/docs/learn/interface-value) and the reader is built on it: ```rux let source: Reader = TextSource { text: document, offset: 0 }; var reader = JsonEventReader::New(allocator, source, JsonLimits::Default())?; ``` `New` allocates the reader's buffer, and fails with an `IoError` if it cannot. `JsonLimits::Default()` sets the limits a document must stay within — its size, the length of one string, how deeply it nests — generous for ordinary documents and well short of what would exhaust the machine. ## Events Each call to `Next()` returns the next `JsonEvent`, and `Depth()` says how many arrays and objects are open. The first book of the catalogue produces: | Token | Event | `Depth()` | `Text()` | | --------- | ------------- | --------- | -------- | | `[` | `ArrayStart` | 1 | | | `{` | `ObjectStart` | 2 | | | `"title"` | `Name` | 2 | `title` | | `"Dune"` | `Text` | 2 | `Dune` | | `"year"` | `Name` | 2 | `year` | | `1965` | `Number` | 2 | `1965` | | `}` | `ObjectEnd` | 1 | | …and at the very end, `ArrayEnd` and then `End`. Commas and colons produce no events; `True`, `False` and `Null` complete the set, and `Error` means the document went wrong. ## The loop `Titles` asks for events until `End`, counts each object one level inside the top-level array as a book, and prints the text that follows a `title` name: ```rux var books: uint = 0; var wantTitle = false; var event = reader.Next(); while event != JsonEvent::End { if event == JsonEvent::Error { // After an `Error` event `Failure()` always holds the reason; the fallback is // only there because the type allows `none`. fail reader.Failure() ?? JsonParseError::Unexpected(0); } // Each book is an object one level inside the top-level array. if event == JsonEvent::ObjectStart && reader.Depth() == 2 { books += 1; } // Print the title now: its text is gone once `Next()` is called again. if event == JsonEvent::Text && wantTitle { PrintLine(" title: {}", reader.Text()); } wantTitle = event == JsonEvent::Name && IsTitle(reader.Text()); event = reader.Next(); } return books; ``` Order inside a book does not matter: Emma's `year` comes before its `title`, and the title is still found, because the program reacts to whatever name it meets. ## Two jobs that move onto the program Streaming saves memory by keeping almost nothing, and that hands two jobs to the program. **Lifetimes.** `Text()` borrows the reader's buffer, and the next `Next()` reuses it. So the text of an event must be used — or copied — before asking for another. That is why the title is printed the moment it arrives, and why `wantTitle` remembers only a `bool`, not the name's text. The buffer itself belongs to the reader, and the reader's [destructor](https://rux-lang.dev/docs/learn/destructor) gives it back however `Titles` ends: at the `return`, or at an early `fail`. **Errors arrive late.** A stream cannot know that the end of a document is wrong until it gets there. When it does, `Next()` returns `Error`, and `Failure()` says why and at which byte of the whole document — however it was split into reads. By then the earlier events have already been delivered: ```mermaid flowchart LR n["Next()"] --> e{"Event?"} e -- "ObjectStart, Name,
Text, …" --> use["use it now"] --> n e -- "End" --> ok(["books"]) e -- "Error" --> f(["fail reader.Failure()"]) ``` In the output, both broken catalogues print their titles *before* being refused. A program that streams must be ready to discover, late, that its input was bad — and must not have committed anything it cannot take back. ## Two kinds of failure `Titles` returns `uint ! (IoError | JsonParseError)`: the reader could not start, or the document was wrong. `Report` takes them apart with [typed patterns](https://rux-lang.dev/docs/learn/typed-pattern): ```rux match Titles(allocator, document) { .Success(books) => PrintLine(" {} books", books), .Failure(error) => match error { reason: JsonParseError => PrintLine(" refused at byte {}: {}", reason.Offset(), reason), _: IoError => PrintLine(" could not start reading") } } ``` A source that fails partway through is reported as `JsonParseError::Source`, with the source's own `IoError` kept on the reader by `SourceError()`. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/DataFormats/JsonStream){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `Parse` builds the whole tree before a program sees any of it, so a large document costs its // size several times over in memory. `JsonEventReader` reads the same text as a stream of // events instead — an object opened, a name, a text value, an array closed — pulling bytes from // a `Reader` as it needs them and keeping only the token it is on. The program keeps whatever it // wants and lets the rest go by. // // Streaming moves two jobs onto the program: // // - Lifetimes. `Text()` borrows the reader's buffer, which the next `Next()` reuses, so the text // of an event must be used or copied before asking for another. The reader owns that buffer, // and its destructor gives it back however the function holding it ends. // - Errors. `Next()` answers `JsonEvent::Error` when the document goes wrong, and `Failure()` // then says why and at which byte of the whole document, however it was split into reads. // A source that fails is reported as `JsonParseError::Source`, its own `IoError` kept by // `SourceError()`. Events before the mistake were already delivered, so a program must be // ready to find out late that its input was bad. import Allocator::{ Allocator, SystemAllocator }; import Io::{ IoError, IoErrorKind, PrintLine, Reader }; import Json::{ JsonEvent, JsonEventReader, JsonLimits, JsonParseError }; import Text::StringView; // Stands in for a file or a socket: serves its text at most 8 bytes per read, so tokens arrive // split across reads, as they would from a real stream. struct TextSource { text: char8[..]; offset: uint; } extend TextSource : Reader { func Read(self: &var TextSource, into: var char8[..]) -> uint ! IoError { let remaining = self.text.length - self.offset; if remaining == 0 { fail IoError::Of(IoErrorKind::EndOfStream); } var count: uint = 8; if remaining < count { count = remaining; } if into.length < count { count = into.length; } for i in 0..count { into[i] = self.text[self.offset + i]; } self.offset += count; return count; } } func IsTitle(text: char8[..]) -> bool { let title = StringView::FromValidated("title"); return StringView::FromValidated(text).Equals(title); } // Prints the title of every book and counts the books, without ever building a tree. func Titles(allocator: Allocator, document: char8[..]) -> uint ! (IoError | JsonParseError) { let source: Reader = TextSource { text: document, offset: 0 }; // The reader's buffer lives as long as `reader` does. Whether this function ends at the // `return` or at an early `fail`, the destructor frees it on the way out. var reader = JsonEventReader::New(allocator, source, JsonLimits::Default())?; var books: uint = 0; var wantTitle = false; var event = reader.Next(); while event != JsonEvent::End { if event == JsonEvent::Error { // After an `Error` event `Failure()` always holds the reason; the fallback is // only there because the type allows `none`. fail reader.Failure() ?? JsonParseError::Unexpected(0); } // Each book is an object one level inside the top-level array. if event == JsonEvent::ObjectStart && reader.Depth() == 2 { books += 1; } // Print the title now: its text is gone once `Next()` is called again. if event == JsonEvent::Text && wantTitle { PrintLine(" title: {}", reader.Text()); } wantTitle = event == JsonEvent::Name && IsTitle(reader.Text()); event = reader.Next(); } return books; } func Report(allocator: Allocator, document: char8[..]) { PrintLine("{}", document); match Titles(allocator, document) { .Success(books) => PrintLine(" {} books", books), .Failure(error) => match error { reason: JsonParseError => PrintLine(" refused at byte {}: {}", reason.Offset(), reason), _: IoError => PrintLine(" could not start reading") } } } func Main() -> int { var system = SystemAllocator(); let allocator: Allocator = system; let catalog = "[{\"title\": \"Dune\", \"year\": 1965}, {\"year\": 1815, \"title\": \"Emma\"}]"; Report(allocator, catalog); // Two broken documents. In both, titles were printed before the mistake was found: a // stream cannot know the end is wrong until it gets there. Report(allocator, "[{\"title\": \"Dune\"}, {\"title\": \"Emma\"]"); Report(allocator, "[{\"title\": \"Dune\"}, {\"title\":"); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Json` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/DataFormats/JsonStream rux run ``` ```text [{"title": "Dune", "year": 1965}, {"year": 1815, "title": "Emma"}] title: Dune title: Emma 2 books [{"title": "Dune"}, {"title": "Emma"] title: Dune title: Emma refused at byte 36: token that cannot appear here [{"title": "Dune"}, {"title": title: Dune refused at byte 29: document ended in the middle of a value ``` ## Common mistakes ::warning **Not checking for `Error`.**:br Once a document has gone wrong, `Next()` returns `Error` on every call, and never `End`. Leave out the `if event == JsonEvent::Error` check and the loop for the first broken catalogue prints its titles and then runs forever. :: ::warning **Keeping an event's text.**:br The `char8[..]` from `Text()` points into the reader's buffer, which the next `Next()` overwrites. Use it straight away, or copy it into a `String` you own. :: ::warning **A reader declared with `let`.**:br Reading advances the reader, so `Next()` needs it mutable. With `let reader = …` the call fails with `error: cannot call 'Next' on immutable 'reader'`, and the help says `declare 'reader' with 'var' to make it mutable`. :: ::warning **Handling only the error you expect.**:br The failure is a sum of two types, and the `match` must cover both. Without the `IoError` arm it fails with `error: match on 'IoError | JsonParseError' is not exhaustive; missing _: IoError`. :: ## Try it yourself 1. Print each book's year as well: react to a `Number` event that follows a `year` name. 2. Change `TextSource` to serve one byte per read. Does any line of the output change — including the byte offsets? 3. Add a third book that has no title. Does the count still include it? 4. Count how many events the first catalogue produces in all, `End` excluded. ## Learn more - [JSON](https://rux-lang.dev/docs/learn/json) — the tree parser, for documents small enough to hold at once - [Interface value](https://rux-lang.dev/docs/learn/interface-value) — how `TextSource` becomes a `Reader` - [Destructor](https://rux-lang.dev/docs/learn/destructor) — how the reader's buffer is returned on every path - [Error sum](https://rux-lang.dev/docs/learn/error-sum) and [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) — the two-kind failure and how `Report` splits it - [Buffered I/O](https://rux-lang.dev/docs/learn/buffered-io) — reading a real file a block at a time # TOML ::note **You'll need**: [JSON](https://rux-lang.dev/docs/learn/json), [String builder](https://rux-lang.dev/docs/learn/string-builder), [Date](https://rux-lang.dev/docs/learn/date), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) :: TOML is a configuration format — the one every `Rux.toml` in this course is written in. It is made for people to write by hand: one `key = value` per line, comments with `#`, and `[name]` to open a section. And unlike JSON, its values have real types. An integer is not a float even when the float is whole, and a date is a date, not a string that happens to look like one. This lesson reads a small settings document with the `Toml` package. Reading the result works the same way as in [JSON](https://rux-lang.dev/docs/learn/json): find a key, ask what kind of value it holds, take it out. ```rux import Text::{ String, StringBuilder, StringView, TextError }; import Time::Date; import Toml::{ TomlParse, TomlValue }; ``` ## The document TOML is line-based, so the program writes the document as one literal per line: ```rux let good: char8[..][8] = [ "name = \"Rux\"", "released = 2026-10-04", "", "[server]", "port = 8080", "ratio = 0.75", "tags = [\"web\", \"api\"]", "# A comment, which the parser skips." ]; let document = Lines(allocator, good)?; ``` and `Lines` joins them, each followed by a newline, with a [`StringBuilder`](https://rux-lang.dev/docs/learn/string-builder). Unescaped, the text it builds is: ```toml name = "Rux" released = 2026-10-04 [server] port = 8080 ratio = 0.75 tags = ["web", "api"] # A comment, which the parser skips. ``` `name` and `released` belong to the top-level table; `[server]` opens a nested table that holds the rest. Joining can fail — the builder needs memory — so `Lines` returns `String ! TextError`, and `Main` is a [fallible `Main`](https://rux-lang.dev/docs/learn/fallible-main), `func Main() -> ! TextError`, so that `?` can pass the failure on. ## Parsing ```text TomlParse(allocator, text) -> TomlValue ! TomlParseFailure ``` The result is the top-level table as a `TomlValue` tree, or no tree at all — only the reason, and where it was found: ```rux func Load(allocator: Allocator, text: char8[..]) { match TomlParse(allocator, text) { .Success(root) => Inspect(root), .Failure(failure) => PrintLine("refused at line {}, column {}: {}", failure.line, failure.column, failure) } } ``` A TOML document is written in lines, so a `TomlParseFailure` reports a line and a column rather than only a byte — the place a person would look in an editor. `Load` takes a `char8[..]`, so the `String` from `Lines` is passed as `document.View().Bytes()`. ## Reading values `Key` looks a key up in a table. Like JSON's `Find`, it answers a pointer that is null when the key is missing: ```rux func Key(table: &TomlValue, key: char8[..]) -> *TomlValue { return table.Find(StringView::FromValidated(key)); } ``` Each kind has its own question, answered through an out-parameter, and each answers only for its own kind: ```rux let server = Key(root, "server"); var port: int64 = 0; var ratio = 0.0; let portIsInteger = (*Key(*server, "port")).AsInteger(@port); let portIsFloat = (*Key(*server, "port")).AsFloat(@ratio); PrintLine("port {} (integer: {}, float: {})", port, portIsInteger, portIsFloat); ``` `8080` is an integer, so `AsInteger` says `true` and `AsFloat` says `false`. In JSON both would be "a number"; in TOML the spelling decides the type, and `8080.0` would be a float. | TOML value | Kind | Read with | | ------------------- | ---------- | -------------------------------- | | `"Rux"`, `'Rux'` | text | `AsText()` | | `8080`, `0x1F90` | integer | `AsInteger(@x)`, into an `int64` | | `0.75`, `8080.0` | float | `AsFloat(@x)`, into a `float64` | | `true` | boolean | `AsBoolean(@x)` | | `2026-10-04` | local date | `AsDate(@x)`, into a `Date` | | `["web", "api"]` | array | `Length()`, `At(i)` | | `[server]`, `{ … }` | table | `Find(key)`, `Length()` | A date comes out as the `Date` from the [Date](https://rux-lang.dev/docs/learn/date) lesson, ready to compare or do arithmetic on: ```rux var released = Date { year: 1970, month: 1, day: 1 }; (*Key(root, "released")).AsDate(@released); PrintLine("released {}", released); ``` Arrays are walked by index, and a key that is not there is simply a null pointer: ```rux let tags = Key(*server, "tags"); for i in 0..(*tags).Length() { PrintLine("tags[{}] {}", i, (*(*tags).At(i)).AsText()); } PrintLine("debug present: {}", Key(*server, "debug") != null); ``` ## Refusals ```rux let twice: char8[..][3] = ["[server]", "port = 8080", "port = 9090"]; let repeated = Lines(allocator, twice)?; Load(allocator, repeated.View().Bytes()); Load(allocator, "released = 2026-13-04"); ``` In TOML a key given twice in one table is an **error**, not a quiet overwrite — two values for one setting is almost always a mistake, and refusing it is the only way to make sure nobody acts on the wrong one. And a value must be spelled the way its type is: `2026-13-04` is shaped like a date, but there is no thirteenth month, so it is not the shape TOML defines for any value. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/DataFormats/Toml){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // TOML is a configuration format — the one every `Rux.toml` in this course is written in. A // document is a table of keys and values, `[name]` opens a nested table, and unlike JSON the // values have real types: an integer is not a float even when the float is whole, and a date is // a date rather than a string that looks like one. // // TomlParse(allocator, text) -> TomlValue ! TomlParseFailure // // The result is a `TomlValue` tree, read the same way as a JSON one: `Find` answers a pointer // that is null when the key is missing, and `AsInteger`, `AsFloat`, `AsDate` and the rest // answer `false` when the value is some other kind. A refused document gives no tree at all, // only the reason and where it was found, by line and column. import Allocator::{ Allocator, SystemAllocator }; import Io::PrintLine; import Text::{ String, StringBuilder, StringView, TextError }; import Time::Date; import Toml::{ TomlParse, TomlValue }; // A TOML document is lines, so it is written here one literal per line and joined. func Lines(allocator: Allocator, lines: char8[..][..]) -> String ! TextError { var builder = StringBuilder(allocator); for i in 0..lines.length { builder.Append(lines[i])?; builder.Append("\n")?; } return builder.IntoString(); } func Key(table: &TomlValue, key: char8[..]) -> *TomlValue { return table.Find(StringView::FromValidated(key)); } func Inspect(root: &TomlValue) { PrintLine("name {}", (*Key(root, "name")).AsText()); // The same question asked of an integer and a float. Each answers only its own kind. let server = Key(root, "server"); var port: int64 = 0; var ratio = 0.0; let portIsInteger = (*Key(*server, "port")).AsInteger(@port); let portIsFloat = (*Key(*server, "port")).AsFloat(@ratio); PrintLine("port {} (integer: {}, float: {})", port, portIsInteger, portIsFloat); (*Key(*server, "ratio")).AsFloat(@ratio); PrintLine("ratio {}", ratio); var released = Date { year: 1970, month: 1, day: 1 }; (*Key(root, "released")).AsDate(@released); PrintLine("released {}", released); let tags = Key(*server, "tags"); for i in 0..(*tags).Length() { PrintLine("tags[{}] {}", i, (*(*tags).At(i)).AsText()); } PrintLine("debug present: {}", Key(*server, "debug") != null); } func Load(allocator: Allocator, text: char8[..]) { match TomlParse(allocator, text) { .Success(root) => Inspect(root), .Failure(failure) => PrintLine("refused at line {}, column {}: {}", failure.line, failure.column, failure) } } func Main() -> ! TextError { var system = SystemAllocator(); let allocator: Allocator = system; let good: char8[..][8] = [ "name = \"Rux\"", "released = 2026-10-04", "", "[server]", "port = 8080", "ratio = 0.75", "tags = [\"web\", \"api\"]", "# A comment, which the parser skips." ]; let document = Lines(allocator, good)?; Load(allocator, document.View().Bytes()); // Two refusals. A key given twice in one table is an error in TOML, not a quiet overwrite, // and a value must be spelled the way its type is: there is no thirteenth month. PrintLine(""); let twice: char8[..][3] = ["[server]", "port = 8080", "port = 9090"]; let repeated = Lines(allocator, twice)?; Load(allocator, repeated.View().Bytes()); Load(allocator, "released = 2026-13-04"); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Text`, `Time` and `Toml` under `[Dependencies]`. ## Run it ```sh cd Examples/DataFormats/Toml rux run ``` ```text name Rux port 8080 (integer: true, float: false) ratio 0.75 released 2026-10-04 tags[0] web tags[1] api debug present: false refused at line 3, column 12: key given a value twice in the same table refused at line 1, column 17: not the shape TOML defines ``` ## Common mistakes ::warning **Ignoring the answer of `AsInteger`.**:br The out-parameter is written only when the answer is `true`. Change `port = 8080` to `port = 8080.0` and the program prints `port 0 (integer: false, float: true)`: the value is a float now, and `port` kept its starting value. Check what the conversion answers before using the result. :: ::warning **Passing a `String` where text is wanted.**:br`Load` and `TomlParse` take a `char8[..]`. `Load(allocator, document)` fails with `error: argument 2 to 'Load' has type 'String', but parameter 'text' requires 'char8[..]'`. Pass `document.View().Bytes()`. :: ::warning **A pointer where a reference is wanted.**:br`Key` answers a pointer, but takes its table as a reference. `Key(server, "tags")` fails with `error: argument 1 to 'Key' has type '*TomlValue', but parameter 'table' requires '&TomlValue'`. Dereference it: `Key(*server, "tags")`. :: ::warning **Reading through a missing key.**:br`Key` returns null for a key that is not there, and reading through null crashes the program. The lesson reads `server`, `port` and the rest unchecked only because its document is fixed; for a real configuration file, check each pointer — or treat a missing key as "use the default". :: ## Try it yourself 1. Write the port in hexadecimal, `port = 0x1F90`. What does `AsInteger` read? 2. Add `debug = true` to the `[server]` table and read it with `AsBoolean`. (The array's length, `[8]`, has to grow too.) 3. Open `[server]` a second time further down the document. Is that refused like a repeated key? 4. Add a key with a value of your own and read it back. Then change the spelling of its value so the parser refuses it, and read the line and column. ## Learn more - [JSON](https://rux-lang.dev/docs/learn/json) — the same way of reading a tree, for a format without types - [TOML write](https://rux-lang.dev/docs/learn/toml-write) — changing the tree and writing it back - [Date](https://rux-lang.dev/docs/learn/date) — the type `AsDate` fills in - [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) — `Main` returning `! TextError` - [Manifest](https://rux-lang.dev/docs/packaging/manifest) — the `Rux.toml` file, the TOML you write most often # Writing TOML ::note **You'll need**: [TOML](https://rux-lang.dev/docs/learn/toml), [Writing JSON](https://rux-lang.dev/docs/learn/json-write), [Error sum](https://rux-lang.dev/docs/learn/error-sum) :: Reading a configuration file is half the job; a program that changes its own settings has to write the file back. `TomlWriteDocument` turns a `TomlValue` table into TOML text, the way `WriteValue` did for JSON in [JSON write](https://rux-lang.dev/docs/learn/json-write): ```text TomlWriteDocument(writer, table) -> ! FormatError ``` This lesson reads a document a person wrote, adds a key, writes it back, and looks closely at what the trip through the tree keeps and what it loses. ```rux import Text::{ FormatError, String, StringBuilder, StringView, TextError, TextWriter }; import Toml::{ TomlParse, TomlParseFailure, TomlValue, TomlWriteDocument }; ``` ## A document as a person writes it ```rux let lines: char8[..][6] = [ "# Settings for the demo server", "name = 'Rux' # a literal string", "mask = 0xFF", "server = { host = \"localhost\", port = 8080 }", "", "tags = [\"web\", \"api\"]" ]; let text = Lines(allocator, lines)?; PrintLine("read:\n{}", text); var root = TomlParse(allocator, text.View().Bytes())?; ``` It has everything a hand-written file collects: a comment on a line of its own and another after a value, a `'literal string'` in single quotes, a number in hexadecimal, an *inline table* in braces, and a blank line. `Main` returns `! (TextError | FormatError | TomlParseFailure)` — an [error sum](https://rux-lang.dev/docs/learn/error-sum) of the three things that can go wrong: building text, writing it, and parsing it. So every step here can use `?`, including the parse. ## Changing the tree To change a table, borrow it for changing. `FindMutable` is `Find`'s writable twin: it answers a `*var TomlValue`, and `Insert` adds a key and value to the table it points at, taking ownership of both: ```rux let server = root.FindMutable(StringView::FromValidated("server")); (*server).Insert(String::FromBytes(allocator, "debug")?, TomlValue::Boolean(allocator, true)); ``` `root` itself is declared `var`, because borrowing part of it for changing changes it. ## Writing it back `Written` is the same shape as in JSON write: a `StringBuilder` collects the text, and the caller gets it as a `String`: ```rux func Written(allocator: Allocator, table: &TomlValue) -> String ! FormatError { var builder = StringBuilder(allocator); let sink: &var TextWriter = builder; TomlWriteDocument(sink, table)?; return builder.IntoString(); } ``` ## What survives the trip The parser keeps **values, not spelling**. So writing a parsed document back gives an equivalent document, not the same file: | Read | Written | | ---------------------------------------------- | ------------------------------------------------ | | `# Settings for the demo server` | gone — comments are not values | | `name = 'Rux' # a literal string` | `name = "Rux"` — an ordinary string, no comment | | `mask = 0xFF` | `mask = 255` — the number, in decimal | | `server = { host = "localhost", port = 8080 }` | a `[server]` section, written last | | the blank line | gone; one blank line now comes before `[server]` | | `tags = ["web", "api"]` | unchanged, but now before `[server]` | The inline table moved because TOML requires it: once a `[header]` opens a section, every following `key = value` belongs to that section, so the plain keys of a table must be written before any of its sections. What does survive is every key, every value, and their order within each table — `debug` appears after `host` and `port`, where it was inserted. ## Writing is stable The writer is **deterministic**: the same tree always writes the same bytes. So write, read the output back, write again — and the second text is the first one exactly: ```rux let again = TomlParse(allocator, once.View().Bytes())?; let twice = Written(allocator, again)?; let first = once.View(); PrintLine("written twice, same text: {}", twice.View().Equals(first)); ``` ```mermaid flowchart LR hand["hand-written text"] -- "TomlParse" --> t1["tree"] t1 -- "Insert debug" --> t1b["changed tree"] t1b -- "TomlWriteDocument" --> once["once"] once -- "TomlParse" --> t2["tree"] t2 -- "TomlWriteDocument" --> twice["twice"] twice -. "Equals: true" .-> once ``` The first trip normalises the file; after that nothing moves. That matters for a program that saves its settings every time it runs: the file changes when a setting does, and not otherwise. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/DataFormats/TomlWrite){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `TomlWriteDocument` turns a `TomlValue` table back into TOML text, written into any // `TextWriter`: // // TomlWriteDocument(writer, table) -> ! FormatError // // The parser keeps values, not spelling, so writing a parsed document back gives an equivalent // document rather than the same file. Comments and blank lines are gone, `0xFF` becomes `255`, // a literal string becomes an ordinary one and an inline table becomes a `[header]` section, // written after the plain keys because TOML requires it. What does survive is every key, every // value, and their order within each table. And the writer is deterministic: the same tree // always writes the same bytes, so write, read and write again, and the second text is the // first one exactly. import Allocator::{ Allocator, SystemAllocator }; import Io::PrintLine; import Text::{ FormatError, String, StringBuilder, StringView, TextError, TextWriter }; import Toml::{ TomlParse, TomlParseFailure, TomlValue, TomlWriteDocument }; func Lines(allocator: Allocator, lines: char8[..][..]) -> String ! TextError { var builder = StringBuilder(allocator); for i in 0..lines.length { builder.Append(lines[i])?; builder.Append("\n")?; } return builder.IntoString(); } // Writes `table` as TOML and hands the text back as a String the caller owns. func Written(allocator: Allocator, table: &TomlValue) -> String ! FormatError { var builder = StringBuilder(allocator); let sink: &var TextWriter = builder; TomlWriteDocument(sink, table)?; return builder.IntoString(); } func Main() -> ! (TextError | FormatError | TomlParseFailure) { var system = SystemAllocator(); let allocator: Allocator = system; // A document as a person might write it. let lines: char8[..][6] = [ "# Settings for the demo server", "name = 'Rux' # a literal string", "mask = 0xFF", "server = { host = \"localhost\", port = 8080 }", "", "tags = [\"web\", \"api\"]" ]; let text = Lines(allocator, lines)?; PrintLine("read:\n{}", text); var root = TomlParse(allocator, text.View().Bytes())?; // Change the tree before writing it. `FindMutable` lends a table for changing, and // `Insert` takes ownership of a new key and value. let server = root.FindMutable(StringView::FromValidated("server")); (*server).Insert(String::FromBytes(allocator, "debug")?, TomlValue::Boolean(allocator, true)); let once = Written(allocator, root)?; PrintLine("written:\n{}", once); // Read the output back and write it again: nothing moves the second time. let again = TomlParse(allocator, once.View().Bytes())?; let twice = Written(allocator, again)?; let first = once.View(); PrintLine("written twice, same text: {}", twice.View().Equals(first)); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Text` and `Toml` under `[Dependencies]`. ## Run it ```sh cd Examples/DataFormats/TomlWrite rux run ``` ```text read: # Settings for the demo server name = 'Rux' # a literal string mask = 0xFF server = { host = "localhost", port = 8080 } tags = ["web", "api"] written: name = "Rux" mask = 255 tags = ["web", "api"] [server] host = "localhost" port = 8080 debug = true written twice, same text: true ``` ## Common mistakes ::warning **Looking up a table with `Find` to change it.**:br`Find` lends the table read-only. `root.Find(…)` followed by `(*server).Insert(…)` fails with `error: cannot call 'Insert' on an immutable receiver`. Use `FindMutable`. :: ::warning **A parsed document declared with `let`.**:br`let root = TomlParse(…)?;` and then `root.FindMutable(…)` fails with `error: cannot call 'FindMutable' on immutable 'root'`. A tree you mean to change must be a `var`. :: ::warning **Expecting `Insert` to replace a value.**:br`Insert` always appends, even when the table already has that key. Insert a second `port` and the writer writes `port` twice; reading that text back is refused, because a repeated key is an error in TOML — so this program's `?` ends `Main` with status 1. Check with `Find` before inserting. :: ::warning **Rewriting a file a person maintains.**:br Comments, blank lines and the spelling of values do not survive. That is fine for a file only the program writes, and a nasty surprise in one a person has annotated. Write such settings to a separate file, or leave the person's file alone. :: ## Try it yourself 1. Insert a top-level key, `version = 2`, with `TomlValue::Integer(allocator, 2)`. Where in the output does it land, and why there? 2. Write the mask in binary instead: `mask = 0b1111_1111`. What does the written text say? 3. Add a comment after `tags`. Is any trace of it left in the output? 4. In the tree `again`, find `server` and read `debug` back with `AsBoolean`. Did the value survive the trip as a boolean? ## Learn more - [TOML](https://rux-lang.dev/docs/learn/toml) — reading values out of the tree this lesson writes - [JSON write](https://rux-lang.dev/docs/learn/json-write) — the same writer pattern for JSON - [Error sum](https://rux-lang.dev/docs/learn/error-sum) — `Main` failing with one of three error types - [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) — saving the written text so a crash cannot leave half a file # Part 22: Packages Every program so far has been one package with one source file. Real projects are bigger: many files, libraries of your own, packages from the registry, sometimes a whole tree of packages worked on together. This part shows how Rux organises all of that — from a module inside one file up to a workspace of several packages — and the `rux` commands that keep such a project tidy. By the end you can split a program into a library and an executable, decide what the library shows to the world, and test and document it. ## What you will learn - Splitting one package across several files, and grouping names into modules with `module A::B { }`. - Choosing what a package shows to other packages with `pub`, and what it keeps private. - Reading a `Rux.toml` manifest, and the four package types it can declare. - Depending on registry packages and on packages in a folder beside your own. - Keeping a program and its libraries in one workspace. - What source, static and shared libraries are, and what each one produces. - Documenting a public API with `///` comments and generating pages with `rux doc`. - Formatting, linting and testing a package with `rux fmt`, `rux lint` and `rux test`. ## From module to workspace Each level groups the one before it: ```mermaid flowchart LR item["Items
functions, types,
constants"] --> mod["Module
module Shape::Circle { }
a namespace"] mod --> pkg["Package
Rux.toml + Src/
one Type, one Name"] pkg --> ws["Workspace
a root Rux.toml
with [Workspace]"] pkg -.->|"[Dependencies]:
Path or Namespace + Version"| pkg2["Another package"] ``` | Level | Declared by | Groups | `pub` matters? | | --------- | ----------------------------- | ----------------- | ------------------------------------- | | Module | `module Name { }` in a file | items | no — a package sees all its modules | | Package | `Rux.toml` with `[Package]` | files and modules | yes — at the border to other packages | | Workspace | `Rux.toml` with `[Workspace]` | packages | no — membership is not a dependency | ## Lessons | | Lesson | What you will learn | | ----- | ---------------------------------------------------------------- | --------------------------------------------------- | | 22.1 | [Module](https://rux-lang.dev/docs/learn/module) | split a package across source files and modules | | 22.2 | [Visibility](https://rux-lang.dev/docs/learn/visibility) | choose what a package shows to others with `pub` | | 22.3 | [Package](https://rux-lang.dev/docs/learn/package) | what a manifest says about a package | | 22.4 | [Dependency](https://rux-lang.dev/docs/learn/dependency) | depend on another package | | 22.5 | [Workspace](https://rux-lang.dev/docs/learn/workspace) | build several packages together | | 22.6 | [Source library](https://rux-lang.dev/docs/learn/source-library) | write a library and use it from an executable | | 22.7 | [Static library](https://rux-lang.dev/docs/learn/static-library) | build a static library | | 22.8 | [Shared library](https://rux-lang.dev/docs/learn/shared-library) | build a shared library | | 22.9 | [Documentation](https://rux-lang.dev/docs/learn/documentation) | document your code with `///` comments | | 22.10 | [Tooling](https://rux-lang.dev/docs/learn/tooling) | format, lint, test and document with the `rux` tool | ## Before you start This part leans on [Part 4: Functions](https://rux-lang.dev/docs/learn/functions) and [Part 6: Types](https://rux-lang.dev/docs/learn/types) — the libraries here export functions, structs, constructors and methods — and Source library uses a [generic](https://rux-lang.dev/docs/learn/generic) function over a [slice](https://rux-lang.dev/docs/learn/slice). Each lesson's package is in the Examples repository's `Packages/` folder: ```sh cd Examples/Packages/Module rux run ``` Several lessons hold more than one package: the library sits in a folder beside `Src/` with its own `Rux.toml`, and the lesson page shows every file. Workspace is run from its `App/` member, and the two native library lessons are built with `rux build` rather than run. ## After this part [Part 23: Compile time](https://rux-lang.dev/docs/learn/compile-time) is about code chosen while compiling — `when`, the target and build mode you met as `#build` in [Package](https://rux-lang.dev/docs/learn/package), and your own defines. [Part 24: Platform](https://rux-lang.dev/docs/learn/platform) then calls native code with `extern`, which is how a separately built program uses a [shared library](https://rux-lang.dev/docs/learn/shared-library). This part has no checkpoint project of its own; the next one, [Melody](https://rux-lang.dev/docs/learn/melody), follows Part 24. For the full rules, see [Modules](https://rux-lang.dev/docs/lang/modules/overview) in the Rux Reference, and the packaging guides: [Package manifest](https://rux-lang.dev/docs/packaging/manifest), [Package types](https://rux-lang.dev/docs/packaging/types) and [Dependencies](https://rux-lang.dev/docs/packaging/dependencies). Every command used here is described in the [CLI reference](https://rux-lang.dev/docs/cli). # Module ::note **You'll need**: [Function](https://rux-lang.dev/docs/learn/function), [Const](https://rux-lang.dev/docs/learn/const) :: Until now every program has fitted in one file, `Src/Main.rux`. Real programs outgrow that quickly, and Rux gives you two separate tools for organising them. **Files** are how you store the source: a package may have as many as you like. **Modules** are how you organise the *names* in it: a module is a named namespace, so two functions can both be called `Area` as long as they live in different modules. This lesson uses both, in one package. ## Many files, one package Every `.rux` file under `Src/` belongs to the package, and the compiler reads them all together as one unit. This lesson has three: `Main.rux`, `Rectangle.rux` and `Circle.rux`. You do not list them anywhere — putting a file in `Src/` is enough. The file's own name means nothing to the compiler. What decides where its functions live is the `module` declaration wrapped around them, in `Src/Rectangle.rux`: ```rux module Shape::Rectangle { func Area(width: int, height: int) -> int { return width * height; } func Perimeter(width: int, height: int) -> int { return 2 * (width + height); } } ``` You could rename the file to `Boxes.rux` and nothing would change. Keeping file and module names in step is still a good habit, because it tells a reader where to look. ## A module is a namespace `Src/Circle.rux` declares `Area` and `Perimeter` again. That is not a clash, because a module is a namespace: the full name of each function includes the module it is in. ```rux module Shape::Circle { const Pi: float64 = 3.14159; func Area(radius: float64) -> float64 { return Pi * radius * radius; } ``` `A::B` nests one module inside another, so both modules sit under a shared parent called `Shape`. Writing `module Shape { module Circle { … } }` would mean exactly the same thing; the `::` form just saves a level of indentation. Seen from outside, every path starts with the package name, which is `Module` — the `Name` in this lesson's `Rux.toml`: ```mermaid flowchart LR pkg(["package Module"]) --> shape["module Shape"] pkg --> main["Main
(Src/Main.rux)"] shape --> rect["module Rectangle
(Src/Rectangle.rux)"] shape --> circ["module Circle
(Src/Circle.rux)"] rect --> ra["Area, Perimeter"] circ --> ca["Pi, Area, Perimeter"] ``` | Function | Declared in | Full path | | ------------------ | ------------------- | --------------------------------- | | Rectangle's `Area` | `Src/Rectangle.rux` | `Module::Shape::Rectangle::Area` | | Circle's `Area` | `Src/Circle.rux` | `Module::Shape::Circle::Area` | | `Main` | `Src/Main.rux` | at the package root, in no module | ## Importing an item or a whole module `Main.rux` reaches the other two files with imports, and they show the two things an import can name: ```rux import Io::PrintLine; import Module::Shape::Circle; import Module::Shape::Rectangle::{ Area, Perimeter }; ``` The last line imports two **items**, the rectangle's functions. Imported items are used by their bare names, as if they were declared in `Main.rux`: ```rux PrintLine("rectangle 3 x 4 area {}", Area(3, 4)); PrintLine("rectangle 3 x 4 perimeter {}", Perimeter(3, 4)); ``` The second line imports a whole **module**. Its items are then reached through the module's name, which is what keeps both `Area`s usable side by side: ```rux PrintLine("circle radius 4 area {}", Circle::Area(4.0)); PrintLine("circle radius 4 perimeter {}", Circle::Perimeter(4.0)); ``` Importing a module rather than its items is the better choice whenever a name on its own would be unclear. `Circle::Area(4.0)` says which area you mean; a bare `Area(4.0)` makes the reader go and look. ## No `pub` inside a package Nothing in `Rectangle.rux` or `Circle.rux` is marked `pub`, and `Main.rux` uses them anyway. Every file of a package sees everything the package declares. `pub` is about what *other packages* may see — which is the next lesson, [Visibility](https://rux-lang.dev/docs/learn/visibility). ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/Module){rel=""nofollow""}. Its comments explain every step. ::code-tree{default-value="Src/Main.rux"} ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Module" Version = "0.1.0" Type = "Executable" Description = "Source files, modules and imports inside one package" Authors = ["Rux Contributors "] [Dependencies] Io = { Namespace = "Rux", Version = "*" } ``` ```rux [Src/Circle.rux] // A second file, adding a second module beside the first one under the same `Shape` parent. // // It declares `Area` and `Perimeter` again. That is not a clash: a module is a namespace, so // `Shape::Circle::Area` and `Shape::Rectangle::Area` are two different names. module Shape::Circle { const Pi: float64 = 3.14159; func Area(radius: float64) -> float64 { return Pi * radius * radius; } func Perimeter(radius: float64) -> float64 { return 2.0 * Pi * radius; } } ``` ```rux [Src/Main.rux] // Until now every package has been a single `Main.rux`. A package may hold as many source files // as it likes under `Src/`, and the compiler reads them all as one package. // // Each of the other two files wraps its contents in `module Shape::Rectangle { }` or // `module Shape::Circle { }`. A module is a named namespace inside the package, and `A::B` nests // one module inside another, so both of these live under a shared parent called `Shape`. // // An import can bring in two kinds of thing: // // - an item, such as a function, which is then used by its bare name, or // - a whole module, whose items are then reached through it: `Circle::Area(4.0)`. // // Nothing in the other files is marked `pub`. Every file of a package sees everything the package // declares; `pub` is about what other packages may see, which is the next lesson. import Io::PrintLine; import Module::Shape::Circle; import Module::Shape::Rectangle::{ Area, Perimeter }; func Main() -> int { // Imported items read like functions declared in this file. PrintLine("rectangle 3 x 4 area {}", Area(3, 4)); PrintLine("rectangle 3 x 4 perimeter {}", Perimeter(3, 4)); // The circle's functions have the same names, so they are reached through their module. // That keeps both `Area`s usable side by side. PrintLine("circle radius 4 area {}", Circle::Area(4.0)); PrintLine("circle radius 4 perimeter {}", Circle::Perimeter(4.0)); return 0; } ``` ```rux [Src/Rectangle.rux] // This file adds a module called `Shape::Rectangle` to the package. The file's own name means // nothing to the compiler; the `module` declaration is what decides where these functions live. // // The path is written relative to the package. Seen from an import, the full name starts with the // package name, as every import does: `Module::Shape::Rectangle`. module Shape::Rectangle { func Area(width: int, height: int) -> int { return width * height; } func Perimeter(width: int, height: int) -> int { return 2 * (width + height); } } ``` :: ## Run it ```sh cd Examples/Packages/Module rux run ``` ```text rectangle 3 x 4 area 12 rectangle 3 x 4 perimeter 14 circle radius 4 area 50.26544 circle radius 4 perimeter 25.13272 ``` ## Common mistakes ::warning **Leaving the package name out of the import.**:br An import always starts with a package. Write `import Shape::Circle;` and the compiler looks for a package called `Shape`: `error: package 'Shape' is not listed in [Dependencies]`, with the help line "add the package under [Dependencies] or correct the import path". Your own modules start with your own package's name, `Module::Shape::Circle`. :: ::warning **Using a module without importing it.**:br Without `import Module::Shape::Circle;`, the call `Circle::Area(4.0)` fails with `error: name 'Circle' is not defined in this scope`. Spelling out the whole path does not help either — `Module::Shape::Circle::Area(4.0)` fails the same way, naming `'Module'`. The first segment of a path has to be brought in by an import. :: ::warning **Importing a package as if it were a module.**:br`import Io;` fails with `error: import 'Io' does not name a module`, and the help suggests `import Io::Name`. Import the items you need from a package, or one of its modules. :: ::warning **Two imported functions nobody can tell apart.**:br Here both `Area`s could even be imported by name together, because one takes two `int`s and the other a `float64`, so every call picks one. If they took the same parameters, `Area(2, 3)` would fail with `error: call to 'Area' is ambiguous: 2 overloads accept argument types (int, int)`. Import the module and qualify the call instead. :: ## Try it yourself 1. Add a fourth file, `Src/Square.rux`, with a module `Shape::Square` and a function `Area(side: int) -> int`. Import the module and print the area of a 5 × 5 square. 2. Replace `import Module::Shape::Circle;` with `import Module::Shape;` and change the calls to `Shape::Circle::Area(4.0)`. Does it still run? 3. Change `Name` in `Rux.toml` to `Shapes`. Which lines does the compiler reject, and what do you have to change? Look at the name of the program in `Bin/` afterwards. ## Learn more - [Module declaration](https://rux-lang.dev/docs/lang/modules/overview#module-declarations) and [Import](https://rux-lang.dev/docs/lang/modules/imports) in the Rux Reference - [Visibility](https://rux-lang.dev/docs/learn/visibility) — what `pub` means between packages - [Directory layout](https://rux-lang.dev/docs/packaging/layout) — where the files of a package go # Visibility ::note **You'll need**: [Module](https://rux-lang.dev/docs/learn/module), [Constructor](https://rux-lang.dev/docs/learn/constructor), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) :: In [Module](https://rux-lang.dev/docs/learn/module), nothing was marked `pub` and everything still worked, because every file of a package sees everything the package declares. `pub` matters at a different border: the one between **packages**. It marks what a package lets the packages that depend on it see, and everything without it stays private. That is how a library keeps a promise — here, that a counter never passes its limit — no matter what the code using it tries. ## Two packages in one lesson Showing a border between packages needs two of them. The program is the lesson's own package; the library, `Tally`, sits in a folder beside `Src/` with its own manifest: ```text Visibility/ ├── Rux.toml the program, which depends on Tally ├── Src/ │ └── Main.rux └── Tally/ ├── Rux.toml Type = "SourceLibrary" └── Src/ └── Counter.rux ``` The program's `Rux.toml` names the library under `[Dependencies]`: ```toml Tally = { Path = "Tally" } ``` That is a *path dependency*, and [Dependency](https://rux-lang.dev/docs/learn/dependency) covers it properly. For now it is enough that one `rux run` compiles both packages, and that `Main.rux` can then write `import Tally::Counter;`. ## `pub`, one declaration at a time Visibility is chosen for each declaration separately, and a member does not inherit it from its type. In `Tally/Src/Counter.rux`: ```rux pub struct Counter { pub step: int; count: int; } ``` `Counter` is public and so is its `step` field, but `count` is not. The functions in the `extend` block are each marked too, and the limit and the helper that enforces it are left private: | Declaration in `Tally` | `pub`? | What `Main.rux` can do with it | | ------------------------- | ------ | ------------------------------ | | `struct Counter` | yes | import it and name the type | | field `step` | yes | read it and change it | | field `count` | no | nothing — not even read it | | `Counter(step)`, `Tick()` | yes | call them | | `Count()` | yes | call it, to learn the count | | `const Limit`, `Clamp(…)` | no | nothing — not even import them | ## A private field keeps a promise The library's one promise is that `count` never goes past `Limit`. Every change to it goes through `Tick`, and `Tick` clamps: ```rux pub func Tick(self: &var Counter) { self.count = Clamp(self.count + self.step); } ``` Because `count` is private, there is no other way in. The program ticks three times with a step of 4: ```rux var counter = Counter(4); counter.Tick(); counter.Tick(); counter.Tick(); ``` Three ticks of 4 would make 12, but the program prints `count 10`. Nothing in `Main.rux` can push it further, because nothing in `Main.rux` can touch `count`. The public constructor is the only way to make a `Counter` at all: a struct literal would have to set `count`, so it is refused. ```mermaid flowchart LR main["Visibility
Src/Main.rux"] subgraph tally ["package Tally"] direction TB pubs["pub: Counter, step,
Counter(), Tick(), Count()"] privs["private: count,
Limit, Clamp()"] pubs -- "used inside
the package" --> privs end main -- "allowed" --> pubs main -.->|"refused"| privs ``` This is the usual shape of a good library: a small public surface, and private details that it is free to change later without breaking anyone. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/Visibility){rel=""nofollow""}. Its comments explain every step. ::code-tree{default-value="Src/Main.rux"} ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Visibility" Version = "0.1.0" Type = "Executable" Description = "What pub lets a dependent package see, and what it keeps private" Authors = ["Rux Contributors "] [Dependencies] Io = { Namespace = "Rux", Version = "*" } Tally = { Path = "Tally" } ``` ```toml [Tally/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Tally" Version = "0.1.0" Type = "SourceLibrary" Description = "A counter library whose count can only grow up to its limit" Authors = ["Rux Contributors "] ``` ```rux [Src/Main.rux] // Inside one package, every file sees every declaration. `pub` matters at the boundary between // packages: it marks what a package lets the packages that depend on it see. // // This lesson needs two packages to show that. The companion library lives in `Tally/`, beside // `Src/`, with its own `Rux.toml`. This package's manifest lists it under `[Dependencies]` as // `Tally = { Path = "Tally" }`, so building this program compiles the library as well. // // Visibility is chosen per declaration, and a member does not inherit it from its type. `Counter` // is public, its `step` field is public, and its `count` field is not. A caller can read `step`, // but can learn the count only by asking `Count()`, and can change it only through `Tick()`, which // never lets it pass the library's private limit. import Io::PrintLine; import Tally::Counter; // A private function cannot even be imported. Adding it to the import above is rejected: // // import Tally::{ Counter, Clamp }; // // error: function 'Clamp' is private to package 'Tally' // help: add 'pub' to the declaration of 'Clamp' func Main() -> int { // The public constructor is the only way to make a `Counter` here. A struct literal would have // to set the private `count`, and is refused for that reason. var counter = Counter(4); counter.Tick(); counter.Tick(); counter.Tick(); // Three ticks of 4 would make 12; the library's private limit keeps it at 10. PrintLine("step {}, count {}", counter.step, counter.Count()); // The private field itself is out of reach, for reading as much as for writing: // // counter.count = 0; // // error: struct field 'count' is private to package 'Tally' // help: add 'pub' before the declaration of 'count' return 0; } ``` ```rux [Tally/Src/Counter.rux] // The companion library. It is a package of its own, with its own `Rux.toml`, so everything in it // is private to it unless it says `pub`. // // The public surface is the `Counter` type, its `step` field and three of its functions. The // running total, the limit and the helper that enforces the limit stay private, and that is the // whole point: no caller can push `count` past `Limit`, because no caller can touch `count`. pub struct Counter { pub step: int; count: int; } extend Counter { pub func Counter(step: int) -> Counter { return Counter { step: step, count: 0 }; } pub func Tick(self: &var Counter) { self.count = Clamp(self.count + self.step); } pub func Count(self: &Counter) -> int { return self.count; } } const Limit: int = 10; func Clamp(value: int) -> int { return value > Limit ? Limit : value; } ``` :: ## Run it ```sh cd Examples/Packages/Visibility rux run ``` ```text step 4, count 10 ``` ## Common mistakes ::warning **Importing a private item.**:br A private function cannot even be imported. `import Tally::{ Counter, Clamp };` fails with `error: function 'Clamp' is private to package 'Tally'`, and the help line says "add 'pub' to the declaration of 'Clamp'". The private constant is refused the same way, as `constant 'Limit' is private to package 'Tally'`. :: ::warning **Reaching for a private field.**:br`counter.count = 0;` fails with `error: struct field 'count' is private to package 'Tally'`. Reading it is refused just the same. Ask the type instead, through `Count()`. :: ::warning **Building a struct that has private fields.**:br`Counter { step: 4, count: 0 }` fails with `error: struct 'Counter' cannot be initialized outside its package because it has private fields`, and the help line says "use a public constructor instead". Call `Counter(4)`. :: ::warning **Making the type public but not its methods.**:br`pub` on a struct does not reach its methods. Remove it from `Tick` and the call fails with `error: method 'Tick' is private to package 'Tally'`. Take it off the struct instead, and even the import fails: `error: type 'Counter' is private to package 'Tally'`. :: ## Try it yourself 1. `step` is public, so the program may change it. Set `counter.step = 1;` before the ticks and predict the output. 2. Add a public `Reset` method to `Counter` in `Tally/Src/Counter.rux` that sets `count` back to 0, and call it from `Main`. 3. Make `Limit` public, import it next to `Counter`, and print it. Is the promise about `count` any weaker now? ## Learn more - [Items visibility](https://rux-lang.dev/docs/lang/modules/visibility) in the Rux Reference - [Constructor](https://rux-lang.dev/docs/learn/constructor) and [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) — the two kinds of function `Counter` exposes - [Dependency](https://rux-lang.dev/docs/learn/dependency) — the `Path` line that connects the two packages - [Documentation](https://rux-lang.dev/docs/learn/documentation) — describing the `pub` items for the people who use them # Package ::note **You'll need**: [Module](https://rux-lang.dev/docs/learn/module), [Enum](https://rux-lang.dev/docs/learn/enum), [Match expression](https://rux-lang.dev/docs/learn/match-expression) :: Every lesson so far has had a `Rux.toml` beside its `Src/` folder, and you have mostly ignored it. That file is the **package manifest**: it tells `rux` what the package is called, what it builds and what it needs. This lesson reads one section by section, and its program asks the compiler to report back on its own manifest. ## The manifest, section by section Here is this lesson's `Rux.toml` in full: ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Package" Version = "0.1.0" Type = "Executable" Description = "The parts of a Rux.toml manifest and the four package types" Authors = ["Rux Contributors "] [Dependencies] Core = { Namespace = "Rux", Version = "*" } Io = { Namespace = "Rux", Version = "*" } ``` It has three sections, each about something different: | Section | Describes | Fields used here | | ---------------- | ----------------------------- | ---------------------------------------------------------------------------------------- | | `[Manifest]` | the file itself | `Version`, the schema; `MinRux`, the oldest compiler | | `[Package]` | the package | `Name`, `Version`, `Type`, `Description`, `Authors` | | `[Dependencies]` | the packages this one imports | one line per import name — the [next lesson](https://rux-lang.dev/docs/learn/dependency) | The manifest is strict. Field names are case-sensitive, and an unknown field is an error rather than something quietly ignored, so a typo fails the build instead of silently changing it. ## Two versions that have nothing to do with each other The file has two `Version` lines, and they answer different questions. `[Manifest].Version = 1` is the **schema** version: which set of rules the file is written in. It is always written out, never inferred, and 1 is the only one so far. `[Package].Version = "0.1.0"` is the package's own **release number**, a semantic version that you raise as the package changes. `MinRux` sits next to the schema version because it is also about reading the file: a compiler older than `MinRux` refuses the package before compiling anything. ## `Name` reaches further than it looks `Name` is more than a label. It is the first segment of every import path into the package — you used that in [Module](https://rux-lang.dev/docs/learn/module), where every path began with `Module::` — and it is the name of what the build produces. `rux run` here builds `Bin/Debug///Package.exe` on Windows. ## Four package types `Type` is required, and it decides what `rux build` makes: ```mermaid flowchart LR t(["[Package].Type"]) --> exe["Executable
a program with Main"] t --> src["SourceLibrary
compiled into each
package that uses it"] t --> sta["StaticLibrary
.lib or .a archive"] t --> sha["SharedLibrary
.dll, .so or .dylib"] exe --> run["rux build and rux run"] src --> check["rux check only"] sta --> build["rux build"] sha --> build ``` Every lesson so far has been an `Executable`. The other three are the subject of [Source library](https://rux-lang.dev/docs/learn/source-library), [Static library](https://rux-lang.dev/docs/learn/static-library) and [Shared library](https://rux-lang.dev/docs/learn/shared-library). ## The program reads its own manifest `#build` and `#compiler` are values the compiler fills in while it compiles, imported from `Core` like any other item. [Part 23: Compile time](https://rux-lang.dev/docs/learn/compile-time) covers them properly; here they let the program see its manifest from the inside. `#build.outputKind` is `[Package].Type`: ```rux let kind = match #build.outputKind { .Executable => "Executable", .SourceLibrary => "SourceLibrary", .StaticLibrary => "StaticLibrary", .SharedLibrary => "SharedLibrary" }; ``` `#compiler.version` is the version of the compiler doing the build, so the program can compare it with the `MinRux` it declares: ```rux let minimum = SemanticVersion(0, 4, 0); PrintLine("MinRux 0.4.0 met: {}", #compiler.version.Compare(minimum) >= 0); ``` A program that runs at all always prints `true` here — a compiler that did not meet `MinRux` would have refused to build it. And `#build.profile` is `Debug` for a plain `rux run`, or `Release` with `rux run --release`. ## Starting a new package You rarely write a manifest from nothing. `rux new Name` creates a folder with a minimal `Rux.toml`, a `Src/Main.rux` and a `.gitignore`; `--source`, `--static` and `--shared` pick the other three types. The manifest it writes has only the required fields — no `MinRux`, which is optional until you publish — so you add the rest, and the `[Dependencies]` you need, by hand. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/Package){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Every lesson so far has had a `Rux.toml` beside its `Src/` directory. That file is the package // manifest, and this lesson reads it section by section. // // - `[Manifest]` describes the file itself. `Version = 1` is the manifest schema, and `MinRux` is // the oldest compiler allowed to build the package. // - `[Package]` describes the package. `Name` is also the first segment of every import path into // it. `Version` is the package's own version, unrelated to the schema version above. `Type` // decides what `rux build` produces. // - `[Dependencies]` lists the packages this one imports, one line per import name. // // `Type` is required, and it is one of four: // // Executable a program with a `Main`; `rux run` builds it and starts it // SourceLibrary source compiled into every package that depends on it // StaticLibrary a native archive: `.lib` on Windows, `.a` elsewhere // SharedLibrary a native library loaded at run time: `.dll`, `.so` or `.dylib` // // The program asks the compiler what it is building. `#build` and `#compiler` are values the // compiler fills in while it compiles, imported from `Core` like any other item. The Compile time // part covers them properly; here they let the program report on its own manifest. import Core::{ #build, #compiler, OutputKind, SemanticVersion }; import Io::PrintLine; func Main() -> int { // `outputKind` is `[Package].Type`, seen from inside the program. let kind = match #build.outputKind { .Executable => "Executable", .SourceLibrary => "SourceLibrary", .StaticLibrary => "StaticLibrary", .SharedLibrary => "SharedLibrary" }; PrintLine("Type {}", kind); // A compiler older than `MinRux` refuses the package before compiling anything, so a program // that runs at all always finds this true. let minimum = SemanticVersion(0, 4, 0); PrintLine("MinRux 0.4.0 met: {}", #compiler.version.Compare(minimum) >= 0); // `rux run` builds the Debug profile, into `Bin/Debug///Package.exe` on Windows. // The file name comes from `[Package].Name`. PrintLine("Profile {}", #build.profile); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/Packages/Package rux run ``` ```text Type Executable MinRux 0.4.0 met: true Profile Debug ``` This package's own `Rux.toml`, line by line: | Line | Meaning | | --------------------------------------------- | -------------------------------------------------------------------------- | | `[Manifest]` | Facts about the file itself | | `Version = 1` | The manifest schema; 1 is the only one, and it is never inferred | | `MinRux = "0.4.0"` | The oldest compiler that may build the package | | `[Package]` | Facts about the package | | `Name = "Package"` | Its name, the first segment of its import paths and of its artifact's name | | `Version = "0.1.0"` | The package's own semantic version | | `Type = "Executable"` | What it builds: one of the four package types in `Src/Main.rux` | | `Description = "..."` | One line about it | | `Authors = [...]` | Who wrote it | | `[Dependencies]` | The packages it imports, one per import name | | `Core = { Namespace = "Rux", Version = "*" }` | `Core` from the registry, any version installed | `rux new Name` writes a manifest like this one; `--source`, `--static` and `--shared` select the other three types. ## Common mistakes ::warning **Leaving out `Type`.**:br There is no default package type. Without the line, `rux` stops at `error: [Package] must declare 'Type'`, and the help line lists the four choices. :: ::warning **An old or invented type name.**:br`Type = "Program"` fails with `error: [Package].Type must be 'Executable', 'SharedLibrary', 'StaticLibrary' or 'SourceLibrary', found 'Program'`. The older spellings `Program`, `Library` and `Source` are gone, with no aliases. :: ::warning **A misspelt field.**:br The manifest is strict: `Descripton = "…"` fails with `error: unknown field 'Descripton' in [Package]`. That is a feature — a typo cannot quietly drop a setting. :: ::warning **Raising the wrong `Version`.**:br`[Manifest].Version` is not your release number. Set it to 2 and the build stops with `error: unsupported manifest version 2 in [Manifest].Version; this compiler accepts version 1`. Release numbers go in `[Package].Version`. :: ::warning **A `MinRux` newer than your compiler.**:br With `MinRux = "9.0.0"`, nothing is compiled: `error: package 'Package' requires Rux '9.0.0' or newer, but this is Rux '0.4.0'`. Install a newer `rux`, or lower `MinRux` if the package really builds with an older one. :: ## Try it yourself 1. Run `rux run --release` and compare the `Profile` line with a plain `rux run`. 2. Change `Type` to `StaticLibrary` and try `rux run`. Then try `rux build` and find the file it writes under `Bin/`. 3. Run `rux info` in this folder. Which manifest fields does it show? 4. Outside the Examples repository, run `rux new Sketch --source` and compare its `Rux.toml` and `Src/` with this lesson's. ## Learn more - [Package manifest](https://rux-lang.dev/docs/packaging/manifest) — every section and field, with the validation rules - [Package types](https://rux-lang.dev/docs/packaging/types) — what each `Type` builds on each platform - [`rux new`](https://rux-lang.dev/docs/cli/new) and [`rux info`](https://rux-lang.dev/docs/cli/info) in the CLI reference - [Match expression](https://rux-lang.dev/docs/learn/match-expression) — the `match` that turns `outputKind` into text # Dependency ::note **You'll need**: [Package](https://rux-lang.dev/docs/learn/package), [Visibility](https://rux-lang.dev/docs/learn/visibility) :: Almost every program in this course has started with `import Io::PrintLine;`, and that import only works because the manifest lists `Io` as a **dependency**. This lesson looks at that list properly. There are two kinds of dependency — packages from the registry, and packages in a folder on your own disk — and this program uses both. ## The `[Dependencies]` section The lesson's `Rux.toml` ends with three lines: ```toml [Dependencies] Io = { Namespace = "Rux", Version = "*" } Math = { Namespace = "Rux", Version = "*" } Units = { Path = "Units" } ``` The key on the left of each line is the **import name**: the first segment of every `import` that reaches into that package. `Io = …` is what makes `import Io::PrintLine;` mean something. The inline table on the right says where the package comes from, and its shape decides which kind of dependency it is. ## Registry dependencies A registry dependency names a **namespace** and a **version requirement**. `Io` and `Math` are standard packages, published under the `Rux` namespace. They ship with the compiler, so they are already in the package cache on your machine; for any other registry package, `rux install` downloads a matching version into that cache once, and builds read it from there without going online again. The requirement says which versions are acceptable: | Requirement | Accepts | | ----------------- | ------------------------------------ | | `*` | any version — whichever is installed | | `^0.1.0` | 0.1.x, but not 0.2.0 | | `>=1.2.0, <2.0.0` | a range spelt out | `rux list` shows what each line resolved to — here `Resolved Rux/Io @ * to 0.1.0`, the same for `Math`, and `Path Units at 'Units'`. ::note **Standard packages are declared by hand.**:br In Rux 0.4.0, `rux add Rux/Io` does not work for the standard packages. Write their line into `Rux.toml` yourself, as every lesson in this course does: `Io = { Namespace = "Rux", Version = "*" }`. :: ## Path dependencies A path dependency names a folder holding a `Rux.toml`, relative to this manifest. Here it is `Units/`, right beside `Src/`: ```rux pub const KilometresPerMile: float64 = 1.609344; pub func ToMiles(kilometres: float64) -> float64 { return kilometres / KilometresPerMile; } ``` Its source is compiled with the program, so an edit to `Units/Src/Units.rux` shows up on the next build, and nothing needs installing. The price is that a package with a path dependency cannot be **published**: the path means nothing on anyone else's machine. ## Both kinds look the same in the code Once declared, nothing in the source says where a package came from: ```rux import Io::PrintLine; import Math::{ Hypot, Round }; import Units::ToMiles; ``` ```mermaid flowchart LR reg[("registry")] -- "rux install
(once)" --> cache[("package cache
Io, Math")] cache -- "Namespace + Version" --> prog["Dependency
Src/Main.rux"] folder["Units/
beside Src/"] -- "Path" --> prog prog --> exe["Dependency.exe"] ``` `Hypot(3.0, 4.0)` from `Math` is the length of the long side of a right triangle — five kilometres for three east and four north — and `ToMiles` from `Units` converts it. `Round(… * 100.0) / 100.0` keeps two decimal places. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/Dependency){rel=""nofollow""}. Its comments explain every step. ::code-tree{default-value="Src/Main.rux"} ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Dependency" Version = "0.1.0" Type = "Executable" Description = "Registry dependencies from the package cache and a path dependency beside the source" Authors = ["Rux Contributors "] [Dependencies] Io = { Namespace = "Rux", Version = "*" } Math = { Namespace = "Rux", Version = "*" } Units = { Path = "Units" } ``` ```toml [Units/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Units" Version = "0.1.0" Type = "SourceLibrary" Description = "Distance conversions, reached by a path dependency" Authors = ["Rux Contributors "] ``` ```rux [Src/Main.rux] // A package names what it needs in the `[Dependencies]` section of its `Rux.toml`. Each line's key // is the import name, the first segment of every `import` that reaches into that package. This // program uses both kinds of dependency there are: // // Io = { Namespace = "Rux", Version = "*" } // Math = { Namespace = "Rux", Version = "*" } // Units = { Path = "Units" } // // A registry dependency names a namespace and a version requirement. `rux install` downloads a // matching version into the package cache on this machine, and builds then read it from there, // without going online again. `*` accepts any version; `^0.1.0` would accept 0.1.x but not 0.2.0, // and `>=1.2.0, <2.0.0` spells a range out. // // A path dependency names a directory holding a `Rux.toml`, relative to this manifest. The source // there is compiled with this program, edits to it are seen on the next build, and nothing needs // installing. A package with a path dependency cannot be published, because the path means // nothing on anyone else's machine. // // Once declared, the two kinds look the same in the code. import Io::PrintLine; import Math::{ Hypot, Round }; import Units::ToMiles; func Main() -> int { // Three kilometres east, then four north: how far from the start, as the crow flies? let kilometres = Hypot(3.0, 4.0); let miles = Round(ToMiles(kilometres) * 100.0) / 100.0; PrintLine("straight line {} km", kilometres); PrintLine("which is {} miles", miles); return 0; } ``` ```rux [Units/Src/Units.rux] // A small library of our own, kept in a directory beside the program that uses it. Nothing about it // is published anywhere: the program finds it by its path. pub const KilometresPerMile: float64 = 1.609344; pub func ToMiles(kilometres: float64) -> float64 { return kilometres / KilometresPerMile; } ``` :: ## Run it ```sh cd Examples/Packages/Dependency rux run ``` ```text straight line 5.0 km which is 3.11 miles ``` ## Common mistakes ::warning **Importing a package the manifest does not list.**:br Delete the `Math` line and the build stops before any code is checked: `error: package 'Math' is not listed in [Dependencies]`, with the note "the import requires a package dependency with the same import name". Every import name needs its line. :: ::warning **A path that points nowhere.**:br With `Path = "Unit"`, `rux` reports `error: could not open the manifest` for the missing folder, then `error: cannot load dependency package 'Units'`. The path is relative to the manifest that declares it, not to the folder you run `rux` from. :: ::warning **Leaving out the version.**:br`Math = { Namespace = "Rux" }` fails with `error: registry dependency 'Math' must declare 'Version'`. Write `Version = "*"` if any version will do. :: ::warning **Mixing the two kinds.**:br A dependency is one kind or the other. `Units = { Path = "Units", Version = "*" }` fails with `error: path dependency 'Units' cannot also declare 'Namespace' or 'Version'`. :: ::warning **A requirement nothing installed can meet.**:br With `Version = "^9.0.0"` for `Math`, the build fails with `error: no installed version of 'Rux/Math' satisfies '^9.0.0'`, and a note lists the versions that are installed. Loosen the requirement, or install a version that matches. :: ## Try it yourself 1. Change the requirement for `Math` to `^0.1.0` and run `rux list`. Does anything change in the build? 2. Add a `ToKilometres` function to `Units` and use it to convert the rounded `miles` back. Why is the answer not exactly 5.0? 3. The import name does not have to match the package's own name. Rename the line to `Distance = { Path = "Units", Package = "Units" }` and change the import to match. ## Learn more - [Dependencies](https://rux-lang.dev/docs/packaging/dependencies) — requirement syntax, the package cache and target-specific dependencies - [`rux install`](https://rux-lang.dev/docs/cli/install) and [`rux list`](https://rux-lang.dev/docs/cli/list) in the CLI reference - [Math](https://rux-lang.dev/docs/learn/math) — the standard package this program borrows `Hypot` and `Round` from - [Workspace](https://rux-lang.dev/docs/learn/workspace) — several packages, with path dependencies between them, kept in one tree # Workspace ::note **You'll need**: [Dependency](https://rux-lang.dev/docs/learn/dependency), [For](https://rux-lang.dev/docs/learn/for) :: A project often grows into several packages that you work on together: a program, plus the libraries it is built from. You could keep them in unrelated folders and connect them with path dependencies, as [Dependency](https://rux-lang.dev/docs/learn/dependency) did. A **workspace** goes one step further: it puts them in one tree under a single root manifest, so one command can check, build or lint all of them at once. ## A root manifest with `[Workspace]` The `Rux.toml` at the root of this lesson has no `[Package]` section. It has `[Workspace]` instead: ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Workspace] Packages = [ "App", "Temperature", ] ``` `Packages` lists every **member** by its folder. The list is explicit: there are no wildcards, and every member must sit inside the root, so adding a package to the project means adding a line here. A manifest has either `[Package]` or `[Workspace]`, never both — a workspace is not a package. It has no source of its own, declares no dependencies and has nothing to run. ```text Workspace/ ├── Rux.toml [Workspace], listing the two members ├── App/ │ ├── Rux.toml Type = "Executable" │ └── Src/Main.rux └── Temperature/ ├── Rux.toml Type = "SourceLibrary" └── Src/Temperature.rux ``` ## Members are ordinary packages Each member has its own manifest and is written exactly as a lone package would be. Being listed changes nothing about it — and in particular, membership does **not** create dependencies. The program still names the library in its own `App/Rux.toml`: ```toml Temperature = { Path = "../Temperature" } ``` Then `App/Src/Main.rux` imports it like any other package: ```rux import Io::PrintLine; import Temperature::ToFahrenheit; ``` So a workspace answers "which packages make up this project?", while each member's `[Dependencies]` still answers "which packages does this one use?": ```mermaid flowchart LR root(["Workspace/Rux.toml
[Workspace]"]) -. "member" .-> app["App
Executable"] root -. "member" .-> temp["Temperature
SourceLibrary"] app -- "Path = ../Temperature" --> temp app --> exe["App.exe"] ``` ## Commands at the root and in a member At the root, `rux` works on every member in turn: | Where you are | Command | What happens | | ------------- | --------------------------------- | ----------------------------------------------------- | | root | `rux check` | checks both members: `Checked 2 packages` | | root | `rux build` | builds `App`; the source library has nothing to build | | root | `rux lint` | lints every member | | root | `rux run` | refused — the workspace has nothing to run | | root | `rux --manifest App/Rux.toml run` | runs the program without changing folder | | `App/` | `rux run` | builds and runs the program, as for any package | `--manifest` is a global option: it tells any `rux` command which `Rux.toml` to use instead of the one in the current folder. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/Workspace){rel=""nofollow""}. Its comments explain every step. ::code-tree{default-value="Temperature/Src/Temperature.rux"} ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Workspace] Packages = [ "App", "Temperature", ] ``` ```toml [App/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "App" Version = "0.1.0" Type = "Executable" Description = "The program member of the Workspace lesson" Authors = ["Rux Contributors "] [Dependencies] Io = { Namespace = "Rux", Version = "*" } Temperature = { Path = "../Temperature" } ``` ```toml [Temperature/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Temperature" Version = "0.1.0" Type = "SourceLibrary" Description = "The library member of the Workspace lesson" Authors = ["Rux Contributors "] ``` ```rux [App/Src/Main.rux] // A project often grows into several packages that are worked on together: a program and the // libraries it is built from. A workspace keeps them in one tree under a single root manifest. // // The root `Rux.toml` of this lesson has no `[Package]` section. It has `[Workspace]` instead, // listing every member by its directory: // // [Workspace] // Packages = [ // "App", // "Temperature", // ] // // The list is explicit. There are no wildcards, and a member must sit inside the root, so adding // a package to the project means adding a line here. A workspace is not a package: it declares no // dependencies and has no source of its own. `rux check` or `rux build` at the root works on every // member, but the root has nothing to run, so the program is run from its own member directory. // // Membership does not create dependencies either. This program still names the library in its // own manifest, `Temperature = { Path = "../Temperature" }`, exactly as a lone package would. import Io::PrintLine; import Temperature::ToFahrenheit; func Main() -> int { let readings: float64[4] = [-40.0, 0.0, 21.5, 100.0]; for celsius in readings { PrintLine("{} C = {} F", celsius, ToFahrenheit(celsius)); } return 0; } ``` ```rux [Temperature/Src/Temperature.rux] // The library member of the workspace. It is an ordinary package with its own manifest; being // listed in the workspace changes nothing about how it is written. pub func ToFahrenheit(celsius: float64) -> float64 { return celsius * 9.0 / 5.0 + 32.0; } ``` :: ## Run it ```sh cd Examples/Packages/Workspace/App rux run ``` ```text -40.0 C = -40.0 F 0.0 C = 32.0 F 21.5 C = 70.7 F 100.0 C = 212.0 F ``` From the root, `rux check`, `rux build` and `rux lint` work on every member. `rux run` there stops, because the workspace "has nothing to run", and suggests `rux --manifest App/Rux.toml run`, which runs the program without changing directory. ## Common mistakes ::warning **Running the workspace.**:br`rux run` at the root stops with `error: manifest '…\Workspace\Rux.toml' is a workspace and has nothing to run`, and the help line suggests `rux --manifest App/Rux.toml run`. Run that, or change into `App/` first. :: ::warning **Expecting membership to connect packages.**:br Remove the `Temperature` line from `App/Rux.toml` and the import fails, even though both packages are members: `error: package 'Temperature' is not listed in [Dependencies]`. Each member declares what it uses. :: ::warning **A wildcard in the member list.**:br`Packages = ["*"]` is not a pattern. `rux` takes it as a folder name and reports `error: workspace member '*' has no Rux.toml`. List every member by name. :: ::warning **A member outside the root.**:br`"../Dependency"` in the list fails with `error: '[Workspace].Packages item' cannot contain a '..' component`. A workspace owns its members, so they live inside it. A package elsewhere can still be a path dependency. :: ## Try it yourself 1. From the workspace root, run `rux check`, then `rux --manifest App/Rux.toml run`. 2. Delete `"Temperature",` from the root manifest and run `rux check` at the root again. How many packages are checked now — and does `App` still build? 3. Add a third member: a source library called `Kelvin` with a `pub` function `ToKelvin(celsius: float64) -> float64`. List it in the workspace, depend on it from `App`, and print each reading in kelvin too. ## Learn more - [Workspaces](https://rux-lang.dev/docs/packaging/manifest#workspaces) in the package manifest reference - [Global options](https://rux-lang.dev/docs/cli/global) — `--manifest` and the other options every command accepts - [`rux check`](https://rux-lang.dev/docs/cli/check) and [`rux build`](https://rux-lang.dev/docs/cli/build) in the CLI reference - [Source library](https://rux-lang.dev/docs/learn/source-library) — the kind of package `Temperature` is # Source library ::note **You'll need**: [Dependency](https://rux-lang.dev/docs/learn/dependency), [Generic](https://rux-lang.dev/docs/learn/generic), [Slice](https://rux-lang.dev/docs/learn/slice) :: You have already used several libraries: `Tally`, `Units` and `Temperature` earlier in this part, and `Io` in nearly every lesson before that. All of them are **source libraries**, `Type = "SourceLibrary"`, the usual kind of library in Rux. This lesson looks at what that type means: a source library is never compiled on its own. Its source is compiled into each package that depends on it, and that is what lets a generic function in it work with types the library has never heard of. ## Compiled into the program that uses it The library is in `Stats/`, beside `Src/`, and the program depends on it by path: ```toml Stats = { Path = "Stats" } ``` When you build the program, the compiler reads `Stats/Src/Stats.rux` along with the program's own `Src/Main.rux`, almost as if the library's files were part of the program. "Almost", because `pub` still guards the border between the two, exactly as in [Visibility](https://rux-lang.dev/docs/learn/visibility). One build, one executable: ```mermaid flowchart LR lib["Stats/Src/Stats.rux
generic Largest"] --> c["rux build
(in SourceLibrary/)"] main["Src/Main.rux
int32 and float64 calls"] --> c c --> exe["SourceLibrary.exe
Largest for int32
Largest for float64"] ``` So a source library produces nothing by itself — no `.lib`, no `.dll`, nothing in `Bin/`. ## A generic function, made to order The whole library is one generic function: ```rux pub func Largest(values: T[..]) -> T { var best = values[0]; for value in values { if value > best { best = value; } } return best; } ``` There is no single machine-code `Largest` anywhere, because the library is never compiled alone. Each program that depends on it compiles this source with its own and gets one `Largest` for every element type it actually uses. This one passes it two arrays: ```rux let scores: int32[5] = [72, 95, 88, 61, 90]; let prices: float64[3] = [4.25, 19.99, 7.5]; PrintLine("highest score {}", Largest(scores)); PrintLine("highest price {}", Largest(prices)); ``` so `Largest` is compiled twice, once with `T` as `int32` and once with `T` as `float64`. A different program could use it for `char` and get a third. That is the main reason Rux libraries are shipped as source: generic code can serve the user's types. ## Checked alone, never built alone You can still work on a source library by itself. In `Stats/`, `rux check` parses and type-checks it. The commands that would make an artifact refuse: | Command in `Stats/` | Result | | ------------------- | ---------------------------------------------------------------- | | `rux check` | `Checked 1 package` — the library on its own is valid | | `rux build` | refused: it "cannot be built or run as a top-level target" | | `rux run` | refused: "package 'Stats' is a source library and cannot be run" | To see the library do anything, build or run a package that uses it — here, the lesson's own program. ## Errors show up where the type meets the code A generic function only promises to work for types that support what it does. `Largest` uses `>`, so it works for numbers and characters, but not for a struct that declares no `>`. Because the library is compiled with your program, that problem is found when your program is built — and reported inside the library's source, with a note pointing back at your call. [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) shows how a library can state such a requirement up front instead. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/SourceLibrary){rel=""nofollow""}. Its comments explain every step. ::code-tree{default-value="Src/Main.rux"} ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "SourceLibrary" Version = "0.1.0" Type = "Executable" Description = "A source library compiled into the program that depends on it" Authors = ["Rux Contributors "] [Dependencies] Io = { Namespace = "Rux", Version = "*" } Stats = { Path = "Stats" } ``` ```toml [Stats/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Stats" Version = "0.1.0" Type = "SourceLibrary" Description = "Generic statistics, shipped as source" Authors = ["Rux Contributors "] ``` ```rux [Src/Main.rux] // `Type = "SourceLibrary"` is the usual kind of library in Rux, and every standard package such as // `Io` is one. Its source is compiled into each package that depends on it, as if its files were // part of that package, while `pub` still guards the boundary between the two. // // So a source library produces nothing by itself. In `Stats/`, `rux check` works, but the commands // that would make an artifact refuse: // // rux build // error: package 'Stats' has Type = "SourceLibrary" and is compiled into dependent packages; // it cannot be built or run as a top-level target // // The program is where it all comes together. Building this package compiles `Stats` with it, and // that is what lets a generic library function serve types the library never heard of: `Largest` // is compiled here for `int32` and again for `float64`, because those are what this program // passes it. import Io::PrintLine; import Stats::Largest; func Main() -> int { let scores: int32[5] = [72, 95, 88, 61, 90]; let prices: float64[3] = [4.25, 19.99, 7.5]; PrintLine("highest score {}", Largest(scores)); PrintLine("highest price {}", Largest(prices)); return 0; } ``` ```rux [Stats/Src/Stats.rux] // A generic function in a source library. The library is never compiled on its own, so there is // no single machine-code `Largest` here. Each program that depends on the library compiles this // source along with its own and gets a `Largest` for every element type it actually uses. pub func Largest(values: T[..]) -> T { var best = values[0]; for value in values { if value > best { best = value; } } return best; } ``` :: ## Run it ```sh cd Examples/Packages/SourceLibrary rux run ``` ```text highest score 95 highest price 19.99 ``` ## Common mistakes ::warning **Building the library itself.**:br In `Stats/`, `rux build` fails with `error: package 'Stats' has Type = "SourceLibrary" and is compiled into dependent packages; it cannot be built or run as a top-level target`. Use `rux check` there, and build the program that depends on it. :: ::warning **A type the generic code cannot handle.**:br Pass `Largest` an array of a struct `Point` and the error is reported at `value > best` in `Stats.rux`: `error: operator '>' is not defined for 'Point'`. The note under it, "in 'Largest' instantiated with T = Point by the call at …", leads back to the line in your program. Strings fail the same way, as `operator '>' is not defined for slice type 'char8[..]'`. :: ::warning **An empty slice.**:br`Largest` reads `values[0]` before it looks at anything else. Give it an empty slice and the program stops at run time with `Panic: index out of range`. The library trusts its caller here; a safer version would return an optional instead. :: ## Try it yourself 1. Add `pub func Smallest(values: T[..]) -> T` to `Stats` and print the lowest score and the lowest price. 2. Call `Largest` with an array of `char`, such as `['r', 'u', 'x']`. Which letter wins, and why? 3. Change `Largest` to return `T?` and give back `none` when `values.length` is 0. Update the program to print a fallback with `??`. ## Learn more - [Package types](https://rux-lang.dev/docs/packaging/types) — how a source library differs from the other three - [Generic](https://rux-lang.dev/docs/learn/generic) and [Generic bound](https://rux-lang.dev/docs/learn/generic-bound) — writing functions that work for many types - [Static library](https://rux-lang.dev/docs/learn/static-library) — a library compiled ahead of time instead - [`rux check`](https://rux-lang.dev/docs/cli/check) — the one command a source library answers to on its own # Static library ::note **You'll need**: [Package](https://rux-lang.dev/docs/learn/package), [Source library](https://rux-lang.dev/docs/learn/source-library) :: A [source library](https://rux-lang.dev/docs/learn/source-library) waits to be compiled into whatever uses it. A **static library** is the opposite: the package is compiled ahead of time, straight to machine code, and packed into a native archive. Archives are what native toolchains — C and C++ linkers, for instance — know how to use, so this is how Rux code is handed to them. This package has no `Main` and nothing to run; the lesson is about what the build produces. ## Asking for an archive One line of the manifest changes: ```toml Type = "StaticLibrary" ``` `rux build` then writes an archive whose name follows each platform's convention: | Platform | File written by `rux build` | | --------------------- | ---------------------------------------------------------- | | Windows | `Bin/Debug/Windows/x86-64/StaticLibrary.lib` | | Linux, macOS, FreeBSD | `libStaticLibrary.a`, in the matching `Bin/Debug/…` folder | `rux build --release` writes the optimised version under `Bin/Release/` instead. A native linker treats an archive as a box of parts: when it builds a program, it copies in the functions that program calls, and leaves the rest. The finished program carries its own copy of everything it took, so nothing extra has to ship beside it. ## One symbol per function Inside the archive, each function is a **symbol** — a name a linker can bind a call to. The source decides which names are offered to the outside: ```rux pub func Area(width: int, height: int) -> int { return width * height; } pub func Perimeter(width: int, height: int) -> int { return Double(width + height); } func Double(value: int) -> int { return 2 * value; } ``` `Area` and `Perimeter` are `pub`, so they become **global** symbols that other code can link against. `Double` is package-private, so it becomes a **local** symbol: it is in the archive, because `Perimeter` needs it, but nothing outside can bind to it. A symbol lister shows the difference. With LLVM installed, `llvm-nm` on the Windows archive prints: ```text Main.obj: 00000000 T Area 00000137 t Double 0000008e T Perimeter ``` Capital `T` means a global function, lower-case `t` a local one. The numbers are positions within the compiled code and change as the code does. `Main.obj` is named after the source file, `Src/Main.rux`. ```mermaid flowchart LR src["Src/Main.rux"] -- "rux build" --> lib["StaticLibrary.lib
T Area, T Perimeter
t Double"] other["another program
(C, C++, …)"] --> link["native linker"] lib --> link link --> exe["program.exe
with its own copy of
Area and Perimeter"] ``` ## What a static library is not It is **not a program**. There is no `Main`, so `rux run` refuses to start it. It is also **not what Rux packages consume**. If another Rux package lists this one under `[Dependencies]`, it compiles this package's *source*, exactly as it would a source library, and `pub` works as it always does: `Area` can be imported, `Double` cannot. In Rux 0.4.0 every dependency is taken as source. The archive is for native toolchains, not for other Rux packages. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/StaticLibrary){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `Type = "StaticLibrary"` asks `rux build` for a native archive: the package compiled ahead of // time to machine code and packed into `StaticLibrary.lib` on Windows, or `libStaticLibrary.a` on // Linux, macOS and FreeBSD. A native linker copies what it needs from an archive into the program // it is building, so nothing extra has to ship beside that program. // // The archive holds one symbol per function. `Area` and `Perimeter` are `pub`, so they become // global symbols that other code can link against. `Double` is package-private, so it becomes a // local symbol: it is in the archive, because `Perimeter` calls it, but nothing outside can bind // to it. // // Two things a static library is not: // // - It is not a program. There is no `Main`, and `rux run` refuses with "package 'StaticLibrary' // produces a static library and cannot be run". // - It is not what Rux packages consume. A package that lists this one under `[Dependencies]` // compiles its source, exactly as it would a source library; in Rux 0.4.0 dependencies are // always taken as source. The archive is for native toolchains. pub func Area(width: int, height: int) -> int { return width * height; } pub func Perimeter(width: int, height: int) -> int { return Double(width + height); } func Double(value: int) -> int { return 2 * value; } ``` ## Run it ```sh cd Examples/Packages/StaticLibrary rux build ``` ```text Compiling StaticLibrary v0.1.0 (Debug, Windows x86-64) Built StaticLibrary (Debug, Windows x86-64) in 1 ms Output: Bin\Debug\Windows\x86-64\StaticLibrary.lib 1 file | 28 LOC | 61 tokens | 18.6K LOC/s | StaticLibrary.lib 1 KB ``` Any symbol lister shows what the archive holds; with LLVM installed, `llvm-nm Bin/Debug/Windows/x86-64/StaticLibrary.lib` prints `T` for the global `Area` and `Perimeter` and `t` for the local `Double`. ## Common mistakes ::warning **Running a library.**:br`rux run` fails with `error: package 'StaticLibrary' produces a static library and cannot be run`, noting that "only executable packages have an entry point". Use `rux build`. :: ::warning **Expecting a private function to be linkable.**:br`Double` is in the archive, but only as a local symbol, so a native linker cannot resolve a call to it from outside. Add `pub` to anything other code should be able to call. :: ::warning **Expecting a Rux dependency to use the archive.**:br A Rux package that depends on this one compiles its source instead. Building the archive first changes nothing for it, and `import StaticLibrary::{ Area, Double };` still fails with `error: function 'Double' is private to package 'StaticLibrary'`. :: ## Try it yourself 1. Add a `pub` function `Volume(width: int, height: int, depth: int) -> int`, rebuild, and list the symbols again. Which letter does `Volume` get? 2. Make `Double` public and list the symbols. What changed? 3. Run `rux build --release`. Where does the archive go, and does `llvm-nm` show the same three symbols in it? 4. Write a small executable package beside this one that depends on it with `Path` and prints `Area(3, 4)`. Does its build use `StaticLibrary.lib`? ## Learn more - [Package types](https://rux-lang.dev/docs/packaging/types) — the archive names on each platform - [`rux build`](https://rux-lang.dev/docs/cli/build) in the CLI reference - [Shared library](https://rux-lang.dev/docs/learn/shared-library) — the other native library type, loaded at run time - [Visibility](https://rux-lang.dev/docs/learn/visibility) — the same `pub` that decides global and local symbols here # Shared library ::note **You'll need**: [Static library](https://rux-lang.dev/docs/learn/static-library) :: A [static library](https://rux-lang.dev/docs/learn/static-library) is copied into each program that links it. A **shared library** is not copied anywhere: it ships as a file of its own, and programs load it while they run. Every program that uses it shares the one copy on disk, and the library can be replaced without rebuilding them. The source of this lesson is identical to the static library's; only `Type` differs, and with it what the build produces. ## Asking for a shared library ```toml Type = "SharedLibrary" ``` Each platform has its own name and extension for the file: | Platform | File written by `rux build` | | -------------- | ---------------------------------------------------------------- | | Windows | `SharedLibrary.dll`, plus the import library `SharedLibrary.lib` | | Linux, FreeBSD | `libSharedLibrary.so` | | macOS | `libSharedLibrary.dylib` | On Windows the build writes two files into `Bin/Debug/Windows/x86-64/`. The `.dll` is the library itself. The small `.lib` beside it is an **import library**: a native linker reads it when it builds a program, to learn which functions that program will find in the `.dll` when it starts. ## The export table: what `pub` lets out What other programs can call is the library's **export table**, and `pub` decides what goes in it: ```rux pub func Area(width: int, height: int) -> int { return width * height; } pub func Perimeter(width: int, height: int) -> int { return Double(width + height); } func Double(value: int) -> int { return 2 * value; } ``` `Area` and `Perimeter` are exported under exactly those names. `Double` is compiled into the library, because `Perimeter` calls it, but it is never exported, so no program can look it up. On Windows, `llvm-readobj --coff-exports` (with LLVM installed) or `dumpbin /exports` (from a Visual Studio prompt) lists the table; for this library it names `Area` and `Perimeter`, and nothing else. The same rule covers dependencies. A shared library built on `Io` contains the parts of `Io` it uses, but exports only its own `pub` functions, not `Io`'s. ## Static or shared? | | Static library | Shared library | | ---------------------- | -------------------------- | ------------------------------------------ | | File on Windows | `.lib` | `.dll` and an import `.lib` | | Joined to the program | when the program is linked | when the program starts | | Ships with the program | no — already copied inside | yes — the program needs the file beside it | | Updating the library | rebuild every program | replace one file | | What `pub` controls | global or local symbols | the export table | ```mermaid flowchart LR src["Src/Main.rux"] -- "rux build" --> dll["SharedLibrary.dll
exports Area, Perimeter"] src -- "rux build" --> imp["SharedLibrary.lib
(import library)"] imp -- "read when linking" --> prog["a program"] dll -- "loaded when it runs" --> prog ``` ## Calling it Like a static library, a shared library has no `Main`, so `rux run` refuses it. And a Rux package that depends on it compiles its source instead, as for every dependency in Rux 0.4.0. To call the exports from a separately built program, you declare them with `extern` and name the library with `#Link` — the subject of [Extern](https://rux-lang.dev/docs/learn/extern) in Part 24. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/SharedLibrary){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `Type = "SharedLibrary"` asks `rux build` for a library that programs load while they run: // `SharedLibrary.dll` on Windows, `libSharedLibrary.so` on Linux and FreeBSD, and // `libSharedLibrary.dylib` on macOS. Unlike an archive, it is not copied into the program. It // ships as its own file, and every program that loads it shares the one copy. // // What other programs can call is the library's export table, and `pub` decides what goes in it. // `Area` and `Perimeter` are exported under exactly those names. `Double` is package-private, so // it is compiled into the library but never exported: no program can look it up. The same holds // for dependencies: a library built on `Io` contains the code it uses, but exports only its own // `pub` functions, not `Io`'s. // // On Windows, the build also writes `SharedLibrary.lib` beside the `.dll`. That small file is an // import library, which a native linker reads to know which exports the program will find in the // `.dll` at run time. // // Like a static library, a shared library has no `Main`, so `rux run` refuses it; and a Rux // package that depends on it compiles its source instead, as for every dependency in Rux 0.4.0. // Calling an export from a separately built program is done with `extern` and `#Link`, which the // Platform part covers. pub func Area(width: int, height: int) -> int { return width * height; } pub func Perimeter(width: int, height: int) -> int { return Double(width + height); } func Double(value: int) -> int { return 2 * value; } ``` ## Run it ```sh cd Examples/Packages/SharedLibrary rux build ``` ```text Compiling SharedLibrary v0.1.0 (Debug, Windows x86-64) Built SharedLibrary (Debug, Windows x86-64) in 1 ms Output: Bin\Debug\Windows\x86-64\SharedLibrary.dll 1 file | 30 LOC | 61 tokens | 24.6K LOC/s | SharedLibrary.dll 2 KB ``` `SharedLibrary.lib`, the import library, is written beside the `.dll`. To see the export table, run `dumpbin /exports` from a Visual Studio prompt, or `llvm-readobj --coff-exports` with LLVM installed, on `Bin/Debug/Windows/x86-64/SharedLibrary.dll`. Both list `Area` and `Perimeter`, and not `Double`. ## Common mistakes ::warning **Running a library.**:br`rux run` fails with `error: package 'SharedLibrary' produces a shared library and cannot be run`, and the help line says "build it with 'rux build'". :: ::warning **Forgetting `pub` on a function you mean to export.**:br Nothing warns you: the library builds, and the function is simply missing from the export table. The failure comes later, when a program that expects it is linked or loaded. List the exports after any change to the public surface. :: ::warning **Shipping the program without the library.**:br A program that loads `SharedLibrary.dll` needs that file at run time, where the system can find it — usually beside the program. The import `.lib` is only for linking and does not need to ship. :: ## Try it yourself 1. Add a `pub` function `Volume(width: int, height: int, depth: int) -> int`, rebuild, and list the exports. Is `Volume` there? 2. Remove `pub` from `Perimeter`, rebuild and list the exports again. What happens to `Double`, which only `Perimeter` called? 3. Compare the folders that `rux build` fills in this lesson and in [Static library](https://rux-lang.dev/docs/learn/static-library). Which file appears in both, and does it mean the same thing? ## Learn more - [Package types](https://rux-lang.dev/docs/packaging/types) — shared library names on each platform - [Extern](https://rux-lang.dev/docs/learn/extern) and [C interop](https://rux-lang.dev/docs/learn/c-interop) — calling native libraries from Rux - [Link](https://rux-lang.dev/docs/lang/attributes/link) in the Rux Reference — the `#Link` attribute - [`rux build`](https://rux-lang.dev/docs/cli/build) in the CLI reference # Documentation ::note **You'll need**: [Comment](https://rux-lang.dev/docs/learn/comment), [Visibility](https://rux-lang.dev/docs/learn/visibility), [Method](https://rux-lang.dev/docs/learn/method) :: Ordinary comments are for whoever reads the source. **Documentation comments** are for whoever *uses* the code: tools collect them and turn them into reference pages, the kind you read for `Io` or `Math`. Once your package has `pub` items that other packages call, describing them is part of the job. This lesson writes documentation comments for a small `Rectangle` type and generates its pages with `rux doc`. ## Two spellings, one rule You met documentation comments briefly in [Comment](https://rux-lang.dev/docs/learn/comment). There are two spellings: `///` at the start of each line, or one `/** … */` block. Both attach to the declaration **directly below** them: ```rux /// A rectangle measured in whole units. pub struct Rectangle { /// The horizontal side. pub width: int; /// The vertical side. pub height: int; } ``` The block form suits a longer description: ```rux /** The space the rectangle covers. @returns `width` times `height` */ pub func Area(self: &Rectangle) -> int { return self.width * self.height; } ``` A blank line between the comment and the declaration detaches it. The comment then documents nothing, and the declaration is left undocumented. ## The summary and the tags The **first sentence** is the summary. Indexes show it on its own, so it should make sense without the rest: "The space the rectangle covers." rather than "Returns this.". After the prose come **tags**, which give the generator structure to work with: | Tag | Describes | | ------------- | --------------------------------------------------------------- | | `@param name` | one named parameter | | `@returns` | the result | | `@see` | a related item or a web address | | `@deprecated` | that the item should no longer be used, and what to use instead | `Scaled` documents its parameter and its result: ```rux /// Makes a copy with both sides multiplied. The original is left as it was. /// @param factor what each side is multiplied by /// @returns the scaled copy pub func Scaled(self: &Rectangle, factor: int) -> Rectangle { ``` and `Size`, an older name kept for callers who still use it, says so and points to its replacement: ```rux /// The space the rectangle covers, under its earlier name. /// @returns the same value as `Area` /// @deprecated call `Area`, whose name says what it measures /// @see `Rectangle::Area` pub func Size(self: &Rectangle) -> int { ``` Text inside a documentation comment is Markdown, which is why `` `Area` `` comes out as code in the generated page. ## Generating the pages `rux doc` checks the package as `rux check` does, then writes the pages to `Bin/Docs/`, starting at `index.html`; `rux doc --open` opens them in your browser. Only `pub` items appear, because the pages describe what other packages can use. `Main`, which is not `pub`, is left out. ```mermaid flowchart LR src["/// and /** */ comments
on pub items"] --> lint["rux lint
warns about missing docs
and unknown tags"] src --> doc["rux doc"] doc --> pages["Bin/Docs/index.html
one entry per pub item"] ``` `rux lint` reads the same comments and warns about two kinds of slip: a `pub` struct or function with no documentation at all, and a tag it does not know. Neither stops the program compiling — which is exactly why a separate check is useful. [Tooling](https://rux-lang.dev/docs/learn/tooling) puts `rux lint` and `rux doc` next to the other commands. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/Documentation){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Ordinary comments, like this one, are for whoever reads the source. Documentation comments are // for whoever uses the API: tools collect them and turn them into reference pages. There are two // spellings, `///` on each line or one `/** ... */` block, and both attach to the declaration // directly below them. A blank line in between detaches the comment. // // The first sentence is the summary, shown on its own in indexes, so it should stand alone. Tags // come after the prose and give the generator structure to work with: // // @param name ... one named parameter // @returns ... the result // @see ... a related item or a web address // @deprecated ... what to use instead // // `rux doc` writes the pages to `Bin/Docs/`, starting at `index.html`; `rux doc --open` opens them. // Only `pub` items appear, because the pages describe what other packages can use. // // Tag names are exact. Write `@return` instead of `@returns`, and the program still compiles, but // `rux lint` warns "unknown documentation tag '@return'" and `rux doc` refuses to generate the // page until it is fixed. `rux lint` also warns about any `pub` item left without documentation. import Io::PrintLine; /// A rectangle measured in whole units. pub struct Rectangle { /// The horizontal side. pub width: int; /// The vertical side. pub height: int; } extend Rectangle { /** The space the rectangle covers. @returns `width` times `height` */ pub func Area(self: &Rectangle) -> int { return self.width * self.height; } /// Makes a copy with both sides multiplied. The original is left as it was. /// @param factor what each side is multiplied by /// @returns the scaled copy pub func Scaled(self: &Rectangle, factor: int) -> Rectangle { return Rectangle { width: self.width * factor, height: self.height * factor }; } /// The space the rectangle covers, under its earlier name. /// @returns the same value as `Area` /// @deprecated call `Area`, whose name says what it measures /// @see `Rectangle::Area` pub func Size(self: &Rectangle) -> int { return self.Area(); } } func Main() -> int { let tile = Rectangle { width: 3, height: 2 }; let floor = tile.Scaled(4); PrintLine("tile {} x {} covers {}", tile.width, tile.height, tile.Area()); PrintLine("floor {} x {} covers {}", floor.width, floor.height, floor.Area()); return 0; } ``` ## Run it ```sh cd Examples/Packages/Documentation rux run ``` ```text tile 3 x 2 covers 6 floor 12 x 8 covers 96 ``` Then generate the pages, which land in `Bin/Docs/index.html`: ```sh rux doc --open ``` ## Common mistakes ::warning **A misspelt tag.**:br Tag names are exact. Write `@return` instead of `@returns` and the program still compiles, but `rux lint` warns `unknown documentation tag '@return'`, and `rux doc` refuses with `error: invalid documentation: unknown documentation tag '@return'` until you fix it. :: ::warning **A blank line under the comment.**:br Leave an empty line between `/// A rectangle measured in whole units.` and the struct, and `rux lint` reports two warnings: `documentation comment is not attached to an item`, and `public struct 'Rectangle' has no documentation comment`. :: ::warning **A public function with no documentation.**:br It compiles, and the pages list it with nothing to say. Add a bare `pub func Half(value: int) -> int` to this program and `rux lint` catches it: `warning: public function 'Half' has no documentation comment`, and its help line suggests a comment above the declaration. :: ::warning **Documenting private items and expecting them in the pages.**:br`rux doc` writes pages for `pub` items only. Its `--document-private-items` option includes the rest, which can help while you work on the package itself. :: ## Try it yourself 1. Run `rux doc --open` and find `Size` in the pages. How is its `@deprecated` tag shown? 2. Add a documented `pub` method `Perimeter` with a `@returns` tag, and regenerate the pages. 3. Misspell `@param` as `@parameter` and run `rux lint`, then `rux doc`. What does each say? 4. Run `rux doc --document-private-items`. What appears that was missing before? ## Learn more - [Comments](https://rux-lang.dev/docs/lang/lexical/comments) in the Rux Reference - [`rux doc`](https://rux-lang.dev/docs/cli/doc) and [`rux lint`](https://rux-lang.dev/docs/cli/lint) in the CLI reference - [Visibility](https://rux-lang.dev/docs/learn/visibility) — the `pub` that decides what is documented - [Tooling](https://rux-lang.dev/docs/learn/tooling) — the other commands that keep a package tidy # Tooling ::note **You'll need**: [Source library](https://rux-lang.dev/docs/learn/source-library), [Documentation](https://rux-lang.dev/docs/learn/documentation), [Range](https://rux-lang.dev/docs/learn/range) :: So far you have used three `rux` commands: `run`, `check` and `build`. A few more keep a package tidy once it is more than a single file — they format it, lint it, test it and document it. None of them changes what the program does; together they catch the mistakes the compiler lets through. This lesson has a small `Calendar` library, a program that uses it, and a test for it, so that every command has something to work on. ## The pieces of this lesson ```text Tooling/ ├── Rux.toml the program; depends on Calendar ├── Src/Main.rux ├── Calendar/ the code under test, a source library │ ├── Rux.toml │ └── Src/Calendar.rux └── Tests/ └── LeapYear/ one test, an executable package ├── Rux.toml └── Src/Main.rux ``` `Calendar` lives in its own library so that both the program and the test can depend on it. The program prints the length of February for five years: ```rux for year in 1900..=1904 { PrintLine("February {} has {} days", year, DaysInMonth(year, 2)); } ``` ## The commands All of them run from the package folder, `Tooling/`: | Command | What it does | | ----------------- | ------------------------------------------------------------------- | | `rux fmt` | rewrites the sources and `Rux.toml` in the standard layout | | `rux fmt --check` | changes nothing; lists the files `rux fmt` would rewrite, and fails | | `rux lint` | warns about what compiles but is still wrong, such as missing docs | | `rux test` | builds and runs every executable package below `Tests/` | | `rux doc` | writes reference pages for the `pub` items to `Bin/Docs/` | ```mermaid flowchart LR edit["edit the code"] --> fmt["rux fmt"] fmt --> lint["rux lint"] lint --> test["rux test"] test --> doc["rux doc"] test -- "a test fails" --> edit ``` ## Formatting `rux fmt` puts every file in the one standard layout, so a diff shows real changes and not someone's spacing habits. For `Rux.toml` that means the canonical order and spelling — sections in a fixed order, `Description` before `Authors`, spaces around every `=`. For source files in Rux 0.4.0 it is line-level clean-up: trailing spaces go and line endings are made consistent, while indentation is left as you wrote it. `--check` turns formatting into a pass-or-fail question. It touches nothing, names each file that `rux fmt` would rewrite, and exits with status 1 if there is one. That is the form to use in a script or in continuous integration, where nobody wants the build to rewrite their files. ## Testing A test in Rux is an ordinary executable package below `Tests/`. `rux test` builds and runs each one, and a test **passes when its `Main` returns 0**. This lesson's test checks the leap-year rules with `Assert` from `Core`: ```rux Assert(IsLeapYear(2024), "2024 is a leap year"); Assert(!IsLeapYear(2023), "2023 is not a leap year"); // A century is not a leap year, unless it divides by 400. Assert(!IsLeapYear(1900), "1900 is not a leap year"); Assert(IsLeapYear(2000), "2000 is a leap year"); ``` When an `Assert` condition is false, it prints its message and ends the program with a failing status, so a failed test says which check it was. Because a test is a package, it has its own `Rux.toml`, which depends on the code under test by path and on `Core` from the registry: ```toml [Dependencies] Calendar = { Path = "../../Calendar" } Core = { Namespace = "Rux", Version = "*" } ``` Add more folders under `Tests/` and `rux test` finds them by itself; there is no list to keep. If you break the rules in `Calendar`, the report shows which test failed and why — here, after an assertion that claims 2100 is a leap year (the timings and the exit code vary by machine): ```text Testing Tooling v0.1.0 (Debug, Windows x86-64) Running 1 test Failed LeapYear in 692 ms note: test 'LeapYear' exited with code -1073741795 Output: Assertion failed: 2100 is a leap year at Main (Src/Main.rux:16:5) Failed 1 test in 692 ms (0 passed, 1 failed) ``` ## Linting and documenting `rux lint` and `rux doc` work as in [Documentation](https://rux-lang.dev/docs/learn/documentation). `Calendar`'s functions are documented with `@param` and `@returns`, so its pages have something to show. ## What this course never runs `rux pack` and `rux publish` also exist, for sharing a source library through the package registry. They need an account and change something public, so this course stops short of them. The [publishing guide](https://rux-lang.dev/docs/packaging/publishing) covers them when you are ready. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Packages/Tooling){rel=""nofollow""}. Its comments explain every step. ::code-tree{default-value="Src/Main.rux"} ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Tooling" Version = "0.1.0" Type = "Executable" Description = "Formatting, linting, testing and documenting a package with the rux tools" Authors = ["Rux Contributors "] [Dependencies] Calendar = { Path = "Calendar" } Io = { Namespace = "Rux", Version = "*" } ``` ```toml [Calendar/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Calendar" Version = "0.1.0" Type = "SourceLibrary" Description = "Leap years and month lengths, the code under test in the Tooling lesson" Authors = ["Rux Contributors "] ``` ```rux [Src/Main.rux] // `rux run` and `rux check` are not the only commands. A few more keep a package tidy, and all of // them are run from the package directory: // // rux fmt rewrite every source file and the manifest in the standard layout // rux fmt --check change nothing; list the files that `rux fmt` would rewrite, and fail // rux lint warn about what compiles but is still wrong, such as a `pub` item with no // documentation or a misspelled documentation tag // rux test build and run every executable package below `Tests/` // rux doc generate reference pages for the `pub` items, in `Bin/Docs/` // // `--check` exists for scripts and continuous integration: it makes formatting a pass-or-fail // question without touching anyone's files. // // This lesson has one test, in `Tests/LeapYear/`, for the small `Calendar` library beside `Src/`. // The program below uses the same library, so a passing test says something about the program. // // `rux pack` and `rux publish` also exist, for sharing a source library through the registry. They // need an account and change something public, so this course never runs them. import Calendar::DaysInMonth; import Io::PrintLine; func Main() -> int { for year in 1900..=1904 { PrintLine("February {} has {} days", year, DaysInMonth(year, 2)); } return 0; } ``` ```toml [Tests/LeapYear/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "LeapYear" Version = "0.1.0" Type = "Executable" Description = "Tests for the leap year rules in Calendar" Authors = ["Rux Contributors "] [Dependencies] Calendar = { Path = "../../Calendar" } Core = { Namespace = "Rux", Version = "*" } ``` ```rux [Calendar/Src/Calendar.rux] // The code under test. It lives in its own small library so that both the program and the test // package can depend on it. /// Whether `year` has a 29 February in the Gregorian calendar. /// @param year the year, counted in the usual way /// @returns true for every fourth year, except centuries not divisible by 400 pub func IsLeapYear(year: int) -> bool { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } /// How many days `month` has in `year`. /// @param year the year, which matters only for February /// @param month the month, from 1 for January to 12 for December /// @returns the number of days, from 28 to 31 pub func DaysInMonth(year: int, month: int) -> int { if month == 2 { return IsLeapYear(year) ? 29 : 28; } if month == 4 || month == 6 || month == 9 || month == 11 { return 30; } return 31; } ``` ```rux [Tests/LeapYear/Src/Main.rux] // A test is an executable package below `Tests/`. `rux test` builds and runs each one, and a test // passes when its `Main` returns 0. // // `Assert` from `Core` does the checking. When its condition is false it prints its message and // ends the program with a failing status, so a failed test says which check it was. A test is an // ordinary package, so it depends on the code under test by path and on `Core` from the registry. import Calendar::{ DaysInMonth, IsLeapYear }; import Core::Assert; func Main() -> int { Assert(IsLeapYear(2024), "2024 is a leap year"); Assert(!IsLeapYear(2023), "2023 is not a leap year"); // A century is not a leap year, unless it divides by 400. Assert(!IsLeapYear(1900), "1900 is not a leap year"); Assert(IsLeapYear(2000), "2000 is a leap year"); Assert(DaysInMonth(2024, 2) == 29, "February 2024 has 29 days"); Assert(DaysInMonth(2023, 2) == 28, "February 2023 has 28 days"); Assert(DaysInMonth(2023, 4) == 30, "April has 30 days"); return 0; } ``` :: ## Run it ```sh cd Examples/Packages/Tooling rux run ``` ```text February 1900 has 28 days February 1901 has 28 days February 1902 has 28 days February 1903 has 28 days February 1904 has 29 days ``` The other commands, all run from this directory: | Command | What it does | | ----------------- | ------------------------------------------------------------------ | | `rux fmt` | Rewrites the sources and `Rux.toml` in the standard layout | | `rux fmt --check` | Changes nothing; fails if `rux fmt` would rewrite a file | | `rux lint` | Warns about what compiles but is still wrong, such as missing docs | | `rux test` | Builds and runs each package below `Tests/`; status 0 passes | | `rux doc` | Writes reference pages for the `pub` items to `Bin/Docs/` | `rux test` prints one line per test; the timings vary: ```text Testing Tooling v0.1.0 (Debug, Windows x86-64) Running 1 test Passed LeapYear in 427 ms Passed 1 test in 427 ms (1 passed, 0 failed) ``` ## Common mistakes ::warning **A test that cannot fail.**:br A test passes when `Main` returns 0, and nothing else is looked at. A test that prints "wrong!" and still returns 0 passes. Check with `Assert`, which ends the program with a failing status. :: ::warning **A test without its dependencies.**:br A test is a package like any other. Remove the `Core` line from `Tests/LeapYear/Rux.toml` and `rux test` reports `Failed LeapYear`, with the note "the test package did not compile" and the familiar `package 'Core' is not listed in [Dependencies]`. :: ::warning **Expecting `rux fmt --check` to fix anything.**:br It only reports, for example `error: source file '…\Src\Main.rux' is not formatted`, and exits with status 1. Run `rux fmt` to rewrite the files. :: ## Try it yourself 1. Break `IsLeapYear` in `Calendar/Src/Calendar.rux` by removing the `year % 400 == 0` part, and run `rux test`. Which assertion fails? Then run `rux run` — does the program's output show the bug? 2. Add a second test, `Tests/MonthLength/`, that checks `DaysInMonth` for January, April and December. Run `rux test` and count the tests. 3. Add a few spaces to the end of a line in `Src/Main.rux`. Run `rux fmt --check`, then `echo $?`, then `rux fmt`, then `rux fmt --check` again. 4. Run `rux doc --open` in `Calendar/`. Does a source library get pages even though it cannot be built? ## Learn more - [`rux fmt`](https://rux-lang.dev/docs/cli/fmt), [`rux lint`](https://rux-lang.dev/docs/cli/lint), [`rux test`](https://rux-lang.dev/docs/cli/test) and [`rux doc`](https://rux-lang.dev/docs/cli/doc) in the CLI reference - [Assert](https://rux-lang.dev/docs/learn/assert) — the check every test is made of - [Publishing](https://rux-lang.dev/docs/packaging/publishing) — `rux pack` and `rux publish`, for when you share a package - [Source library](https://rux-lang.dev/docs/learn/source-library) — the kind of package `Calendar` is # Part 23: Compile time Everything so far has happened while the program runs: an `if` tests a value, a loop counts, a function is called. But some questions are settled before the program exists at all — which compiler is building it, which operating system and processor it is for, whether it is a debug or a release build, which switches you passed on the command line. This part is about asking those questions in the source and letting the answers decide which code is compiled in. By the end you can write one program that builds differently for Windows and Linux, for debug and release, and refuses — with a message of your choosing — to build where it cannot work. ## What you will learn - Choosing code while compiling with `when` and `else when`, and why the branches it skips may name things that do not exist. - Branching on the machine being built for with `#target.os` and `#target.arch`, and building for another one with `--target`. - Telling a debug build from a release build with `#build.mode`, and checks that vanish from a release build. - Reading the file, line and function of an expression with `#source`, and which place it really describes. - Stopping or warning a build from the source with `#Error` and `#Warn`, and silencing one lint rule with `#Allow`. - Handing the build values of your own with `--define` and `[Build.Defines]`, and reading them with `#config`. - How `int8`, `#target` and `Assert` reach a program through `intrinsic` declarations. ## How `when` shapes a program Most lessons in this part feed the same machine. The compiler knows some facts before it starts; `when` turns them into a choice; only the chosen code is compiled: ```mermaid flowchart LR subgraph known ["Known while compiling"] cv["#compiler
version · 23.1"] tg["#target
os, arch · 23.2"] bd["#build
mode · 23.3"] cf["#config
your defines · 23.6"] end known --> w{"when"} w -- "the taken branch" --> keep["resolved, type-checked
and compiled in"] w -- "the other branches" --> drop["discarded unread"] w -- "a branch holding #Error · 23.5" --> stop(["the build stops
with your message"]) ``` `#source` (23.4) is known while compiling too, but it describes a place in the code rather than the build, so it is read as a value rather than branched on. All of them come from `Core` as `intrinsic` declarations — [Intrinsic](https://rux-lang.dev/docs/learn/intrinsic) (23.7) shows what that means by declaring a few of them in a package of its own. | Ask about… | Read | Lesson | | ---------------------------- | ----------- | ------------------------------------------------------------------ | | the compiler doing the build | `#compiler` | [When](https://rux-lang.dev/docs/learn/when) | | the machine being built for | `#target` | [Target](https://rux-lang.dev/docs/learn/target) | | debug or release | `#build` | [Build mode](https://rux-lang.dev/docs/learn/build-mode) | | where this expression is | `#source` | [Source location](https://rux-lang.dev/docs/learn/source-location) | | a value you passed in | `#config` | [Define](https://rux-lang.dev/docs/learn/define) | ## Lessons | | Lesson | What you will learn | | ---- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | | 23.1 | [When](https://rux-lang.dev/docs/learn/when) | select code at compile time with `when` | | 23.2 | [Target](https://rux-lang.dev/docs/learn/target) | the operating system and architecture being compiled for | | 23.3 | [Build mode](https://rux-lang.dev/docs/learn/build-mode) | tell debug builds from release builds | | 23.4 | [Source location](https://rux-lang.dev/docs/learn/source-location) | the file and line of an expression | | 23.5 | [Compile error](https://rux-lang.dev/docs/learn/compile-error) | stop the build with `#Error`, or warn with `#Warn` | | 23.6 | [Define](https://rux-lang.dev/docs/learn/define) | values passed to the build from the manifest or the command line | | 23.7 | [Intrinsic](https://rux-lang.dev/docs/learn/intrinsic) | declarations the compiler implements itself | ## Before you start The early lessons need little beyond Parts 1–4 — [Const](https://rux-lang.dev/docs/learn/const), [If](https://rux-lang.dev/docs/learn/if), [Match](https://rux-lang.dev/docs/learn/match) and [Function](https://rux-lang.dev/docs/learn/function) — plus [Enum](https://rux-lang.dev/docs/learn/enum) from Part 6 and [Assert](https://rux-lang.dev/docs/learn/assert) from Part 9. The last two lean on [Part 22: Packages](https://rux-lang.dev/docs/learn/packages): [Compile error](https://rux-lang.dev/docs/learn/compile-error) uses `rux lint` from [Tooling](https://rux-lang.dev/docs/learn/tooling), and [Intrinsic](https://rux-lang.dev/docs/learn/intrinsic) is a two-package lesson built on [Dependency](https://rux-lang.dev/docs/learn/dependency). Each lesson's package is in the Examples repository's `CompileTime/` folder: ```sh cd Examples/CompileTime/When rux run ``` Several lessons are worth running more than once — with `--release`, with `--define`, or built for another system with `--target` — and each says which. ## After this part [Part 24: Platform](https://rux-lang.dev/docs/learn/platform) puts `when #target` to work: calling the operating system's own libraries with `extern`, talking to C, choosing calling conventions and writing assembly, each guarded so that a build for the wrong system stops with a clear `#Error`. Its checkpoint project, [Melody](https://rux-lang.dev/docs/learn/melody), plays a tune through the Windows console speaker. For the full rules behind this part, see [Compile-time programming](https://rux-lang.dev/docs/lang/comptime/overview), [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional), [Build context](https://rux-lang.dev/docs/lang/comptime/context) and [Attributes](https://rux-lang.dev/docs/lang/attributes/overview) in the Rux Reference. # When ::note **You'll need**: [Const](https://rux-lang.dev/docs/learn/const), [If](https://rux-lang.dev/docs/learn/if), [Function](https://rux-lang.dev/docs/learn/function) :: Every `if` you have written so far decides while the program runs. Both of its branches are compiled, because until the program runs nobody knows which one will be needed. Some questions, though, are answered before the program even exists: which compiler is building it, which operating system it is for, whether this is a debug or a release build. For those, Rux has `when`, the compile-time `if`. Its condition must be something the compiler already knows, and only the branch it picks becomes part of the program. That one difference has a big consequence. The branches `when` skips are never resolved or type-checked, so they may name functions that do not exist. That is exactly what code for a newer compiler, or for another platform, looks like from where you are building — and it is what makes one source file able to serve them all. ## `if` and `when` side by side | | `if` | `when` | | ------------------- | -------------------------------------- | --------------------------------------- | | Decided | while the program runs | while the program is compiled | | Condition | any `bool` | a value the compiler knows | | Untaken branch | compiled and type-checked all the same | dropped before it is resolved | | Next test in chain | `else if` | `else when` | | Scope of a branch | its own: bindings end with the branch | none: bindings stay after it | | Where it can appear | inside a function body | inside a body, and between declarations | The values a `when` can read come from the `Core` package. This lesson uses `#compiler.version`, the version of the compiler doing the build; [Target](https://rux-lang.dev/docs/learn/target), [Build mode](https://rux-lang.dev/docs/learn/build-mode) and [Define](https://rux-lang.dev/docs/learn/define) add the operating system, the build mode and values of your own. ## Constants the compiler can compare `#compiler.version` is a `SemanticVersion`: three numbers, `major`, `minor` and `patch`, that compare the way version numbers should. To compare it against a version of your own, declare that version as a constant: ```rux const Rux040 = SemanticVersion { major: 0, minor: 4, patch: 0 }; const Rux100 = SemanticVersion { major: 1, minor: 0, patch: 0 }; ``` Notice the struct literal. `SemanticVersion(1, 0, 0)` is a call to a constructor, and a call only runs when the program does — far too late for a `when`, which has to be decided before there is a program to run. The compiler refuses it, and its help line says what a constant may be built from: literals, operators, casts and other constants. ## Choosing declarations Between declarations, `when` decides which declarations exist at all. Each branch here declares a function called `Channel`, so the rest of the program calls `Channel()` without knowing — or caring — which one it got: ```rux when #compiler.version >= Rux100 { func Channel() -> char8[..] { return "stable"; } } else when #compiler.version >= Rux040 { func Channel() -> char8[..] { return "preview"; } } else { // Never resolved by this compiler, so naming a function nobody wrote is not an error. func Channel() -> char8[..] { return LegacyChannel(); } } ``` A chain keeps the keyword it opened with: after `when` comes `else when`, never `else if`. That keeps it obvious which tests the compiler answers and which the running program answers. The compiler walks the chain top to bottom, keeps the first branch whose condition holds, and throws the others away unread: ```mermaid flowchart LR w{"when, while compiling:
which compiler is this?"} -- "1.0.0 or newer" --> s["Channel returns stable"] w -- "0.4.0 or newer
(Rux 0.4.0 stops here)" --> p["Channel returns preview"] w -- "anything older" --> l["Channel calls
LegacyChannel()"] p --> kept(["kept: resolved, type-checked
and compiled into the program"]) s -.-> gone(["discarded unread: a name
nobody wrote is no error here"]) l -.-> gone ``` `LegacyChannel` is never written anywhere. With Rux 0.4.0 the last branch is discarded, so nothing ever goes looking for it. ## Choosing statements Inside a function body, `when` decides which statements exist. It opens no scope of its own, so a binding made in the taken branch is still there after it: ```rux when #compiler.version.minor >= 4 { let greeting = "when picked this branch while compiling"; } else { let greeting = NotWrittenYet(); } PrintLine("{}", greeting); ``` After compiling, it is as if the `when` and the `else` branch had never been written and the first `let` stood on its own. The same code with `if` fails twice over: `NotWrittenYet` does not exist, and `greeting` would end with the branch that made it. The version itself is printed with ordinary field reads. They cost nothing at run time — the compiler folds them into the program as plain numbers. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/CompileTime/When){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `if` chooses while the program runs, so both of its branches have to compile. `when` chooses // while the program is being compiled: its condition must be something the compiler already // knows, and only the branch it picks becomes part of the program. // // The branches `when` skips are never resolved or type-checked. They may call functions that do // not exist, which is exactly what code for a newer compiler, or for another platform, looks like // from where you are building. Written with `if`, the same code would not compile. // // A chain keeps the keyword it opened with: after `when`, the next test is `else when`, and // writing `else if` there is an error. import Core::{ #compiler, SemanticVersion }; import Io::PrintLine; // A constant is written as a struct literal: a call such as `SemanticVersion(1, 0, 0)` would only // run when the program does, and is "not a compile-time value". const Rux040 = SemanticVersion { major: 0, minor: 4, patch: 0 }; const Rux100 = SemanticVersion { major: 1, minor: 0, patch: 0 }; // Between declarations, `when` decides which declarations exist. Each branch declares the same // function, so the rest of the program calls `Channel` without knowing which one it got. when #compiler.version >= Rux100 { func Channel() -> char8[..] { return "stable"; } } else when #compiler.version >= Rux040 { func Channel() -> char8[..] { return "preview"; } } else { // Never resolved by this compiler, so naming a function nobody wrote is not an error. func Channel() -> char8[..] { return LegacyChannel(); } } func Main() -> int { // The version is a compile-time constant, folded into the binary as plain numbers. PrintLine("Compiled with Rux {}.{}.{}", #compiler.version.major, #compiler.version.minor, #compiler.version.patch); PrintLine("Release channel: {}", Channel()); // Inside a body, `when` decides which statements exist. It opens no scope of its own, so a // binding made in the taken branch is still there after it. when #compiler.version.minor >= 4 { let greeting = "when picked this branch while compiling"; } else { let greeting = NotWrittenYet(); } PrintLine("{}", greeting); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/CompileTime/When rux run ``` ```text Compiled with Rux 0.4.0 Release channel: preview when picked this branch while compiling ``` The version numbers are those of the compiler that built the program. ## Common mistakes ::warning **Writing `else if` after `when`.**:br A chain cannot switch from compile time to run time halfway through. `} else if …` after a `when` fails with `error: expected 'when' after 'else' in a compile-time 'when' chain; 'if' is the run-time conditional`. :: ::warning **A condition only the running program knows.**:br`let minor = 4;` followed by `when minor >= 4` fails with `error: 'minor' is not a compile-time constant`. A `let` gets its value when the program runs; a `when` has to be settled before that. Use a `const`, or a value from `Core` such as `#compiler`. :: ::warning **Building a constant with a call.**:br`const Rux100 = SemanticVersion(1, 0, 0);` fails with `error: call to 'SemanticVersion' is not a compile-time value`. Write the struct literal instead, as the lesson does. :: ::warning **Using `if` where only one branch can compile.**:br An `if` compiles both branches, so a branch that names something missing breaks the build: `error: name 'NotWrittenYet' is not defined in this scope`. When a branch only makes sense for some builds, it needs `when`. :: ## Try it yourself 1. Change `Rux040` to version 0.5.0 and predict what happens before you build. (The `else` branch is now the one that is kept, so `LegacyChannel` finally has to exist.) 2. After the `PrintLine` of `greeting`, add a `when` that prints "a patch release" when `#compiler.version.patch` is above zero, and "the first release of 0.4" otherwise, with the numbers read from `#compiler.version`. 3. Change the statement-level `when` to `if`, keep the `else` as it is, and read both errors. ## Learn more - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) and [Build context](https://rux-lang.dev/docs/lang/comptime/context) in the Rux Reference - [Compile-time programming](https://rux-lang.dev/docs/lang/comptime/overview) — how `when`, the build context and intrinsics fit together - [`#compiler`](https://rux-lang.dev/docs/api/core/compiler) in the Core API reference - [If](https://rux-lang.dev/docs/learn/if) — the run-time conditional `when` mirrors - [Target](https://rux-lang.dev/docs/learn/target) — the next lesson, which branches on the machine being built for # Target ::note **You'll need**: [When](https://rux-lang.dev/docs/learn/when), [Enum](https://rux-lang.dev/docs/learn/enum), [Match](https://rux-lang.dev/docs/learn/match) :: Every build is for one machine: an operating system and a processor architecture. A path is written `Users\Ada` on Windows and `/home/ada` on Linux; a function from `Kernel32.dll` exists on one system and not the others; assembly for an Intel processor means nothing to an ARM one. Code that has to be different from machine to machine needs a way to ask which machine it is being built for. `#target` answers that. It describes the machine as ordinary values — an enum for the system, another for the processor, a few numbers and a name — and the compiler fills them in. Because they are known while compiling, [`when`](https://rux-lang.dev/docs/learn/when) can branch on them and keep only the code that fits. ## What `#target` holds `#target` comes from `Core`, like `#compiler` did. The fields you will reach for most: | Field | Type | On this Windows PC | Other values | | ------------- | ----------------- | ------------------ | ------------------------------ | | `os` | `OperatingSystem` | `.Windows` | `.Linux`, `.macOS`, `.FreeBSD` | | `arch` | `Architecture` | `.X86_64` | `.AArch64` | | `pointerBits` | `uint` | `64` | `64` on every supported target | | `triple` | `char8[..]` | `"windows-x86_64"` | `"linux-aarch64"`, … | | `dataModel` | `DataModel` | `.LLP64` | `.LP64` on every Unix | There are also `abi`, `endian` and `objectFormat`, which matter once you start talking to code written in other languages in [Part 24](https://rux-lang.dev/docs/learn/platform). Both enums have an `.Unknown` variant too, which no supported build produces. ## The match form To pick one of several values, `when` has a match form. It looks at one value and keeps the arm whose variant matches: ```rux when #target.os { .Windows => const SystemName: char8[..] = "Windows"; .Linux => const SystemName: char8[..] = "Linux"; .macOS => const SystemName: char8[..] = "macOS"; .FreeBSD => const SystemName: char8[..] = "FreeBSD"; else => const SystemName: char8[..] = "an unknown system"; } ``` This sits between declarations, so it decides which declaration exists. Each arm declares the same constant, `SystemName`, and the rest of the program uses it without caring which arm produced it. It reads like a [`match`](https://rux-lang.dev/docs/learn/match), and the enum shorthand `.Windows` works the same way — but only the kept arm is ever compiled. The `else` arm is worth keeping even though the four arms cover every system Rux builds for today. Without it, a build for a system no arm names stops with `no arm of this 'when' matches …`, which is a fine outcome for code that really cannot run there, and a poor one for code that simply forgot. ## The ordinary form For a yes-or-no question about the target, the ordinary form reads better: ```rux when #target.os == OperatingSystem::Windows { const PathSeparator: char8[..] = "\\"; } else { const PathSeparator: char8[..] = "/"; } ``` Enum-valued fields such as `os` and `arch` compare only for equality. "Is the system less than Windows?" has no meaning, and the compiler rejects it. ## The target is not the machine you are on The target is the machine the program will **run** on, not the one compiling it. Ask for another with `--target`, using the same names `#target.triple` prints: ```sh rux build --target linux-x86_64 ``` The compiler is still running on Windows, yet every branch above is chosen again for Linux: `SystemName` becomes `"Linux"` and `PathSeparator` becomes `"/"`. The result lands in `Bin/Debug/Linux/x86-64/` — a Linux program, which you copy to a Linux machine to run. This is why platform code branches on `#target` and never on anything it could only discover by looking around at run time: by then, it is too late to change what was compiled. ```mermaid flowchart LR host["The machine compiling
(here: Windows x86-64)"] --> rux["rux build --target …"] rux -- "no --target" --> w["target os is .Windows
→ a Windows program"] rux -- "--target linux-x86_64" --> l["target os is .Linux
→ a Linux program"] rux -- "--target macos-aarch64" --> m["target arch is .AArch64
→ a program for an Apple silicon Mac"] ``` ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/CompileTime/Target){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Every build is for one machine: an operating system and a processor architecture. `#target` // describes that machine as ordinary values, filled in by the compiler, so `when` can branch on // them and keep only the code that fits. // // The target is the machine the program will run on, not the one compiling it. Ask for another // with `rux build --target linux-x86_64`, and every branch below is chosen again for Linux, even // though the compiler is still running on Windows. That is why platform code branches on // `#target` and never on anything it could discover at run time. import Core::{ #target, OperatingSystem }; import Io::PrintLine; // The match form of `when` picks one arm by value. Each arm declares the same constant, so the // rest of the program uses `SystemName` without caring which arm produced it. when #target.os { .Windows => const SystemName: char8[..] = "Windows"; .Linux => const SystemName: char8[..] = "Linux"; .macOS => const SystemName: char8[..] = "macOS"; .FreeBSD => const SystemName: char8[..] = "FreeBSD"; else => const SystemName: char8[..] = "an unknown system"; } when #target.arch { .X86_64 => const ProcessorName: char8[..] = "x86-64"; .AArch64 => const ProcessorName: char8[..] = "AArch64"; else => const ProcessorName: char8[..] = "an unknown processor"; } // The ordinary form works too, for a yes-or-no question about the target. when #target.os == OperatingSystem::Windows { const PathSeparator: char8[..] = "\\"; } else { const PathSeparator: char8[..] = "/"; } func Main() -> int { PrintLine("Built for {} on {}", SystemName, ProcessorName); PrintLine("Target name: {}", #target.triple); PrintLine("A path here looks like Users{}Ada{}Notes.txt", PathSeparator, PathSeparator); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/CompileTime/Target rux run ``` ```text Built for Windows on x86-64 Target name: windows-x86_64 A path here looks like Users\Ada\Notes.txt ``` This is the output of a Windows x86-64 build; other targets print their own names and separator. ## Common mistakes ::warning **Leaving out the `else` arm.**:br A match-form `when` with no arm for the system being built stops the build. Remove the `.FreeBSD` and `else` arms, run `rux check --target freebsd-x86_64`, and it fails with `error: no arm of this 'when' matches .FreeBSD`. :: ::warning **Spelling a variant the old way.**:br The variant is `.macOS`, with a lower-case m. `.MacOS` fails with `error: '.MacOS' is not a variant of 'OperatingSystem'` — and the message goes on to list the variants that do exist. :: ::warning **Ordering an enum.**:br`when #target.os < .Windows` fails with `error: 'when' condition is not a valid compile-time expression`. Systems and architectures compare only with `==` and `!=`. :: ## Try it yourself 1. Run `rux build --target linux-x86_64`, then `rux build --target macos-aarch64`, and look at the folders that appear under `Bin/Debug/`. 2. Remove the `.FreeBSD` and `else` arms from the first `when`. Check the program for each of `windows-x86_64`, `linux-x86_64`, `macos-aarch64` and `freebsd-x86_64` with `rux check --target …`. Which ones still build? 3. Add a `when #target.dataModel == .LLP64` that prints "C's long is 32 bits here", with an `else` that prints "64 bits". What do you expect for Windows, and for Linux? 4. Print `#target.pointerBits` on its own line. ## Learn more - [Build context](https://rux-lang.dev/docs/lang/comptime/context) in the Rux Reference — every field of `#target` - [`#target`](https://rux-lang.dev/docs/api/core/target) in the Core API reference - [`rux build`](https://rux-lang.dev/docs/cli/build) — `--target` and the other build options - [Enum](https://rux-lang.dev/docs/learn/enum) and [Match](https://rux-lang.dev/docs/learn/match) — the run-time forms of what this lesson does at compile time - [Extern](https://rux-lang.dev/docs/learn/extern) — calling a library that only one system has # Build mode ::note **You'll need**: [When](https://rux-lang.dev/docs/learn/when), [Assert](https://rux-lang.dev/docs/learn/assert) :: The same source can be built two ways. While you are writing a program you want it to compile quickly and to catch every mistake as early as possible. When you hand it to someone, you want it small and fast. Rux calls these two a **debug build** and a **release build**, and `#build` lets the program ask which one it is part of. The [Assert](https://rux-lang.dev/docs/learn/assert) lesson already met the difference from the outside: `DebugAssert` is checked in one and gone from the other. This lesson looks at it from the inside. ## Debug and release `rux run` and `rux build` make a debug build unless you say otherwise. Add `--release` for the other kind: ```sh rux run # debug rux run --release # release ``` | | Debug | Release | | ----------------- | -------------------------------- | -------------------------- | | Optimized | no — the code follows the source | yes | | Debug information | emitted | left out | | `DebugAssert` | checked | removed | | Output folder | `Bin/Debug///` | `Bin/Release///` | | Good for | writing and testing | shipping | ## Asking which build this is `#build` comes from `Core`, like `#target`, and is just as free to read: every field is fixed before compiling starts and folds into the program as a constant. ```rux PrintLine("Profile: {}", #build.profile); when #build.mode == BuildMode::Debug { PrintLine("Debug build: unoptimized, with every check switched on"); } else { PrintLine("Release build: optimized, with the debug checks compiled out"); } ``` `profile` is a name — `Debug` or `Release` here, but a project may name its own profiles, so the name is not the thing to branch on. `mode` is the part every profile has: always `BuildMode::Debug` or `BuildMode::Release`. Because `mode` is known while compiling, the `when` keeps only one of the two `PrintLine` calls; the release program does not contain the debug message at all. `#build` has a few more fields worth knowing: | Field | Type | Holds | | ----------------- | ----------- | ------------------------------------------------------ | | `profile` | `char8[..]` | the profile's name | | `mode` | `BuildMode` | `.Debug` or `.Release` | | `debugAssertions` | `bool` | whether `DebugAssert` checks are kept | | `debugInfo` | `bool` | whether debug information is emitted | | `isTest` | `bool` | whether this is a `rux test` build | | `date`, `time` | `char8[..]` | when the build started, as `YYYY-MM-DD` and `HH:MM:SS` | The program prints `#build.debugAssertions` because it answers the narrower question the rest of the program cares about: will the next line be checked? ## A check that costs nothing once you ship `DebugAssert` takes a condition and a message, just like `Assert`. In a debug build it behaves exactly like `Assert`. In a release build it is removed entirely — and its arguments are **not even evaluated**: ```rux // Stands in for a slow consistency check. The line it prints shows whether it ran at all. func ScoresAreSorted() -> bool { PrintLine(" ...checking that the scores are sorted"); return true; } ``` ```rux DebugAssert(ScoresAreSorted(), "the scores must be sorted"); ``` Compare the two outputs below: the line `...checking that the scores are sorted` appears only in the debug run. In the release program `ScoresAreSorted` is never called, so an expensive check costs exactly nothing. ```mermaid flowchart LR d["DebugAssert(condition, message)"] --> m{"Which build?"} m -- "debug" --> ev["condition is evaluated"] ev -- "true" --> on["the program carries on"] ev -- "false" --> stop["Assertion failed: message
the program stops"] m -- "release" --> gone["the call is removed;
condition is never evaluated"] gone --> on ``` That is also the catch. Anything a `DebugAssert` argument does — print, count, save — happens in a debug build and silently does not happen in a release one. Keep the condition a pure question, and use [`Assert`](https://rux-lang.dev/docs/learn/assert) for checks that must still run in the program you ship. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/CompileTime/BuildMode){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `rux run` makes a debug build: unoptimized, quick to compile, full of checks. `rux run --release` // makes a release build: optimized, with the debug-only checks taken out. `#build` describes the // build in progress, and `#build.mode` says which of the two it is. // // `DebugAssert` is the check that comes and goes. In a debug build it behaves like `Assert`. In a // release build it is removed entirely, and its arguments are not even evaluated, so an expensive // check costs nothing once you ship. The other side of that bargain: never put work the program // needs inside one, or a release build will quietly skip it. import Core::{ #build, BuildMode, DebugAssert }; import Io::PrintLine; // Stands in for a slow consistency check. The line it prints shows whether it ran at all. func ScoresAreSorted() -> bool { PrintLine(" ...checking that the scores are sorted"); return true; } func Main() -> int { // A project may name its own profiles; the mode is the part every profile has. PrintLine("Profile: {}", #build.profile); when #build.mode == BuildMode::Debug { PrintLine("Debug build: unoptimized, with every check switched on"); } else { PrintLine("Release build: optimized, with the debug checks compiled out"); } // `#build.debugAssertions` answers the narrower question of whether those checks are kept. PrintLine("Debug assertions kept: {}", #build.debugAssertions); DebugAssert(ScoresAreSorted(), "the scores must be sorted"); PrintLine("Done"); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/CompileTime/BuildMode rux run ``` ```text Profile: Debug Debug build: unoptimized, with every check switched on Debug assertions kept: true ...checking that the scores are sorted Done ``` ```sh rux run --release ``` ```text Profile: Release Release build: optimized, with the debug checks compiled out Debug assertions kept: false Done ``` ## Common mistakes ::warning **Putting needed work inside `DebugAssert`.**:br`DebugAssert(SaveScores(), "the scores were saved")` saves the scores in a debug build and never calls `SaveScores` in a release one. Do the work first, keep the result in a variable, and assert on the variable. :: ::warning **Using `DebugAssert` for a promise the shipped program relies on.**:br A release build removes it, so a broken assumption sails straight past. If the check must hold for your users too, write `Assert`. :: ::warning **Branching on the profile's name.**:br A project can name its own profiles, so comparing `#build.profile` against `"Release"` misses every release profile with another name. Branch on `#build.mode`. :: ## Try it yourself 1. Make `ScoresAreSorted` return `false`. Run the program with and without `--release`. The debug build stops with `Assertion failed: the scores must be sorted`; what does the release build print? 2. Print `#build.date` and `#build.time`, then build twice and compare. 3. Print `#build.debugInfo` in both builds. ## Learn more - [Build context](https://rux-lang.dev/docs/lang/comptime/context) in the Rux Reference — every field of `#build` - [`#build`](https://rux-lang.dev/docs/api/core/build) and [`Assert`](https://rux-lang.dev/docs/api/core/assert) in the Core API reference - [`rux run`](https://rux-lang.dev/docs/cli/run) and [`rux build`](https://rux-lang.dev/docs/cli/build) — the `--release` option - [Assert](https://rux-lang.dev/docs/learn/assert) — `Assert` and `DebugAssert` from the run-time side - [Define](https://rux-lang.dev/docs/learn/define) — build switches of your own # Source location ::note **You'll need**: [Function](https://rux-lang.dev/docs/learn/function), [Target](https://rux-lang.dev/docs/learn/target) :: When something goes wrong, the first question is usually "where?". A log line that says `Main.rux:31` saves a search through the whole program, and that is why every compiler error you have read so far starts with a file, a line and a column. `#source` gives your own program the same knowledge: the file, the line and column, and the function an expression is written in. Like [`#target`](https://rux-lang.dev/docs/learn/target), `#source` is filled in by the compiler while it compiles. A read costs nothing at run time — by then it is just a constant number or a piece of text. ## Reading `#source` `#source` comes from `Core`. Read its fields with a dot: ```rux PrintLine("This read is at line {}, column {} of {}, in {}", #source.line, #source.column, #source.fileName, #source.function); ``` This prints `line 24, column 23 of Main.rux, in Main`. Look closely at the column: 23 is not where the `PrintLine` starts, nor where `#source.line` starts. It is exactly where `#source.column` is written. Every read describes **itself** — the expression that does the reading — and nothing else. | Field | Type | In this program | | ---------- | ----------- | ---------------- | | `line` | `uint` | `24` | | `column` | `uint` | `23` | | `fileName` | `char8[..]` | `"Main.rux"` | | `filePath` | `char8[..]` | `"Src/Main.rux"` | | `function` | `char8[..]` | `"Main"` | | `module` | `char8[..]` | `"Main"` | `fileName` is the file's name alone; `filePath` is its path inside the package, which tells two `Main.rux` files in different folders apart. ## Which place does it describe? That rule — a read describes where it is written — has a consequence that surprises almost everyone. A helper that reads `#source.line` inside its own body reports its own line, every time, no matter who called it: ```rux // Reads `#source` in its own body, so it always describes this function. func LogHere(message: char8[..]) { PrintLine(" {}:{} in {}: {}", #source.fileName, #source.line, #source.function, message); } ``` Both calls print `Main.rux:14 in LogHere`. Line 14 is the line inside `LogHere`, and `LogHere` is the function around it — true, and useless for finding the caller. To report where a call came from, the **caller** has to read `#source.line` and pass the value in: ```rux // Is handed the location by its caller, so it describes the call. func LogAt(line: uint, message: char8[..]) { PrintLine(" line {}: {}", line, message); } ``` ```rux LogAt(#source.line, "first call"); LogAt(#source.line, "second call"); ``` Now the two calls print lines 31 and 32 — their own lines, because that is where `#source.line` is written. ```mermaid flowchart LR c1["line 27: LogHere(…)"] --> h["LogHere reads #source.line
at line 14"] c2["line 28: LogHere(…)"] --> h h --> o1["both print 14"] c3["line 31: LogAt(#source.line, …)"] --> o2["prints 31"] c4["line 32: LogAt(#source.line, …)"] --> o3["prints 32"] ``` ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/CompileTime/SourceLocation){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `#source` tells a program where it is in its own source code: the file, the line and column, // and the function around it. Like `#target`, the compiler fills it in while compiling, so a read // costs nothing at run time — it is just a constant. // // The surprise is *which* place it describes. Every read of `#source` describes the expression // that reads it, wherever that is written. A helper that reads `#source.line` inside its own body // therefore reports its own line, every time, no matter who called it. To report where a call // came from, the caller has to read `#source.line` itself and pass the value in. import Core::{ #source }; import Io::PrintLine; // Reads `#source` in its own body, so it always describes this function. func LogHere(message: char8[..]) { PrintLine(" {}:{} in {}: {}", #source.fileName, #source.line, #source.function, message); } // Is handed the location by its caller, so it describes the call. func LogAt(line: uint, message: char8[..]) { PrintLine(" line {}: {}", line, message); } func Main() -> int { PrintLine("This read is at line {}, column {} of {}, in {}", #source.line, #source.column, #source.fileName, #source.function); PrintLine("A helper that reads #source itself:"); LogHere("first call"); LogHere("second call"); PrintLine("A helper that is passed #source.line:"); LogAt(#source.line, "first call"); LogAt(#source.line, "second call"); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/CompileTime/SourceLocation rux run ``` ```text This read is at line 24, column 23 of Main.rux, in Main A helper that reads #source itself: Main.rux:14 in LogHere: first call Main.rux:14 in LogHere: second call A helper that is passed #source.line: line 31: first call line 32: second call ``` ## Common mistakes ::warning **Expecting a helper to know its caller.**:br A helper that reads `#source` in its own body describes its own body. If a log line should name the call site, read `#source.line` at the call and pass it in, as `LogAt` does. :: ::warning **Forgetting the import.**:br`#source` is declared in `Core`, so without `import Core::{ #source };` a read fails with `error: name '#source' is not defined in this scope`. :: ## Try it yourself 1. Print `#source.filePath` and `#source.module` next to `#source.fileName`. 2. Write `func Check(condition: bool, line: uint)` that prints `check failed at line N` when the condition is false. Call it twice from `Main`, once with `2 + 2 == 5`, passing `#source.line` each time. 3. Join the first `PrintLine` call onto a single line. Predict the new line and column before you run. ## Learn more - [Build context](https://rux-lang.dev/docs/lang/comptime/context) in the Rux Reference — every field of `#source` - [`#source`](https://rux-lang.dev/docs/api/core/source) in the Core API reference - [Assert](https://rux-lang.dev/docs/learn/assert) and [Panic](https://rux-lang.dev/docs/learn/panic) — they report the file, line and column of the call that failed - [Compile error](https://rux-lang.dev/docs/learn/compile-error) — making the compiler itself report a location # Compile error ::note **You'll need**: [When](https://rux-lang.dev/docs/learn/when), [Target](https://rux-lang.dev/docs/learn/target), [Tooling](https://rux-lang.dev/docs/learn/tooling) :: Some mistakes are better caught by the compiler than by whoever runs the program. A program that needs 64-bit pointers should refuse to be built for anything else, with a message that says why, instead of building and then misbehaving. A function that has a better replacement should tell everyone who still calls it. These are messages from the source code to the person building it, and three directives carry them: | Directive | Effect | | ---------------- | --------------------------------------------------------- | | `#Error("…")` | stops the build with that message | | `#Warn("…")` | prints a warning and lets the build carry on | | `#Allow("rule")` | silences one `rux lint` rule for the declaration below it | ## Stopping the build with `#Error` `#Error` stops the build wherever the compiler reaches it. So it is almost always written inside a [`when`](https://rux-lang.dev/docs/learn/when) branch — the branch a build should never take: ```rux when #target.pointerBits < 64 { #Error("this program needs a 64-bit target"); } ``` Every target Rux supports today has 64-bit pointers, so this branch is never taken, and a branch that is not taken is never looked at: the directive in it never fires. Flip the condition to `>= 64` and the build stops on the spot, pointing at the directive: ```text Src/Main.rux:35:9: error: this program needs a 64-bit target ``` Used like this, `#Error` is a statement, so it is imported from `Core` like any other name: `import Core::{ #Error, #target };`. Its message must be a string literal written right there — the compiler prints it while compiling, long before any variable has a value. ## Warning every caller with `#Warn` Written above a declaration, as an attribute, a directive fires at every **use** of that declaration rather than where it is written. That is how an old function points its callers at the new one: ```rux // Fires at each call below, as a warning; the program still builds and runs. #Warn("Average rounds toward zero; call AverageRounded instead") func Average(total: int, count: int) -> int { return total / count; } ``` The warning at the top of the output below comes from this attribute. It points at line 38, column 41 — the call to `Average` in `Main`, not the declaration — and the program still builds and runs. `#Error` works as an attribute too: the build then stops at the first call, which is how a removed function can explain where to go instead of just vanishing. As an attribute, neither directive needs an import; that is why the program imports `#Error` but not `#Warn`. Written as a statement inside a body, `#Warn("…");` is imported from `Core` exactly like `#Error`. | Written as | Fires | Import from `Core` | | ----------------------------- | ------------------------------------- | ------------------ | | a statement, `#Error("…");` | where it is reached; stops the build | yes | | a statement, `#Warn("…");` | where it is reached; build carries on | yes | | an attribute on a declaration | at every use of that declaration | no | ## Silencing one lint rule with `#Allow` [`rux lint`](https://rux-lang.dev/docs/learn/tooling) checks style rules the compiler does not. One of them asks for PascalCase constant names, which would turn kilobytes (kB) into KB. The spelling is deliberate here, so the rule is switched off for this one declaration and nowhere else: ```rux #Allow("naming.const") const kB: int = 1000; ``` Without the attribute, `rux lint` reports `warning: constant name 'kB' should be PascalCase` and suggests renaming it to `KB`. The rules `#Allow` accepts are `naming.type`, `naming.const` and `docs.missing`. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/CompileTime/CompileError){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Some mistakes are better caught by the compiler than by whoever runs the program. Three // directives let source code speak to the build: // // - `#Error("...")` stops the build with that message, wherever it is reached. // - `#Warn("...")` prints a warning and lets the build carry on. // - `#Allow("rule")` silences one `rux lint` rule for the declaration it is attached to. // // `#Error` is almost always written inside a `when` branch: the branch a build should never take. // A branch that is not taken is never looked at, so the directive in it never fires. Written as // an attribute on a declaration instead, `#Error` or `#Warn` fires at every use of that // declaration, which is how an old function points its callers at the new one. import Core::{ #Error, #target }; import Io::PrintLine; // Fires at each call below, as a warning; the program still builds and runs. #Warn("Average rounds toward zero; call AverageRounded instead") func Average(total: int, count: int) -> int { return total / count; } func AverageRounded(total: int, count: int) -> int { return (total + count / 2) / count; } // `rux lint` asks for PascalCase constant names, which would turn kilobytes (kB) into KB. The // spelling is deliberate here, so the rule is switched off for this one declaration. #Allow("naming.const") const kB: int = 1000; func Main() -> int { // Every supported target has 64-bit pointers, so this branch is never taken. Flip the // condition to `>= 64` and the build stops with: // Src/Main.rux:35:9: error: this program needs a 64-bit target when #target.pointerBits < 64 { #Error("this program needs a 64-bit target"); } PrintLine("Average of 7 and 8: {}", Average(15, 2)); PrintLine("Rounded average: {}", AverageRounded(15, 2)); PrintLine("A 3 kB file holds {} bytes", 3 * kB); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/CompileTime/CompileError rux run ``` ```text Src\Main.rux:38:41: warning: Average rounds toward zero; call AverageRounded instead 38 | PrintLine("Average of 7 and 8: {}", Average(15, 2)); | ^ note: compiler phase: Analyzing Average of 7 and 8: 7 Rounded average: 8 A 3 kB file holds 3000 bytes ``` The compiler prints the file's full path where this shows `Src\Main.rux`. ## Common mistakes ::warning **An `#Error` with no `when` around it.**:br A directive that is always reached always fires. Written straight into a body, `#Error("…");` stops every build, on every target. Put it in the branch that should never be taken. :: ::warning **A message that is not a literal.**:br`#Warn(Message);` with `Message` a constant fails with `error: '#Warn' message must be a string literal`. Write the text in place. :: ::warning **The statement form without its import.**:br Inside a body, `#Warn("…");` without importing it fails with `error: name '#Warn' is not defined in this scope`. Add it to the `import Core::{ … }` line. :: ::warning **Misspelling a lint rule.**:br`#Allow("naming.konst")` stops the build with `error: unknown lint rule 'naming.konst'; valid rules are: naming.type, naming.const, docs.missing`. :: ## Try it yourself 1. Flip the condition to `when #target.pointerBits >= 64` and build. Then flip it back. 2. Delete the `#Allow` line, run `rux lint`, and read the suggestion. 3. Replace the `#Warn` on `Average` with `#Error("Average was removed; call AverageRounded instead")`. Where does the build stop? Change the call to make it build again. 4. Add a statement `#Warn("remember to update the scores table");` at the top of `Main`, and the import it needs. ## Learn more - [Error](https://rux-lang.dev/docs/lang/attributes/error), [Warn](https://rux-lang.dev/docs/lang/attributes/warn) and [Allow](https://rux-lang.dev/docs/lang/attributes/allow) in the Rux Reference - [`#Error`](https://rux-lang.dev/docs/api/core/error) and [`#Warn`](https://rux-lang.dev/docs/api/core/warn) in the Core API reference - [`rux lint`](https://rux-lang.dev/docs/cli/lint) — the rules `#Allow` can silence - [Tooling](https://rux-lang.dev/docs/learn/tooling) — `rux fmt`, `rux lint` and friends - [Extern](https://rux-lang.dev/docs/learn/extern) — an `#Error` that explains why a build for the wrong system stops # Define ::note **You'll need**: [When](https://rux-lang.dev/docs/learn/when), [Build mode](https://rux-lang.dev/docs/learn/build-mode) :: `#target` and `#build` answer questions the compiler already knows: which machine, which kind of build. A **define** is a question you answer yourself — a named value handed to the build, such as a feature switch, a customer's name or the address of a test server. The program reads it while compiling with `#config`, so a `when` can branch on it and keep only the code that applies. ## Handing a value to the build On the command line, `--define` takes a name and, optionally, a value. Repeat it for each define: ```sh rux run --define Name=Ada --define Verbose ``` A define without a value, like `Verbose` here, holds the text `"true"`. Defines that belong to the project rather than to one build go in a `[Build.Defines]` table in `Rux.toml`: ```toml [Build.Defines] Name = "Grace" ``` The two combine: the manifest gives the defaults, and `--define` overrides them for one build. With the table above, `rux run` greets Grace and `rux run --define Name=Ada` greets Ada. A manifest value may be a string, a boolean or an integer, but `#config` always hands it to the program as text — `Retries = 3` reads as `"3"`. ```mermaid flowchart LR m["Rux.toml
[Build.Defines]"] --> merge{"the build's defines"} c["--define Name=Value
on the command line"] -- "overrides" --> merge merge --> has["Has: is it
defined at all?"] merge --> get["Get: its text,
or empty"] has --> w["when picks the code
while compiling"] get --> w ``` ## Has and Get `#config` comes from `Core` and has two questions to ask: - `#config.Has("Name")` says whether the build defines `Name` at all. - `#config.Get("Name")` gives its text, or an empty string when it is not defined. `Get` alone cannot tell "not defined" from "defined as empty", so the program asks `Has` first: ```rux when #config.Has("Name") { let name = #config.Get("Name"); } else { let name = "World"; } PrintLine("Hello, {}!", name); ``` | How the build was started | `Has("Name")` | `Get("Name")` | Greeting | | --------------------------- | ------------- | ------------- | --------------- | | `rux run` | `false` | `""` | `Hello, World!` | | `rux run --define Name=Ada` | `true` | `"Ada"` | `Hello, Ada!` | | `rux run --define Name=` | `true` | `""` | `Hello, !` | As in the [When](https://rux-lang.dev/docs/learn/when) lesson, the `when` opens no scope, so `name` is still there for the `PrintLine` after it. The last line of the program shows `Get` on a name nobody defined: its `length` is 0. ## A feature switch The most common use of a define is to switch a feature on or off: ```rux when #config.Has("Verbose") { PrintLine("Verbose is on (its value is \"{}\")", #config.Get("Verbose")); } else { PrintLine("Verbose is off; rebuild with --define Verbose to turn it on"); } ``` The untaken branch is not part of the program at all — a build without `Verbose` contains no trace of the verbose code. The flip side is that a define is fixed once the program is built. Changing one means **rebuilding**: `rux run` does that for you, but an `.exe` you built earlier keeps the defines it was built with. Because both questions are answered while compiling, the name must be written as a string literal in the source. Even a `const` holding the name is refused. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/CompileTime/Define){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // `#target` and `#build` answer questions the compiler already knows. A define is a question you // answer yourself: a named value handed to the build, such as a feature switch or a customer's // name. Pass one on the command line with `--define Name=Value`, or list it in a `[Build.Defines]` // table in `Rux.toml`. The program reads it with `#config`: // // - `#config.Has("Name")` says whether the build defines `Name` at all. // - `#config.Get("Name")` gives its text, or an empty string when it is not defined. // // A define without a value, such as `--define Verbose`, holds the text "true". Both questions are // answered while compiling, so `when` can branch on them, and changing a define means rebuilding. // For the same reason the name must be written as a string literal: passing a variable is refused // with "the argument to 'Has' must be a string literal written in the source". import Core::{ #config }; import Io::PrintLine; func Main() -> int { // Has tells "not defined" apart from "defined as empty", which Get alone cannot. when #config.Has("Name") { let name = #config.Get("Name"); } else { let name = "World"; } PrintLine("Hello, {}!", name); // A feature switch: the untaken branch is not part of the program at all. when #config.Has("Verbose") { PrintLine("Verbose is on (its value is \"{}\")", #config.Get("Verbose")); } else { PrintLine("Verbose is off; rebuild with --define Verbose to turn it on"); } PrintLine("An undefined name reads as {} characters", #config.Get("Missing").length); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/CompileTime/Define rux run ``` ```text Hello, World! Verbose is off; rebuild with --define Verbose to turn it on An undefined name reads as 0 characters ``` ```sh rux run --define Name=Ada --define Verbose ``` ```text Hello, Ada! Verbose is on (its value is "true") An undefined name reads as 0 characters ``` ## Common mistakes ::warning **Naming the define with a variable.**:br`#config.Has(key)` fails with `error: the argument to 'Has' must be a string literal written in the source`, and so does a constant. Inside a `when` condition the same mistake reads `error: 'key' is not a compile-time constant`. Write the name in quotes. :: ::warning **Testing presence with `Get`.**:br An empty answer from `Get` means either "not defined" or "defined as empty". Ask `Has` when the difference matters. :: ::warning **Expecting a built program to see a new define.**:br A define is folded in while compiling. Running an old `.exe` with a new `--define` in mind changes nothing; build again. :: ## Try it yourself 1. Run the program with `--define Name=Ada --define Verbose`, then with `--define Name=` alone. Explain the second greeting. 2. Add a `[Build.Defines]` table to `Rux.toml` with `Name = "Grace"`. Run with and without `--define Name=Ada`. 3. Add a `when #config.Get("Name") == "Ada"` that prints an extra line just for Ada. ## Learn more - [Build context](https://rux-lang.dev/docs/lang/comptime/context) in the Rux Reference — `#config` and the rest of the build context - [`#config`](https://rux-lang.dev/docs/api/core/config) in the Core API reference - [Package manifest](https://rux-lang.dev/docs/packaging/manifest) — the `[Build.Defines]` table - [`rux run`](https://rux-lang.dev/docs/cli/run) and [`rux build`](https://rux-lang.dev/docs/cli/build) — the `--define` option - [Build mode](https://rux-lang.dev/docs/learn/build-mode) — the other switch every build has # Intrinsic ::note **You'll need**: [Dependency](https://rux-lang.dev/docs/learn/dependency), [Target](https://rux-lang.dev/docs/learn/target), [Assert](https://rux-lang.dev/docs/learn/assert) :: `int8::Max`, `#target` and `Assert` have come from `Core` in every lesson so far, so it is easy to think of them as part of the language itself. They are not. The compiler knows how to implement them — the arithmetic of an `int8`, the fields of `#target`, the code an `Assert` turns into — but a program reaches them only through **declarations**, and `Core` is simply the package that usually provides those declarations. An `intrinsic` declaration names something the compiler supplies and gives its type, with no body. This lesson writes a few of them in `Basis`, a small provider package of its own, and runs a program that takes everything from `Basis` instead of `Core`. Nothing changes: the program works exactly as it would with `Core`. ## Four kinds of intrinsic Open `Basis/Src/Basis.rux` in the code tree below. It is a handful of lines, and each part declares a different kind of intrinsic. A **type**. `int8` is a type only the compiler can implement, so the provider just declares it. The constants on it are ordinary source, though, so the provider decides which ones to offer: ```rux pub intrinsic type int8; extend int8 { pub const Min: int8 = -128i8; pub const Max: int8 = 127i8; } ``` A **constant** whose value no literal can spell. There is no way to write infinity in source, so the compiler supplies it: ```rux extend float64 { pub intrinsic const Infinity: float64; } ``` A **compile-time value**. `#target` is declared as an ordinary struct plus one intrinsic line that says "the compiler fills this in". The provider lists only the fields it wants to expose: ```rux pub struct Target { pub pointerBits: uint; } pub intrinsic #target: Target; ``` A **function** the compiler emits inline. The provider writes the signature, which must be the one the compiler expects: ```rux pub intrinsic func Assert(condition: bool, message: char8[..]); ``` `Core` declares all of these in exactly the same way — its own `Assert` is that same line, word for word. The package gets no special powers from being called `Core`. | Kind | The compiler supplies | The provider writes | | ----------------- | --------------------------- | -------------------------------------- | | `intrinsic type` | the type and its operations | constants and methods, as plain source | | `intrinsic const` | the value | the name and the type | | `intrinsic #name` | each field's value | the struct, with the fields it exposes | | `intrinsic func` | the code at every call | the signature the compiler expects | ## Taking them from a provider `Basis` is a [source library](https://rux-lang.dev/docs/learn/source-library) in a folder beside the program, and the program's `Rux.toml` names it with a path [dependency](https://rux-lang.dev/docs/learn/dependency): `Basis = { Path = "Basis" }`. There is no `Core` in that manifest at all. The program imports from `Basis` the same names it would otherwise import from `Core`: ```rux import Basis::{ #target, Assert, float64, int8 }; ``` ```mermaid flowchart LR c(["The compiler implements
int8, Infinity, #target, Assert"]) --> core["Core declares them
(the usual provider)"] c --> basis["Basis declares them
(this lesson's provider)"] core -.->|"every other lesson"| p["Main.rux imports
the declarations"] basis -->|"this lesson"| p ``` Since only `Basis` is imported, only what `Basis` declares is available. The type `int16` still exists, but `int16::Max` does not: no provider in this program offers it. ## Only real intrinsics `intrinsic` cannot invent new features. Every declaration is checked against what the compiler really implements, and anything else is refused: a type called `int7`, a function called `Square`, a `wordSize` field on `Target`, an `Assert` whose condition is an `int`. Writing a provider is a way to replace `Core`, not a way to extend the language. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/CompileTime/Intrinsic){rel=""nofollow""}. Its comments explain every step. ::code-tree{default-value="Src/Main.rux"} ```toml [Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Intrinsic" Version = "0.1.0" Type = "Executable" Description = "Declaring compiler-provided types and values in a provider package of your own" Authors = ["Rux Contributors "] [Dependencies] Basis = { Path = "Basis" } Io = { Namespace = "Rux", Version = "*" } ``` ```toml [Basis/Rux.toml] [Manifest] Version = 1 MinRux = "0.4.0" [Package] Name = "Basis" Version = "0.1.0" Type = "SourceLibrary" Description = "A small provider of intrinsic declarations, standing in for part of Core" Authors = ["Rux Contributors "] ``` ```rux [Src/Main.rux] // `int8::Max`, `#target` and `Assert` have always come from `Core`, so they can look like part of // the language. They are not. The compiler knows how to implement them, but a program reaches them // only through *declarations*, and `Core` is simply the package that usually provides those // declarations. An `intrinsic` declaration names something the compiler supplies and gives its // type, with no body. // // This lesson takes the declarations from `Basis`, a small provider package beside this one, // instead of from `Core`. Open `Basis/Src/Basis.rux`: it is a handful of lines, and the program // below works the same as it would with `Core`. // // Only real intrinsics can be declared this way. `intrinsic type int7` is refused because "'int7' // is not a supported intrinsic scalar type", and an `intrinsic func` the compiler does not // implement "is not a supported intrinsic function". Writing a provider is a way to replace // `Core`, not a way to invent new language features. import Basis::{ #target, Assert, float64, int8 }; import Io::PrintLine; func Main() -> int { PrintLine("int8 runs from {} to {}", int8::Min, int8::Max); PrintLine("float64 can hold {}", float64::Infinity); PrintLine("Pointers on this target are {} bits wide", #target.pointerBits); let level: int8 = 100; Assert(level < int8::Max, "the level must leave room to grow"); PrintLine("Level {} passed the check", level); return 0; } ``` ```rux [Basis/Src/Basis.rux] // Everything here is a declaration the compiler already knows how to implement. `intrinsic` says // "the compiler supplies this; here is its name and its type", and gives it no body. The package // gets no special powers from this: Core declares the same things in exactly the same way. // The one-byte signed integer. Its arithmetic belongs to the compiler; the constants are ordinary // source, so the provider chooses which ones to offer. pub intrinsic type int8; extend int8 { pub const Min: int8 = -128i8; pub const Max: int8 = 127i8; } // Infinity cannot be written as a literal, so the compiler supplies its value. pub intrinsic type float64; extend float64 { pub intrinsic const Infinity: float64; } // A compile-time value. The provider declares only the fields it wants to expose, and only ones // the compiler fills in: a `wordSize` field is refused as "not one the compiler supplies for // '#target'". pub struct Target { pub pointerBits: uint; } pub intrinsic #target: Target; // A function the compiler emits inline. The signature must be the one the compiler expects: // declared with `condition: int`, it is refused with "intrinsic 'Assert' must be declared as // 'func Assert(condition: bool, message: char8[..])'". pub intrinsic func Assert(condition: bool, message: char8[..]); ``` :: ## Run it ```sh cd Examples/CompileTime/Intrinsic rux run ``` ```text int8 runs from -128 to 127 float64 can hold Inf Pointers on this target are 64 bits wide Level 100 passed the check ``` The provider is the companion package in `Basis/`. ## Common mistakes ::warning **Declaring a type the compiler does not have.**:br`pub intrinsic type int7;` fails with `error: 'int7' is not a supported intrinsic scalar type`. Likewise, an `intrinsic func` the compiler does not implement fails with `error: 'Square' is not a supported intrinsic function`. :: ::warning **Adding a field the compiler does not fill in.**:br A `wordSize` field on `Target` fails with `error: field 'wordSize' of 'Target' is not one the compiler supplies for '#target'`. Every field of an intrinsic struct must be one the compiler knows. :: ::warning **Getting an intrinsic signature wrong.**:br Declared with `condition: int`, `Assert` fails with `error: intrinsic 'Assert' must be declared as 'func Assert(condition: bool, message: char8[..])'`. The message tells you the one signature it accepts. :: ::warning **Using a constant the provider does not offer.**:br Only what `Basis` declares is in scope. `int16::Max` fails with `error: 'Max' not found in extend for type 'int16'`, because `Basis` declares no constants for `int16`. :: ## Try it yourself 1. Add `pub triple: char8[..];` to the `Target` struct in `Basis` and print `#target.triple` from `Main`. 2. Change `level` to `127` and run. Which line stops the program, and with what message? 3. Add `pub intrinsic type int7;` to `Basis`, read the error, and take it out again. 4. Print `int16::Max` from `Main`. Then add the constants to `Basis`, the way `int8` has them, until it builds. ## Learn more - [Intrinsics](https://rux-lang.dev/docs/lang/comptime/intrinsics) and [Intrinsic constants](https://rux-lang.dev/docs/lang/comptime/context) in the Rux Reference - [The Core package](https://rux-lang.dev/docs/api/core) — the provider every other lesson uses - [Dependency](https://rux-lang.dev/docs/learn/dependency) and [Source library](https://rux-lang.dev/docs/learn/source-library) — how `Basis` reaches the program - [Target](https://rux-lang.dev/docs/learn/target) — the full `#target` that `Core` declares # Part 24: Platform Under every Rux program sits an operating system, full of functions written in C long before your program existed, and a processor that runs nothing but its own instructions. The standard packages usually stand between you and both. This part removes them: you declare and call a system function yourself, use the C runtime directly with its own types and conventions, let C call a Rux function back, and finally write function bodies in assembly for two different processors. Nothing here is everyday code — but after this part you will know what every `Io::PrintLine` eventually turns into, and how to reach a library no package covers yet. ## What you will learn - Declaring a function from a system library with `extern`, and naming the library with `#Link`. - Why the compiler takes such a declaration on trust, and what happens when it is wrong. - Calling the C runtime through the `C` package: C's own types, opaque pointers, C structs and variadic calls. - Checking C's failure values — null pointers and negative counts — because nothing panics for you. - Giving a foreign function a Rux name of its own, and choosing a calling convention with `#Abi`. - Writing an `asm func` in x86-64 assembly, and its AArch64 version, selected with `when #target.arch`. ## The path of a foreign call From the call site, a foreign function looks like any other. Underneath, each lesson adds one piece of the agreement between the two sides: ```mermaid flowchart LR call["A Rux call
GetCurrentProcessId()"] --> decl["extern declaration
name and types,
taken on trust · 24.1"] decl --> conv["calling convention
which registers carry
the arguments · 24.3"] conv --> link["#Link
library and symbol
24.1, 24.3"] link --> lib(["at run time:
the function in Kernel32.dll
or the C runtime · 24.2"]) lib -.->|"a callback, such as
qsort's comparison · 24.3"| back["a Rux function
marked #Abi(.C)"] asm["A call to an asm func
24.4, 24.5"] --> pin["#Abi: the convention
the body was written for"] pin --> body(["your instructions,
emitted as written"]) ``` | Situation | Tool | Lesson | | ------------------------------------------ | ------------------------- | ------------------------------------------------------ | | a system function no package declares | `extern` and `#Link` | [Extern](https://rux-lang.dev/docs/learn/extern) | | anything in the C runtime | the `C` package | [C interop](https://rux-lang.dev/docs/learn/c-interop) | | a C name you would rather not spell | `#Link`'s second argument | [ABI](https://rux-lang.dev/docs/learn/abi) | | a Rux function that C calls back | `#Abi(.C)` | [ABI](https://rux-lang.dev/docs/learn/abi) | | an instruction the language cannot express | `asm func` | [Assembly](https://rux-lang.dev/docs/learn/asm) | ## Lessons | | Lesson | What you will learn | | ---- | ------------------------------------------------------- | ---------------------------------------------------------------------- | | 24.1 | [Extern](https://rux-lang.dev/docs/learn/extern) | call a platform API directly through an extern declaration and `#Link` | | 24.2 | [C interop](https://rux-lang.dev/docs/learn/c-interop) | C-compatible types, pointers and handles | | 24.3 | [ABI](https://rux-lang.dev/docs/learn/abi) | choose a calling convention with `#Abi` | | 24.4 | [Assembly](https://rux-lang.dev/docs/learn/asm) | write a function body in assembly | | 24.5 | [ARM assembly](https://rux-lang.dev/docs/learn/asm-arm) | the same function in AArch64 assembly, chosen with `when` | ## Before you start Finish [Part 23: Compile time](https://rux-lang.dev/docs/learn/compile-time) first: every lesson here uses `when #target` to keep platform code out of builds it cannot work in, and `#Error` to explain why. The C lessons also lean on [Part 15: Memory](https://rux-lang.dev/docs/learn/memory) — [Pointer](https://rux-lang.dev/docs/learn/pointer), [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice) and [Layout](https://rux-lang.dev/docs/learn/layout) — and on [Defer](https://rux-lang.dev/docs/learn/defer). Not every lesson runs everywhere. [Extern](https://rux-lang.dev/docs/learn/extern) calls `Kernel32.dll` and is Windows only; [Assembly](https://rux-lang.dev/docs/learn/asm) needs an x86-64 processor; the assembly in [ARM assembly](https://rux-lang.dev/docs/learn/asm-arm) runs only on AArch64, though the lesson builds anywhere. [C interop](https://rux-lang.dev/docs/learn/c-interop) and [ABI](https://rux-lang.dev/docs/learn/abi) run on Windows, Linux, macOS and FreeBSD. Each lesson's package is in the Examples repository's `Platform/` folder: ```sh cd Examples/Platform/Extern rux run ``` ## After this part That completes the tour of the language and its standard packages. [Part 25: Projects](https://rux-lang.dev/docs/learn/projects) puts it all together in complete small programs, and this part's checkpoint, [Melody](https://rux-lang.dev/docs/learn/melody), plays the opening of Ode to Joy through the console speaker by calling the Windows `Beep` function with `extern` — so it, too, is Windows only. For the full rules behind this part, see [Foreign function interface](https://rux-lang.dev/docs/lang/ffi/overview), [Link](https://rux-lang.dev/docs/lang/attributes/link), [Abi](https://rux-lang.dev/docs/lang/attributes/abi) and [Assembler functions](https://rux-lang.dev/docs/lang/ffi/assembly) in the Rux Reference, and [the C package](https://rux-lang.dev/docs/api/c) in the API reference. # Extern ::note **You'll need**: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Target](https://rux-lang.dev/docs/learn/target), [Compile error](https://rux-lang.dev/docs/learn/compile-error) :: ::tip **Windows only.**:br The functions in this lesson come from `Kernel32.dll`, which only Windows has. On Linux, macOS or FreeBSD the build stops with an `#Error` that says so. :: Not every function a program calls was written in Rux. The operating system offers thousands of its own — to ask the time, open a window, find out which process you are — compiled long ago and shipped in **shared libraries**: `.dll` files on Windows, `.so` on Linux and FreeBSD, `.dylib` on macOS. The standard packages call them for you all the time. This lesson calls two directly. `extern` declares a function that lives in one of those libraries: its name, its parameters and its result, with no body, because the body is already in the library. `#Link` names the library, and the linker connects the two. ## Declaring a foreign function ```rux // One `#Link` on a block applies to every declaration inside it. #Link("Kernel32.dll") extern { // DWORD GetCurrentProcessId(void): the number Windows gave this running program. func GetCurrentProcessId() -> uint32; // int lstrlenA(const char *text): counts bytes up to the first zero byte. func lstrlenA(text: *char8) -> int32; } ``` The comments show each function as Microsoft's documentation writes it, in C. Writing the Rux declaration means translating that line, type by type: | In the C documentation | Means | In Rux | | ---------------------- | --------------------------------------- | -------- | | `DWORD` | a 32-bit unsigned number | `uint32` | | `int` | a 32-bit signed number | `int32` | | `const char *` | the address of bytes it will not change | `*char8` | | `void` (parameters) | no parameters | `()` | An `extern { }` block holds as many declarations as you like, and one `#Link` above it covers them all. A single function can be declared on its own too: `#Link("Kernel32.dll")` on one line, `extern func GetCurrentProcessId() -> uint32;` on the next. Once declared, a foreign function is called like any other — nothing at the call site says it is unusual: ```rux PrintLine("process id {}", GetCurrentProcessId()); ``` ## The compiler takes it on trust The compiler cannot look inside `Kernel32.dll` to check what you wrote. It takes the declaration on trust: declare `lstrlenA` with an `int64` parameter, and the call goes ahead and hands the function the wrong bytes. Nothing reports it; the program just behaves strangely. So copy a declaration carefully from the library's documentation — the [Windows API pages](https://rux-lang.dev/docs/api/windows) of this site list the declarations the `Windows` package already uses. On Windows the linker does check one thing: that the library really exports a function of that name. A misspelt name stops the build at the linking step instead of failing on the user's machine. ## Strings that end in a zero byte C functions do not receive a length with a string. They find the end by looking for a zero byte. A Rux string literal keeps one after its last character — `.length` does not count it — so `.data`, the address of the first byte, is exactly what a C function expects: ```rux let word = "extern"; PrintLine("Rux length {}", word.length); PrintLine("lstrlenA {}", lstrlenA(word.data)); ``` Both lines print 6: Rux counted the characters it stores, `lstrlenA` counted bytes up to the zero. That is true of string **literals**. A slice cut out of the middle of a string has no zero byte after it, and a C function handed its `.data` would read straight past the end. ## Building for the right system `Kernel32.dll` exists only on Windows, so the declarations are wrapped in a [`when`](https://rux-lang.dev/docs/learn/when) on the [target](https://rux-lang.dev/docs/learn/target), and every other system gets a [compile error](https://rux-lang.dev/docs/learn/compile-error) that explains itself: ```rux when #target.os { .Windows => { // … the extern block above … }, else => #Error("This lesson calls Kernel32.dll, so it builds for Windows only") } ``` Run `rux build --target linux-x86_64` on any machine and the build stops with exactly that message — far better than a program that builds and then cannot find its library. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Platform/Extern){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Not every function a program calls was written in Rux. The operating system offers thousands // of its own, compiled long ago and shipped in shared libraries. `extern` declares one of them: // its name, its parameters and its result, with no body, because the body is already in the // library. `#Link` names that library, and the linker connects the two. // // The compiler cannot look inside the library to check the declaration. It takes what you wrote // on trust, so a wrong parameter type is not a compile error: the call goes ahead and passes the // wrong bytes. Copy a declaration carefully from the library's documentation. // // The functions below live in Kernel32.dll, which only Windows has. `when #target.os` keeps // them out of every other build, and `#Error` explains why such a build stops. import Core::{ #Error, #target }; import Io::PrintLine; when #target.os { .Windows => { // One `#Link` on a block applies to every declaration inside it. #Link("Kernel32.dll") extern { // DWORD GetCurrentProcessId(void): the number Windows gave this running program. func GetCurrentProcessId() -> uint32; // int lstrlenA(const char *text): counts bytes up to the first zero byte. func lstrlenA(text: *char8) -> int32; } }, else => #Error("This lesson calls Kernel32.dll, so it builds for Windows only") } func Main() -> int { // Called like any other function. Neither of these can fail, which is why they were // chosen: most system functions can, and the next lesson checks for that. PrintLine("process id {}", GetCurrentProcessId()); // C functions find the end of a string by looking for a zero byte. A Rux string literal // keeps one after its last character, though `.length` does not count it, so `.data`, the // address of the first byte, is exactly what a C function expects. let word = "extern"; PrintLine("Rux length {}", word.length); PrintLine("lstrlenA {}", lstrlenA(word.data)); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/Platform/Extern rux run ``` ```text process id 6640 Rux length 6 lstrlenA 6 ``` The process id is different on every run. ## Common mistakes ::warning **Passing a Rux string where C expects an address.**:br`lstrlenA(word)` fails with `error: argument 1 to 'lstrlenA' has type 'char8[..]', but parameter 'text' requires '*char8'`. Pass `word.data`. :: ::warning **Forgetting `#Link`.**:br Without it the compiler does not know where the function lives: `error: extern function 'GetCurrentProcessId' must specify a source DLL via #Link("dll.dll")`. :: ::warning **Misspelling the function's name.**:br Library names are exact, capitals included. `GetCurrentProcessID` stops the build when linking: `error: cannot link PE/COFF executable 'Extern': import function 'GetCurrentProcessID' was not found in DLL 'Kernel32.dll'`. :: ::warning **A declaration that does not match the library.**:br A wrong parameter or result type is not an error at all: the compiler believes the declaration, and the function receives or returns the wrong bytes. Check every type against the documentation. :: ## Try it yourself 1. Add `GetCurrentThreadId`, which also takes nothing and returns a `DWORD`, to the `extern` block and print it. 2. Declare `GetTickCount64() -> uint64` and `Sleep(milliseconds: uint32)` from the same library. Read the tick count, sleep for 500 milliseconds, and print how long the sleep really took. 3. Run `rux build --target linux-x86_64` and read the message. ## Learn more - [Foreign function interface](https://rux-lang.dev/docs/lang/ffi/overview), [Extern declarations](https://rux-lang.dev/docs/lang/ffi/overview) and [Linking libraries](https://rux-lang.dev/docs/lang/ffi/linking) in the Rux Reference - [Link](https://rux-lang.dev/docs/lang/attributes/link) — the attribute in full, including a different symbol name - [`GetCurrentProcessId`](https://rux-lang.dev/docs/api/windows/getcurrentprocessid) and the rest of the [Windows package](https://rux-lang.dev/docs/api/windows) - [Shared library](https://rux-lang.dev/docs/learn/shared-library) — building a library of your own that `extern` can call - [C interop](https://rux-lang.dev/docs/learn/c-interop) — the next lesson, which calls the C runtime on every system # C interop ::note **You'll need**: [Extern](https://rux-lang.dev/docs/learn/extern), [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice), [Layout](https://rux-lang.dev/docs/learn/layout), [Defer](https://rux-lang.dev/docs/learn/defer) :: ::tip **Runs on Windows, Linux, macOS and FreeBSD.**:br The `C` package picks each system's own C runtime, so the same source builds everywhere. :: The C runtime is the one library almost every system has: memory allocation, formatted text, dates and times, sorting. The [Extern](https://rux-lang.dev/docs/learn/extern) lesson declared foreign functions by hand. For the C runtime you do not have to — the `C` package already declares much of it, and you import functions from it like from any other package: ```rux import C::{ c_int, free, gmtime, malloc, size_t, sprintf, time_t }; ``` What the package cannot do is make C feel like Rux. Every C interface is built from four shapes that Rux code does not otherwise meet, and this lesson uses each of them once. ## C's own types C's `int` is 32 bits, but Rux's `int` is 64. C's `long` is 32 bits on Windows and 64 everywhere else. So bindings are written in aliases named after C, which stand for whichever Rux type matches on the target being built: | Alias | C type | Rux type on Windows | Rux type on Linux, macOS, FreeBSD | | -------- | -------- | ------------------- | --------------------------------- | | `c_int` | `int` | `int32` | `int32` | | `c_long` | `long` | `int32` | `int64` | | `size_t` | `size_t` | `uint` | `uint` | | `time_t` | `time_t` | `int64` | `int64` | The `C` package chooses `c_long` with a `when #target.dataModel` — the same field the [Target](https://rux-lang.dev/docs/learn/target) lesson printed. Write a C binding's types in these aliases, never as `int` or `int64` directly, and it stays right on every system. ## Opaque pointers `malloc` returns C's `void *`: an address with no type. Rux spells it `*opaque`. You convert it with `as` to the type you meant, and check it, because C reports "no memory" by returning null: ```rux let capacity: size_t = 64; let buffer = malloc(capacity) as *var char8; if buffer == null { PrintLine("malloc failed"); return 1; } defer free(buffer as *var opaque); ``` The [`defer`](https://rux-lang.dev/docs/learn/defer) hands the memory back on every path out of `Main`. Handles such as C's `FILE *` go one step further: the package declares `FILE` as an empty struct, so you can hold a `*FILE` and pass it back to C, but there is nothing inside for Rux code to look at. ## Variadic functions `sprintf` writes formatted text into a buffer. The `...` at the end of its C declaration accepts any number of arguments of any type, and **nothing** checks them against the format string. You must pass each one at the type its `%` code reads: ```rux let written = sprintf(buffer, "%s has %d legs and weighs %.1f g".data, "spider".data, 8 as c_int, 0.5); ``` | Code | Reads | Passed as | | ------ | ------------------------------- | --------------- | | `%s` | the address of zero-ended bytes | `"spider".data` | | `%d` | a C `int` | `8 as c_int` | | `%.1f` | a 64-bit float, one decimal | `0.5` | `sprintf` is never told how big the buffer is, so the buffer must be big enough for any result — 64 bytes for 34 here. It returns how many bytes it wrote, and `buffer[..written as uint]` turns the pointer and that count back into a Rux slice that `PrintLine` can print. ## C structs `gmtime` turns a count of seconds since 1970 into a calendar date. It takes the address of a `time_t` — that is what `@stamp` gives it — and returns the address of a `tm` struct: ```rux var stamp: time_t = 1000000000; let calendar = gmtime(@stamp); if calendar == null { PrintLine("gmtime failed"); return 1; } ``` The package declares `tm` field for field in C's order, so Rux reads the struct exactly where C wrote it: `calendar.tm_hour` is C's `tm_hour`. Two of the fields keep C's own habits — `tm_year` counts from 1900 and `tm_mon` from zero — which is why the program adds 1900 and 1. The struct belongs to the C runtime, so the program reads it and does not free it. ## Failure is a value you must check None of these calls panics or returns a [fallible](https://rux-lang.dev/docs/learn/fallible). C reports failure with a sentinel value, and the caller has to check for it: | Function | On success | On failure | | --------- | --------------------- | ---------------- | | `malloc` | an address | `null` | | `sprintf` | the bytes it wrote | a negative count | | `gmtime` | the address of a `tm` | `null` | Skip a check and the program carries on with a null pointer or a negative length, and fails somewhere far from the cause. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Platform/CInterop){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // The C runtime is the library almost every system has, and the `C` package declares much of it // for you. Using it means meeting the four shapes every C interface is made of. // // C's own types. C's `int` is 32 bits, but Rux's `int` is 64, and C's `long` is 32 bits on // Windows and 64 elsewhere. So bindings are written in aliases named after C, such as `c_int`, // `size_t` and `time_t`, which stand for whichever Rux type matches on the target being built. // // Opaque pointers. `malloc` returns C's `void *`, which Rux spells `*opaque`: an address with no // type, which you convert to the type you meant. Handles such as C's `FILE *` go one step // further: `FILE` is declared as an empty struct, so you can hold one and pass it back, but // never look inside. // // C structs. `tm` is declared field for field in C's order, so Rux reads the struct exactly // where C wrote it. // // Variadic functions. The `...` in `sprintf` accepts any arguments, and nothing checks them // against the format. You must pass each one at the type its `%` code reads. // // None of these calls panics or returns a fallible: C reports failure with a sentinel value, // such as null or a negative count, and the caller has to check for it. import C::{ c_int, free, gmtime, malloc, size_t, sprintf, time_t }; import Io::PrintLine; func Main() -> int { // An opaque pointer becomes a typed one with `as`. Null means there was no memory. let capacity: size_t = 64; let buffer = malloc(capacity) as *var char8; if buffer == null { PrintLine("malloc failed"); return 1; } defer free(buffer as *var opaque); // `%s` reads the address of zero-terminated bytes, `%d` a C int and `%f` a 64-bit float. // sprintf is never told how big the buffer is, so it must be big enough for any result. let written = sprintf(buffer, "%s has %d legs and weighs %.1f g".data, "spider".data, 8 as c_int, 0.5); if written < 0 { PrintLine("sprintf failed"); return 1; } PrintLine("{} ({} bytes)", buffer[..written as uint], written); // gmtime fills a `tm` struct that the C runtime owns, and returns its address, or null. var stamp: time_t = 1000000000; let calendar = gmtime(@stamp); if calendar == null { PrintLine("gmtime failed"); return 1; } PrintLine("{} seconds after 1970 began was {}-{}-{}, {}:{}:{} UTC", stamp, calendar.tm_year + 1900, calendar.tm_mon + 1, calendar.tm_mday, calendar.tm_hour, calendar.tm_min, calendar.tm_sec); return 0; } ``` Besides `Io`, its `Rux.toml` lists `C` under `[Dependencies]`. ## Run it ```sh cd Examples/Platform/CInterop rux run ``` ```text spider has 8 legs and weighs 0.5 g (34 bytes) 1000000000 seconds after 1970 began was 2001-9-9, 1:46:40 UTC ``` ## Common mistakes ::warning **Using the opaque pointer as it is.**:br Without the `as *var char8`, the buffer is still a `*var opaque`, and the `sprintf` call fails with `error: argument 1 to 'sprintf' has type '*var opaque', but parameter 'buffer' requires '*char8'`. :: ::warning **Passing a Rux string as the format.**:br A C function takes the address of the bytes, not a Rux slice. Without `.data` the call fails with `error: argument 2 to 'sprintf' has type 'char8[..]', but parameter 'format' requires '*char8'`. :: ::warning **An argument that does not match its `%` code.**:br After the format, the compiler checks nothing. Pass the integer `1` where `%.1f` expects a float, or `"spider"` without `.data` where `%s` expects an address, and the program still builds. What it prints is undefined — on Windows x86-64 the first gave `0.0` and the second a few bytes of garbage. :: ::warning **Skipping the null check.**:br`malloc` and `gmtime` return null when they fail. Reading through a null pointer is never valid, and when it goes wrong it goes wrong a long way from the call that caused it. :: ## Try it yourself 1. Set `stamp` to `0`, then to `2000000000`. Predict both dates before you run. 2. Format `255 as c_int` and `48879 as c_int` with `"%x %X"` into the buffer and print the result. 3. Import `strlen` from `C` and check that `strlen(buffer)` agrees with `written`. 4. Print `calendar.tm_wday`, the day of the week counted from Sunday as 0. Which day was 9 September 2001? ## Learn more - [The C package](https://rux-lang.dev/docs/api/c) — every declaration it offers, with [C types](https://rux-lang.dev/docs/api/c/types), [`malloc`](https://rux-lang.dev/docs/api/c/malloc), [`sprintf`](https://rux-lang.dev/docs/api/c/sprintf) and [`gmtime`](https://rux-lang.dev/docs/api/c/gmtime) - [Pointers and `extern`](https://rux-lang.dev/docs/lang/ffi/overview) in the Rux Reference - [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice) and [Layout](https://rux-lang.dev/docs/learn/layout) — the memory ideas this lesson leans on - [ABI](https://rux-lang.dev/docs/learn/abi) — the next lesson: names, calling conventions, and a Rux function that C calls back # ABI ::note **You'll need**: [Extern](https://rux-lang.dev/docs/learn/extern), [C interop](https://rux-lang.dev/docs/learn/c-interop), [Target](https://rux-lang.dev/docs/learn/target) :: ::tip **Runs on Windows, Linux, macOS and FreeBSD.**:br The program calls each system's C runtime, choosing the library's name with `when`. :: When compiled code calls compiled code, both sides must agree on things no source file shows: what the function is called inside the library, which processor registers carry the arguments, where the result comes back. That agreement is the **ABI**, the application binary interface. Between two Rux functions the compiler handles it silently, because it compiles both sides. Across the border to C it cannot — and two parts of the agreement can be written down in Rux. ## A Rux name for a C symbol The **symbol** is the name a library exports. It is normally the name you declare, but `#Link` takes a second argument when the two should differ, so a terse C name can be given a readable Rux one: ```rux // Rux name first, then the symbol the library exports. #Link(CRuntime, "abs") extern func AbsoluteValue(n: c_int) -> c_int; #Link(CRuntime, "strlen") extern func CStringLength(text: *char8) -> size_t; ``` The program calls `AbsoluteValue(-7)`; the library sees a call to `abs`. The first argument is the library, and `#Link` accepts only a constant declared in the same file, so the program chooses the C runtime's name itself: ```rux when #target.os { .FreeBSD => const CRuntime = "libc.so.7"; .Linux => const CRuntime = "libc.so.6"; .macOS => const CRuntime = "libSystem.B.dylib"; .Windows => const CRuntime = "ucrtbase.dll"; else => #Error("This lesson needs to know the name of this system's C runtime") } ``` ## Calling conventions The **calling convention** decides which registers and stack slots carry the arguments and the result. x86-64 has two in common use, and AArch64 one: | Convention | Used by | First integer arguments | Result | | ---------- | ---------------------------------- | -------------------------------------- | ------ | | Win64 | Windows on x86-64 | `rcx`, `rdx`, `r8`, `r9` | `rax` | | System V | Linux, macOS and FreeBSD on x86-64 | `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9` | `rax` | | AAPCS64 | every system on AArch64 | `x0` to `x7` | `x0` | `#Abi` chooses one, with three names: `.Win64`, `.SysV`, and `.C`, which means "the C convention of whatever target is being built". Externs use `.C` unless told otherwise, which is why `AbsoluteValue` needed no `#Abi` — on Windows it is Win64, on Linux System V, and always what the C runtime expects. ## A Rux function that C calls `qsort` sorts an array of anything. It does not know how to compare your values, so you hand it a function that does, and `qsort` calls that function itself, again and again: ```rux #Abi(.C) func Descending(left: *opaque, right: *opaque) -> c_int { let a = *(left as *c_int); let b = *(right as *c_int); if a > b { return -1; } if a < b { return 1; } return 0; } ``` ```rux var values: c_int[5] = [3, 9, 1, 7, 5]; qsort(@values[0] as *var opaque, values.length, sizeof(c_int), Descending); ``` ```mermaid sequenceDiagram participant M as Main (Rux) participant Q as qsort (C runtime) participant D as Descending (Rux, C convention) M->>Q: qsort(values, 5, 4, Descending) loop for each pair it compares Q->>D: Descending(left, right) D-->>Q: negative, zero or positive end Q-->>M: values sorted in place ``` Now C is the caller, so `Descending` must receive its arguments where C puts them. Rux functions already use the C convention on every target Rux supports, so the program would work without the attribute. `#Abi(.C)` writes the promise down, so that it stays true — and so the next reader knows this function is called from outside Rux. The comparison gets two untyped addresses, because `qsort` works on any type. The function converts them back to `*c_int` and reads the numbers, then answers the way C expects: negative when `left` should come first, positive when `right` should, zero for a tie. Returning −1 for the larger value is what makes the order descending. ## When the two sides disagree If two Rux functions disagree about a convention, the compiler sorts it out. Code it did not compile is different. Change the attribute on `Descending` to `#Abi(.SysV)` and build on Windows: `qsort` puts the two addresses in `rcx` and `rdx`, `Descending` reads `rdi` and `rsi`, and compares whatever happened to be there. Nothing reports it. One run printed `sorted by C 1 3 5 9 7` — not descending, not ascending, just wrong. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Platform/Abi){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // When compiled code calls compiled code, both sides must agree on things no source file shows. // That agreement is the ABI, the application binary interface, and two parts of it can be // written in Rux. // // The symbol: the name the library exports. It is normally the Rux name, but `#Link` takes a // second argument when the two differ, so a terse C name can be given a readable Rux one. // // The calling convention: which registers and stack slots carry the arguments and the result. // `#Abi` chooses it. `.C` is the C convention of whatever target is being built. On x86-64 it // is `.Win64` on Windows and `.SysV` on Linux, macOS and FreeBSD, and those two can be named // directly. Externs use `.C` unless told otherwise. // // If two Rux functions disagree, the compiler sorts it out, since it compiles both sides. Code // it did not compile is different: if a declaration names the wrong convention, the arguments // arrive in the wrong registers. Nothing reports that. The function simply reads whatever // happened to be there. import Core::{ #Error, #target }; import C::{ c_int, qsort, size_t }; import Io::{ Print, PrintLine }; // `#Link` needs a constant from this file, so the C runtime's library name is chosen here. when #target.os { .FreeBSD => const CRuntime = "libc.so.7"; .Linux => const CRuntime = "libc.so.6"; .macOS => const CRuntime = "libSystem.B.dylib"; .Windows => const CRuntime = "ucrtbase.dll"; else => #Error("This lesson needs to know the name of this system's C runtime") } // Rux name first, then the symbol the library exports. #Link(CRuntime, "abs") extern func AbsoluteValue(n: c_int) -> c_int; #Link(CRuntime, "strlen") extern func CStringLength(text: *char8) -> size_t; // C's qsort calls this function itself, so the function must use the convention C expects. // Rux functions already use that convention on every target it supports. `#Abi(.C)` writes the // promise down, so it stays true. #Abi(.C) func Descending(left: *opaque, right: *opaque) -> c_int { let a = *(left as *c_int); let b = *(right as *c_int); if a > b { return -1; } if a < b { return 1; } return 0; } func Main() -> int { PrintLine("AbsoluteValue(-7) {}", AbsoluteValue(-7)); PrintLine("CStringLength(\"abi\") {}", CStringLength("abi".data)); var values: c_int[5] = [3, 9, 1, 7, 5]; qsort(@values[0] as *var opaque, values.length, sizeof(c_int), Descending); Print("sorted by C "); for value in values { Print(" {}", value); } PrintLine(); return 0; } ``` Besides `Io`, its `Rux.toml` lists `C` and `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/Platform/Abi rux run ``` ```text AbsoluteValue(-7) 7 CStringLength("abi") 3 sorted by C 9 7 5 3 1 ``` ## Common mistakes ::warning **Taking the library name from another package.**:br The `C` package has its own `CRuntime` constant, but `#Link` cannot use it: imported from `C`, it fails with `error: '#Link' library name 'CRuntime' is not a compile-time constant`. Declare the name in the file that uses it. :: ::warning **Naming a convention that does not exist.**:br`#Abi(.Fast)` fails with `error: unknown ABI '.Fast'; valid ABIs are: .C, .SysV, .Win64`. :: ::warning **The wrong convention on a callback.**:br A function that C calls must use the convention C uses. Name the wrong one and nothing complains: the arguments arrive in registers the function never reads, and it computes with garbage. Prefer `.C` for anything handed to C. :: ## Try it yourself 1. Bind the C runtime's `toupper` under the Rux name `UpperCase`, taking and returning a `c_int`, and print `UpperCase('r' as c_int) as char8`. 2. Turn `Descending` into `Ascending`, so the program prints `1 3 5 7 9`. 3. On Windows, change `#Abi(.C)` on `Descending` to `#Abi(.SysV)` and run it a few times. On Linux or macOS, try `#Abi(.Win64)` instead. ## Learn more - [Abi](https://rux-lang.dev/docs/lang/attributes/abi) and [Link](https://rux-lang.dev/docs/lang/attributes/link) in the Rux Reference - [`abs`](https://rux-lang.dev/docs/api/c/abs) and the rest of [the C package](https://rux-lang.dev/docs/api/c) - [C interop](https://rux-lang.dev/docs/learn/c-interop) — C's types, pointers and structs - [Callback](https://rux-lang.dev/docs/learn/callback) — passing a function as a value, entirely inside Rux - [Assembly](https://rux-lang.dev/docs/learn/asm) — the next lesson, where you write the convention's registers by hand # Assembly ::note **You'll need**: [ABI](https://rux-lang.dev/docs/learn/abi), [When](https://rux-lang.dev/docs/learn/when), [Target](https://rux-lang.dev/docs/learn/target), [Compile error](https://rux-lang.dev/docs/learn/compile-error) :: ::tip **x86-64 only.**:br The program runs on Windows, Linux and macOS on an x86-64 processor. Built for any other architecture, it stops with an `#Error`; the AArch64 version is the next lesson, [ARM assembly](https://rux-lang.dev/docs/learn/asm-arm). :: Every function ends up as machine code: a list of processor instructions. Normally the compiler writes that list for you. Once in a while you need to write it yourself — for an instruction the language has no way to express, or for exact control over what runs. An `asm func` is a function whose body is processor instructions instead of statements. The compiler emits them as written and does not look inside. That is the point, and also the danger. Nothing in the body is checked. Get a register wrong and the function returns the wrong answer without any warning. Read this lesson as a look under the floor, not as a way to write everyday code. ## A body made of instructions ```rux // Win64: the first two integers arrive in rcx and rdx. The result goes back in rax. #Abi(.Win64) asm func AddWin64(a: int64, b: int64) -> int64 { mov rax, rcx add rax, rdx ret } ``` The signature is ordinary Rux: two `int64` parameters, an `int64` result. But `a` and `b` are not names inside the body. The body works on **registers** — the processor's own handful of 64-bit storage slots, named `rax`, `rcx`, `rdx` and so on — and the [calling convention](https://rux-lang.dev/docs/learn/abi) decides which registers the arguments arrive in. Under Win64, `a` is in `rcx`, `b` is in `rdx`, and the caller will look for the result in `rax`. The instructions are written destination first: | Instruction | Means | | -------------- | ------------------------------------------- | | `mov rax, rcx` | copy `rcx` into `rax` — so `rax` is now `a` | | `add rax, rdx` | `rax = rax + rdx` — so `rax` is now `a + b` | | `ret` | return to the caller, which reads `rax` | Nothing is added around the body: no setup, no clean-up, not even the `ret`. Leave out the `ret` and the function still builds — and the processor carries on into whatever bytes happen to follow it. ## The body is written against a convention The convention decides which registers the arguments arrive in, so every body here carries an `#Abi` naming the one it was written for. Here is the same addition for System V, the convention of Linux, macOS and FreeBSD: ```rux // The same addition for System V, whose first two integers arrive in rdi and rsi. #Abi(.SysV) asm func AddSysV(a: int64, b: int64) -> int64 { mov rax, rdi add rax, rsi ret } ``` Pinned like that, the same body works on Windows, Linux and macOS alike: the compiler adapts each call to the function's convention, so on Windows it calls `AddSysV` with the arguments in `rdi` and `rsi`. That is why the program can call both versions and get 42 twice. Without an `#Abi`, an `asm func` uses the target's C convention — Win64 on Windows, System V elsewhere — and a body that reads `rcx` would read the wrong register as soon as it was built for Linux. ## A loop is a label and a jump There is no `while` in assembly. A loop is made of the pieces `while` is built from: a **label** to jump back to, a test, and a **conditional jump**: ```rux // A loop is a label and a conditional jump. These are the pieces `while` is built from. #Abi(.Win64) asm func SumTo(n: int64) -> int64 { xor rax, rax next: test rcx, rcx jle done add rax, rcx dec rcx jmp next done: ret } ``` `xor rax, rax` sets the total to zero. `test rcx, rcx` compares `n` with zero, and `jle done` leaves the loop when it is zero or less. Otherwise `n` is added to the total, `dec` takes one off `n`, and `jmp next` goes round again. Drawn out, it is the `while` loop you would have written in Rux: ```mermaid flowchart LR start(["total = 0
xor rax, rax"]) --> next{"next:
n ≤ 0?
test, jle"} next -- "no" --> body["total += n
n -= 1
add, dec"] body -- "jmp next" --> next next -- "yes" --> done(["done:
return total
ret"]) ``` ## One architecture, chosen with `when` Assembly belongs to one processor family. These instructions mean nothing to an ARM processor, so the functions sit inside a [`when`](https://rux-lang.dev/docs/learn/when) on the [target](https://rux-lang.dev/docs/learn/target)'s architecture: ```rux when #target.arch { .X86_64 => { // … the three asm functions … }, else => #Error("This lesson is x86-64 assembly; the AArch64 version is the AsmArm lesson") } ``` `rux build --target linux-aarch64` stops with that message. The assembler also knows only a chosen subset of x86-64 — enough for stubs, system calls and arithmetic, but not every instruction the processor has. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Platform/Asm){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Every function ends up as machine code. Once in a while you need to write that code yourself, // for an instruction the language has no way to express, or for exact control over what runs. // // An `asm func` is a function whose body is processor instructions instead of statements. The // compiler emits them as written and does not look inside. That is the point, and also the // danger: nothing in the body is checked. Get a register wrong and the function returns the // wrong answer without any warning. // // The body is written against a calling convention, because the convention decides which // registers the arguments arrive in and where the result must go. So every x86-64 body here // carries an `#Abi` naming the one it was written for. Pinned like that, the same body works // on Windows, Linux and macOS alike, and the compiler adapts each call to it. // // Assembly also belongs to one architecture. This lesson is x86-64 only, and `when` stops the // build anywhere else. The AArch64 version is the next lesson. import Core::{ #Error, #target }; import Io::PrintLine; when #target.arch { .X86_64 => { // Win64: the first two integers arrive in rcx and rdx. The result goes back in rax. #Abi(.Win64) asm func AddWin64(a: int64, b: int64) -> int64 { mov rax, rcx add rax, rdx ret } // The same addition for System V, whose first two integers arrive in rdi and rsi. #Abi(.SysV) asm func AddSysV(a: int64, b: int64) -> int64 { mov rax, rdi add rax, rsi ret } // A loop is a label and a conditional jump. These are the pieces `while` is built from. #Abi(.Win64) asm func SumTo(n: int64) -> int64 { xor rax, rax next: test rcx, rcx jle done add rax, rcx dec rcx jmp next done: ret } }, else => #Error("This lesson is x86-64 assembly; the AArch64 version is the AsmArm lesson") } func Main() -> int { // Nothing at the call site says these functions are unusual. PrintLine("AddWin64(20, 22) {}", AddWin64(20, 22)); PrintLine("AddSysV(20, 22) {}", AddSysV(20, 22)); PrintLine("SumTo(10) {}", SumTo(10)); PrintLine("SumTo(100) {}", SumTo(100)); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/Platform/Asm rux run ``` ```text AddWin64(20, 22) 42 AddSysV(20, 22) 42 SumTo(10) 55 SumTo(100) 5050 ``` ## Common mistakes ::warning **A body written for one convention, pinned to another.**:br Mark `AddSysV` with `#Abi(.Win64)` and it still builds, but the caller now puts the arguments in `rcx` and `rdx` while the body adds `rdi` and `rsi`. On Windows the call then returned 0 instead of 42. Always pin the convention the body was written for. :: ::warning **An instruction the assembler does not implement.**:br`popcnt rax, rdx` fails with `error: instruction 'popcnt' is recognized for target 'windows-x86_64' but is not implemented by its assembler`. Stick to the subset in the Reference's assembler page. :: ::warning **Using a parameter name in the body.**:br The parameters are not names inside an `asm func`; the arguments exist only in their registers. `add rax, b` fails with `error: unsupported operands for 'add'` — write `add rax, rdx`. :: ## Try it yourself 1. Write `Subtract(a: int64, b: int64) -> int64` for Win64, using `sub` in place of `add`. Check that `Subtract(50, 8)` is 42. 2. Write `Negate(n: int64) -> int64` with `neg`, which flips the sign of a register. 3. Write a System V version of `SumTo`, reading `n` from `rdi`, and call it on Windows. 4. Write `Max(a: int64, b: int64) -> int64` for Win64: copy `rcx` to `rax`, compare `rcx` with `rdx`, jump to the end with `jge` when `rcx` is already the larger, and otherwise copy `rdx` to `rax`. ## Learn more - [Assembler functions](https://rux-lang.dev/docs/lang/ffi/assembly) in the Rux Reference — operands, labels and the supported instructions - [Abi](https://rux-lang.dev/docs/lang/attributes/abi) — pinning the convention a body is written for - [ABI](https://rux-lang.dev/docs/learn/abi) — calling conventions from the Rux side - [ARM assembly](https://rux-lang.dev/docs/learn/asm-arm) — the same functions for AArch64 # ARM assembly ::note **You'll need**: [Assembly](https://rux-lang.dev/docs/learn/asm), [When](https://rux-lang.dev/docs/learn/when), [Target](https://rux-lang.dev/docs/learn/target) :: ::tip **The assembly runs only on AArch64.**:br That means an Apple silicon Mac, a Windows on ARM laptop or ARM Linux. The lesson still builds and runs on an x86-64 machine, but there `when` leaves the AArch64 functions out and the program prints a note instead. :: The [Assembly](https://rux-lang.dev/docs/learn/asm) lesson wrote its functions in x86-64 assembly, and that code means nothing to an ARM processor. AArch64 — the 64-bit ARM architecture in Apple silicon Macs, Windows on ARM laptops and most phones — has its own instructions, its own registers and its own rules. Code that should run on both needs one body per architecture, and `when #target.arch` picks the right one while compiling. ## A different processor | | x86-64 | AArch64 | | ------------------- | -------------------------------- | -------------------------------------- | | Registers | `rax`, `rcx`, `rdx`, … 16 in all | `x0` to `x30`, plus `xzr`, always zero | | Calling conventions | Win64 or System V, by system | AAPCS64, on every system | | Integer arguments | `rcx`, `rdx` … or `rdi`, `rsi` … | `x0`, `x1`, `x2` … | | Result | `rax` | `x0` | | Arithmetic | two operands: `add rax, rdx` | three: `add x0, x0, x1` | | A number | `0` | `#0` | The second row is good news. With a single calling convention on every operating system, there is no `#Abi` to choose: an AArch64 body works the same on macOS, Windows and Linux. Arguments arrive in `x0`, `x1` and onwards, and the result goes back in `x0` — so the first argument and the result share a register. ## Add, in three operands ```rux asm func Add(a: int64, b: int64) -> int64 { add x0, x0, x1 ret } ``` Most AArch64 instructions take a destination and two sources, so `add x0, x0, x1` means `x0 = x0 + x1`. The first argument is already in `x0`, where the result belongs, so one instruction does the whole job. ## The same loop ```rux // The same loop as the x86-64 lesson. xzr is a register that always reads as zero. asm func SumTo(n: int64) -> int64 { mov x1, x0 mov x0, xzr next: cmp x1, #0 b.le done add x0, x0, x1 sub x1, x1, #1 b next done: ret } ``` Because `x0` is both `n` and the result, the loop first moves `n` aside into `x1`, then zeroes the total by copying `xzr`. After that it is the x86-64 loop word for word: `cmp` and the conditional branch `b.le` (branch if less or equal) leave when `n` runs out, `b` jumps back unconditionally. Note the `#` on numbers written into an instruction: `#0`, `#1`. ## Choosing without a jump ```rux // csel picks one of two registers based on the comparison before it, with no jump. asm func Max(a: int64, b: int64) -> int64 { cmp x0, x1 csel x0, x0, x1, gt ret } ``` `csel` — conditional select — reads "`x0` becomes `x0` if the comparison said greater than, otherwise `x1`". The x86-64 version of `Max` needs a label and a jump; here it is one instruction with no branch at all. ## Building on either machine The three functions sit in an AArch64 arm, and every other architecture gets an empty one: ```rux when #target.arch { .AArch64 => { // … Add, SumTo and Max … }, else => {} } ``` A branch that is not taken is never checked or assembled, so on an x86-64 machine the functions simply do not exist. `Main` must not call them there, so it asks the same question again: ```rux when #target.arch == .AArch64 { PrintLine("Add(20, 22) {}", Add(20, 22)); PrintLine("SumTo(100) {}", SumTo(100)); PrintLine("Max(4, 9) {}", Max(4, 9)); } else { PrintLine("This lesson's assembly is for AArch64, and this machine is not one."); PrintLine("The AArch64 functions were left out of this build, so none of them ran."); } ``` ```mermaid flowchart LR src["Main.rux"] --> q{"target arch?"} q -- ".AArch64" --> a["Add, SumTo and Max
assembled and called"] q -- "anything else" --> x["no AArch64 functions;
Main prints a note"] ``` Unlike the Assembly lesson, nothing here stops the build: a program can carry assembly for one architecture and still be useful on another. You do not need an ARM machine to check the AArch64 code, either — `rux build --target linux-aarch64` assembles it on any machine and reports the instructions it cannot encode. Running the result is the only step that needs real AArch64 hardware. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Platform/AsmArm){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // The Asm lesson wrote its functions in x86-64 assembly, and that code means nothing to an ARM // processor. AArch64, the 64-bit ARM architecture in Apple silicon Macs, Windows on ARM laptops // and most phones, has its own instructions, its own registers and its own rules. Code that // should run on both needs one body per architecture. // // `when #target.arch` picks the body while compiling. A branch that is not taken is never // checked or assembled, so this file builds on an x86-64 machine too: the AArch64 functions // below are simply left out, and `Main` says so instead of calling them. // // Unlike x86-64, AArch64 has a single calling convention, AAPCS64, on every operating system, // so there is no `#Abi` to choose. Integer arguments arrive in x0, x1, x2 and so on, and the // result goes back in x0. The instructions are different too: most take a destination and two // sources, so `add x0, x0, x1` means x0 = x0 + x1. import Core::{ #target }; import Io::PrintLine; when #target.arch { .AArch64 => { asm func Add(a: int64, b: int64) -> int64 { add x0, x0, x1 ret } // The same loop as the x86-64 lesson. xzr is a register that always reads as zero. asm func SumTo(n: int64) -> int64 { mov x1, x0 mov x0, xzr next: cmp x1, #0 b.le done add x0, x0, x1 sub x1, x1, #1 b next done: ret } // csel picks one of two registers based on the comparison before it, with no jump. asm func Max(a: int64, b: int64) -> int64 { cmp x0, x1 csel x0, x0, x1, gt ret } }, else => {} } func Main() -> int { when #target.arch == .AArch64 { PrintLine("Add(20, 22) {}", Add(20, 22)); PrintLine("SumTo(100) {}", SumTo(100)); PrintLine("Max(4, 9) {}", Max(4, 9)); } else { PrintLine("This lesson's assembly is for AArch64, and this machine is not one."); PrintLine("The AArch64 functions were left out of this build, so none of them ran."); } return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/Platform/AsmArm rux run ``` On an AArch64 machine it prints the first block below. That output comes from the Examples repository and needs real AArch64 hardware to reproduce; on an x86-64 machine you will see the second block. ```text Add(20, 22) 42 SumTo(100) 5050 Max(4, 9) 9 ``` On an x86-64 machine: ```text This lesson's assembly is for AArch64, and this machine is not one. The AArch64 functions were left out of this build, so none of them ran. ``` ## Common mistakes ::warning **Calling an AArch64 function outside the guard.**:br On an x86-64 machine the functions were never compiled, so a call to `Add` outside `when #target.arch == .AArch64` fails with `error: name 'Add' is not defined in this scope`. :: ::warning **x86-64 habits in an AArch64 body.**:br The register names and operand counts are different. Built for AArch64, `mov rax, rcx` fails with `error: 'mov' takes a register as operand 1, found the symbol 'rax'`, and `add rax, rdx` with `error: 'add' takes 3 operands, found 2`. :: ::warning **Trusting an x86-64 build to check the ARM code.**:br`rux run` on an x86-64 machine never looks inside the AArch64 branch, so a mistake there stays hidden. Build with `--target linux-aarch64` to have it assembled. :: ## Try it yourself 1. Run `rux build --target linux-aarch64`, then `rux build --target macos-aarch64`, and find the two programs under `Bin/Debug/`. 2. Add `Subtract(a: int64, b: int64) -> int64` with a single `sub`, and call it from the AArch64 branch of `Main`. Build for `linux-aarch64` to check it assembles. 3. Add `Min` next to `Max`, using `csel` with the condition `lt`. 4. If you have an Apple silicon Mac, a Windows on ARM laptop or an ARM Linux board, run the program there and compare its output with the first block above. ## Learn more - [Assembler functions](https://rux-lang.dev/docs/lang/ffi/assembly) in the Rux Reference - [Assembly](https://rux-lang.dev/docs/learn/asm) — the x86-64 version of these functions - [Target](https://rux-lang.dev/docs/learn/target) — `#target.arch` and building for another machine with `--target` - [`rux build`](https://rux-lang.dev/docs/cli/build) — the targets `--target` accepts # Part 25: Projects Every other part of the course teaches one idea per lesson. This part does the opposite: each project is a complete small program that puts many ideas to work at once — a calculator that explains its errors, a word counter, a guessing game, a to-do list kept in a file. They are not meant to be read after Part 24. Each one is a **checkpoint** for an earlier part, and uses nothing the course has not taught by then, so you can stop after that part and build something real. ## What you will learn - How the single ideas of the lessons combine into a program with a design: small functions with one job each, data described by types, and `Main` reading like a summary. - How to treat input as hostile: reading lines, parsing numbers, and telling the end of the input, a read error, a malformed number and an out-of-range one apart. - How to model every way a program can fail as an error type, and when to stop, pass a failure on, or skip it and carry on. - How to choose the right standard package for a job — collections, algorithms, time, randomness, entropy, files and JSON. - How to run interactive programs, and which projects need a keyboard, a sound card or Windows. ## Which part unlocks which project The arrows point from the part you need to have finished to the projects you can build after it: ```mermaid flowchart LR p3["Part 3
Control flow"] --> thanks["Thanks"] p3 --> fizz["FizzBuzz"] p4["Part 4
Functions"] --> temp["Temperature"] p5["Part 5
Sequences"] --> prime["Prime"] p9["Part 9
Errors"] --> calc["Calculator"] p16["Part 16
Numbers"] --> circle["Circle"] p16 --> quad["Quadratic"] p17["Part 17
Collections"] --> words["Word count"] p17 --> inv["Inventory"] p18["Part 18
Algorithms"] --> stats["Statistics"] p20["Part 20
Utilities"] --> guess["Guess"] p20 --> age["Age"] p20 --> pass["Password"] p20 --> launch["Launch"] p21["Part 21
Data formats"] --> notes["Notes"] p24["Part 24
Platform"] --> melody["Melody"] ``` | After part | Project | What it practises | | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | [3 Control flow](https://rux-lang.dev/docs/learn/control-flow) | [Thanks](https://rux-lang.dev/docs/learn/thanks), [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz) | Loops, `match` expressions, the order of an `else if` chain | | [4 Functions](https://rux-lang.dev/docs/learn/functions) | [Temperature](https://rux-lang.dev/docs/learn/temperature) | Small functions, recursion, default arguments | | [5 Sequences](https://rux-lang.dev/docs/learn/sequences) | [Prime](https://rux-lang.dev/docs/learn/prime) | An array of flags and the sieve of Eratosthenes | | [9 Errors](https://rux-lang.dev/docs/learn/errors) | [Calculator](https://rux-lang.dev/docs/learn/calculator) | An error variant, `fail`, `?`, `match` and `catch` in one program | | [16 Numbers](https://rux-lang.dev/docs/learn/numbers) | [Circle](https://rux-lang.dev/docs/learn/circle), [Quadratic](https://rux-lang.dev/docs/learn/quadratic) | Reading and checking numbers, infinity, NaN and floating-point accuracy | | [17 Collections](https://rux-lang.dev/docs/learn/collections) | [Word count](https://rux-lang.dev/docs/learn/word-count), [Inventory](https://rux-lang.dev/docs/learn/inventory) | Hash and tree maps, a vector of structs, two kinds of failure | | [18 Algorithms](https://rux-lang.dev/docs/learn/algorithms) | [Statistics](https://rux-lang.dev/docs/learn/statistics) | Sorting, folding and extremes, and the empty case | | [20 Utilities](https://rux-lang.dev/docs/learn/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) | Randomness, entropy, calendar arithmetic, pauses and a game loop | | [21 Data formats](https://rux-lang.dev/docs/learn/data-formats) | [Notes](https://rux-lang.dev/docs/learn/notes) | JSON saved to a file atomically, and loaded back | | [24 Platform](https://rux-lang.dev/docs/learn/platform) | [Melody](https://rux-lang.dev/docs/learn/melody) | Calling a Windows function directly, chosen while compiling | Circle and Quadratic are really about [Part 14: Text](https://rux-lang.dev/docs/learn/text) — reading and parsing a line — but they also borrow `IsFinite` and the Math package from Part 16, so they unlock there. ## Lessons | | Lesson | What you will learn | | ----- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------- | | 25.1 | [Thanks](https://rux-lang.dev/docs/learn/thanks) | draw RUX as an ASCII banner and thank everyone who helps build it | | 25.2 | [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz) | the classic counting game | | 25.3 | [Temperature](https://rux-lang.dev/docs/learn/temperature) | a table of Celsius and Fahrenheit temperatures | | 25.4 | [Prime](https://rux-lang.dev/docs/learn/prime) | every prime below 100, found with the sieve of Eratosthenes | | 25.5 | [Calculator](https://rux-lang.dev/docs/learn/calculator) | evaluate expressions and report every way they can go wrong | | 25.6 | [Circle](https://rux-lang.dev/docs/learn/circle) | read a radius, check it, and print the circle's measurements | | 25.7 | [Quadratic](https://rux-lang.dev/docs/learn/quadratic) | solve a quadratic equation, including linear and degenerate cases | | 25.8 | [Word count](https://rux-lang.dev/docs/learn/word-count) | count how often each word appears | | 25.9 | [Inventory](https://rux-lang.dev/docs/learn/inventory) | keep a stock list of items, with updates that can fail | | 25.10 | [Statistics](https://rux-lang.dev/docs/learn/statistics) | count, extremes, mean, variance and median, including empty and single-value input | | 25.11 | [Guess](https://rux-lang.dev/docs/learn/guess) | a number guessing game with seven valid guesses from 1 to 100 | | 25.12 | [Age](https://rux-lang.dev/docs/learn/age) | an age in completed years, months and days, with month-end clamping | | 25.13 | [Password](https://rux-lang.dev/docs/learn/password) | a 16-character password drawn from system entropy with rejection sampling | | 25.14 | [Launch](https://rux-lang.dev/docs/learn/launch) | a mission checklist, a bounded countdown and a launch | | 25.15 | [Notes](https://rux-lang.dev/docs/learn/notes) | keep notes in a JSON file | | 25.16 | [Melody](https://rux-lang.dev/docs/learn/melody) | play a tune through the Windows console speaker (Windows only) | ## Before you start Pick the project for the part you have just finished, rather than reading this part in order. Each project page lists exactly which lessons it leans on, links them where they are used, and ends with exercises that extend the program — those are the real work. Each project is a package in the Examples repository's `Projects/` folder: ```sh cd Examples/Projects/FizzBuzz rux run ``` Most projects print the same output on every run. A few are different: | Project | What to expect | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | [Circle](https://rux-lang.dev/docs/learn/circle), [Quadratic](https://rux-lang.dev/docs/learn/quadratic), [Guess](https://rux-lang.dev/docs/learn/guess), [Launch](https://rux-lang.dev/docs/learn/launch) | They wait for you to type. Answer the prompt and press Enter, or pipe the input in | | [Guess](https://rux-lang.dev/docs/learn/guess), [Password](https://rux-lang.dev/docs/learn/password) | A different number or password on every run | | [Notes](https://rux-lang.dev/docs/learn/notes) | Writes a file in the package's `Bin/` folder, and deletes it again | | [Melody](https://rux-lang.dev/docs/learn/melody) | Windows only, and plays sound through the console speaker | ## After this part This is the end of the course. From here, the best next step is a program of your own — take the project closest to what you want to build and change it until it is yours. The [Rux Reference](https://rux-lang.dev/docs/lang/introduction) has the full rules behind every lesson, the [API reference](https://rux-lang.dev/docs/api/introduction) covers the standard packages, and the [cheat sheet](https://rux-lang.dev/docs/learn/cheatsheet) keeps the syntax on one page. When you want to share what you made, [Part 22: Packages](https://rux-lang.dev/docs/learn/packages) shows how to turn it into a package others can depend on. # Thanks ::note **You'll need**: Parts 1–3 — this project is the checkpoint for Control flow, and leans on [Console](https://rux-lang.dev/docs/learn/console), [For](https://rux-lang.dev/docs/learn/for) and [Match expression](https://rux-lang.dev/docs/learn/match-expression). :: This is the first checkpoint project of the course. It prints the word RUX in large letters, draws a rule under it, and lists everything that goes into building a language — a thank-you card written in code. Nothing here is new. The program uses only what Parts 1–3 taught: `Print` and `PrintLine`, `for` over a range, and `match` used as an expression. The point of the project is to see those few tools add up to something that looks designed. If you can read every line of it, you have Control flow under your belt. ## How it is put together `Main` does three jobs, one after another, and each is a loop: ```mermaid flowchart LR banner["Banner
for row in 0..6
match row → one line"] --> rule["Rule
for i in 0..width
one = per pass"] rule --> list["List
for item in 1..=count
match item → one line"] ``` | Piece | What it does | Lessons it uses | | --------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Banner | Picks the text of each row of the big letters | [For](https://rux-lang.dev/docs/learn/for), [Range](https://rux-lang.dev/docs/learn/range), [Match expression](https://rux-lang.dev/docs/learn/match-expression) | | Rule | Repeats one character `width` times | [For](https://rux-lang.dev/docs/learn/for), [Console](https://rux-lang.dev/docs/learn/console), [Variable](https://rux-lang.dev/docs/learn/variable) | | Thank-you list | Picks the text of each numbered item | [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Range](https://rux-lang.dev/docs/learn/range) | | Closing message | Plain `PrintLine` calls, with empty ones as gaps | [Hello, World](https://rux-lang.dev/docs/learn/hello), [Console](https://rux-lang.dev/docs/learn/console) | There are no arrays yet — they arrive in [Part 5](https://rux-lang.dev/docs/learn/sequences) — so a list of lines cannot be stored and walked. Instead a loop counts the rows, and a `match` says what belongs on each one. ## Big letters are rows of characters A large letter is only a few rows of `#` and spaces printed one under another. Each row here runs across all three letters at once: ```rux for row in 0..6 { let line = match row { 0 => " #### ## ## ## ## ", 1 => " ## ## ## ## ## ## ", 2 => " ## ## ## ## ### ", 3 => " ##### ## ## ### ", 4 => " ## ## ## ## ## ## ", else => " ## ## #### ## ## " }; PrintLine("{}", line); } ``` Read the arms from top to bottom rather than one at a time, and the shapes of R, U and X appear. Three details are worth noticing: - `0..6` is a half-open range: it gives 0, 1, 2, 3, 4 and 5 — six rows — and stops before 6. - The `match` is an **expression**. Each arm produces a piece of text, and the whole `match` hands that text to `let line`. - The last row is the `else` arm. A match over an `int` must have an answer for *every* integer, not only the six the loop will ever ask about, so the final row doubles as the default. ## A rule drawn by repetition The `=` line under the banner is not typed out. A loop prints one character `width` times, and `Print` (not `PrintLine`) keeps them all on one line: ```rux let width = 44; for i in 0..width { Print("="); } PrintLine(); ``` The loop variable `i` is never read — the loop exists only to run its body 44 times. Naming the width once means the rule above and below the title can never disagree, and changing one number resizes both. ## A numbered list without an array The thank-you list uses the same trick as the banner, counted from 1 this time: ```rux for item in 1..=count { let contribution = match item { 1 => "the ideas, and the patience to explain them twice", 2 => "the long discussions, and the short sharp ones", 3 => "the code, the tests, and the unglamorous fixes", 4 => "the critique that was right, and the kind that stung", 5 => "the bug reports written carefully by strangers", 6 => "the documentation nobody is thanked for", 7 => "the funding that bought time to think", 8 => "turning up, reading along, and asking good questions", else => "the posts, the shares, the subscribes, the word of mouth" }; PrintLine(" - {}", contribution); } ``` `count` is 9, and `1..=count` is the inclusive range: it includes `count` itself, so the loop runs for items 1 to 9. As in the banner, the ninth item lives in the `else` arm, which keeps the match complete. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Thanks){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A banner drawn as text, and a thank-you to the people who make Rux. // // Everything here comes from the first three parts of the course: printing, a `for` loop over a // range, and `match` used as an expression. There are no arrays yet, so each list is a loop that // counts rows and a `match` that says what belongs on each one. Large letters are only rows of // characters printed one after another. import Io::{ Print, PrintLine }; func Main() -> int { // Each row runs across all three letters. Read the arms from top to bottom rather than one // at a time, and the shapes of R, U and X appear. PrintLine(); for row in 0..6 { let line = match row { 0 => " #### ## ## ## ## ", 1 => " ## ## ## ## ## ## ", 2 => " ## ## ## ## ### ", 3 => " ##### ## ## ### ", 4 => " ## ## ## ## ## ## ", else => " ## ## #### ## ## " }; PrintLine("{}", line); } PrintLine(); // A rule drawn by repeating one character, rather than by typing it out. let width = 44; for i in 0..width { Print("="); } PrintLine(); PrintLine(" Thank you, everyone"); for i in 0..width { Print("="); } PrintLine(); PrintLine(); // Every kind of help that goes into a language, in no particular order, because none of them // is the one that matters least. The `else` arm is the last item, so a match over numbers // still has an answer for every row. let count = 9; PrintLine("Rux exists because of:"); PrintLine(); for item in 1..=count { let contribution = match item { 1 => "the ideas, and the patience to explain them twice", 2 => "the long discussions, and the short sharp ones", 3 => "the code, the tests, and the unglamorous fixes", 4 => "the critique that was right, and the kind that stung", 5 => "the bug reports written carefully by strangers", 6 => "the documentation nobody is thanked for", 7 => "the funding that bought time to think", 8 => "turning up, reading along, and asking good questions", else => "the posts, the shares, the subscribes, the word of mouth" }; PrintLine(" - {}", contribution); } PrintLine(); PrintLine("A language is a community that happens to have a compiler."); PrintLine("Thank you for being part of this one."); PrintLine(); PrintLine(" -- Ivan Muzyka, creator of Rux"); PrintLine(); return 0; } ``` ## Run it ```sh cd Examples/Projects/Thanks rux run ``` ```text #### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ### ##### ## ## ### ## ## ## ## ## ## ## ## #### ## ## ============================================ Thank you, everyone ============================================ Rux exists because of: - the ideas, and the patience to explain them twice - the long discussions, and the short sharp ones - the code, the tests, and the unglamorous fixes - the critique that was right, and the kind that stung - the bug reports written carefully by strangers - the documentation nobody is thanked for - the funding that bought time to think - turning up, reading along, and asking good questions - the posts, the shares, the subscribes, the word of mouth A language is a community that happens to have a compiler. Thank you for being part of this one. -- Ivan Muzyka, creator of Rux ``` ## Common mistakes ::warning **A match over numbers with no `else` arm.**:br Changing the banner's `else` arm to `5 =>` looks harmless, since the loop never asks for anything else. The compiler disagrees: `error: match on 'int' is not exhaustive; its arms do not cover every value`, with the hint `add an 'else' arm`. It checks the `match` on its own, not the loop around it. See [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive). :: ::warning **`..` where you meant `..=`.**:br Writing `for item in 1..count` compiles and runs, but `1..count` stops before 9, so the last line of the list — the one in the `else` arm — silently disappears. When a range counts things from 1, it almost always wants `..=`. :: ## Try it yourself 1. Make the rule out of `-` instead of `=`, and make it 30 characters wide. How many lines did you have to change? 2. Add a tenth contribution to the list. Remember that the `else` arm has to stay last, and that `count` has to grow too. 3. Number the list instead of using dashes, so it prints `1. the ideas…`, `2. the long discussions…` and so on. 4. Add a fourth letter to the banner — an `!` is the easiest. Every row's text has to grow by the same number of columns, or the letters will lean. ## Learn more - [For](https://rux-lang.dev/docs/learn/for), [Range](https://rux-lang.dev/docs/learn/range) and [Match expression](https://rux-lang.dev/docs/learn/match-expression) — the three tools this project is built from - [Match](https://rux-lang.dev/docs/lang/patterns/match) and [For](https://rux-lang.dev/docs/lang/statements/loops#for) in the Rux Reference - Next project: [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz), the other checkpoint for Control flow # FizzBuzz ::note **You'll need**: Parts 1–3 — this project is a checkpoint for Control flow, and leans on [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic), [Else if](https://rux-lang.dev/docs/learn/else-if) and [For](https://rux-lang.dev/docs/learn/for). :: FizzBuzz is a counting game: go from 1 to 100, but say "Fizz" instead of every multiple of 3, "Buzz" instead of every multiple of 5, and "FizzBuzz" instead of every multiple of both. It began as a children's game for practising division and became the most famous little test in programming — not because it is hard, but because it is so easy to get *almost* right. The program needs only Parts 1–3. It also keeps a tally of each kind of answer, which turns out to be a handy way of checking that the logic is right. ## How it is put together Everything happens inside one `for` loop. Each pass asks a chain of questions about `n`, prints one answer, counts it, and then decides what goes after it — a space or a line break: ```mermaid flowchart LR n(["n from 1 to 100"]) --> q15{"n % 15 == 0?"} q15 -- "yes" --> fb["FizzBuzz"] q15 -- "no" --> q3{"n % 3 == 0?"} q3 -- "yes" --> f["Fizz"] q3 -- "no" --> q5{"n % 5 == 0?"} q5 -- "yes" --> b["Buzz"] q5 -- "no" --> num["the number"] ``` | Piece | Lessons it uses | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | `n % 3 == 0` | [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic) (`%`), [Comparison](https://rux-lang.dev/docs/learn/comparison) | | The chain of questions | [Else if](https://rux-lang.dev/docs/learn/else-if) | | Walking 1 to 100 | [For](https://rux-lang.dev/docs/learn/for), [Range](https://rux-lang.dev/docs/learn/range) | | The tallies | [Mutable](https://rux-lang.dev/docs/learn/mutable), [Assignment](https://rux-lang.dev/docs/learn/assignment) (`+=`) | | Ten answers to each line | [If](https://rux-lang.dev/docs/learn/if), [Console](https://rux-lang.dev/docs/learn/console) (`Print` and `PrintLine`) | ## "Divides exactly" is a remainder of zero `%` gives the remainder of a division. When a number divides by 3 exactly, nothing is left over, so `n % 3 == 0` reads as "n is a multiple of 3". A number that is a multiple of both 3 and 5 is a multiple of 15, so one test covers "both": ```rux if n % 15 == 0 { Print("FizzBuzz"); fizzBuzzes += 1; } else if n % 3 == 0 { Print("Fizz"); fizzes += 1; } else if n % 5 == 0 { Print("Buzz"); buzzes += 1; } else { Print("{}", n); } ``` ## The order of the questions is the whole trick An `else if` chain runs the **first** branch whose condition holds and skips the rest. Fifteen is a multiple of 3, so a chain that asks about 3 first answers "Fizz" for 15 and never gets as far as "FizzBuzz". The most specific question has to come first; the general ones follow. That is also why the tallies are worth printing. In a correct run there are 6 FizzBuzzes. If you move the `% 3` test to the top of the chain, the program still compiles and still prints a hundred answers, but the tally reports `Fizz 33 times` and `FizzBuzz 0 times` — every FizzBuzz has been swallowed by Fizz. ## Ten to a line After each answer the loop decides what comes next. Every tenth number ends the line; every other one is followed by a space: ```rux if n % 10 == 0 { PrintLine(); } else { Print(" "); } ``` `Print` writes without ending the line, so the answers build up across it until `PrintLine()` with no arguments ends it. The same `%` test that finds the multiples of 3 here finds every tenth `n`. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/FizzBuzz){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // FizzBuzz: count from 1 to 100, but say "Fizz" instead of every multiple of 3, "Buzz" instead // of every multiple of 5, and "FizzBuzz" instead of every multiple of both. // // It began as a children's game for practising division, and became the most famous little test // in programming because it is so easy to get almost right. The trap is the order of the // questions. Fifteen is a multiple of 3, so a program that asks about 3 first says "Fizz" and // never gets as far as "FizzBuzz". The multiple of both has to be checked before either one. // // Everything here comes from the first three parts: `%` gives the remainder of a division, so // `n % 3 == 0` means "n divides by 3 exactly"; an `if` / `else if` chain picks the first question // that answers yes; and a `for` loop walks the numbers. import Io::{ Print, PrintLine }; func Main() -> int { var fizzes = 0; var buzzes = 0; var fizzBuzzes = 0; for n in 1..=100 { // A multiple of both 3 and 5 is a multiple of 15, so one test covers it. if n % 15 == 0 { Print("FizzBuzz"); fizzBuzzes += 1; } else if n % 3 == 0 { Print("Fizz"); fizzes += 1; } else if n % 5 == 0 { Print("Buzz"); buzzes += 1; } else { Print("{}", n); } // Ten to a line keeps the output short: a space between entries, and a line break after // every tenth one. if n % 10 == 0 { PrintLine(); } else { Print(" "); } } PrintLine(); PrintLine("Fizz {} times", fizzes); PrintLine("Buzz {} times", buzzes); PrintLine("FizzBuzz {} times", fizzBuzzes); PrintLine("numbers {} times", 100 - fizzes - buzzes - fizzBuzzes); return 0; } ``` ## Run it ```sh cd Examples/Projects/FizzBuzz rux run ``` ```text 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz Fizz 27 times Buzz 14 times FizzBuzz 6 times numbers 53 times ``` ## Common mistakes ::warning **Asking the general question first.**:br`if n % 3 == 0` before `if n % 15 == 0` is still valid code, so the compiler cannot help — 15, 30, 45 and the rest come out as `Fizz`. The tally gives it away: Fizz 33 times, FizzBuzz 0 times. Always order a chain from the most specific case to the least. :: ::warning **`=` where you meant `==`.**:br`if n % 15 = 0` is an assignment, not a comparison, and is refused: `error: operator '=' requires an assignable target, but its left operand has type 'int'`. A condition compares with `==`. :: ::warning **A tally declared with `let`.**:br The counters change on every pass, so they need `var`. With `let fizzes = 0;` the line `fizzes += 1;` fails with `error: cannot modify immutable variable 'fizzes'`. :: ## Try it yourself 1. Add a fourth rule: say "Bazz" for multiples of 7. Where in the chain does each new test have to go, and what should a multiple of 3, 5 and 7 print? 2. Count to 30 instead of 100. The last line of the tally computes the plain numbers as `100 - fizzes - buzzes - fizzBuzzes` — what else has to change so the report stays right? 3. Print five answers to a line instead of ten. 4. Rewrite the chain so it never mentions 15: test 3 and 5 on their own, and use `&&` for the case where both hold. Check that the tallies come out the same. ## Learn more - [Else if](https://rux-lang.dev/docs/learn/else-if) — why the first condition that holds wins - [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic) — `%` and integer division - [If](https://rux-lang.dev/docs/lang/statements/if) and [Arithmetic operators](https://rux-lang.dev/docs/lang/expressions/arithmetic) in the Rux Reference - Next project: [Temperature](https://rux-lang.dev/docs/learn/temperature), the checkpoint for Functions # Temperature ::note **You'll need**: Parts 1–4 — this project is the checkpoint for Functions, and leans on [Function](https://rux-lang.dev/docs/learn/function), [Recursion](https://rux-lang.dev/docs/learn/recursion) and [Default argument](https://rux-lang.dev/docs/learn/default-argument). :: This project prints two conversion tables — Celsius to Fahrenheit and back — with the numbers lined up in columns and a word for how each temperature feels. Then it answers a small puzzle: at which temperature do the two scales show the same number? It is the checkpoint for [Part 4: Functions](https://rux-lang.dev/docs/learn/functions), and the interesting thing about it is not the arithmetic but the **shape**. Every job has its own small function with a name that says what it does, so `Main` reads like a description of the tables rather than a page of formulas. ## How it is put together `Main` only calls other functions. Here is who calls whom: ```mermaid flowchart LR main(["Main"]) --> c2f["CelsiusToFahrenheit"] main --> f2c["FahrenheitToCelsius"] main --> round["Round"] main --> right["PrintRight"] main --> feel["PrintFeeling"] right --> width["Width"] round -. "calls itself
for a negative value" .-> round width -. "calls itself
once per digit" .-> width ``` | Function | Its one job | Lessons it uses | | -------------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `CelsiusToFahrenheit`, `FahrenheitToCelsius` | One formula each | [Function](https://rux-lang.dev/docs/learn/function), [Float](https://rux-lang.dev/docs/learn/float) | | `Round` | A `float64` to the nearest whole `int` | [Recursion](https://rux-lang.dev/docs/learn/recursion), [Convert](https://rux-lang.dev/docs/learn/convert) | | `Width` | How many characters a number takes when printed | [Recursion](https://rux-lang.dev/docs/learn/recursion), [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic) | | `PrintRight` | A number right-aligned in a column | [Default argument](https://rux-lang.dev/docs/learn/default-argument), [Return](https://rux-lang.dev/docs/learn/return) | | `PrintFeeling` | A temperature in words | [Else if](https://rux-lang.dev/docs/learn/else-if) | | `Main` | The two tables and the puzzle | [While](https://rux-lang.dev/docs/learn/while), [For](https://rux-lang.dev/docs/learn/for) | ## One formula per function The conversions are one line each. Every literal in them is written with a decimal point, because the parameters are `float64`: ```rux func CelsiusToFahrenheit(celsius: float64) -> float64 { return celsius * 9.0 / 5.0 + 32.0; } ``` The loops in `Main` count in whole degrees, so each call converts its `int` argument on the way in: `CelsiusToFahrenheit(celsius as float64)`. Rux never turns an `int` into a `float64` by itself. ## Rounding, and a recursive trick for negatives `as int` alone only drops the fraction: 37.8 would become 37, not 38. Adding 0.5 first fixes that for positive values. For a negative value, `Round` rounds its positive twin and negates the result — a function calling itself once: ```rux func Round(value: float64) -> int { if value < 0.0 { return -Round(-value); } return (value + 0.5) as int; } ``` Without the negative case, −17.8 + 0.5 is −17.3, which `as int` truncates towards zero to −17 — the wrong way. The first row of the Fahrenheit table, 0 F, is exactly that value: it prints −18 only because of the recursive branch. ## Columns from counting digits To right-align a number in a column of six, you need to know how wide it will print. `Width` counts digits recursively — one for this digit, plus however many the number divided by ten has — and adds one for a minus sign: ```rux func Width(value: int) -> int { if value < 0 { return 1 + Width(-value); } if value < 10 { return 1; } return 1 + Width(value / 10); } ``` `PrintRight` then prints the missing spaces before the number. Its `width` parameter has a default, so every call in `Main` can leave it out: ```rux func PrintRight(value: int, width: int = 6) { for i in Width(value)..width { Print(" "); } Print("{}", value); } ``` If the number is already as wide as the column, `Width(value)..width` is an empty range and the loop simply does not run. ## Asking the function, not knowing the answer The last loop finds where the scales agree by trying every whole degree from −100 to 100 and asking the conversion function: ```rux for degrees in -100..=100 { if CelsiusToFahrenheit(degrees as float64) == degrees as float64 { PrintLine("{} C is also {} F", degrees, degrees); } } ``` Comparing floats with `==` is usually a warning sign (see [Float](https://rux-lang.dev/docs/learn/float)), but here both sides are whole numbers that a `float64` holds exactly, so the test is safe. The answer is −40. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Temperature){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Temperature: two conversion tables between Celsius and Fahrenheit, built from small functions. // // Each function does one job and has a name that says what. The conversions are one formula // each; `Round` turns a converted value into whole degrees; `Width` and `PrintRight` line the // numbers up in columns; `PrintFeeling` puts a temperature into words. `Main` only calls them, so // it reads like a description of the tables rather than a page of arithmetic. // // The two scales meet at exactly one temperature, and the end of the program finds it by asking // the conversion function, not by knowing the answer. import Io::{ Print, PrintLine }; func CelsiusToFahrenheit(celsius: float64) -> float64 { return celsius * 9.0 / 5.0 + 32.0; } func FahrenheitToCelsius(fahrenheit: float64) -> float64 { return (fahrenheit - 32.0) * 5.0 / 9.0; } // Rounds to the nearest whole degree. `as int` on its own would only drop the fraction, turning // 37.8 into 37 rather than 38. A negative value is rounded as its positive twin, then negated, so // -17.8 becomes -18 and not -17. func Round(value: float64) -> int { if value < 0.0 { return -Round(-value); } return (value + 0.5) as int; } // How many characters a whole number takes when printed: its digits, plus one for a minus sign. func Width(value: int) -> int { if value < 0 { return 1 + Width(-value); } if value < 10 { return 1; } return 1 + Width(value / 10); } // Prints a number right-aligned in a column, by printing spaces first to fill the gap. func PrintRight(value: int, width: int = 6) { for i in Width(value)..width { Print(" "); } Print("{}", value); } func PrintFeeling(celsius: int) { if celsius <= 0 { PrintLine(" frozen"); } else if celsius < 10 { PrintLine(" cold"); } else if celsius < 20 { PrintLine(" cool"); } else if celsius < 30 { PrintLine(" warm"); } else if celsius < 45 { PrintLine(" hot"); } else if celsius < 100 { PrintLine(" too hot to touch"); } else { PrintLine(" boiling"); } } func Main() -> int { // Every multiple of 5 Celsius is a whole number of Fahrenheit, so this table is exact. PrintLine(" C F"); var celsius = -40; while celsius <= 100 { PrintRight(celsius); PrintRight(Round(CelsiusToFahrenheit(celsius as float64))); PrintFeeling(celsius); celsius += 10; } PrintLine(); // The other way round the results have fractions, so they are rounded to whole degrees. PrintLine(" F C"); var fahrenheit = 0; while fahrenheit <= 220 { let converted = Round(FahrenheitToCelsius(fahrenheit as float64)); PrintRight(fahrenheit); PrintRight(converted); PrintFeeling(converted); fahrenheit += 20; } PrintLine(); // Where do the scales agree? Try every whole degree and ask. for degrees in -100..=100 { if CelsiusToFahrenheit(degrees as float64) == degrees as float64 { PrintLine("{} C is also {} F", degrees, degrees); } } return 0; } ``` ## Run it ```sh cd Examples/Projects/Temperature rux run ``` ```text C F -40 -40 frozen -30 -22 frozen -20 -4 frozen -10 14 frozen 0 32 frozen 10 50 cool 20 68 warm 30 86 hot 40 104 hot 50 122 too hot to touch 60 140 too hot to touch 70 158 too hot to touch 80 176 too hot to touch 90 194 too hot to touch 100 212 boiling F C 0 -18 frozen 20 -7 frozen 40 4 cold 60 16 cool 80 27 warm 100 38 hot 120 49 too hot to touch 140 60 too hot to touch 160 71 too hot to touch 180 82 too hot to touch 200 93 too hot to touch 220 104 boiling -40 C is also -40 F ``` ## Common mistakes ::warning **Passing an `int` where a `float64` is expected.**:br`CelsiusToFahrenheit(celsius)` with an `int` argument is refused: `error: argument 1 to 'CelsiusToFahrenheit' has type 'int', but parameter 'celsius' requires 'float64'`. Convert at the call with `as float64`. :: ::warning **Writing `9` instead of `9.0` in a float formula.**:br`celsius * 9 / 5 + 32` looks like the same formula, but in a `float64` function it fails with `error: operator '*' cannot combine left operand 'float64' with right operand 'int'` (and the same for `/` and `+`). Write the literals as `9.0`, `5.0` and `32.0`. :: ::warning **Rounding with `as int` alone.**:br`as int` truncates towards zero. With `return value as int;` in `Round` the program still runs, but 100 F comes out as 37 C instead of 38, and 60 F as 15 instead of 16. :: ## Try it yourself 1. Add a Kelvin column to the first table. Write `CelsiusToKelvin` as its own function rather than doing the sum inside `Main`. 2. Call `PrintRight` with a width of 8 for one column, so the default and an explicit argument are used side by side. 3. Change `PrintFeeling` so it returns the word instead of printing it. What does its return type become, and what changes at the call sites? 4. The puzzle loop only tries whole degrees. Change it to step through every half degree from −100 to 100 and check that −40 is still the only answer. ## Learn more - [Function](https://rux-lang.dev/docs/learn/function), [Recursion](https://rux-lang.dev/docs/learn/recursion) and [Default argument](https://rux-lang.dev/docs/learn/default-argument) - [Convert](https://rux-lang.dev/docs/learn/convert) — what `as int` does to a fraction - [Functions](https://rux-lang.dev/docs/lang/functions/declaration) in the Rux Reference - Next project: [Prime](https://rux-lang.dev/docs/learn/prime), the checkpoint for Sequences # Prime ::note **You'll need**: Parts 1–5 — this project is the checkpoint for Sequences, and leans on [While](https://rux-lang.dev/docs/learn/while), [For](https://rux-lang.dev/docs/learn/for) and [Array repeat](https://rux-lang.dev/docs/learn/array-repeat). :: A prime is a whole number above 1 that only 1 and itself divide. This project finds every prime below 100 with the **sieve of Eratosthenes**, an algorithm more than two thousand years old and still the way to do it. The idea fits in one sentence: write down every number, then repeatedly take the smallest one not yet crossed out and cross out all its multiples. Whatever survives is prime. The sieve never divides anything — it only steps along and marks — which is why it beats testing each number for divisors. It is the checkpoint for [Part 5: Sequences](https://rux-lang.dev/docs/learn/sequences), because the crossing-out needs an array. ## How it is put together The program is one `Main` with three steps, all working on a single array of flags: ```mermaid flowchart LR make["One flag per number
[false; Limit]"] --> mark["Mark 0 and 1
as not prime"] mark --> sieve["For each surviving n,
cross out n·n, n·n + n, …"] sieve --> print["Print every index
still false"] ``` | Piece | Lessons it uses | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `const Limit = 100` | [Const](https://rux-lang.dev/docs/learn/const) | | `[false; Limit]` | [Array repeat](https://rux-lang.dev/docs/learn/array-repeat), [Array](https://rux-lang.dev/docs/learn/array) | | The two nested passes | [While](https://rux-lang.dev/docs/learn/while), [Logical](https://rux-lang.dev/docs/learn/logical) (`!`) | | Printing the survivors | [For](https://rux-lang.dev/docs/learn/for), [Range](https://rux-lang.dev/docs/learn/range), [Console](https://rux-lang.dev/docs/learn/console) | ## One flag per number The array's **index** is the number and its **element** says whether that number has been crossed out. An array repeat makes a hundred `false` flags in one expression: ```rux const Limit = 100; ``` ```rux var crossedOut = [false; Limit]; crossedOut[0] = true; crossedOut[1] = true; ``` The length of an inline array is part of its type, so it must be known while compiling. That is why the limit is a `const` and not a `let`: a constant can size the array *and* bound the loops, and changing the one number changes both. The array is `var` because the sieve writes to it. "Below 100" means the candidates are 0 to 99 — exactly the indices of a 100-element array, and exactly what the half-open range `0..Limit` walks. ## Crossing out multiples For each number that is still standing, an inner loop steps through its multiples and marks them: ```rux var n = 2; while n * n < Limit { if !crossedOut[n] { var multiple = n * n; while multiple < Limit { crossedOut[multiple] = true; multiple += n; } } n += 1; } ``` Two shortcuts make this fast, and both rest on the same observation. A multiple `k * n` with `k` smaller than `n` also has the factor `k`, so it was already crossed out during an earlier pass. That means: - each pass can **start** at `n * n` rather than at `2 * n`, and - the outer loop can **stop** once `n * n` reaches the limit — such an `n` has no multiples left to visit. For a limit of 100 the outer loop only runs for `n` from 2 to 9, and only 2, 3, 5 and 7 survive to do any crossing out. ## Reading off the answer Whatever is still `false` is prime. One `for` over the indices prints them and counts them: ```rux for i in 0..Limit { if !crossedOut[i] { Print(" {}", i); found += 1; } } ``` ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Prime){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Finding every prime below a limit with the sieve of Eratosthenes, an algorithm more than two // thousand years old and still the way to do it. // // The idea: write down every number, then repeatedly take the smallest one not yet crossed out // and cross out all its multiples. Whatever survives is prime. It is faster than testing each // number for divisors, because it never divides at all; it only steps along and marks. // // "Below" means the limit itself is not a candidate: with a limit of 100 the numbers examined are // 0 to 99. That matches the array, whose indices run from 0 to one less than its length, and the // half-open range `0..Limit` that walks it. import Io::{ Print, PrintLine }; // The limit is named once, here. A constant is known at compile time, so it can size the array // as well as bound the loops, and changing it changes both. const Limit = 100; func Main() -> int { // One flag per number, indexed by the number itself, none crossed out yet. var crossedOut = [false; Limit]; // 0 and 1 are not prime, and no pass below would cross them out. crossedOut[0] = true; crossedOut[1] = true; // Cross out the multiples of each surviving number. Each pass starts at n * n: a smaller // multiple k * n, with k below n, also has the factor k, so the pass for k (or for a prime // factor of k) has already crossed it out. For the same reason the passes can stop once // n * n reaches the limit, because such an n has no multiples left to visit. var n = 2; while n * n < Limit { if !crossedOut[n] { var multiple = n * n; while multiple < Limit { crossedOut[multiple] = true; multiple += n; } } n += 1; } // Everything still standing is prime. var found = 0; Print("primes below {}:", Limit); for i in 0..Limit { if !crossedOut[i] { Print(" {}", i); found += 1; } } PrintLine(); PrintLine("{} of them", found); return 0; } ``` ## Run it ```sh cd Examples/Projects/Prime rux run ``` ```text primes below 100: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 25 of them ``` ## Common mistakes ::warning **Sizing the array with a run-time value.**:br`let size = 100;` followed by `[false; size]` is refused: `error: array repeat count must be a non-negative compile-time integer`. An inline array's length is fixed when the program is compiled, so it comes from a literal or a `const`. :: ::warning **Declaring the flags with `let`.**:br`let crossedOut = [false; Limit];` makes the array immutable, and every write to it fails with `error: cannot modify immutable variable 'crossedOut'`. :: ::warning **Indexing one past the end.**:br The last valid index is `Limit - 1`. Writing `crossedOut[Limit] = true;` still compiles, but the run stops with `Panic: index out of range` and the line it happened on. Keep the loops' conditions as `< Limit`, never `<= Limit`. :: ## Try it yourself 1. Raise the limit to 1000. Only one line changes, and the program should report 168 primes. 2. Print the primes ten to a line, the way [FizzBuzz](https://rux-lang.dev/docs/learn/fizz-buzz) lays out its answers. 3. Count how many times `crossedOut[multiple] = true;` runs. Then change the inner loop to start at `2 * n` instead of `n * n` and count again — the answer stays the same, but the work does not. 4. Twin primes are pairs that differ by 2, such as 11 and 13. After the sieve, walk the array once more and print every twin pair below the limit. ## Learn more - [Array repeat](https://rux-lang.dev/docs/learn/array-repeat) and [Array](https://rux-lang.dev/docs/learn/array) — inline arrays and their fixed length - [Const](https://rux-lang.dev/docs/learn/const) — values the compiler knows - [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) in the Rux Reference - Next project: [Calculator](https://rux-lang.dev/docs/learn/calculator), the checkpoint for Errors # Calculator ::note **You'll need**: Parts 1–9 — this project is the checkpoint for Errors, and leans on [Fail](https://rux-lang.dev/docs/learn/fail), [Propagate](https://rux-lang.dev/docs/learn/propagate), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) and [Error variant](https://rux-lang.dev/docs/learn/error-variant). :: A pocket calculator shows "E" when something goes wrong and leaves you guessing. This one works on 32-bit integers and says exactly **why** it cannot give an answer: you divided by zero, pressed a key that is not an operator, or asked for a result too large for an `int32`. It is the checkpoint for [Part 9: Errors](https://rux-lang.dev/docs/learn/errors), and it uses each of the main tools from that part in the place where it fits best: `fail` to raise an error, `?` to pass one on, `match` to take one apart, and `catch` to recover and carry on. ## How it is put together Every way a sum can go wrong is a case of one error variant, and each case carries the detail that explains it: ```rux variant CalcError { DivisionByZero, UnknownOperator(char), Overflow(int64) } ``` Five small functions share the work, plus `PrintTape`, which only echoes a tape. Arrows show who calls whom, and what travels back: ```mermaid flowchart LR main(["Main"]) --> apply["Apply
one sum"] main --> run["Run
a whole tape"] run -- "each step, with ?" --> apply apply -- "the answer, with ?" --> narrow["Narrow
int64 → int32"] main --> show["Show
.Success or .Failure"] show --> explain["Explain
one line per case"] ``` | Function | What it does with an error | Lessons it uses | | --------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Narrow` | Raises `Overflow` when the answer does not fit | [Fail](https://rux-lang.dev/docs/learn/fail), [Convert](https://rux-lang.dev/docs/learn/convert) | | `Apply` | Raises the other two, passes `Narrow`'s on | [Fail](https://rux-lang.dev/docs/learn/fail), [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Propagate](https://rux-lang.dev/docs/learn/propagate) | | `Run` | Passes the first failure of a tape on | [Propagate](https://rux-lang.dev/docs/learn/propagate), [Tuple](https://rux-lang.dev/docs/learn/tuple), [Destructure](https://rux-lang.dev/docs/learn/destructure) | | `Show` | Opens the outcome | [Outcome](https://rux-lang.dev/docs/learn/outcome) | | `Explain` | Turns each case into a sentence | [Error variant](https://rux-lang.dev/docs/learn/error-variant), [Variant match](https://rux-lang.dev/docs/learn/variant-match) | | `Main` | Skips bad steps on the forgiving tape | [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) | ## Overflow is checked in a wider type Adding or multiplying two `int32`s can overflow `int32`, but no sum, difference or product of two `int32`s can overflow `int64`. So `Apply` widens both operands, does the arithmetic there, and `Narrow` checks the answer on its way back down: ```rux func Narrow(wide: int64) -> int32 ! CalcError { if wide > 2147483647 || wide < -2147483648 { fail CalcError::Overflow(wide); } return wide as int32; } ``` That catches the case everyone expects, `2147483647 + 1`, and one people usually miss: `-2147483648 / -1`. The answer, 2147483648, is one more than an `int32` can hold, because the negative range reaches one further than the positive one. ## A match arm that fails Inside `Apply`, a `match` expression picks the operation. Its last arm does not produce a value at all — it leaves the function through the failure channel: ```rux let wide = match op { '+' => a + b, '-' => a - b, '*' => a * b, '/' => a / b, '%' => a % b, else => fail CalcError::UnknownOperator(op) }; return Narrow(wide)?; ``` Division by zero is tested before the `match`, so `/` and `%` are never reached with a zero divisor. And `return Narrow(wide)?;` passes an overflow straight to `Apply`'s caller: `?` keeps the success and hands any failure on unchanged. ## A tape stops at the first bad key A tape is a run of key presses, each applied to the answer so far — like a real pocket calculator, strictly left to right, with no precedence. `Run` walks it, and one `?` is all it takes to make the first failure end the whole run: ```rux for step in tape { let (op, operand) = step; total = Apply(total, op, operand)?; } ``` Each step is a tuple `(char, int32)`, and `let (op, operand) = step;` unpacks it. The bad tape fails at its second key, `/ 0`, so the `x` key after it is never even looked at. ## Forgiving instead of failing The end of `Main` runs the same bad tape a second time, but with `catch` instead of `?`. A step that fails leaves `total` as it was, and the tape carries on: ```rux total = Apply(total, op, operand) catch { else => total }; ``` `/ 0` and `x 2` are skipped, so the tape computes `(0 + 10) * 7 - 6`, which is 64. Which behaviour is right — stop at the first error or skip it — is a design decision, and the two loops show that it is decided by a single token. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Calculator){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Calculator: a pocket calculator that works on 32-bit integers and says exactly why when it // cannot give an answer. // // A real calculator shows "E" and leaves you guessing. This one has an error variant with one case // for each way a sum can go wrong, carrying whatever detail explains it: dividing by zero, a key // that is not an operator, and an answer too large for an `int32`. // // The arithmetic happens in `int64`, where no two `int32` values can overflow, and the answer is // then checked on its way back down. That catches the cases people expect, such as 2147483647 + 1, // and one they usually do not: -2147483648 / -1, whose answer is one more than an `int32` holds. // // The program uses each of the main tools from the Errors part. `Apply` raises one with // `fail`. `Run` passes one on with `?`, so a tape of key presses stops at the first bad step. // `Show` and `Explain` take one apart with `match`. And the forgiving calculator at the end uses // `catch` to skip a bad step and carry on. import Io::{ Print, PrintLine }; variant CalcError { DivisionByZero, UnknownOperator(char), Overflow(int64) } // Brings a wide answer back to `int32`, or fails if it does not fit. func Narrow(wide: int64) -> int32 ! CalcError { if wide > 2147483647 || wide < -2147483648 { fail CalcError::Overflow(wide); } return wide as int32; } func Apply(left: int32, op: char, right: int32) -> int32 ! CalcError { if (op == '/' || op == '%') && right == 0 { fail CalcError::DivisionByZero; } let a = left as int64; let b = right as int64; // An arm may fail instead of producing a value: an unknown key ends the function right there. let wide = match op { '+' => a + b, '-' => a - b, '*' => a * b, '/' => a / b, '%' => a % b, else => fail CalcError::UnknownOperator(op) }; return Narrow(wide)?; } // Presses the keys of a tape one after another, starting from `start`. The first step that fails // fails the whole run, and `?` is all it takes to say so. func Run(start: int32, tape: (char, int32)[..]) -> int32 ! CalcError { var total = start; for step in tape { let (op, operand) = step; total = Apply(total, op, operand)?; } return total; } func Explain(error: CalcError) { match error { .DivisionByZero => PrintLine("error: division by zero"), .UnknownOperator(op) => PrintLine("error: '{}' is not an operator", op), .Overflow(wide) => PrintLine("error: {} does not fit in an int32", wide) } } func Show(outcome: int32 ! CalcError) { match outcome { .Success(answer) => PrintLine("{}", answer), .Failure(error) => Explain(error) } } func PrintTape(start: int32, tape: (char, int32)[..]) { Print("{}", start); for step in tape { let (op, operand) = step; Print(" {} {}", op, operand); } Print(" = "); } func Main() -> int { let sums: (int32, char, int32)[9] = [ (12, '+', 30), (7, '*', 6), (17, '%', 5), (7, '/', 0), (7, '%', 0), (2, '^', 8), (2147483647, '+', 1), (-2147483648, '/', -1), (65536, '*', 65536) ]; for sum in sums { let (left, op, right) = sum; Print("{} {} {} = ", left, op, right); Show(Apply(left, op, right)); } PrintLine(); // A tape is a run of key presses, each applied to the answer so far. Like a pocket calculator, // it works strictly left to right, with no precedence: 1 + 2 * 3 is 9 on a tape, not 7. let good: (char, int32)[4] = [('+', 10), ('*', 7), ('-', 6), ('/', 8)]; let bad: (char, int32)[5] = [('+', 10), ('/', 0), ('*', 7), ('x', 2), ('-', 6)]; PrintTape(0, good); Show(Run(0, good)); PrintTape(0, bad); Show(Run(0, bad)); // A forgiving calculator ignores a key it cannot use. `catch { else => total }` keeps the // answer it already had, whatever went wrong, and the tape carries on past the bad steps. var total: int32 = 0; for step in bad { let (op, operand) = step; total = Apply(total, op, operand) catch { else => total }; } PrintLine("skipping the bad keys instead: {}", total); return 0; } ``` ## Run it ```sh cd Examples/Projects/Calculator rux run ``` ```text 12 + 30 = 42 7 * 6 = 42 17 % 5 = 2 7 / 0 = error: division by zero 7 % 0 = error: division by zero 2 ^ 8 = error: '^' is not an operator 2147483647 + 1 = error: 2147483648 does not fit in an int32 -2147483648 / -1 = error: 2147483648 does not fit in an int32 65536 * 65536 = error: 4294967296 does not fit in an int32 0 + 10 * 7 - 6 / 8 = 8 0 + 10 / 0 * 7 x 2 - 6 = error: division by zero skipping the bad keys instead: 64 ``` ## Common mistakes ::warning **Calling a fallible function and ignoring the result.**:br A line such as `Apply(1, '+', 2);` on its own is refused: `error: fallible result of type 'int32 ! CalcError' is discarded`. A failure must be handled, passed on or discarded on purpose — see [Discard](https://rux-lang.dev/docs/learn/discard). :: ::warning **Forgetting the `?` in `Run`.**:br Without it, `total = Apply(total, op, operand);` tries to store the whole outcome in an `int32`: `error: cannot assign 'int32 ! CalcError' to 'int32'`. The `?` is what unwraps the success. :: ::warning **A match that misses a case.**:br Delete the `.DivisionByZero` arm from `Explain` and the compiler names what is missing: `error: match on 'CalcError' is not exhaustive; missing CalcError::DivisionByZero`. Adding a case to `CalcError` later makes every such `match` point at itself, which is exactly what you want. :: ::warning **A comma after the last arm.**:br The arms of a `match` are separated by commas, and the last one has none. Leaving one there fails with `error: trailing comma is not allowed in match blocks`. :: ## Try it yourself 1. Add a `^` key for powers, with `2 ^ 8` giving 256. Compute it with a loop in `int64` and let `Narrow` catch a power that is too large. What should a negative exponent do — and does it need a new case in `CalcError`? 2. Add a case `NegativeRoot` and an `r` key that takes an integer square root, failing for a negative operand. The compiler will show you every `match` that needs a new arm. 3. Make the forgiving calculator report what it skipped. Replace the `catch` with a `match` on the outcome: store the answer on `.Success`, and call `Explain` on `.Failure` while `total` stays as it was. 4. Change `Run` so that it returns the number of the step that failed along with the error. Hint: a new error type that carries a `uint` and a `CalcError`, and [Error mapping](https://rux-lang.dev/docs/learn/error-mapping) to build it. ## Learn more - [Error variant](https://rux-lang.dev/docs/learn/error-variant), [Fail](https://rux-lang.dev/docs/learn/fail), [Propagate](https://rux-lang.dev/docs/learn/propagate) and [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) - [Error handling](https://rux-lang.dev/docs/lang/errors/overview) in the Rux Reference - Next projects: [Circle](https://rux-lang.dev/docs/learn/circle) and [Quadratic](https://rux-lang.dev/docs/learn/quadratic), which apply the same ideas to input a person types # Circle ::note **You'll need**: Parts 1–14 — this project is the checkpoint for Text, and leans on [Input](https://rux-lang.dev/docs/learn/input), [Parse](https://rux-lang.dev/docs/learn/parse), [String view](https://rux-lang.dev/docs/learn/string-view), [Error variant](https://rux-lang.dev/docs/learn/error-variant) and [Destructor](https://rux-lang.dev/docs/learn/destructor) — plus [Float special](https://rux-lang.dev/docs/learn/float-special) and [Math](https://rux-lang.dev/docs/learn/math) from Part 16. :: This program asks for the radius of a circle and prints its circumference and area. The arithmetic is two lines. Everything else is about the input: a person can type anything, or nothing, and a good program tells them which kind of unusable answer they gave. It is a checkpoint for [Part 14: Text](https://rux-lang.dev/docs/learn/text) — reading a line, trimming it and parsing a number — and it borrows `IsFinite` and `Pi` from [Float special](https://rux-lang.dev/docs/learn/float-special) and [Math](https://rux-lang.dev/docs/learn/math) in Part 16. ## How it is put together Every way the input can be unusable is a case of one error variant: ```rux variant RadiusError { Ended, Unreadable, NotANumber, Negative(float64), NotFinite } ``` `ReadRadius` reads, parses and checks, failing with the first case that applies. `Main` asks once and opens the outcome with one exhaustive `match` — a radius on `.Success`, a sentence for each case on `.Failure`: ```mermaid flowchart LR read["ReadLine"] -- "end of input" --> ended["Ended"] read -- "other read error" --> unreadable["Unreadable"] read -- "a line" --> parse["ParseFloat64
of the trimmed line"] parse -- "too large" --> nf["NotFinite"] parse -- "malformed" --> nan["NotANumber"] parse -- "a float64" --> finite{"IsFinite?"} finite -- "no: Inf or NaN" --> nf finite -- "yes" --> sign{"below zero?"} sign -- "yes" --> neg["Negative"] sign -- "no" --> ok(["the radius"]) ``` | Piece | Lessons it uses | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `ReadLine` into a `StringBuilder` | [Input](https://rux-lang.dev/docs/learn/input), [String builder](https://rux-lang.dev/docs/learn/string-builder) | | `.View().Trim()` | [String view](https://rux-lang.dev/docs/learn/string-view) | | `ParseFloat64` and its `catch` | [Parse](https://rux-lang.dev/docs/learn/parse), [Catch](https://rux-lang.dev/docs/learn/catch) | | The match guard on `error.kind` | [Guard](https://rux-lang.dev/docs/learn/guard), [Outcome](https://rux-lang.dev/docs/learn/outcome) | | `RadiusError` and its `match` | [Error variant](https://rux-lang.dev/docs/learn/error-variant), [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) | | `IsFinite`, `Pi` | [Float special](https://rux-lang.dev/docs/learn/float-special), [Math](https://rux-lang.dev/docs/learn/math) | | The builder's memory | [Allocator](https://rux-lang.dev/docs/learn/allocator), [Destructor](https://rux-lang.dev/docs/learn/destructor) | ## Reading a line, and the end of the input `ReadLine` appends one line to a builder and returns a fallible. Reaching the end of the input is not a crash but an `IoError` whose kind is `EndOfStream`, and a match guard tells it apart from every other read failure: ```rux match ReadLine(builder) { .Success(_) => {}, .Failure(error) if error.kind == IoErrorKind::EndOfStream => fail RadiusError::Ended, .Failure(_) => fail RadiusError::Unreadable } ``` The arms are tried in order, so the guarded arm must come before the general `.Failure(_)`. The input ends when you press Ctrl+Z and Enter on Windows or Ctrl+D elsewhere, or when piped input runs out. ## Parsing, and why a number still needs checking The line is trimmed, so `" 2.5 "` is read as 2.5, and then parsed. `catch` translates the parser's errors into this program's own cases: ```rux let radius = ParseFloat64(builder.View().Trim()) catch { .Overflow(_) => fail RadiusError::NotFinite, else => fail RadiusError::NotANumber }; if !IsFinite(radius) { fail RadiusError::NotFinite; } if radius < 0.0 { fail RadiusError::Negative(radius); } ``` A successful parse is not yet a radius. The parser reads `Inf` and `NaN`, because that is how such values print, so `IsFinite` has to rule them out separately. A number such as `1e400` is too large for a `float64`; the parser reports it as an `Overflow`, and the program counts it as "not finite" too, since it is a number — just not one a `float64` can hold. The order of the checks matters for NaN: every comparison with NaN is false, so `radius < 0.0` alone would let it through. ## Negative zero `-0` parses to −0.0, and it passes `radius < 0.0` because −0.0 equals 0.0. Left alone, it would print as `Radius: -0.0` and `Circumference: -0.0000`. Adding zero turns it into a plain 0: ```rux return radius + 0.0; ``` ## A finite radius with an infinite area The last surprise is in `Main`. A radius can be finite and its area still overflow: `1e200` squared is far past the largest `float64`, so the area is infinity. The program checks the result before printing it: ```rux let circumference = 2.0 * Pi * radius; let area = Pi * radius * radius; PrintLine(); if !IsFinite(area) { PrintLine("A radius of {} is too large: the area is past float64", radius); return 1; } ``` Every refusal exits with status 1, so a script that runs the program can tell success from failure without reading the text. ## No cleanup code The builder takes memory from the allocator, yet `Main` has three `return`s and none of them frees anything. The builder's **destructor** runs when `Main` returns, whichever `return` it leaves by, and gives the memory back. That is [Destructor](https://rux-lang.dev/docs/learn/destructor) doing the work a cleanup call would otherwise have to repeat on every path. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Circle){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Reading a radius the user types and working out the circle it describes. The arithmetic is two // lines; the rest of the program is about every way the input can be unusable, and telling the // user which one happened: // // the input ended end of input before any line arrived (Ctrl+Z, or an empty pipe) // the input failed a read error, or a line that is not valid UTF-8 // not a number the line does not spell a number at all // not a radius a number, but negative, infinite or NaN // // Each is a case of one error variant, so `Main` handles them in one exhaustive `match`. The // parser reads "Inf" and "NaN" because that is how such values print, which is why a parsed // number still has to be checked before it is trusted. import Allocator::{ Allocator, SystemAllocator }; import Core::IsFinite; import Format::ParseFloat64; import Io::{ IoErrorKind, Print, PrintLine, ReadLine }; import Math::Pi; import Text::StringBuilder; variant RadiusError { Ended, Unreadable, NotANumber, Negative(float64), NotFinite } func ReadRadius(builder: &var StringBuilder) -> float64 ! RadiusError { match ReadLine(builder) { .Success(_) => {}, .Failure(error) if error.kind == IoErrorKind::EndOfStream => fail RadiusError::Ended, .Failure(_) => fail RadiusError::Unreadable } // Trimming lets " 2.5 " through. A number too large for a float64 is reported as an // `Overflow`: it is a number, just not a finite one, so it joins Inf and NaN. let radius = ParseFloat64(builder.View().Trim()) catch { .Overflow(_) => fail RadiusError::NotFinite, else => fail RadiusError::NotANumber }; if !IsFinite(radius) { fail RadiusError::NotFinite; } if radius < 0.0 { fail RadiusError::Negative(radius); } // `-0` passes the test above, since -0.0 == 0.0. Adding zero makes it a plain 0. return radius + 0.0; } func Main() -> int { var system = SystemAllocator(); let allocator: Allocator = system; // The builder owns memory from the allocator. Its destructor gives that memory back when // `Main` returns, whichever `return` it leaves by, so no path needs a cleanup call. var builder = StringBuilder(allocator); // `Print` rather than `PrintLine`, so an answer typed at the console stays on the same line. Print("Circle radius: "); match ReadRadius(builder) { .Success(radius) => { // A finite radius can still give an infinite area: 1e200 squared is past float64. let circumference = 2.0 * Pi * radius; let area = Pi * radius * radius; PrintLine(); if !IsFinite(area) { PrintLine("A radius of {} is too large: the area is past float64", radius); return 1; } PrintLine("Radius: {}", radius); PrintLine("Circumference: {:.4}", circumference); PrintLine("Area: {:.4}", area); return 0; }, .Failure(error) => { // Piped input leaves the cursor after the prompt, so the report starts a new line. PrintLine(); match error { .Ended => PrintLine("The input ended before a radius was entered"), .Unreadable => PrintLine("The input could not be read"), .NotANumber => PrintLine("That is not a number"), .Negative(radius) => PrintLine("A radius cannot be negative, and {} is", radius), .NotFinite => PrintLine("A radius must be a finite number") } return 1; } } } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Core`, `Format`, `Math` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Circle rux run ``` ```text Circle radius: 2.5 Radius: 2.5 Circumference: 15.7080 Area: 19.6350 ``` Piped input works too. The typed value is not echoed then, so each report follows the prompt on a line of its own: ```sh "-3" | rux run "two" | rux run $null | rux run ``` ```text Circle radius: A radius cannot be negative, and -3.0 is Circle radius: That is not a number Circle radius: The input ended before a radius was entered ``` `Inf`, `NaN` and numbers too large for a `float64` are refused as not finite. Every refusal exits with status 1. The program waits for you to type. Run it, type a radius after the prompt and press Enter. To try the refusals, type `-3`, `two`, `Inf` or `1e200`, or end the input straight away with Ctrl+Z and Enter on Windows (Ctrl+D elsewhere). The piped examples above are written for PowerShell; in a POSIX shell, `echo -3 | rux run` does the same. ## Common mistakes ::warning **Calling `ReadRadius` without looking at the result.**:br A bare `ReadRadius(builder);` is refused: `error: fallible result of type 'float64 ! RadiusError' is discarded`. The whole program is about what that result says, so it has to be matched. :: ::warning **Leaving a case out of the final match.**:br Remove the `.Unreadable` arm from `Main` and the build stops with `error: match on 'RadiusError' is not exhaustive; missing RadiusError::Unreadable`. This is the reason to put every failure in one variant: adding a sixth case later makes the compiler point at every `match` that must learn about it. :: ::warning **Trusting a parsed number.**:br Take the `IsFinite` check out of `ReadRadius` and type `Inf`: the program no longer refuses it as a radius, but reaches the area check and reports `A radius of Inf is too large`. Take the sign check out and `-3` gives a negative circumference. Parsing tells you the text is a number, not that it is a sensible one. :: ## Try it yourself 1. Also print the diameter, and the radius of a circle with twice the area. ([Math](https://rux-lang.dev/docs/learn/math) has `Sqrt`.) 2. Refuse a radius of exactly zero with a case of its own, `Zero`, and a message to match. Let the compiler show you where the new case must be handled. 3. Keep asking until a valid radius arrives: put the `Print` and the call in a loop, and leave it only on success or on `Ended`. Remember that the builder is reused, so it must be cleared before each read — [Quadratic](https://rux-lang.dev/docs/learn/quadratic) shows how. 4. Ask for a second radius and print the area of the ring between the two circles. ## Learn more - [Input](https://rux-lang.dev/docs/learn/input), [Parse](https://rux-lang.dev/docs/learn/parse) and [String view](https://rux-lang.dev/docs/learn/string-view) - [Float special](https://rux-lang.dev/docs/learn/float-special) — infinity, NaN and negative zero - [ReadLine](https://rux-lang.dev/docs/api/io/readline) and the [Math](https://rux-lang.dev/docs/api/math) package in the API reference - Next project: [Quadratic](https://rux-lang.dev/docs/learn/quadratic), the same input handling for three numbers # Quadratic ::note **You'll need**: Parts 1–14 — this project is the checkpoint for Text, and leans on [Input](https://rux-lang.dev/docs/learn/input), [Parse](https://rux-lang.dev/docs/learn/parse), [String view](https://rux-lang.dev/docs/learn/string-view) and [Error variant](https://rux-lang.dev/docs/learn/error-variant) — plus [Float special](https://rux-lang.dev/docs/learn/float-special) and [Math](https://rux-lang.dev/docs/learn/math) from Part 16. :: This program reads three numbers, a, b and c, and solves the equation a x² + b x + c = 0. It is the classic calculation whose **shape** depends on its input: depending on the numbers there are two answers, one, a pair of complex ones, or — when `a` is zero and the equation is not really quadratic at all — one answer, none, or infinitely many. Like [Circle](https://rux-lang.dev/docs/learn/circle), it is a checkpoint for [Part 14: Text](https://rux-lang.dev/docs/learn/text), and it reads its input with the same care. What it adds is a decision tree with six leaves, and a lesson in floating-point arithmetic that the textbook formula gets wrong. ## How it is put together The program splits cleanly into reading and solving: | Function | Its job | Lessons it uses | | ----------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ReadCoefficient` | Prompt, read one line, parse it, check it is finite | [Input](https://rux-lang.dev/docs/learn/input), [Parse](https://rux-lang.dev/docs/learn/parse), [String view](https://rux-lang.dev/docs/learn/string-view), [Fail](https://rux-lang.dev/docs/learn/fail) | | `Explain` | One sentence for each `EntryError` | [Error variant](https://rux-lang.dev/docs/learn/error-variant) | | `SolveLinear` | The three cases where `a` is zero | [Else if](https://rux-lang.dev/docs/learn/else-if) | | `SolveQuadratic` | The discriminant and the three quadratic cases | [Math](https://rux-lang.dev/docs/learn/math) (`Sqrt`), [Float special](https://rux-lang.dev/docs/learn/float-special), [Ternary](https://rux-lang.dev/docs/learn/ternary) | | `Main` | Reads a, b and c, stopping at the first bad one, then picks a solver | [Catch](https://rux-lang.dev/docs/learn/catch), [Destructor](https://rux-lang.dev/docs/learn/destructor) | Once the three numbers are in, the cases fall out like this: ```mermaid flowchart LR start(["a, b, c"]) --> qa{"a = 0?"} qa -- "no" --> d{"discriminant
b² − 4ac"} d -- "positive" --> two["two real roots"] d -- "zero" --> one["one repeated root"] d -- "negative" --> cx["a complex pair"] qa -- "yes" --> qb{"b = 0?"} qb -- "no" --> lin["linear: x = −c / b"] qb -- "yes" --> qc{"c = 0?"} qc -- "no" --> none["no solution"] qc -- "yes" --> all["every x"] ``` ## One reader, three times `ReadCoefficient` prints its own prompt and reuses one builder for every line, so it clears the builder first. Otherwise the second line would be appended to the first: ```rux func ReadCoefficient(name: char8[..], builder: &var StringBuilder) -> float64 ! EntryError { Print("{} = ", name); builder.Clear(); ``` The rest is [Circle](https://rux-lang.dev/docs/learn/circle)'s reader: end of input, read failure, not a number, not finite. In `Main` each call handles its failure on the spot with a `catch` arm that explains and leaves: ```rux let a = ReadCoefficient("a", builder) catch { error => { Explain(error); return 1; } }; let b = ReadCoefficient("b", builder) catch { error => { Explain(error); return 1; } }; let c = ReadCoefficient("c", builder) catch { error => { Explain(error); return 1; } }; ``` The arm `error =>` binds whichever case it was, so a single `Explain` covers them all. ## The degenerate cases When `a` is zero the quadratic formula would divide by zero, so those inputs never reach it. `SolveLinear` handles them with a three-way chain: ```rux if b != 0.0 { PrintLine("linear, not quadratic: x = {}", -c / b + 0.0); } else if c != 0.0 { PrintLine("inconsistent: {} = 0 is never true, so there is no solution", c); } else { PrintLine("an identity: 0 = 0 holds for every x"); } ``` The `+ 0.0` is the negative-zero fix from Circle: with c = 0, `-c / b` is −0.0, and adding zero makes it print as `0.0`. ## Two real roots without losing one The textbook formula is (−b ± √disc) / 2a. When b is large and 4ac is small, √disc is almost exactly |b|, and one of the two roots comes from subtracting two nearly equal numbers. Most of their digits cancel, and the rounding error that is left becomes most of the answer. The program avoids the subtraction. It computes the larger root by **adding** two numbers of the same sign — which never cancels — and gets the other from Vieta's rule, x₁ · x₂ = c / a: ```rux let root = Sqrt(discriminant); let q = b >= 0.0 ? -(b + root) / 2.0 : (root - b) / 2.0; let first = q / a; let second = c / q; ``` The difference is real. For a = 1, b = 1e8, c = 1 the program prints the small root as `-1.0e-08`, which is right to every digit shown. The textbook formula, written the obvious way, gives `-7.450580596923828e-09` — wrong by a quarter. ## Complex roots A negative discriminant has no real square root, but the roots still exist as a complex pair: a real part plus or minus an imaginary part. The program prints them as text, dividing by |2a| so that the imaginary part is always positive and the `+` and `-` it prints are the signs it means: ```rux let real = -b / (2.0 * a) + 0.0; let twiceA = a < 0.0 ? -2.0 * a : 2.0 * a; let imaginary = Sqrt(-discriminant) / twiceA; ``` Before any of this, `SolveQuadratic` checks that the discriminant itself is finite: b = 1e200 is a perfectly finite coefficient, but b² is not. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Quadratic){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Solving a x^2 + b x + c = 0, the classic calculation whose *shape* depends on its input. // // With `a` not zero it is a quadratic, and the discriminant b^2 - 4ac decides the answer: two // real roots when it is positive, one repeated root when it is zero, and a complex pair when it // is negative. With `a` zero it is not a quadratic at all, and the formula would divide by zero: // // a = 0, b != 0 linear, one root x = -c / b // a = 0, b = 0, c != 0 inconsistent, no x makes c equal 0 // a = 0, b = 0, c = 0 an identity, every x is a solution // // Reading comes first, and every way it can go wrong is a case of one error variant: the input // may end early, fail to read, not spell a number, or spell one that is not finite. The parser // accepts "Inf" and "NaN", because that is how such values print, so finiteness is checked // separately. import Allocator::{ Allocator, SystemAllocator }; import Core::IsFinite; import Format::ParseFloat64; import Io::{ IoErrorKind, Print, PrintLine, ReadLine }; import Math::Sqrt; import Text::StringBuilder; variant EntryError { Ended, Unreadable, NotANumber, NotFinite } // Reads one coefficient. The builder is reused for every line, so it is cleared first, and the // view is trimmed so that " 2 " reads as 2. func ReadCoefficient(name: char8[..], builder: &var StringBuilder) -> float64 ! EntryError { Print("{} = ", name); builder.Clear(); match ReadLine(builder) { .Success(_) => {}, .Failure(error) if error.kind == IoErrorKind::EndOfStream => fail EntryError::Ended, .Failure(_) => fail EntryError::Unreadable } // A number past the largest float64 is an `Overflow`, not a malformed one: it is a number, // just not a finite float64, so it joins Inf and NaN. let value = ParseFloat64(builder.View().Trim()) catch { .Overflow(_) => fail EntryError::NotFinite, else => fail EntryError::NotANumber }; if !IsFinite(value) { fail EntryError::NotFinite; } return value; } // Each report starts on a fresh line, since piped input leaves the cursor after the prompt. func Explain(error: EntryError) { PrintLine(); match error { .Ended => PrintLine("the input ended before all three coefficients were given"), .Unreadable => PrintLine("the input could not be read"), .NotANumber => PrintLine("that is not a number"), .NotFinite => PrintLine( "a coefficient must be finite: Inf, NaN and numbers past float64 are refused") } } func SolveLinear(b: float64, c: float64) { if b != 0.0 { // Adding zero turns -0 into 0, so c = 0 prints "x = 0.0" rather than "x = -0.0". PrintLine("linear, not quadratic: x = {}", -c / b + 0.0); } else if c != 0.0 { PrintLine("inconsistent: {} = 0 is never true, so there is no solution", c); } else { PrintLine("an identity: 0 = 0 holds for every x"); } } func SolveQuadratic(a: float64, b: float64, c: float64) { // Finite coefficients can still overflow: b = 1e200 makes b^2 infinite. let discriminant = b * b - 4.0 * a * c; if !IsFinite(discriminant) { PrintLine("the coefficients are too large to solve in float64"); return; } if discriminant > 0.0 { // The textbook formula subtracts two nearly equal numbers when 4ac is small, and loses // the smaller root to rounding. Adding numbers of the same sign never cancels, so find // the larger root that way, then the other from x1 * x2 = c / a. let root = Sqrt(discriminant); let q = b >= 0.0 ? -(b + root) / 2.0 : (root - b) / 2.0; let first = q / a; let second = c / q; let low = first < second ? first : second; let high = first < second ? second : first; PrintLine("two real roots: x1 = {}, x2 = {}", low, high); } else if discriminant == 0.0 { PrintLine("one repeated root: x = {}", -b / (2.0 * a) + 0.0); } else { // No real root, but the complex pair is real part +/- imaginary part. Dividing by |2a| // keeps the imaginary part positive, so the signs printed are the signs meant. let real = -b / (2.0 * a) + 0.0; let twiceA = a < 0.0 ? -2.0 * a : 2.0 * a; let imaginary = Sqrt(-discriminant) / twiceA; PrintLine("complex roots: x = {} + {}i and x = {} - {}i", real, imaginary, real, imaginary); } } func Main() -> int { var system = SystemAllocator(); let allocator: Allocator = system; // The builder's destructor returns its memory on every path out of `Main`. var builder = StringBuilder(allocator); PrintLine("Solving a x^2 + b x + c = 0"); let a = ReadCoefficient("a", builder) catch { error => { Explain(error); return 1; } }; let b = ReadCoefficient("b", builder) catch { error => { Explain(error); return 1; } }; let c = ReadCoefficient("c", builder) catch { error => { Explain(error); return 1; } }; PrintLine(); if a == 0.0 { SolveLinear(b, c); } else { SolveQuadratic(a, b, c); } return 0; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Core`, `Format`, `Math` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Quadratic rux run ``` ```text Solving a x^2 + b x + c = 0 a = 1 b = -3 c = 2 two real roots: x1 = 1.0, x2 = 2.0 ``` Piped input works too. The typed values are not echoed then, so the prompts run together: ```sh "1", "2", "5" | rux run "0", "0", "3" | rux run "1", "Inf" | rux run $null | rux run ``` ```text Solving a x^2 + b x + c = 0 a = b = c = complex roots: x = -1.0 + 2.0i and x = -1.0 - 2.0i Solving a x^2 + b x + c = 0 a = b = c = inconsistent: 3.0 = 0 is never true, so there is no solution Solving a x^2 + b x + c = 0 a = b = a coefficient must be finite: Inf, NaN and numbers past float64 are refused Solving a x^2 + b x + c = 0 a = the input ended before all three coefficients were given ``` Unusable input, including input that ends early, exits with status 1. The program waits for three answers. Run it and type a number after each prompt, pressing Enter each time. Try `1`, `2`, `1` for a repeated root, `0`, `2`, `0` for a linear equation, or `1`, `1e8`, `1` to see the careful formula at work. The piped examples above are written for PowerShell; in a POSIX shell, `printf '1\n2\n5\n' | rux run` does the same. ## Common mistakes ::warning **Not clearing a reused builder.**:br`ReadLine` appends to the builder, without the line break. Leave out `builder.Clear()` and the answers `1`, `2`, `1` are read as 1, 12 and 121 — each line glued onto the ones before it — so instead of the repeated root −1 the program reports a complex pair, −6 ± 9.2195…i. Nothing fails; the answer is just wrong. :: ::warning **Dividing by `a` before checking it.**:br With a = 0 the quadratic formula divides by zero. On floats that does not stop the program — it produces infinity or NaN, and the output is nonsense rather than an error. Decide the shape of the problem first, then calculate. :: ::warning **Using the textbook formula for both roots.**:br It compiles and passes every small example, which is what makes it dangerous. The cancellation only shows up when b² is much larger than 4ac, as with b = 1e8 above. :: ## Try it yourself 1. Print the discriminant before the roots, so you can see which branch was taken and why. 2. Change the textbook-formula experiment into a permanent feature: print both the careful roots and the textbook ones, and the difference between them. 3. For two real roots, also print the vertex of the parabola: x = −b / 2a, and y found by putting that x back into the equation. 4. Let the user solve several equations in one run: after printing a result, ask for the next a, b and c, and stop cleanly when the input ends. ## Learn more - [Float special](https://rux-lang.dev/docs/learn/float-special) — infinity, NaN and negative zero - [Math](https://rux-lang.dev/docs/learn/math) — `Sqrt` and its domain - [Catch](https://rux-lang.dev/docs/learn/catch) — handling a failure in place with one arm per case - Previous project: [Circle](https://rux-lang.dev/docs/learn/circle), which reads one number the same way - Next project: [Word count](https://rux-lang.dev/docs/learn/word-count), a checkpoint for Collections # Word count ::note **You'll need**: Parts 1–17 — this project is a checkpoint for Collections, and leans on [String builder](https://rux-lang.dev/docs/learn/string-builder), [Hash map](https://rux-lang.dev/docs/learn/hash-map) and [Tree map](https://rux-lang.dev/docs/learn/tree-map). :: How often does each word appear in "How much wood would a woodchuck chuck"? This program counts every word of the tongue twister, prints the counts in alphabetical order with a little bar chart, and names the favourite word. Counting is what a hash map is for, and this project is a checkpoint for [Part 17: Collections](https://rux-lang.dev/docs/learn/collections). It also shows two habits that make text programs cheap: normalising the text once, up front, and using **views** into it as keys instead of copying every word into a new string. ## How it is put together The program is a pipeline of four steps, each feeding the next: ```mermaid flowchart LR lines["Five lines
of the twister"] --> lower["One lower-case copy
in a StringBuilder"] lower --> count["HashMap
word → count"] count --> sorted["TreeMap
same entries, sorted"] sorted --> report["Bars and
the favourite"] ``` | Step | Lessons it uses | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Lower-case copy | [String builder](https://rux-lang.dev/docs/learn/string-builder), [Encoding](https://rux-lang.dev/docs/learn/encoding) (`c8'a'`), [Propagate](https://rux-lang.dev/docs/learn/propagate) | | Splitting into words | [Slice](https://rux-lang.dev/docs/learn/slice), [Continue](https://rux-lang.dev/docs/learn/continue), [Range](https://rux-lang.dev/docs/learn/range) | | Counting | [Hash map](https://rux-lang.dev/docs/learn/hash-map), [Coalesce](https://rux-lang.dev/docs/learn/coalesce) (`?? 0`) | | Sorting | [Tree map](https://rux-lang.dev/docs/learn/tree-map) | | The report | [Format](https://rux-lang.dev/docs/learn/format) (`{:10}`), [For](https://rux-lang.dev/docs/learn/for) | | Failures | [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) | ## Normalise once, up front "How" and "how" should be one word. Rather than compare words case-insensitively everywhere, the program makes **one** lower-case copy of the whole text, with a space where each line ended: ```rux func AppendLowercase(builder: &var StringBuilder, text: char8[..]) -> ! TextError { for c in text { if c >= c8'A' && c <= c8'Z' { builder.AppendAscii(c - c8'A' + c8'a')?; } else { builder.AppendAscii(c)?; } } } ``` `c - c8'A' + c8'a'` moves a capital letter to its small twin: the letters A to Z and a to z each sit in a row in ASCII, so the distance from `A` is the same as the distance from `a`. Appending can fail — the builder may need more memory — so each call ends in `?`, and the function's `! TextError` passes the failure on. ## Words are views, not copies The text is walked once. `start` marks where the current word began; any byte that is not a letter ends it, and the slice between the two is the word: ```rux for i in 0..=text.length { let inWord = i < text.length && IsLetter(text[i]); if inWord { continue; } if i > start { let word = text[start..i]; counts.Insert(word, (counts.Get(word) ?? 0) + 1)?; words += 1; } start = i + 1; } ``` Three details make this loop correct: - The range is `0..=text.length`, one step **past** the last byte. At that step `inWord` is false, so a word that runs to the very end of the text is still counted. The `i < text.length &&` test comes first, and `&&` short-circuits, so `text[i]` is never read out of range. - `i > start` skips empty words, which appear wherever two separators sit side by side, such as `", "`. - `text[start..i]` is a view into the lower-case copy: making it costs nothing. That is also why nothing may be appended to the builder after this point — the keys all point into it. ## Counting with a hash map The counting line reads like a sentence: look the word up, add one, store it back. `Get` returns an `int32?` — `none` for a word not seen before — and `?? 0` turns that absence into a count of zero: ```rux counts.Insert(word, (counts.Get(word) ?? 0) + 1)?; ``` The map is built with three things: an allocator, a function that hashes a slice, and one that compares two slices. A slice's `==` would compare the *views*, not the text they show, so the map is told how to compare the contents: ```rux var counts = HashMap(allocator, HashSlice, EqualsSlice); ``` ## Sorting by copying into a tree map A hash map keeps no order — print it straight out and the words come in whatever order the hashing scattered them. A tree map always walks its keys in order, so copying the finished counts into one sorts them for free: ```rux var sorted = TreeMap(allocator, CompareSlice); for entry in counts { sorted.Insert(entry.key, entry.value)?; } ``` The report then walks `sorted`. `{:10}` pads each word to ten columns so the counts line up, and a strict `>` when looking for the favourite means a tie goes to the word earlier in the alphabet. ## Two kinds of failure `Main` is declared `-> ! (CollectionError | TextError)`: the maps can fail with a `CollectionError` and the builder with a `TextError`. With a sum of both as `Main`'s failure, every `?` in the program passes its error straight up, and a failure ends the program with exit status 1. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/WordCount){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // WordCount: how often does each word appear in a tongue twister? // // Counting is what a hash map is for. Each word is a key and its count the value: look the word // up, add one, store it back. A word not seen before is simply absent, and `?? 0` turns that // absence into a count of zero to add to. // // A hash map keeps no order, though, so printing it straight out would list the words however the // hashing scattered them. Copying the finished counts into a `TreeMap` sorts them by word for // free, because a tree map walks its keys in order. // // Two details make the words come out right. "How" and "how" should count as one word, so the // text is first copied in lower case. And the map's keys are slices of that copy, not new strings: // each word is a view of the bytes it occupies, which costs nothing to make. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, CompareSlice, EqualsSlice, HashMap, HashSlice, TreeMap }; import Io::{ Print, PrintLine }; import Text::{ StringBuilder, TextError }; func IsLetter(c: char8) -> bool { return c >= c8'a' && c <= c8'z'; } // Adds the text to the builder with every capital letter A to Z made small, and the rest as it is. func AppendLowercase(builder: &var StringBuilder, text: char8[..]) -> ! TextError { for c in text { if c >= c8'A' && c <= c8'Z' { builder.AppendAscii(c - c8'A' + c8'a')?; } else { builder.AppendAscii(c)?; } } } func Main() -> ! (CollectionError | TextError) { var system = SystemAllocator(); let allocator: Allocator = system; let twister = [ "How much wood would a woodchuck chuck", "if a woodchuck could chuck wood?", "He would chuck, he would, as much as he could,", "and chuck as much wood as a woodchuck would", "if a woodchuck could chuck wood." ]; // One lower-case copy of the whole text, with a space where each line ended. Every key in the // maps below is a view into it, so nothing is appended to it after this loop. var lower = StringBuilder(allocator); for line in twister { AppendLowercase(lower, line)?; lower.AppendAscii(c8' ')?; } let text = lower.Bytes(); // Walk the text once. `start` marks where the current word began; a byte that is not a // letter ends the word, and the slice between them is the key. var counts = HashMap(allocator, HashSlice, EqualsSlice); var words = 0; var start: uint = 0; for i in 0..=text.length { let inWord = i < text.length && IsLetter(text[i]); if inWord { continue; } if i > start { let word = text[start..i]; counts.Insert(word, (counts.Get(word) ?? 0) + 1)?; words += 1; } start = i + 1; } PrintLine("{} words, {} different", words, counts.Length()); PrintLine(); // Same entries, now in alphabetical order. var sorted = TreeMap(allocator, CompareSlice); for entry in counts { sorted.Insert(entry.key, entry.value)?; } var top: char8[..] = ""; var topCount = 0; for entry in sorted { Print("{:10} {:2} ", entry.key, entry.value); for i in 0..entry.value { Print("*"); } PrintLine(); // Strictly greater, so a tie goes to the word earlier in the alphabet. if entry.value > topCount { top = entry.key; topCount = entry.value; } } PrintLine(); PrintLine("the favourite word is \"{}\", {} times", top, topCount); } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Collections` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/WordCount rux run ``` ```text 38 words, 12 different a 4 **** and 1 * as 4 **** chuck 5 ***** could 3 *** he 3 *** how 1 * if 2 ** much 3 *** wood 4 **** woodchuck 4 **** would 4 **** the favourite word is "chuck", 5 times ``` ## Common mistakes ::warning **Using a lookup as if it were a count.**:br`counts.Get(word) + 1` is refused, because `Get` may find nothing: `error: operator '+' cannot combine left operand 'int32?' with right operand 'int'`. Decide what absence means first — here `?? 0`. :: ::warning **Dropping the `?` from an insertion.**:br`Insert` can fail when the map needs memory, so a bare `counts.Insert(…);` fails with `error: fallible result of type '! CollectionError' is discarded`. :: ::warning **Normalising only half the program.**:br`IsLetter` accepts only `a` to `z`. Skip the lower-casing and the capitals become separators: "How" splits into a lost `H` and a word `ow`, "He" into `e`, and the report says 13 different words instead of 12. Normalise once, and then the rest of the program can assume it. :: ## Try it yourself 1. Print the words sorted by count, highest first, instead of alphabetically. One way: copy the entries into a [Vector](https://rux-lang.dev/docs/learn/vector) of structs and order it with `SortBy` from [Sort](https://rux-lang.dev/docs/learn/sort), in the next part. 2. Count letters as well as words. No map is needed: an array of 26 counters, `[0; 26]`, indexed by `c - c8'a'`, does the job. 3. Ignore the short words "a", "as", "he" and "if" by keeping them in a [Hash set](https://rux-lang.dev/docs/learn/hash-set) and skipping any word it contains. 4. Report the longest word as well as the most frequent one. When two are equally long, which one does your loop keep? ## Learn more - [Hash map](https://rux-lang.dev/docs/learn/hash-map) and [Tree map](https://rux-lang.dev/docs/learn/tree-map) — when order matters and when it does not - [String builder](https://rux-lang.dev/docs/learn/string-builder) and [Slice](https://rux-lang.dev/docs/learn/slice) - [Format](https://rux-lang.dev/docs/learn/format) — widths and alignment in placeholders - Next project: [Inventory](https://rux-lang.dev/docs/learn/inventory), the other checkpoint for Collections # Inventory ::note **You'll need**: Parts 1–17 — this project is a checkpoint for Collections, and leans on [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error), [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) and [Vector](https://rux-lang.dev/docs/learn/vector). :: This program keeps the stock list of a small shop — apples, bread, candles, milk — and applies a day of orders to it. Some orders are fine. Others ask for more than is on the shelf, for an item the shop has never stocked, or for a negative amount, and those are refused with a reason. It is a checkpoint for [Part 17: Collections](https://rux-lang.dev/docs/learn/collections), but most of what it practises comes from earlier: structs, variants with data, and above all fallible functions. Its rule is that **a refused order changes nothing**, so the stock is never left half-updated. ## How it is put together The data is three types. A `StockItem` is one line of the list, an `Order` is one thing that can happen to it, and a `StockError` is one reason an order can be refused, carrying the details that explain it: ```rux variant StockError { UnknownItem(char8[..]), BadAmount(int32), NotEnough { name: char8[..]; have: int32; wanted: int32; }, StillInStock { name: char8[..]; count: int32; } } ``` The functions are layered. `Main` hands each order to `Apply`, which dispatches to one function per kind of order; all of them look items up through `Find`: ```mermaid flowchart LR main(["Main
for each order"]) --> apply["Apply
match order"] apply --> sell["Sell"] apply --> deliver["Deliver"] apply --> retire["Retire"] sell --> find["Find
uint?"] deliver --> find retire --> find deliver -- "new item" --> push["Vector.Push
may fail: CollectionError"] ``` | Piece | Lessons it uses | | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `StockItem`, `Order`, `StockError` | [Struct](https://rux-lang.dev/docs/learn/struct), [Variant](https://rux-lang.dev/docs/learn/variant), [Error variant](https://rux-lang.dev/docs/learn/error-variant) | | The stock as `Vector` | [Vector](https://rux-lang.dev/docs/learn/vector), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) | | `Find` returning `uint?` | [Optional](https://rux-lang.dev/docs/learn/optional), [Presence](https://rux-lang.dev/docs/learn/presence) | | `?? fail` in `Sell` and `Retire` | [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) | | `Apply` failing with two error types | [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Propagate](https://rux-lang.dev/docs/learn/propagate) | | Taking orders and errors apart | [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern), [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) | ## Finding an item, or not `Find` answers with the item's position in the vector, or `none`. Names are compared with `EqualsSlice`, because `==` on two slices is refused — it would ask whether they are the same view, not whether they hold the same text: ```rux func Find(stock: &Vector, name: char8[..]) -> uint? { let items = stock.AsSlice(); for i in 0..items.length { if EqualsSlice(items[i].name, name) { return i; } } return none; } ``` In `Sell`, an absent position is a refusal. `?? fail` turns the `none` into a failure on the spot, so the line after it can use `index` as a plain `uint`: ```rux let index = Find(stock, name) ?? fail StockError::UnknownItem(name); ``` ## Check everything, then change `Sell` makes every check before it touches the stock. Only when all of them pass does the count change, which is what makes a refused order harmless: ```rux func Sell(stock: &var Vector, name: char8[..], amount: int32) -> ! StockError { if amount <= 0 { fail StockError::BadAmount(amount); } // An absent position becomes a failure on the spot. let index = Find(stock, name) ?? fail StockError::UnknownItem(name); var items = stock.AsMutableSlice(); if items[index].count < amount { fail StockError::NotEnough { name: name, have: items[index].count, wanted: amount }; } items[index].count -= amount; } ``` `AsMutableSlice` gives a writable view of the vector's elements, so `items[index].count -= amount` changes the stock in place. `Sell` returns `! StockError` with no success value: reaching the end of the body is the success. ## Two kinds of failure, kept apart A delivery of something new adds an item, and adding to a vector can run out of memory. That is a `CollectionError` — a different kind of failure altogether, and nobody's fault. So `Deliver` and `Apply` fail with the **sum** of the two error types, and every `?` passes either kind through unchanged: ```rux func Apply(stock: &var Vector, order: Order) -> ! (StockError | CollectionError) { ``` `Main` opens the outcome with typed patterns. A `StockError` is explained and counted; a `CollectionError` ends the day, because no order can be blamed for it: ```rux match Apply(stock, order) { .Success(()) => PrintLine(" done"), .Failure(error: StockError) => { Explain(error); refused += 1; }, // Running out of memory is no fault of the order, so it ends the day. .Failure(error: CollectionError) => fail error } ``` `Main` itself is declared `-> ! CollectionError`, so `fail error` ends the program with exit status 1. ## Retiring a sold-out item `Retire` refuses to drop an item that is still on the shelf. Once it has sold out, `RemoveAt` takes it out of the vector. In the run above, retiring milk is refused with 6 still in stock, candles (at 0) are removed, and later honey — delivered as a new item — appears at the end of the closing list. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Inventory){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Inventory: the stock list of a small shop, kept as structs in a `Vector`, and a day of orders // applied to it, some of which cannot be carried out. // // Every change to the stock is a fallible function, and each way it can be refused is a case of // `StockError` carrying the details: which item is unknown, how many were wanted and how many // were there. A refused order changes nothing, so the stock is never left half-updated. // // A delivery of something new adds an item to the vector, and adding can run out of memory. That // is a `CollectionError`, a different kind of failure altogether, so `Apply` fails with the sum // `StockError | CollectionError` and both kinds pass through its `?` unchanged. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ CollectionError, EqualsSlice, Vector }; import Io::PrintLine; struct StockItem { name: char8[..]; count: int32; } variant StockError { UnknownItem(char8[..]), BadAmount(int32), NotEnough { name: char8[..]; have: int32; wanted: int32; }, StillInStock { name: char8[..]; count: int32; } } variant Order { Sell { name: char8[..]; amount: int32; }, Deliver { name: char8[..]; amount: int32; }, Retire(char8[..]) } // Where the item sits in the vector, or `none` if the shop has never stocked it. Names are // compared with `EqualsSlice`: `==` on two slices is refused, because it would ask whether they // are the same view rather than whether they hold the same text. func Find(stock: &Vector, name: char8[..]) -> uint? { let items = stock.AsSlice(); for i in 0..items.length { if EqualsSlice(items[i].name, name) { return i; } } return none; } func Sell(stock: &var Vector, name: char8[..], amount: int32) -> ! StockError { if amount <= 0 { fail StockError::BadAmount(amount); } // An absent position becomes a failure on the spot. let index = Find(stock, name) ?? fail StockError::UnknownItem(name); var items = stock.AsMutableSlice(); if items[index].count < amount { fail StockError::NotEnough { name: name, have: items[index].count, wanted: amount }; } items[index].count -= amount; } func Deliver(stock: &var Vector, name: char8[..], amount: int32) -> ! (StockError | CollectionError) { if amount <= 0 { fail StockError::BadAmount(amount); } match Find(stock, name) { index? => { var items = stock.AsMutableSlice(); items[index].count += amount; }, none => stock.Push(StockItem { name: name, count: amount })? } } // An item leaves the list only once it has sold out. func Retire(stock: &var Vector, name: char8[..]) -> ! StockError { let index = Find(stock, name) ?? fail StockError::UnknownItem(name); let count = stock.AsSlice()[index].count; if count > 0 { fail StockError::StillInStock { name: name, count: count }; } // `RemoveAt` hands back the item it took out, as an optional; it is not needed here. stock.RemoveAt(index); } func Apply(stock: &var Vector, order: Order) -> ! (StockError | CollectionError) { match order { .Sell { name, amount } => { PrintLine("sell {} {}", amount, name); Sell(stock, name, amount)?; }, .Deliver { name, amount } => { PrintLine("deliver {} {}", amount, name); Deliver(stock, name, amount)?; }, .Retire(name) => { PrintLine("retire {}", name); Retire(stock, name)?; } } } func Explain(error: StockError) { match error { .UnknownItem(name) => PrintLine(" refused: no such item as {}", name), .BadAmount(amount) => PrintLine(" refused: {} is not an amount", amount), .NotEnough { name, have, wanted } => PrintLine(" refused: wanted {} {}, only {} left", wanted, name, have), .StillInStock { name, count } => PrintLine(" refused: {} {} still on the shelf", count, name) } } func PrintStock(stock: &Vector) { for item in stock { PrintLine(" {:10} {:3}", item.name, item.count); } } func Main() -> ! CollectionError { var system = SystemAllocator(); let allocator: Allocator = system; var stock = Vector(allocator); stock.Push(StockItem { name: "apples", count: 12 })?; stock.Push(StockItem { name: "bread", count: 3 })?; stock.Push(StockItem { name: "candles", count: 0 })?; stock.Push(StockItem { name: "milk", count: 6 })?; PrintLine("opening stock"); PrintStock(stock); let orders = [ Order::Sell { name: "apples", amount: 5 }, Order::Sell { name: "bread", amount: 4 }, Order::Sell { name: "honey", amount: 1 }, Order::Deliver { name: "bread", amount: 10 }, Order::Sell { name: "bread", amount: 4 }, Order::Deliver { name: "honey", amount: 2 }, Order::Sell { name: "milk", amount: -2 }, Order::Retire("milk"), Order::Retire("candles"), Order::Sell { name: "milk", amount: 6 } ]; var refused = 0; for order in orders { match Apply(stock, order) { .Success(()) => PrintLine(" done"), .Failure(error: StockError) => { Explain(error); refused += 1; }, // Running out of memory is no fault of the order, so it ends the day. .Failure(error: CollectionError) => fail error } } PrintLine("closing stock, after {} refused orders", refused); PrintStock(stock); } ``` Besides `Io`, its `Rux.toml` lists `Allocator` and `Collections` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Inventory rux run ``` ```text opening stock apples 12 bread 3 candles 0 milk 6 sell 5 apples done sell 4 bread refused: wanted 4 bread, only 3 left sell 1 honey refused: no such item as honey deliver 10 bread done sell 4 bread done deliver 2 honey done sell -2 milk refused: -2 is not an amount retire milk refused: 6 milk still on the shelf retire candles done sell 6 milk done closing stock, after 4 refused orders apples 7 bread 9 milk 0 honey 2 ``` ## Common mistakes ::warning **Comparing names with `==`.**:br`items[i].name == name` is refused: `error: operator '==' is not defined for slice type 'char8[..]'`, with the note that comparing the views would compare addresses rather than elements. Use `EqualsSlice` from Collections. :: ::warning **Letting a `CollectionError` into a function that fails with `StockError` only.**:br Declare `Deliver` as `-> ! StockError` and its `stock.Push(…)?` stops compiling: `error: '?' propagates error type 'CollectionError', but the enclosing function fails with 'StockError'`. `?` never converts one error into another; the function has to admit both. :: ::warning **Handling only the errors you expected.**:br Remove the `.Failure(error: CollectionError)` arm from `Main` and the match is incomplete: `error: match on '! (CollectionError | StockError)' is not exhaustive; missing .Failure(_: CollectionError)`. Every member of the sum needs an answer, even if the answer is "give up". :: ## Try it yourself 1. Add an order `Return { name; amount; }` for goods a customer brings back. It should behave like a delivery, but refuse an item the shop has never stocked. 2. Give each item a price in cents and print the value of the closing stock. Which functions have to change, and which do not? 3. Add a `LowStock` warning: after each successful sale, print a note if fewer than three are left. Is that a new case of `StockError`, or something else? 4. Apply all of a customer's orders together or none of them: take a small array of orders, check them all first, and only then change the stock. ## Learn more - [Vector](https://rux-lang.dev/docs/learn/vector) — `Push`, `AsSlice` and `AsMutableSlice` - [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) and [Error sum](https://rux-lang.dev/docs/learn/error-sum) - [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) — matching a sum by the type it holds - Next project: [Statistics](https://rux-lang.dev/docs/learn/statistics), the checkpoint for Algorithms # Statistics ::note **You'll need**: Parts 1–18 — this project is the checkpoint for Algorithms, and leans on [Writable slice](https://rux-lang.dev/docs/learn/writable-slice), [Sort](https://rux-lang.dev/docs/learn/sort), [Min and max](https://rux-lang.dev/docs/learn/min-max), [Fold](https://rux-lang.dev/docs/learn/fold) and [Math](https://rux-lang.dev/docs/learn/math). :: Given a handful of numbers, where is the middle, and how spread out are they around it? This program summarises a slice of measurements: how many there are, the smallest and largest, the mean, two kinds of variance and the median. Then it does the same for awkward inputs — repeated values, a single value and no values at all — because a summary that only works on nice data is not finished. It is the checkpoint for [Part 18: Algorithms](https://rux-lang.dev/docs/learn/algorithms), and most of the work is done by that part's functions: `MinIndex`, `MaxIndex`, `Sum` and `Sort`. ## How it is put together One function, `Summarize`, does all the work, and `Main` calls it four times with different data. Inside, the order of the steps matters: ```mermaid flowchart LR s(["Summarize"]) --> ext["count, smallest, largest
(fine for zero values)"] ext --> empty{"count == 0?"} empty -- "yes" --> stop(["stop: nothing to divide"]) empty -- "no" --> mean["mean
first pass"] mean --> var["variance
second pass"] var --> sort["Sort, then median
(changes the slice)"] ``` | Statistic | How it is found | Lessons it uses | | ------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | Smallest, largest | `MinIndex`, `MaxIndex` → `uint?` | [Min and max](https://rux-lang.dev/docs/learn/min-max), [Presence](https://rux-lang.dev/docs/learn/presence) | | Mean | `Sum(values, 0.0) / n` | [Fold](https://rux-lang.dev/docs/learn/fold), [Convert](https://rux-lang.dev/docs/learn/convert) | | Variance, deviation | A loop of squared distances, `Sqrt` | [For](https://rux-lang.dev/docs/learn/for), [Math](https://rux-lang.dev/docs/learn/math) | | Median | `Sort`, then the middle element(s) | [Sort](https://rux-lang.dev/docs/learn/sort), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) | | Four data sets | Views of arrays, including an empty one | [Slice](https://rux-lang.dev/docs/learn/slice), [Array](https://rux-lang.dev/docs/learn/array) | ## Extremes that cope with nothing `MinIndex` and `MaxIndex` do not return a value — they return the **position** of one, as an optional. An empty slice has no smallest element, and `none` says so without any special case: ```rux match MinIndex(values) { at? => PrintLine(" smallest {}", values[at]), none => PrintLine(" smallest none") } ``` Everything after this point divides by the count, so the function stops early when there is nothing to divide: ```rux if count == 0 { PrintLine(" mean, variance and median need at least one value"); PrintLine(); return; } ``` ## Mean and variance: two passes The **variance** is the average squared distance from the mean, so the mean has to be known before any distance can be measured — hence two passes over the data. The distances are squared so that values above and below the mean both count, instead of cancelling each other out: ```rux let n = count as float64; let mean = Sum(values, 0.0) / n; var squares = 0.0; for value in values { let distance = value - mean; squares += distance * distance; } ``` `Sum` is the fold of `+` from the [Fold](https://rux-lang.dev/docs/learn/fold) lesson, already written. Its second argument is the starting value, `0.0`, which also fixes the result's type as `float64`. ## Population or sample? There are two conventions for "average squared distance", and a summary should say which it uses: | Name | Divide by | Use it when | | ------------------- | --------- | ---------------------------------------- | | Population variance | n | the data is everything there is | | Sample variance | n − 1 | the data is a sample of something larger | Dividing by n − 1 corrects for the sample's mean having been computed from the sample itself. With one value there is nothing to correct with — n − 1 is zero — so the program reports the sample variance as undefined rather than printing a number. The **standard deviation** is the square root of either variance, which brings the answer back into the units of the data. ## The median sorts the data The median is the middle value once the data is in order, or the average of the two middle values when the count is even: ```rux Sort(values); let middle = count / 2; if count % 2 == 1 { PrintLine(" median {} (the middle value)", values[middle]); } else { let low = values[middle - 1]; let high = values[middle]; PrintLine(" median {} (between {} and {})", (low + high) / 2.0, low, high); } ``` `Sort` works in place, so `Summarize` takes a **writable** slice, `var float64[..]`, and the caller's array is left sorted afterwards. That is why the median comes last: the `values` line at the top of each summary still shows the data in its original order. ## Four kinds of input `Main` keeps each data set in a `var` array and passes a view of it. The last call passes `readings[..0]`, an empty view of an array that already exists — the simplest way to make "no values" without a new type: ```rux var single: float64[1] = [42.0]; Summarize("a single value", single[..]); // An empty view of an array, so there is nothing to summarise. Summarize("no values", readings[..0]); ``` ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Statistics){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Summarising a set of numbers: where the middle is, and how spread out the numbers are around it. // // The mean is the total divided by the count. The variance is the average squared distance from // the mean, and the standard deviation is its square root, which brings the answer back to the // units of the data. There are two conventions for that average, and a summary should say which // one it uses: // // population variance divide by n the data is everything there is // sample variance divide by n - 1 the data is a sample of something larger // // Dividing by n - 1 corrects for the sample's mean being computed from the sample itself. With one // value there is nothing to correct with, so the sample variance is undefined rather than zero. // // The median is the middle value once the data is sorted, or the average of the two middle values // when the count is even. `Sort` works in place, so `Summarize` takes a writable slice and leaves // the caller's data in order. import Algorithms::{ MaxIndex, MinIndex, Sort, Sum }; import Io::{ Print, PrintLine }; import Math::Sqrt; func Summarize(label: char8[..], values: var float64[..]) { PrintLine("{}", label); Print(" values "); for value in values { Print(" {}", value); } PrintLine(); let count = values.length; PrintLine(" count {}", count); // The extremes answer with an optional index, so an empty slice is handled right here. match MinIndex(values) { at? => PrintLine(" smallest {}", values[at]), none => PrintLine(" smallest none") } match MaxIndex(values) { at? => PrintLine(" largest {}", values[at]), none => PrintLine(" largest none") } // Everything else divides by the count, and there is no mean of nothing. if count == 0 { PrintLine(" mean, variance and median need at least one value"); PrintLine(); return; } // Two passes: the mean has to be known before the distances from it can be measured. The // distances are squared so that values above and below both count, instead of cancelling. // `Sum` is the fold of `+` from the Fold lesson, already written. let n = count as float64; let mean = Sum(values, 0.0) / n; var squares = 0.0; for value in values { let distance = value - mean; squares += distance * distance; } PrintLine(" mean {:.4}", mean); let population = squares / n; PrintLine(" population variance {:.4}, deviation {:.4}", population, Sqrt(population)); if count > 1 { let sample = squares / (n - 1.0); PrintLine(" sample variance {:.4}, deviation {:.4}", sample, Sqrt(sample)); } else { PrintLine(" sample variance undefined for one value"); } // The median needs the values in order, so it comes last. Sort(values); let middle = count / 2; if count % 2 == 1 { PrintLine(" median {} (the middle value)", values[middle]); } else { let low = values[middle - 1]; let high = values[middle]; PrintLine(" median {} (between {} and {})", (low + high) / 2.0, low, high); } PrintLine(); } func Main() -> int { // An odd count: one value sits exactly in the middle. var readings: float64[9] = [12.5, 9.0, 15.25, 11.0, 8.75, 14.0, 10.5, 13.25, 11.75]; Summarize("nine readings", readings[..]); // An even count with repeated values. Duplicates are ordinary data: each one counts. var scores: float64[6] = [4.0, 7.0, 4.0, 1.0, 7.0, 7.0]; Summarize("six scores with repeats", scores[..]); // One value: no spread at all, and no sample variance. var single: float64[1] = [42.0]; Summarize("a single value", single[..]); // An empty view of an array, so there is nothing to summarise. Summarize("no values", readings[..0]); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Algorithms` and `Math` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Statistics rux run ``` ```text nine readings values 12.5 9.0 15.25 11.0 8.75 14.0 10.5 13.25 11.75 count 9 smallest 8.75 largest 15.25 mean 11.7778 population variance 4.3117, deviation 2.0765 sample variance 4.8507, deviation 2.2024 median 11.75 (the middle value) six scores with repeats values 4.0 7.0 4.0 1.0 7.0 7.0 count 6 smallest 1.0 largest 7.0 mean 5.0000 population variance 5.0000, deviation 2.2361 sample variance 6.0000, deviation 2.4495 median 5.5 (between 4.0 and 7.0) a single value values 42.0 count 1 smallest 42.0 largest 42.0 mean 42.0000 population variance 0.0000, deviation 0.0000 sample variance undefined for one value median 42.0 (the middle value) no values values count 0 smallest none largest none mean, variance and median need at least one value ``` ## Common mistakes ::warning **Passing data that cannot be sorted.**:br`Summarize` sorts, so its parameter is `var float64[..]`. An array declared with `let` gives only a read-only view, and the call fails: `error: argument 2 to 'Summarize' has type 'float64[..]', but parameter 'values' requires 'var float64[..]'`. Dropping the `var` from the parameter just moves the problem to `Sort`, which needs a `var T[..]` too. :: ::warning **Taking the median before sorting.**:br Without `Sort(values);` the program still runs, but the "median" is whatever sits in the middle of the unsorted data: 8.75 for the nine readings instead of 11.75. :: ::warning **Forgetting the empty case.**:br Remove the `count == 0` check and the empty summary prints a mean of `NaN` — zero divided by zero — and then stops with `Panic: index out of range`: `middle - 1` on a count of 0 is not −1 but a huge unsigned number. :: ## Try it yourself 1. Add the **range** (largest minus smallest) to the summary. Where must it go so that it still works for an empty slice? 2. Add the **mode**, the most frequent value. Because the slice is sorted by then, equal values sit side by side, so one pass that counts runs is enough. 3. Print the values again after the median, to see that the caller's array really has been sorted. 4. Summarise a copy instead: make `Summarize` take a read-only `float64[..]` and sort a copy of the data in a local array, so the caller's data keeps its order. What limits the size of that local array? ## Learn more - [Min and max](https://rux-lang.dev/docs/learn/min-max), [Fold](https://rux-lang.dev/docs/learn/fold) and [Sort](https://rux-lang.dev/docs/learn/sort) - [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) — why sorting needs `var T[..]` - [Sqrt](https://rux-lang.dev/docs/api/math/sqrt) in the API reference - Next projects: [Guess](https://rux-lang.dev/docs/learn/guess), [Age](https://rux-lang.dev/docs/learn/age), [Password](https://rux-lang.dev/docs/learn/password) and [Launch](https://rux-lang.dev/docs/learn/launch), the checkpoints for Utilities # Guess ::note **You'll need**: Parts 1–20 — this project is a checkpoint for Utilities, and leans on [Entropy](https://rux-lang.dev/docs/learn/entropy), [Input](https://rux-lang.dev/docs/learn/input) and [Parse](https://rux-lang.dev/docs/learn/parse). :: The program thinks of a number from 1 to 100, and you have seven guesses to find it. After each wrong guess it says "higher" or "lower". It is the first project in the course that you **play** rather than just run. Seven guesses are exactly enough if you play well: halve what is left every time, and seven halvings take a hundred candidates down to one. That is also why the program is strict about what counts as a guess — text that is not a number, or a number outside 1 to 100, is answered and asked again without using a guess up. It is a checkpoint for [Part 20: Utilities](https://rux-lang.dev/docs/learn/utilities), which supplies the random number; the input handling comes from [Part 14: Text](https://rux-lang.dev/docs/learn/text). ## How it is put together All of it lives in `Main`: pick a secret, then loop until the guesses run out. Every line the player types takes one of these paths: ```mermaid flowchart LR read["ReadLine"] -- "end of input" --> leave(["reveal the number
and stop"]) read -- "read error" --> fail(["stop, status 1"]) read -- "a line" --> parse{"a number?"} parse -- "no" --> again["try again,
no guess used"] parse -- "yes" --> range{"1 to 100?"} range -- "no" --> again range -- "yes" --> used["count the guess"] used --> hit{"the secret?"} hit -- "yes" --> win(["found it"]) hit -- "no" --> hint["higher or lower"] ``` | Piece | Lessons it uses | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | A generator seeded from entropy | [Entropy](https://rux-lang.dev/docs/learn/entropy), [Random](https://rux-lang.dev/docs/learn/random) | | A secret from 1 to 100 | [Distribution](https://rux-lang.dev/docs/learn/distribution) (`UniformInRange`) | | Reading a line, the end of input | [Input](https://rux-lang.dev/docs/learn/input), [Guard](https://rux-lang.dev/docs/learn/guard) | | Turning the line into a number | [Parse](https://rux-lang.dev/docs/learn/parse), [String view](https://rux-lang.dev/docs/learn/string-view) | | Skipping a bad guess | [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback), [Continue](https://rux-lang.dev/docs/learn/continue) | | The hint | [Ternary](https://rux-lang.dev/docs/learn/ternary) | ## A different number every game A seeded generator such as `Pcg64Dxsm` gives the same numbers for the same seed — useful for tests, useless for a game. So the generator is seeded from the operating system's entropy instead. Asking for entropy can fail, and then there is no fair number to pick, so the program says so and stops: ```rux var generator = PcgFromEntropy() catch { else => { PrintLine("There is no entropy to pick a number with, so there is no game."); return 1; } }; let secret = UniformInRange(generator, 1, 100) as int32; ``` `UniformInRange` returns a `uint64` from 1 to 100 inclusive, every value equally likely. The guesses will be parsed as `int32`, so the secret is converted once here, and every comparison after it is between two `int32`s. ## Four ways the game can end Each pass of the loop prints a prompt, clears the builder and reads a line. The match on the read is where two of the endings live: ```rux match ReadLine(builder) { .Success(_) => {}, .Failure(error) if error.kind == IoErrorKind::EndOfStream => { PrintLine(); PrintLine("Leaving already? It was {}.", secret); return 0; }, .Failure(_) => { PrintLine("The input could not be read."); return 1; } } ``` Ending the input is a polite way to give up, so it reveals the number and exits with status 0. A read error is a real fault, so it exits with status 1. The other two endings — found it, and out of guesses — are ordinary `return 0`s further down. ## A guess that does not count The guess is parsed from the trimmed line. If parsing fails, the `catch` arm prints a message and uses `continue` to start the next pass of the loop — the `catch` is inside the loop, so it can leave the pass, not just the expression: ```rux let guess = ParseInt32(builder.View().Trim()) catch { else => { PrintLine("That is not a number. It does not count; try again."); continue; } }; if guess < 1 || guess > 100 { PrintLine("That is outside 1 to 100. It does not count; try again."); continue; } used += 1; ``` `used` only grows after both checks, so the prompt keeps saying `guess 1:` until a real guess arrives. ## The hint The last line of the loop picks between two words with the conditional operator: ```rux PrintLine("{}", guess < secret ? "higher" : "lower"); ``` ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Guess){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A guessing game: the program picks a number from 1 to 100, and you have seven guesses to find // it, with a "higher" or "lower" after each one. // // Seven is enough if you halve what is left every time, because seven halvings take a hundred // candidates down to one. So a guess only counts when it is a real one: text that is not a // number, or a number outside 1 to 100, is answered and asked again without using a guess up. // // The game ends in one of four ways, and each is handled on purpose: the number is found, the // guesses run out, the input ends (Ctrl+Z and Enter on Windows, Ctrl+D elsewhere, or the end of // piped input), or the input cannot be read at all. // // The number comes from a generator seeded from the system's entropy, so every game is // different. Asking for entropy can fail, and then there is no fair number to pick, so the // program says so and stops. import Allocator::{ Allocator, SystemAllocator }; import Format::ParseInt32; import Io::{ IoErrorKind, Print, PrintLine, ReadLine }; import Random::{ Pcg64Dxsm, PcgFromEntropy, UniformInRange }; import Text::StringBuilder; const Guesses = 7; func Main() -> int { var generator = PcgFromEntropy() catch { else => { PrintLine("There is no entropy to pick a number with, so there is no game."); return 1; } }; let secret = UniformInRange(generator, 1, 100) as int32; PrintLine("I am thinking of a number from 1 to 100. You have {} guesses.", Guesses); var system = SystemAllocator(); let allocator: Allocator = system; var builder = StringBuilder(allocator); var used = 0; while used < Guesses { Print("guess {}: ", used + 1); builder.Clear(); match ReadLine(builder) { .Success(_) => {}, .Failure(error) if error.kind == IoErrorKind::EndOfStream => { PrintLine(); PrintLine("Leaving already? It was {}.", secret); return 0; }, .Failure(_) => { PrintLine("The input could not be read."); return 1; } } // Spaces around the number are forgiven; anything else that is not a number is not. let guess = ParseInt32(builder.View().Trim()) catch { else => { PrintLine("That is not a number. It does not count; try again."); continue; } }; if guess < 1 || guess > 100 { PrintLine("That is outside 1 to 100. It does not count; try again."); continue; } used += 1; if guess == secret { PrintLine("Yes, {}! Found in {} of {} guesses.", secret, used, Guesses); return 0; } PrintLine("{}", guess < secret ? "higher" : "lower"); } PrintLine("Out of guesses. It was {}.", secret); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Format`, `Random` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Guess rux run ``` ```text I am thinking of a number from 1 to 100. You have 7 guesses. guess 1: fifty That is not a number. It does not count; try again. guess 1: 50 higher guess 2: 75 higher guess 3: 88 lower guess 4: 81 higher guess 5: 84 Yes, 84! Found in 5 of 7 guesses. ``` Ending the input (Ctrl+Z and Enter on Windows, Ctrl+D elsewhere) leaves the game and reveals the number. Piped input works too, though the guesses cannot react to the hints: ```sh 1..7 | rux run ``` ```text I am thinking of a number from 1 to 100. You have 7 guesses. guess 1: higher guess 2: higher guess 3: higher guess 4: higher guess 5: higher guess 6: higher guess 7: higher Out of guesses. It was 78. ``` The game waits for you. Run it and type a guess after each prompt, pressing Enter each time. To leave early, end the input — Ctrl+Z and Enter on Windows, Ctrl+D elsewhere — and the program tells you the number. The piped example above is written for PowerShell (`1..7` is the numbers 1 to 7); in a POSIX shell, `seq 1 7 | rux run` does the same. Because the secret is different on every run, your output will differ from the sample. ## Common mistakes ::warning **Comparing the guess with an unconverted secret.**:br`UniformInRange` returns a `uint64`. Leave out the `as int32` and every comparison with the `int32` guess is refused: `error: operator '==' cannot compare left operand 'int32' with right operand 'uint64'`, and the same for `<`. Convert once, where the value is made. :: ::warning **Counting a guess before checking it.**:br If `used += 1;` moves above the range check, typing `500` wastes a guess. The program still works — it is just unfair, which in a game is a bug. :: ::warning **Treating the end of input as an error.**:br Without the guarded `EndOfStream` arm, a player who presses Ctrl+Z (or Ctrl+D) to quit gets "The input could not be read" and exit status 1. The end of the input is expected, so it has its own arm, and it comes before the general `.Failure(_)`. :: ## Try it yourself 1. Let the player choose the upper limit — 10, 100 or 1000 — and work out the number of guesses from it: the smallest `n` with 2ⁿ at least the limit. 2. Keep a list of the guesses so far and refuse a repeated one without counting it. 3. After the game, ask "Play again? (y/n)" and start a new round on `y`, with a new secret. 4. Swap the roles: you think of a number and the program guesses it by halving the range, reading "higher", "lower" or "yes" after each try. ## Learn more - [Entropy](https://rux-lang.dev/docs/learn/entropy) and [Distribution](https://rux-lang.dev/docs/learn/distribution) — where the secret comes from - [Input](https://rux-lang.dev/docs/learn/input) and [Parse](https://rux-lang.dev/docs/learn/parse) — reading and checking a line - [Continue](https://rux-lang.dev/docs/learn/continue) — skipping the rest of a pass - Next project: [Age](https://rux-lang.dev/docs/learn/age) # Age ::note **You'll need**: Parts 1–20 — this project is a checkpoint for Utilities, and leans on [Date](https://rux-lang.dev/docs/learn/date), [Fail](https://rux-lang.dev/docs/learn/fail) and [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error). :: How old is someone, in completed years, months and days? It sounds like a subtraction and is not, because months have different lengths. What is one month after 31 January? When does someone born on 29 February have a birthday in a year without one? This program answers with the rule people actually use, and runs it on eleven pairs of dates chosen to hit every awkward case. It is a checkpoint for [Part 20: Utilities](https://rux-lang.dev/docs/learn/utilities): the Time package does the calendar arithmetic, and the program's job is to ask it the right questions. ## How it is put together | Piece | Its job | Lessons it uses | | ---------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `Age` | Years, months and days, as a struct | [Struct](https://rux-lang.dev/docs/learn/struct) | | `AgeError` | `BornLater` or `OutOfRange` — no details needed, so a plain enum | [Enum](https://rux-lang.dev/docs/learn/enum), [Fail](https://rux-lang.dev/docs/learn/fail) | | `AgeOn` | The calculation | [Date](https://rux-lang.dev/docs/learn/date), [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) | | `Show` | Parses two texts, calls `AgeOn`, prints one row | [Date](https://rux-lang.dev/docs/learn/date), [Catch](https://rux-lang.dev/docs/learn/catch), [Format](https://rux-lang.dev/docs/learn/format) | | `Main` | Eleven rows of test cases | [Function](https://rux-lang.dev/docs/learn/function) | The dates are all written out in the source, so the output is the same on every run. A real program would ask the clock for today — one of the exercises below. ## The rule Count whole years, then whole months, then the days left over. A month is complete when its day comes round again — and when that day does not exist, the month is complete on its **last** day instead: | Born | One month later | Why | | ----------- | --------------- | ------------------------------------- | | 15 March | 15 April | The day exists | | 31 January | 28 February | February has no 31st, so its last day | | 29 February | 28 February | In a year that is not a leap year | `PlusMonths` from the Time package follows exactly that rule — it clamps to the end of the month — so the program never counts the days in a month by hand. ## Counting months, then correcting by one `AgeOn` first refuses a birth after the date asked about. Then it counts the calendar months between the two dates: ```rux var months = ((on.year - birth.year) as int64) * 12 + (on.month as int64) - (birth.month as int64); var reached = birth.PlusMonths(months) ?? fail AgeError::OutOfRange; if on.DaysSince(reached) < 0 { months -= 1; reached = birth.PlusMonths(months) ?? fail AgeError::OutOfRange; } ``` That count is either right or one too many — too many when the day of the month has not come round yet. Born on 15 March 1990 and asked about 4 October 2026, the count says 439 months, but 15 October 2026 is still in the future, so it drops to 438 — 36 years and 6 months. `reached` is then the last date, on or before `on`, that is a whole number of months after the birth. `PlusMonths` returns `Date?`: a date far enough out leaves the range the calendar supports. `?? fail` turns that absence into the `OutOfRange` error on the spot. The answer is then a division and a remainder, and the days left over are a plain `DaysSince`: ```rux return Age { years: months / 12, months: months % 12, days: on.DaysSince(reached) }; ``` ## Step from the birth, not from last month Notice that both calls are `birth.PlusMonths(months)` — always counted from the birth date. Stepping one month at a time would let the clamping drift: | From 31 January 2026 | Result | | --------------------------- | ----------- | | `PlusMonths(1)` | 28 February | | …then `PlusMonths(1)` again | 28 March | | `PlusMonths(2)` directly | 31 March | Once a step has clamped to the 28th, every later step keeps the 28th. Counting from the birth each time keeps the original day of the month. ## Parsing, and dates that do not exist `Show` parses both texts with `ParseDate`, which returns `Date ! TimeParseError`. A text that is not a real date — such as 29 February 2023 — fails, and the `catch` prints a row saying so and leaves `Show` early: ```rux let birth = ParseDate(birthText) catch { else => { PrintLine("{} {} {} is not a date", birthText, onText, birthText); return; } }; ``` The outcome of `AgeOn` is matched with nested patterns, one arm per case of the enum: ```rux match AgeOn(birth, on) { .Success(age) => PrintLine("{} {} {:5} {:7} {:5}", birth, on, age.years, age.months, age.days), .Failure(AgeError::BornLater) => PrintLine("{} {} refused, born after that date", birth, on), .Failure(AgeError::OutOfRange) => PrintLine("{} {} outside the calendar", birth, on) } ``` `{:5}` and `{:7}` pad the numbers to the widths of the header's columns. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Age){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Working out how old someone is, in completed years, months and days. It sounds like // subtraction and is not, because months have different lengths. // // The rule is the one people use: count whole years, then whole months, then the days left over. // A month is complete when its day comes round again. When that day does not exist, the month // is complete on its last day instead: one month after 31 January is 28 February, and someone // born on 29 February has their birthday on 28 February in a year that is not a leap year. // // `PlusMonths` from the Time package follows exactly that rule, clamping to the end of the // month, so the program never counts days in a month by hand. It finds the last date, on or // before the second one, that is a whole number of months after the birth, and the days left // over are a plain `DaysSince`. // // Every date is written out, so the output is the same on every run. A real program would ask // the clock for today. import Io::PrintLine; import Time::{ Date, ParseDate }; struct Age { years: int64; months: int64; days: int64; } enum AgeError { BornLater, OutOfRange } // The age on `on` of someone born on `birth`, or why there is none. func AgeOn(birth: Date, on: Date) -> Age ! AgeError { if on.DaysSince(birth) < 0 { fail AgeError::BornLater; } // Counting the calendar months between the two gives the right answer, or one too many // when the day of the month has not come round yet. Stepping from `birth` rather than // from the previous month keeps the clamping from drifting: 31 January plus two months // is 31 March, not 28 March. var months = ((on.year - birth.year) as int64) * 12 + (on.month as int64) - (birth.month as int64); var reached = birth.PlusMonths(months) ?? fail AgeError::OutOfRange; if on.DaysSince(reached) < 0 { months -= 1; reached = birth.PlusMonths(months) ?? fail AgeError::OutOfRange; } return Age { years: months / 12, months: months % 12, days: on.DaysSince(reached) }; } func Show(birthText: char8[..], onText: char8[..]) { let birth = ParseDate(birthText) catch { else => { PrintLine("{} {} {} is not a date", birthText, onText, birthText); return; } }; let on = ParseDate(onText) catch { else => { PrintLine("{} {} {} is not a date", birthText, onText, onText); return; } }; match AgeOn(birth, on) { .Success(age) => PrintLine("{} {} {:5} {:7} {:5}", birth, on, age.years, age.months, age.days), .Failure(AgeError::BornLater) => PrintLine("{} {} refused, born after that date", birth, on), .Failure(AgeError::OutOfRange) => PrintLine("{} {} outside the calendar", birth, on) } } func Main() -> int { PrintLine("born on years months days"); // A birthday already past this year, one still to come, and the day itself. Show("1990-03-15", "2026-10-04"); Show("1990-12-25", "2026-10-04"); Show("2000-10-04", "2026-10-04"); // The end of a month. 31 January reaches a whole month on 28 February, and the next on // 31 March, so 30 March is one month and 30 days. Show("2026-01-31", "2026-02-28"); Show("2026-01-31", "2026-03-30"); Show("2026-01-31", "2026-03-31"); // A leap-day birthday falls on 28 February in an ordinary year, and on the 29th in a // leap year, which makes 28 February 2028 the day before it. Show("2004-02-29", "2025-02-28"); Show("2004-02-29", "2028-02-28"); Show("2004-02-29", "2028-02-29"); // Dates the other way round, and a date that does not exist. Show("2026-10-04", "1990-03-15"); Show("2023-02-29", "2026-10-04"); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Time` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Age rux run ``` ```text born on years months days 1990-03-15 2026-10-04 36 6 19 1990-12-25 2026-10-04 35 9 9 2000-10-04 2026-10-04 26 0 0 2026-01-31 2026-02-28 0 1 0 2026-01-31 2026-03-30 0 1 30 2026-01-31 2026-03-31 0 2 0 2004-02-29 2025-02-28 21 0 0 2004-02-29 2028-02-28 23 11 30 2004-02-29 2028-02-29 24 0 0 2026-10-04 1990-03-15 refused, born after that date 2023-02-29 2026-10-04 2023-02-29 is not a date ``` ## Common mistakes ::warning **Using a `Date?` as a `Date`.**:br`PlusMonths` may have no answer, so `var reached = birth.PlusMonths(months);` makes `reached` a `Date?`, and the next line fails: `error: argument 1 to 'DaysSince' has type 'Date?', but parameter 'earlier' requires 'Date'`. Decide what absence means — here `?? fail AgeError::OutOfRange`. :: ::warning **Skipping the correction step.**:br Without the `if on.DaysSince(reached) < 0` block the program still compiles, but every birthday not yet reached this year comes out with negative days: 15 March 1990 on 4 October 2026 gives 36 years, 7 months and −11 days. :: ## Try it yourself 1. Use today's date instead of a fixed one. `DateTime::FromTimestamp(Timestamp::Now(), UtcOffset::Utc()).date` from the Time package is today in UTC — see [Date and time](https://rux-lang.dev/docs/learn/date-time). 2. Also print the age in total days, with one `DaysSince`. 3. Print how many days remain until the next birthday. (Careful with 29 February.) 4. Read the birth date from the input instead of from the source, using [Input](https://rux-lang.dev/docs/learn/input), and keep asking until `ParseDate` accepts it. ## Learn more - [Date](https://rux-lang.dev/docs/learn/date) — `ParseDate`, `PlusDays` and friends - [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) — `?? fail` - [Enum](https://rux-lang.dev/docs/learn/enum) — why `AgeError` needs no data - Next project: [Password](https://rux-lang.dev/docs/learn/password) # Password ::note **You'll need**: Parts 1–20 — this project is a checkpoint for Utilities, and leans on [Entropy](https://rux-lang.dev/docs/learn/entropy), [Propagate](https://rux-lang.dev/docs/learn/propagate) and [Loop](https://rux-lang.dev/docs/learn/loop). :: This program makes a 16-character password. It is short, but two decisions in it carry real weight, and getting either one wrong produces a password that *looks* random and is not: - **Where the randomness comes from.** Every character is drawn straight from the operating system's entropy, never from a seeded generator. - **How a random byte becomes a character.** A byte has 256 values and the alphabet has 57 characters. Turning one into the other fairly takes a technique called **rejection sampling**. It is a checkpoint for [Part 20: Utilities](https://rux-lang.dev/docs/learn/utilities). The result has about 93 bits of strength, and it is different on every run. ## How it is put together | Piece | Its job | Lessons it uses | | ----------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Alphabet` | 57 characters that cannot be misread | [Const](https://rux-lang.dev/docs/learn/const), [String literal](https://rux-lang.dev/docs/learn/string-literal) | | `DrawIndex` | One fair index into the alphabet, or an `EntropyError` | [Entropy](https://rux-lang.dev/docs/learn/entropy), [Loop](https://rux-lang.dev/docs/learn/loop), [Propagate](https://rux-lang.dev/docs/learn/propagate), [Pointer](https://rux-lang.dev/docs/learn/pointer) | | `Reason` | An `EntropyError` in words | [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Enum](https://rux-lang.dev/docs/learn/enum) | | `Main` | Sixteen draws into an array, then one line of output | [Array](https://rux-lang.dev/docs/learn/array), [Catch](https://rux-lang.dev/docs/learn/catch), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice) | ## An alphabet for people The alphabet leaves out the characters that look like each other in many fonts — `l`, `I`, `O`, `0` and `1`: ```rux const Alphabet = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; ``` A character that can be misread costs more in mistyped passwords than it adds in strength. Each character drawn from 57 adds log₂ 57 ≈ 5.83 bits, so sixteen of them give about 93 bits — far beyond guessing. ## Why not a seeded generator? A generator such as `Pcg64Dxsm` from [Random](https://rux-lang.dev/docs/learn/random) is **predictable by design**: the same seed always replays the same numbers. A password made from one is only as secret as its seed, and anyone who learns or guesses the seed can make the same password. So every character here comes from the Entropy package directly, and every request is checked: if the system has no randomness to give, the program stops rather than invent some. ## Rejection sampling Taking `byte % 57` is the obvious way to turn a byte into an index, and it is biased. 256 is four whole 57s (228) plus 28 left over, so the first 28 characters of the alphabet would get five chances in 256 while the rest get only four: | Byte values | `byte % 57` gives | Effect | | ----------- | ------------------------- | -------------------------------------- | | 0 to 227 | every index 4 times | Fair | | 228 to 255 | indices 0 to 27 once more | Biased towards the first 28 characters | The fix is to throw away a byte from that uneven top and draw again: ```rux func DrawIndex() -> uint ! EntropyError { let count = Alphabet.length; // The largest multiple of `count` that fits in a byte's 256 values: 228 for 57 characters. // Bytes below it split evenly among the characters; bytes from it upwards are rejected. let limit = 256 - 256 % count; loop { var drawn: byte = 0; Fill(@drawn as *var opaque, 1)?; if (drawn as uint) < limit { return (drawn as uint) % count; } } } ``` `loop` has no condition: it repeats until the `return` inside it fires. A byte is rejected with probability 28 / 256, about one time in nine, so the loop almost always ends on the first or second try. `Fill` writes random bytes into any memory you point it at. It takes an untyped pointer and a length, so `@drawn` — the address of the one-byte variable — is converted to `*var opaque`. The `?` passes an `EntropyError` straight to the caller. ## Nothing printed until it is complete `Main` builds the whole password in an array before printing any of it. A failure halfway therefore leaves nothing on the screen that could be mistaken for a shorter password: ```rux var password: char8[16]; for i in 0..password.length { let index = DrawIndex() catch { error => { PrintLine("no password: {}", Reason(error)); return 1; } }; password[i] = Alphabet[index]; } ``` `PrintLine` prints text views, not fixed-size arrays, so the last line passes `password[..]`, a view of the whole array. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Password){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // A password generator: sixteen characters, each drawn straight from the operating system's // entropy, from an alphabet chosen so nobody misreads the result. // // Two decisions carry the whole program. // // Where the randomness comes from. A seeded generator such as `Pcg64Dxsm` is predictable by // design, so a password made from one is only as secret as its seed. Here every character comes // from the Entropy package directly, and every request is checked: if the system has no // randomness to give, the program stops rather than invent some. // // How a random byte becomes a character. A byte has 256 values and the alphabet has 57 // characters, and 57 does not divide 256. Taking `byte % 57` would make the first 28 characters // (256 - 4 * 57 of them) a little more likely than the rest. So a byte from the uneven top of the // range is thrown away and another is drawn: rejection sampling. Every character is then exactly // as likely as every other. // // The result has 16 * log2(57), about 93 bits of strength. The output differs on every run. import Entropy::{ EntropyError, Fill }; import Io::PrintLine; // Lowercase, uppercase and digits, without the look-alikes l, I, O, 0 and 1. A character that // can be misread costs more in mistyped passwords than it adds in strength. const Alphabet = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // An index into the alphabet, every one equally likely, or why no entropy was available. func DrawIndex() -> uint ! EntropyError { let count = Alphabet.length; // The largest multiple of `count` that fits in a byte's 256 values: 228 for 57 characters. // Bytes below it split evenly among the characters; bytes from it upwards are rejected. let limit = 256 - 256 % count; loop { var drawn: byte = 0; Fill(@drawn as *var opaque, 1)?; if (drawn as uint) < limit { return (drawn as uint) % count; } } } func Reason(error: EntropyError) -> char8[..] { return match error { EntropyError::Unsupported => "this system has no entropy source", EntropyError::Interrupted => "the request was interrupted", EntropyError::Failed => "the system refused", EntropyError::TooLarge => "too many bytes in one request" }; } func Main() -> int { PrintLine("alphabet {} ({} characters)", Alphabet, Alphabet.length); // The password is built in full before any of it is printed, so a failure halfway leaves // nothing on the screen that could be mistaken for a shorter password. var password: char8[16]; for i in 0..password.length { let index = DrawIndex() catch { error => { PrintLine("no password: {}", Reason(error)); return 1; } }; password[i] = Alphabet[index]; } // `PrintLine` prints text views, not fixed-size arrays, so `password[..]` views the whole // array. The view is writable, because the array is `var`, and it prints just the same. PrintLine("password {}", password[..]); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Entropy` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Password rux run ``` ```text alphabet abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789 (57 characters) password 3negVXPww5u2Nzh4 ``` This is a sample: the password is different on every run. ## Common mistakes ::warning **Printing the array itself.**:br`PrintLine("password {}", password);` is refused: `error: argument 2 to 'PrintLine' has type 'char8[16]', but variadic parameter 'args' requires 'Display'`. A fixed-size array is not text to `PrintLine`; a view of it, `password[..]`, is. :: ::warning **Ignoring a failed request for entropy.**:br`Fill` returns `! EntropyError`. Drop the `?` and the call is refused: `error: fallible result of type '! EntropyError' is discarded`. Here that is a safety feature: a password generator that carried on after its randomness failed would print something that only looks like a password. :: ::warning **`byte % count` without rejection.**:br It compiles, it runs, and the passwords look random. The bias is small — 5 chances in 256 instead of 4 for some characters — which is exactly why it goes unnoticed. Tests cannot see it in one password; only the arithmetic shows it. :: ## Try it yourself 1. Let the length be a constant, `const Length = 20;`, and print the strength in bits next to the password. ([Math](https://rux-lang.dev/docs/learn/math) has `Log2`.) 2. Add a few symbols such as `-`, `_` and `!` to the alphabet. `DrawIndex` needs no change at all — why not? 3. Print five passwords, one per line, as a menu to choose from. 4. Demand at least one digit: after drawing, check the password and draw it again if it has none. Does that change the strength? ## Learn more - [Entropy](https://rux-lang.dev/docs/learn/entropy) — `Fill` and `EntropyError` - [Random](https://rux-lang.dev/docs/learn/random) — why a seeded generator is the wrong tool here - [Loop](https://rux-lang.dev/docs/learn/loop) — repeating until a `return` or `break` - Next project: [Launch](https://rux-lang.dev/docs/learn/launch) # Launch ::note **You'll need**: Parts 1–20 — this project is a checkpoint for Utilities, and leans on [Input](https://rux-lang.dev/docs/learn/input), [Parse](https://rux-lang.dev/docs/learn/parse) and [Stopwatch](https://rux-lang.dev/docs/learn/stopwatch). :: Mission control: the program runs a pre-flight checklist, asks how long a countdown you want, counts down with a pause between the numbers, and launches a rocket up the terminal. It is the most playful project in the course, and also a careful one — the answer to its one question is handled in every way it can arrive. Every part of it has appeared before. The checklist is an array of text, the countdown is a loop with a pause from the Time package, and the question is the line reading from [Input](https://rux-lang.dev/docs/learn/input), checked the way [Parse](https://rux-lang.dev/docs/learn/parse) checks a number. It is a checkpoint for [Part 20: Utilities](https://rux-lang.dev/docs/learn/utilities). ## How it is put together `Main` runs four stages in order, and only the question can end the program early: ```mermaid flowchart LR check["Checklist
array + Pause"] --> ask["Ask: count down from?
1 to 10, Enter for 10"] ask -- "end of input" --> scrub(["Mission scrubbed
status 0"]) ask -- "read error" --> radio(["Radio down
status 1"]) ask -- "a valid answer" --> count["T minus …
while count > 0"] count --> lift["Ignition and
rocket frames"] ``` | Piece | Lessons it uses | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Checklist and rocket frames | [Array](https://rux-lang.dev/docs/learn/array), [For](https://rux-lang.dev/docs/learn/for), [Character](https://rux-lang.dev/docs/learn/character) (`\u{…}`) | | `Pause` | [Duration](https://rux-lang.dev/docs/learn/duration), [Discard](https://rux-lang.dev/docs/learn/discard) | | The question | [Input](https://rux-lang.dev/docs/learn/input), [String view](https://rux-lang.dev/docs/learn/string-view), [Guard](https://rux-lang.dev/docs/learn/guard) | | Checking the answer | [Parse](https://rux-lang.dev/docs/learn/parse), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) | | The countdown | [While](https://rux-lang.dev/docs/learn/while), [Assignment](https://rux-lang.dev/docs/learn/assignment) | ## A pause that may be skipped `SleepFor` returns a fallible: the operating system may refuse to sleep. A countdown that skips a pause is only quicker, never wrong, so `Pause` discards a failure — on purpose, and visibly: ```rux func Pause(milliseconds: int64) { SleepFor(Duration::FromMilliseconds(milliseconds)) catch { else => {} }; } ``` `catch { else => {} }` is the standard way to say "I have considered this failure and it does not matter". Writing the call without it is an error, as Common mistakes shows. ## A question with a guaranteed answer The question is asked in a loop that runs until `count` holds a usable number. `count` starts at 0, which is not a valid answer, so the loop runs at least once: ```rux let answer = builder.View().Trim(); if answer.IsEmpty() { count = 10; } else { // Text that is not a number becomes 0, which the range check refuses as well. let asked = ParseInt32(answer) catch { else => 0 }; if asked >= 1 && asked <= 10 { count = asked; } else { PrintLine("Mission rules allow 1 to 10. Say again?"); } } ``` Three kinds of answer are handled with very little code: | Typed | What happens | | ------------- | ------------------------------------------------------------------ | | Nothing | The default, 10 | | 1 to 10 | That number | | Anything else | Asked again — text parses to the fallback 0, which is out of range | Folding "not a number" into "out of range" with `catch { else => 0 }` is a neat trick here, because 0 is never a valid answer. Whatever happens, the countdown is bounded: it can never be asked to count from a million. Before all that, the same guarded match as in [Guess](https://rux-lang.dev/docs/learn/guess) decides what the end of the input means. Here nobody gave the go, so the mission is scrubbed — a normal outcome with status 0. A read error is a broken radio, and exits with status 1. ## Countdown and liftoff The countdown is a plain `while` that pauses between numbers: ```rux while count > 0 { PrintLine(" T minus {}...", count); Pause(400); count -= 1; } ``` The rocket climbs because each frame is printed on a new line with a little less indentation than the one before, and the pauses between them let you watch it go. The emoji are written as `\u{1F680}` escapes in the source, so the file itself stays plain ASCII and every editor shows it the same way. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Launch){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Mission control: run the checklist, ask how long a countdown you want, count down, and launch. // // Every part of this has appeared before. The checklist is an array of text, the countdown is a // loop with a pause from the Time package between steps, and the question is the line reading // from the Input lesson, with the number checked the way the Parse lesson checks it. // // What makes it a whole program is that the input is handled in every way it can arrive. A // number from 1 to 10 sets the countdown and an empty line takes the default of 10, so the // countdown is always bounded. Anything else is asked again. And if the input ends before an // answer arrives (Ctrl+Z and Enter on Windows, Ctrl+D elsewhere, or the end of piped input), // nobody gave the go, so the mission is scrubbed. import Allocator::{ Allocator, SystemAllocator }; import Format::ParseInt32; import Io::{ IoErrorKind, Print, PrintLine, ReadLine }; import Text::StringBuilder; import Time::{ Duration, SleepFor }; // A pause that does not happen only makes the countdown quicker, so a failed sleep is ignored // on purpose rather than by accident. func Pause(milliseconds: int64) { SleepFor(Duration::FromMilliseconds(milliseconds)) catch { else => {} }; } func Main() -> int { PrintLine("\u{1F680} RUX MISSION CONTROL \u{1F680}"); PrintLine("========================"); let checklist = [ "fuel ............. loaded", "crew ............. strapped in", "weather .......... clear skies", "snacks ........... dangerously low", "borrow checker ... satisfied" ]; for item in checklist { PrintLine(" {} \u{2705}", item); Pause(150); } PrintLine(); var system = SystemAllocator(); let allocator: Allocator = system; var builder = StringBuilder(allocator); var count = 0; while count == 0 { Print("Count down from? (1 to 10, Enter for 10) "); builder.Clear(); match ReadLine(builder) { .Success(_) => {}, .Failure(error) if error.kind == IoErrorKind::EndOfStream => { PrintLine(); PrintLine("No go from the flight director. Mission scrubbed. \u{1F6D1}"); return 0; }, .Failure(_) => { PrintLine("The radio is down: the input could not be read."); return 1; } } let answer = builder.View().Trim(); if answer.IsEmpty() { count = 10; } else { // Text that is not a number becomes 0, which the range check refuses as well. let asked = ParseInt32(answer) catch { else => 0 }; if asked >= 1 && asked <= 10 { count = asked; } else { PrintLine("Mission rules allow 1 to 10. Say again?"); } } } PrintLine(); while count > 0 { PrintLine(" T minus {}...", count); Pause(400); count -= 1; } PrintLine(" \u{1F525} IGNITION \u{1F525}"); Pause(500); // Each frame is one line, so the rocket climbs up the terminal as it prints. let frames = [ " \u{1F680}", " \u{1F680}", " \u{1F680}", " \u{1F680}", " \u{1F680}", " \u{1F680}", " \u{1F680}", " \u{1F680}" ]; for frame in frames { PrintLine(frame); Pause(120); } PrintLine(); PrintLine(" \u{2B50} \u{1F30D} \u{2B50}"); PrintLine(" Liftoff! The crew waves from the window. \u{1F44B}"); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Format`, `Text` and `Time` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Launch rux run ``` ```text 🚀 RUX MISSION CONTROL 🚀 ======================== fuel ............. loaded ✅ crew ............. strapped in ✅ weather .......... clear skies ✅ snacks ........... dangerously low ✅ borrow checker ... satisfied ✅ Count down from? (1 to 10, Enter for 10) 20 Mission rules allow 1 to 10. Say again? Count down from? (1 to 10, Enter for 10) 3 T minus 3... T minus 2... T minus 1... 🔥 IGNITION 🔥 🚀 🚀 🚀 🚀 🚀 🚀 🚀 🚀 ⭐ 🌍 ⭐ Liftoff! The crew waves from the window. 👋 ``` The answer can be piped in. With no input at all, nobody gave the go, and after the checklist the run ends with: ```sh "3" | rux run $null | rux run ``` ```text Count down from? (1 to 10, Enter for 10) No go from the flight director. Mission scrubbed. 🛑 ``` The program waits for an answer after the checklist. Run it, type a number from 1 to 10 (or just press Enter for 10) and watch the countdown. End the input instead — Ctrl+Z and Enter on Windows, Ctrl+D elsewhere — to scrub the mission. The piped examples above are written for PowerShell; in a POSIX shell, `echo 3 | rux run` and `rux run < /dev/null` do the same. Whether the emoji show up depends on your terminal and its font. ## Common mistakes ::warning **Calling `SleepFor` and ignoring the result.**:br`SleepFor(Duration::FromMilliseconds(milliseconds));` on its own is refused: `error: fallible result of type '! TimeError' is discarded`. Even a failure you do not care about has to be discarded on purpose, with `catch { else => {} }`. :: ::warning **A countdown nobody bounded.**:br Accept any number that parses and someone will type `1000000`. At 0.4 seconds a step, the program would still be counting four and a half days later. Checking the range on input keeps every later part of the program simple. :: ## Try it yourself 1. Add a "hold" option: if the answer is `h`, print "Holding." and ask again. 2. Let the checklist fail: make one item `"weather .......... storm"` and scrub the launch if any item does not end with a known good word. (Hint: [String view](https://rux-lang.dev/docs/learn/string-view) can test how text ends.) 3. Time the whole countdown with a [Stopwatch](https://rux-lang.dev/docs/learn/stopwatch) and print how long it really took, pauses and all. 4. Make the rocket climb diagonally: change both the indentation and the number of empty lines between frames. ## Learn more - [Duration](https://rux-lang.dev/docs/learn/duration) and [Stopwatch](https://rux-lang.dev/docs/learn/stopwatch) — the Time package - [Discard](https://rux-lang.dev/docs/learn/discard) — ignoring a failure on purpose - [Input](https://rux-lang.dev/docs/learn/input) and [Parse](https://rux-lang.dev/docs/learn/parse) - Next project: [Notes](https://rux-lang.dev/docs/learn/notes), the checkpoint for Data formats # Notes ::note **You'll need**: Parts 1–21 — this project is the checkpoint for Data formats, and leans on [File](https://rux-lang.dev/docs/learn/file), [Atomic file](https://rux-lang.dev/docs/learn/atomic-file), [JSON](https://rux-lang.dev/docs/learn/json) and [Writing JSON](https://rux-lang.dev/docs/learn/json-write). :: A to-do list is only useful if it is still there tomorrow. This program keeps one as JSON in a file: it saves a list, loads it back as if it were a new session, adds a note, saves again, and then deals with the two things that go wrong with real files — one that someone damaged by hand, and one that is not there at all. It is the checkpoint for [Part 21: Data formats](https://rux-lang.dev/docs/learn/data-formats), and it brings together files from [Part 19](https://rux-lang.dev/docs/learn/files), JSON from Part 21, and the error handling of [Part 9](https://rux-lang.dev/docs/learn/errors) at full strength: five different error types meet in one program. ## How it is put together Each note is a small JSON object, `{"title": "...", "done": false}`, and the list is a JSON array of them. Saving turns the tree of `JsonValue`s into text and writes it; loading reads the text and parses it back into a tree: ```mermaid flowchart LR tree["JsonValue tree
in memory"] -- "Encode
WriteValue, pretty" --> text["JSON text
String"] text -- "Save
WriteAtomically" --> file[("Bin/notes.json")] file -- "Load
Open, Size, ReadExact" --> bytes["bytes"] bytes -- "Parse" --> tree ``` | Function | Its job | Can fail with | Lessons it uses | | ------------ | -------------------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Note` | Builds one note object | `TextError` | [Writing JSON](https://rux-lang.dev/docs/learn/json-write), [Move](https://rux-lang.dev/docs/learn/move) | | `Encode` | Turns the tree into indented text | `FormatError` | [Writing JSON](https://rux-lang.dev/docs/learn/json-write), [String builder](https://rux-lang.dev/docs/learn/string-builder) | | `Save` | Replaces the file in one step | `IoError` | [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) | | `Load` | Reads the whole file and parses it | `IoError`, `CollectionError`, `JsonParseError` | [File](https://rux-lang.dev/docs/learn/file), [Dynamic array](https://rux-lang.dev/docs/learn/dynamic-array), [JSON](https://rux-lang.dev/docs/learn/json) | | `PrintNotes` | Prints the list, tolerating missing members | nothing | [JSON](https://rux-lang.dev/docs/learn/json), [Out parameter](https://rux-lang.dev/docs/learn/out-parameter) | | `Main` | Two sessions, a damaged file, a missing file | all five | [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) | ## Building a note A note is an object with two members. Building it can fail — copying text into a new `String` needs memory — so every step ends in `?`: ```rux func Note(allocator: Allocator, title: char8[..], done: bool) -> JsonValue ! TextError { var note = JsonValue::Object(allocator); note.Insert(String::FromBytes(allocator, "title")?, JsonValue::Text(allocator, title)?); note.Insert(String::FromBytes(allocator, "done")?, JsonValue::Boolean(allocator, done)); return <-note; } ``` A `JsonValue` owns the memory of everything inside it, so it cannot be copied — only moved. `return <-note;` hands it to the caller. ## Saving whole or not at all `Save` is one call, and the call is the point: ```rux func Save(allocator: Allocator, path: Path, text: char8[..]) -> ! IoError { WriteAtomically(allocator, path, text)?; } ``` `WriteAtomically` writes the new text beside the old file and swaps it in only once it is complete. If the program crashes halfway through a save, the old list is still there, whole. Writing over the file directly would risk leaving half a list behind — the one outcome a notes file must never have. ## Loading, step by step `Load` asks the file how big it is, reads exactly that many bytes into an array of that size, and parses them: ```rux func Load(allocator: Allocator, path: Path) -> JsonValue ! (IoError | CollectionError | JsonParseError) { var file = File::Open(allocator, path, OpenOptions::Reading())?; let size = file.Size()?; var bytes = Array::Filled(allocator, size as uint, c8' ')?; ReadExact(file, bytes.AsMutableSlice())?; file.Close()?; return Parse(allocator, bytes.AsSlice())?; } ``` Each line can fail in its own way: opening and reading with an `IoError`, allocating the array with a `CollectionError`, parsing with a `JsonParseError`. The return type lists all three as a sum, and every `?` passes its error through unchanged. ## Reading what may not be there A file read from disk may have been edited by hand, so `PrintNotes` does not assume a note has both members. `Find` returns a pointer that is `null` when the member is missing: ```rux let title = (*note).Find(StringView::FromValidated("title")); let flag = (*note).Find(StringView::FromValidated("done")); var done = false; if flag != null { (*flag).AsBoolean(@done); } ``` `AsBoolean` writes its answer through an out-parameter, and leaves `done` alone if the member is not a boolean. A note with no title prints as `(untitled)`. ## Expected failures, and the rest `Main` is declared to fail with all five error types, so `?` can pass anything up — and a failure then ends the program with status 1. But two failures are **expected**, and those are handled where they happen. A damaged file is a `JsonParseError`, which knows the byte where the text stopped making sense: ```rux match Load(allocator, path) { .Success(_) => PrintLine("the damaged file loaded?"), .Failure(error: JsonParseError) => PrintLine("damaged file refused at byte {}: {}", error.Offset(), error), .Failure(error) => fail error } ``` A missing file is an `IoError` of kind `NotFound`, picked out with a typed pattern **and** a guard. In a real program, that arm is where "start a new, empty list" would go: ```rux .Failure(error: IoError) if error.kind == IoErrorKind::NotFound => PrintLine("no notes file any more: starting a new list would begin here"), .Failure(error) => fail error ``` In both matches the last arm, `.Failure(error) => fail error`, passes anything unexpected out of `Main`, which ends the program with status 1. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Notes){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Notes: a to-do list that outlives the program, kept as JSON in a file. // // Each note is a small JSON object, `{"title": "...", "done": false}`, and the list is an array // of them. Saving turns the tree of `JsonValue`s into text and writes it to disk; loading reads // the text back and parses it into a tree again. Between the two, the program could stop and // start a hundred times, which is the whole point of a file. // // A file is also where things go wrong. It may be missing, or it may hold something that is not // JSON, perhaps because someone edited it by hand. Every step that touches the disk or parses // text is fallible, and the program handles the two failures it expects — a damaged file and a // missing one — while `?` hands anything stranger to `Main`, which then ends with status 1. // // The file lives in this package's `Bin/` folder, and the program deletes it at the end. import Allocator::{ Allocator, SystemAllocator }; import Collections::{ Array, CollectionError }; import FileSystem::{ DeleteFile, File, OpenOptions, WriteAtomically }; import Io::{ IoError, IoErrorKind, PrintLine, ReadExact }; import Json::{ JsonParseError, JsonStyle, JsonValue, Parse, WriteValue }; import Path::{ OsString, Path }; import Text::{ FormatError, String, StringBuilder, StringView, TextError, TextWriter }; func Note(allocator: Allocator, title: char8[..], done: bool) -> JsonValue ! TextError { var note = JsonValue::Object(allocator); note.Insert(String::FromBytes(allocator, "title")?, JsonValue::Text(allocator, title)?); note.Insert(String::FromBytes(allocator, "done")?, JsonValue::Boolean(allocator, done)); return <-note; } // Turns the notes into indented JSON text, ready to be written or shown. func Encode(allocator: Allocator, notes: &JsonValue) -> String ! FormatError { var builder = StringBuilder(allocator); let sink: &var TextWriter = builder; WriteValue(sink, notes, JsonStyle::Pretty())?; return builder.IntoString(); } // Writing atomically means the old file is replaced only once the new one is complete, so a // crash halfway through a save can never leave half a list behind. func Save(allocator: Allocator, path: Path, text: char8[..]) -> ! IoError { WriteAtomically(allocator, path, text)?; } // Asks the file how big it is, reads exactly that many bytes, and parses them. func Load(allocator: Allocator, path: Path) -> JsonValue ! (IoError | CollectionError | JsonParseError) { var file = File::Open(allocator, path, OpenOptions::Reading())?; let size = file.Size()?; var bytes = Array::Filled(allocator, size as uint, c8' ')?; ReadExact(file, bytes.AsMutableSlice())?; file.Close()?; return Parse(allocator, bytes.AsSlice())?; } // A note read from a file might lack a member, so each one is checked before it is read. func PrintNotes(notes: &JsonValue) { for i in 0..notes.Length() { let note = notes.At(i); let title = (*note).Find(StringView::FromValidated("title")); let flag = (*note).Find(StringView::FromValidated("done")); var done = false; if flag != null { (*flag).AsBoolean(@done); } let mark = done ? "x" : " "; if title != null { PrintLine(" [{}] {}", mark, (*title).AsText()); } else { PrintLine(" [{}] (untitled)", mark); } } } func Main() -> ! (IoError | TextError | FormatError | CollectionError | JsonParseError) { var system = SystemAllocator(); let allocator: Allocator = system; var holder = OsString::FromText(allocator, "Bin/notes.json")?; let path = Path::FromView(holder.View()); // The first session: start a list and save it. var notes = JsonValue::Array(allocator); notes.Push(Note(allocator, "buy milk", true)?); notes.Push(Note(allocator, "water the plants", false)?); let first = Encode(allocator, notes)?; Save(allocator, path, first.View().Bytes())?; PrintLine("saved {} notes to {}", notes.Length(), path); // The next session knows nothing but the file: load it, add a note, save it again. var loaded = Load(allocator, path)?; loaded.Push(Note(allocator, "write a Rux program", false)?); let second = Encode(allocator, loaded)?; Save(allocator, path, second.View().Bytes())?; PrintLine("added a note; the file now reads:"); PrintLine("{}", second); // And one more time, to read the list as a person would. let final = Load(allocator, path)?; PrintLine("{} notes:", final.Length()); PrintNotes(final); // Someone edits the file by hand and cuts it short. Loading now fails as a parse error, which // says where the text stopped making sense. Save(allocator, path, "[{\"title\": \"half a note\"")?; match Load(allocator, path) { .Success(_) => PrintLine("the damaged file loaded?"), .Failure(error: JsonParseError) => PrintLine("damaged file refused at byte {}: {}", error.Offset(), error), .Failure(error) => fail error } // Clean up, then show that a missing file is told apart from every other I/O failure. DeleteFile(allocator, path)?; match Load(allocator, path) { .Success(_) => PrintLine("the deleted file loaded?"), .Failure(error: IoError) if error.kind == IoErrorKind::NotFound => PrintLine("no notes file any more: starting a new list would begin here"), .Failure(error) => fail error } } ``` Besides `Io`, its `Rux.toml` lists `Allocator`, `Collections`, `FileSystem`, `Json`, `Path` and `Text` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Notes rux run ``` ```text saved 2 notes to Bin/notes.json added a note; the file now reads: [ { "title": "buy milk", "done": true }, { "title": "water the plants", "done": false }, { "title": "write a Rux program", "done": false } ] 3 notes: [x] buy milk [ ] water the plants [ ] write a Rux program damaged file refused at byte 24: document ended in the middle of a value no notes file any more: starting a new list would begin here ``` The file is written to the package's `Bin/` folder, next to the build output, and the program deletes it before it ends, so a second run starts from nothing again. ## Common mistakes ::warning **Returning a `JsonValue` without moving it.**:br`return note;` is refused: `error: move-only value 'note' requires an explicit '<-' in return`, with the note that `JsonValue` prohibits copying. Write `return <-note;` — see [Move](https://rux-lang.dev/docs/learn/move). :: ::warning **Handling only the failure you were thinking of.**:br Drop the last arm from the damaged-file match and the compiler lists what is left: `error: match on 'JsonValue ! (CollectionError | IoError | JsonParseError)' is not exhaustive; missing .Failure(_: CollectionError), .Failure(_: IoError)`. One catch-all arm that passes the rest on is enough. :: ::warning **A `?` the function's type does not allow.**:br If `Load` is declared `-> JsonValue ! (IoError | JsonParseError)`, the array allocation stops compiling: `error: '?' propagates error type 'CollectionError', but the enclosing function fails with 'IoError | JsonParseError'`. The sum must name every error its `?`s can carry. :: ## Try it yourself 1. Mark a note as done: load the list, find "water the plants", set its `done` member to `true`, and save it again. 2. Turn the missing-file arm into real behaviour: when the file is not there, start with an empty array instead of failing. 3. Make the program a tiny command-line tool: read one line from the input with [Input](https://rux-lang.dev/docs/learn/input), add it as a note, save, and print the list. Remove the `DeleteFile` call so the notes survive between runs. 4. Damage the file differently — a missing comma, a stray `}` — and see what offset and message the parser reports for each. ## Learn more - [JSON](https://rux-lang.dev/docs/learn/json) and [Writing JSON](https://rux-lang.dev/docs/learn/json-write) - [File](https://rux-lang.dev/docs/learn/file) and [Atomic file](https://rux-lang.dev/docs/learn/atomic-file) - [Error sum](https://rux-lang.dev/docs/learn/error-sum) and [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern) — one function, several kinds of failure - Next project: [Melody](https://rux-lang.dev/docs/learn/melody), the checkpoint for Platform # Melody ::note **You'll need**: Parts 1–24 — this project is the checkpoint for Platform, and leans on [Extern](https://rux-lang.dev/docs/learn/extern), [Target](https://rux-lang.dev/docs/learn/target) and [Compile error](https://rux-lang.dev/docs/learn/compile-error). :: This program plays the opening of Beethoven's *Ode to Joy*, one note at a time, through the console speaker. There is no portable way to make a sound and no standard package that wraps one, so it does what a systems language is for: it calls the operating system directly. The function it calls is `Beep` in Windows' Kernel32 library, which plays a tone of a given frequency for a given number of milliseconds — exactly the shape of a musical note. That makes this the checkpoint for [Part 24: Platform](https://rux-lang.dev/docs/learn/platform), and the last project in the course. It builds and runs **only on Windows**, and it says so clearly everywhere else. ## How it is put together | Piece | Its job | Lessons it uses | | --------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `when #target.os` | Declares `Beep` on Windows, stops the build elsewhere | [When](https://rux-lang.dev/docs/learn/when), [Target](https://rux-lang.dev/docs/learn/target), [Compile error](https://rux-lang.dev/docs/learn/compile-error) | | `#Link` and `extern` | Binds `Beep` to Kernel32 | [Extern](https://rux-lang.dev/docs/learn/extern) | | `C4` … `G4` | Note frequencies as named constants | [Const](https://rux-lang.dev/docs/learn/const) | | `Note` and the `tune` array | The music, as data | [Struct](https://rux-lang.dev/docs/learn/struct), [Array](https://rux-lang.dev/docs/learn/array) | | `Main` | Plays each note, stops at the first failure | [For](https://rux-lang.dev/docs/learn/for), [Boolean](https://rux-lang.dev/docs/learn/boolean) | When `Main` calls `Beep`, nothing in Rux sits in between — the call goes straight to the system library: ```mermaid flowchart LR main(["Main
for note in tune"]) -- "Beep(frequency, duration)" --> decl["extern Beep
declared in Rux"] decl -- "linked by #Link" --> dll["Kernel32.dll"] dll --> spk["console speaker"] dll -- "bool32: did it play?" --> main ``` ## Windows only, on purpose The declaration of `Beep` sits inside a compile-time `when`, which picks a branch while compiling rather than while running: ```rux when #target.os { .Windows => { #Link("Kernel32.dll") extern { func Beep(frequency: uint32, duration: uint32) -> bool32; } }, else => #Error("Melody plays through the Windows Beep function, so it builds only for Windows") } ``` On Windows the first branch declares `Beep` and says which library holds it. On any other system there is no `Beep` to call. Without the `else` branch the build would still stop there, but with `error: no arm of this 'when' matches .Linux` — accurate, and no help to someone who only wanted to hear a tune. `#Error` stops it with a sentence a person can act on instead. Checking for Linux shows it: ```sh rux check --target linux-x86_64 ``` ```text error: Melody plays through the Windows Beep function, so it builds only for Windows ``` The branches `when` skips are never even type-checked, which is why the Windows-only declaration costs other systems nothing. ## The tune as data Raw frequencies such as 330 and 392 mean nothing on the page, so each note gets a name. Then the tune is an array of `Note` structs that reads almost like a score: ```rux const C4: uint32 = 262; const D4: uint32 = 294; const E4: uint32 = 330; const F4: uint32 = 349; const G4: uint32 = 392; ``` ```rux Note { name: "E", frequency: E4, milliseconds: 400 }, Note { name: "E", frequency: E4, milliseconds: 400 }, Note { name: "F", frequency: F4, milliseconds: 400 }, Note { name: "G", frequency: G4, milliseconds: 400 }, ``` The constants are typed `uint32`, because that is what `Beep` takes and what the `frequency` field holds. An untyped constant would be an `int`, and every note would be refused — see Common mistakes. ## A foreign failure, checked by hand A foreign function does not report failure through a Rux fallible. `Beep` returns a `bool32` that is false when no tone could be played — and the compiler will not insist that you look at it. The program looks anyway, and stops at the first note that fails rather than printing notes nobody heard: ```rux for note in tune { Print("{} ", note.name); if !Beep(note.frequency, note.milliseconds) { PrintLine(); PrintLine("Windows could not play a tone, so the tune stops here."); return 1; } } ``` `Beep` also blocks until the note has finished, so the loop needs no pauses of its own: the durations in the tune *are* the rhythm. The whole tune lasts about six seconds. ## The program The whole lesson is one package in the [Examples repository](https://github.com/rux-lang/Examples/tree/main/Projects/Melody){rel=""nofollow""}. Its comments explain every step. ```rux [Src/Main.rux] // Playing a tune by asking the operating system to sound one note at a time. // // There is no portable way to make a sound, and no standard package wraps one, so this program // calls Windows directly: `Beep(frequency, duration)` in Kernel32 plays a tone of that many // hertz for that many milliseconds, which is exactly the shape of a note. It is the extern // declaration and `#Link` from the Extern lesson, put to work. // // That makes the program Windows-only, and it says so where it matters: on any other system the // `when` below stops the build with an `#Error`, instead of letting it fail to link a function // that does not exist there. // // A foreign function reports failure its own way, not through a Rux fallible. `Beep` returns a // `bool32` that is false when no tone could be played, so every call is checked, and the tune // stops at the first note that fails rather than printing notes nobody heard. import Core::{ #Error, #target }; import Io::{ Print, PrintLine }; when #target.os { .Windows => { #Link("Kernel32.dll") extern { func Beep(frequency: uint32, duration: uint32) -> bool32; } }, else => #Error("Melody plays through the Windows Beep function, so it builds only for Windows") } // Note frequencies in hertz, near the middle of a piano. Naming them keeps the tune readable as // music rather than as numbers. const C4: uint32 = 262; const D4: uint32 = 294; const E4: uint32 = 330; const F4: uint32 = 349; const G4: uint32 = 392; struct Note { name: char8[..]; frequency: uint32; milliseconds: uint32; } func Main() -> int { // The opening of Beethoven's Ode to Joy. let tune: Note[15] = [ Note { name: "E", frequency: E4, milliseconds: 400 }, Note { name: "E", frequency: E4, milliseconds: 400 }, Note { name: "F", frequency: F4, milliseconds: 400 }, Note { name: "G", frequency: G4, milliseconds: 400 }, Note { name: "G", frequency: G4, milliseconds: 400 }, Note { name: "F", frequency: F4, milliseconds: 400 }, Note { name: "E", frequency: E4, milliseconds: 400 }, Note { name: "D", frequency: D4, milliseconds: 400 }, Note { name: "C", frequency: C4, milliseconds: 400 }, Note { name: "C", frequency: C4, milliseconds: 400 }, Note { name: "D", frequency: D4, milliseconds: 400 }, Note { name: "E", frequency: E4, milliseconds: 400 }, Note { name: "E", frequency: E4, milliseconds: 600 }, Note { name: "D", frequency: D4, milliseconds: 200 }, Note { name: "D", frequency: D4, milliseconds: 800 } ]; PrintLine("Ode to Joy, through the console speaker:"); for note in tune { Print("{} ", note.name); if !Beep(note.frequency, note.milliseconds) { PrintLine(); PrintLine("Windows could not play a tone, so the tune stops here."); return 1; } } PrintLine(); return 0; } ``` Besides `Io`, its `Rux.toml` lists `Core` under `[Dependencies]`. ## Run it ```sh cd Examples/Projects/Melody rux run ``` ```text Ode to Joy, through the console speaker: E E F G G F E D C C D E E D D ``` Run it on Windows with the sound turned up. Each note name is printed as it plays. On a machine with no sound device `Beep` may fail, and then the program says so and exits with status 1. On Linux, macOS or FreeBSD the build stops with the `#Error` message shown above. ## Common mistakes ::warning **Calling a foreign function as if it could not fail.**:br`Beep(C4, 100);` on its own compiles without a word: a `bool32` result is not a fallible, so nothing makes you check it. The safety a Rux fallible gives you ends at the `extern` boundary, and the checking becomes your job. :: ::warning **A `when` with no answer for other systems.**:br Without the `else => #Error(…)` branch, a build for Linux stops with `error: no arm of this 'when' matches .Linux`. That is correct but unhelpful. Whenever code is platform-specific, say why in an `#Error` of your own. :: ::warning **Untyped note constants.**:br With `const E4 = 330;` the constant is an `int`, and every note that uses it fails: `error: field 'frequency' in initializer for 'Note' has type 'int', but its declaration requires 'uint32'`. Give the constants the type the foreign function expects, once, where they are declared. :: ## Try it yourself 1. Add rests: a `Note` with frequency 0 that pauses instead of beeping. `Beep` only plays frequencies from 37 to 32767 hertz, so the loop has to tell rests apart — [Duration](https://rux-lang.dev/docs/learn/duration) and `SleepFor` provide the pause. 2. Add a tempo: a constant that scales every duration, so the whole tune can be played faster or slower by changing one number. 3. Play the next phrase of *Ode to Joy* (E E F G G F E D C C D E D C C). Which constants and array length have to change? 4. Make the program portable in a different way: on systems other than Windows, instead of stopping the build, print the note names with a pause between them so the rhythm still shows. Use `when` to choose the body of a `PlayNote` function. ## Learn more - [Extern](https://rux-lang.dev/docs/learn/extern), [Target](https://rux-lang.dev/docs/learn/target) and [Compile error](https://rux-lang.dev/docs/learn/compile-error) - [Beep](https://rux-lang.dev/docs/api/windows/beep) in the API reference - [External functions](https://rux-lang.dev/docs/lang/ffi/overview) and [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) in the Rux Reference - Back to the [Projects overview](https://rux-lang.dev/docs/learn/projects) for the full list # Introduction Rux is a fast, compiled, strongly typed, multi-paradigm programming language. It compiles to native machine code, checks types and ownership at compile time, and keeps the cost of every construct visible in the source. The same language serves command-line tools, libraries and system code. This reference describes the language as rux 0.4.0 implements it: what each construct means, which forms are accepted, and what the compiler reports when a rule is broken. Rux is developed in the open across the [rux-lang repositories](https://github.com/rux-lang){rel=""nofollow""} and released under the [MIT license](https://github.com/rux-lang/Rux/blob/main/LICENSE.md){rel=""nofollow""}. ## Design goals | Goal | What it means in practice | | -------------- | ------------------------------------------------------------------------------------------------------------------- | | Performance | Native code, no garbage collector, no hidden allocation. A value's size and layout follow from its type. | | Safety | Strong static types, explicit conversions, explicit moves, and borrows the compiler checks. | | Clarity | Regular syntax and few implicit rules: a cast is written `as`, a move is written `<-`, a failure is written `fail`. | | Multi-paradigm | Procedural code, methods and interfaces, generics, and pattern matching over sums, optionals and fallibles. | | Tooling | One `rux` binary builds, runs, tests, formats, documents and publishes packages. | ## Hello, World ```rux import Io::PrintLine; func Main() -> int { PrintLine("Hello, World!"); return 0; } ``` `import` brings `PrintLine` from the `Io` package into scope; the package is listed under `[Dependencies]` in the project's `Rux.toml`. [`Main`](https://rux-lang.dev/docs/lang/functions/main) is the entry point of an executable, and the `int` it returns is the process exit status. `rux run` builds and runs the program. ## How this reference is organised The chapters build from the bottom up: | Part | Chapters | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Text of a program | [Lexical structure](https://rux-lang.dev/docs/lang/lexical/source-files) | | Values | [Types](https://rux-lang.dev/docs/lang/types/overview), [bindings](https://rux-lang.dev/docs/lang/bindings/overview), [expressions](https://rux-lang.dev/docs/lang/expressions/overview), [statements](https://rux-lang.dev/docs/lang/statements/overview), [patterns](https://rux-lang.dev/docs/lang/patterns/match) | | Declarations | [Functions](https://rux-lang.dev/docs/lang/functions/declaration), [structs](https://rux-lang.dev/docs/lang/structs/overview), [enums](https://rux-lang.dev/docs/lang/enums/overview), [variants](https://rux-lang.dev/docs/lang/variants/overview), [unions](https://rux-lang.dev/docs/lang/unions/overview) | | Compound types | [Tuples](https://rux-lang.dev/docs/lang/tuples/overview), [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), [references](https://rux-lang.dev/docs/lang/references/overview), [pointers](https://rux-lang.dev/docs/lang/pointers/overview) | | Absence and failure | [Optionals](https://rux-lang.dev/docs/lang/optionals/overview), [errors](https://rux-lang.dev/docs/lang/errors/overview), [sums](https://rux-lang.dev/docs/lang/sums/overview) | | Abstraction | [Ownership](https://rux-lang.dev/docs/lang/ownership/overview), [interfaces](https://rux-lang.dev/docs/lang/interfaces/overview), [generics](https://rux-lang.dev/docs/lang/generics/overview), [modules](https://rux-lang.dev/docs/lang/modules/overview) | | Compile time and FFI | [Compile-time evaluation](https://rux-lang.dev/docs/lang/comptime/overview), [attributes](https://rux-lang.dev/docs/lang/attributes/overview), [foreign functions](https://rux-lang.dev/docs/lang/ffi/overview), [memory layout](https://rux-lang.dev/docs/lang/memory/layout) | | Appendix | [Primitive types](https://rux-lang.dev/docs/lang/appendix/primitives), [tokens](https://rux-lang.dev/docs/lang/appendix/tokens), [the RCU object format](https://rux-lang.dev/docs/lang/appendix/rcu) | ## Notation Rux code blocks hold complete Rux, or a fragment of a function body that compiles once placed in one. Comments such as `// 255` show the value an expression produces. Grammar is given in plain-text blocks with this notation: | Notation | Meaning | | ------------ | ----------------------------------------------------- | | `Rule ::= …` | Defines the rule `Rule` | | `'func'` | The exact token, written as it appears in source | | `'a'…'z'` | Any one character in the range | | `Name` | Another rule, written in PascalCase | | `A B` | `A` followed by `B` | | `A | B` | Either `A` or `B` | | `A?` | `A`, optionally | | `A*` | Zero or more `A` | | `A+` | One or more `A` | | `( … )` | Grouping | | `…` | A part described in the prose rather than spelled out | For example, a type alias is: ```text TypeAlias ::= 'pub'? 'type' Identifier '=' Type ';' ``` Error messages are quoted exactly as `rux` prints them, without the file and position prefix: `error: integer literal is out of range for type 'uint8'`. ## Reference and Learn Rux The [Learn Rux](https://rux-lang.dev/docs/learn) course teaches the language in order, one runnable lesson at a time, and explains why each feature exists. This reference is for looking things up: each page states the complete rule for one construct, lists every form and error, and links to the lesson that teaches it. Read the course to learn Rux; come here to check a detail. ## See also - [Learn Rux](https://rux-lang.dev/docs/learn) — the course, from installation to complete programs - [Hello](https://rux-lang.dev/docs/learn/hello) — the first lesson, with this program explained line by line - [Playground](https://rux-lang.dev/play) — run Rux in the browser - [Packaging](https://rux-lang.dev/docs/packaging) — manifests, dependencies and publishing - [Command-line interface](https://rux-lang.dev/docs/cli) — every `rux` command # Source Files A Rux program is written in source files with the `.rux` extension. Each file is UTF-8 text that the compiler reads as a sequence of tokens — identifiers, keywords, literals, operators and punctuation — separated by whitespace and comments. ## Files and packages The source files of a package live under its `Src/` directory, beside the `Rux.toml` manifest. An executable package starts at `Src/Main.rux`, which defines [`Main`](https://rux-lang.dev/docs/lang/functions/main); a library package conventionally starts at `Src/Lib.rux`. Files are named in PascalCase: `Main.rux`, `HttpClient.rux`, `JsonParser.rux`. ```text Hello/ ├── Rux.toml └── Src/ └── Main.rux ``` [Package layout](https://rux-lang.dev/docs/packaging/layout) describes the directory structure, and [Modules](https://rux-lang.dev/docs/lang/modules/overview) how declarations in several files and `module` blocks are named and imported. ## Encoding A source file must be valid UTF-8. Non-ASCII characters may appear inside [comments](https://rux-lang.dev/docs/lang/lexical/comments) and inside [character and string literals](https://rux-lang.dev/docs/lang/lexical/literals); everywhere else the source is ASCII. | Problem | Error | | -------------------------------------- | ------------------------------------------------ | | A byte that is not valid UTF-8 | `error: source contains invalid UTF-8 byte 0xFF` | | A byte-order mark at the start | reported as an unexpected character, U+FEFF | | A non-ASCII character outside literals | `error: unexpected character 'é' (U+00E9)` | | A control byte outside literals | `error: unexpected control byte 0x0C` | Save files as UTF-8 without a byte-order mark. ## Whitespace and line endings Whitespace is the space, the horizontal tab, the carriage return and the line feed. It separates tokens and is otherwise ignored: Rux has no significant indentation, and statements end with `;`, not with a line break. Both LF and CRLF line endings are accepted. ```rux let total = 1 + 2; let same = 1 + 2; ``` A string or character literal cannot contain a raw line break or tab; write `\n` and `\t` instead. ## Tokens The compiler reads the source from left to right and always takes the **longest** token that can start at the current position. That rule settles every spelling that could be read two ways: | Source | Read as | Not as | | -------- | --------------- | -------------- | | `x<-1` | `x` `<-` `1` | `x` `<` `-1` | | `a**p` | `a` `*` `*` `p` | a power | | `a??b` | `a` `??` `b` | `a` `?` `?b` | | `0..=9` | `0` `..=` `9` | `0` `..` `=9` | | `x>>>=2` | `x` `>>>=` `2` | `x` `>>` `>=2` | So `x<-1` is a [move assignment](https://rux-lang.dev/docs/lang/expressions/assignment) into `x`, and on an immutable `x` it fails with `error: cannot modify immutable variable 'x'`. Write `x < -1` to compare. `a**p` multiplies `a` by the value `p` points to. Whitespace matters in exactly one place: before `?`. A `?` with whitespace before it is the conditional operator, `c ? a : b`; a `?` that touches the expression before it is postfix [propagation](https://rux-lang.dev/docs/lang/optionals/propagation), `value?`. So `c? 1 : 2` is reported as `error: expected ';' after the binding declaration before '1'`. The [Token Reference](https://rux-lang.dev/docs/lang/appendix/tokens) lists every token the lexer produces. ## See also - [Comments](https://rux-lang.dev/docs/lang/lexical/comments) — the other thing the lexer skips - [Package layout](https://rux-lang.dev/docs/packaging/layout) — where `Src/` sits in a package - [Hello](https://rux-lang.dev/docs/learn/hello) — a first source file, explained # Comments A comment is text the compiler skips. Rux has two ordinary forms, for notes to the reader, and two documentation forms, which describe the declaration that follows them and feed `rux doc`, `rux lint` and editors. | Form | Kind | Extent | | ------------- | ------------------- | --------------------------- | | `// text` | Line comment | To the end of the line | | `/* text */` | Block comment | To the matching `*/`; nests | | `/// text` | Documentation line | To the end of the line | | `/** text */` | Documentation block | To the matching `*/`; nests | Comment markers inside string and character literals are ordinary characters: `"// not a comment"` is a string. ## Line comments `//` starts a comment that runs to the end of the line. It may stand on a line of its own or follow code. ```rux // The everyday kind of comment. let width = 80; // It can follow code on the same line. ``` ## Block comments `/*` starts a comment that ends at the matching `*/`. It may span lines, or sit between any two tokens. ```rux /* A block comment over several lines. */ PrintLine(/* the message */ "inline"); ``` Block comments **nest**: each `/*` inside a block needs its own `*/`. That makes it safe to comment out code that already contains a block comment. ```rux /* Temporarily disabled: /* PrintLine("nested"); */ PrintLine("still inside the outer comment"); */ ``` A block that never closes is `error: block comment is not terminated`, reported at its opening `/*`. ## Documentation comments `///` and `/** … */` are documentation comments. They attach to the declaration that follows them and describe it as Markdown. ```rux /// Returns the larger of two values. /// @param a the first value /// @param b the second value /// @returns whichever of `a` and `b` is greater func Max(a: int, b: int) -> int { return a > b ? a : b; } /** Returns the smaller of two values. @param a the first value @param b the second value @returns whichever of `a` and `b` is smaller */ func Min(a: int, b: int) -> int { return a < b ? a : b; } /// A point on a plane. struct Point { /// The horizontal coordinate. x: int; /// The vertical coordinate. y: int; } ``` The markers are exact. `////` and longer runs of slashes are ordinary line comments, and `/**/`, `/***/` or any block opened with more than two stars is an ordinary block comment, so a decorative banner never becomes documentation. ### Attachment A documentation comment attaches to the nearest following declaration when: - it starts its own line (indentation is allowed); - no blank line, ordinary comment or other token comes between it and the declaration or the declaration's first attribute. Documentation attaches to functions, types, constants, modules, fields, enum and variant cases, interface and extension methods, and extern members. Parameters and type parameters are described with tags rather than with comments inside a parameter list. Inside a function body there is no declaration to document, so a documentation comment there is an error: ```text error: documentation comment inside a block is not attached to a declaration help: write an ordinary '//' comment here; '///' documents the declaration that follows it ``` A documentation comment that is detached elsewhere — trailing code, followed by a blank line, or at the end of a scope — does not stop compilation; `rux lint` reports it. ### Content and summary The text is Markdown. A line comment loses `///` and one following space; a block comment loses its delimiter lines and common indentation. The first sentence — up to the first `.`, `!` or `?` followed by whitespace — is the summary that tools show on its own. ### Structured tags Tags form a block after the prose. Each starts a line; a continuation line is indented by two spaces after the marker. | Tag | Use | | -------------------------- | ---------------------------------------------------------- | | `@param ` | A parameter of the documented function, other than `self` | | `@typeParam ` | A type parameter introduced by the declaration | | `@returns ` | The value a function returns | | `@see [text]` | A related URL, Rux path or symbol in backticks; repeatable | | `@deprecated ` | Marks the item as deprecated in generated documentation | Tags are case-sensitive. A misspelled or misplaced tag, such as `@return` for `@returns`, never fails compilation; `rux lint` reports it. `@deprecated` is documentation only — the [`#Warn`](https://rux-lang.dev/docs/lang/attributes/warn) attribute is what makes the compiler warn at each use. ## See also - [Source Files](https://rux-lang.dev/docs/lang/lexical/source-files) — how the lexer reads a file - [Comment](https://rux-lang.dev/docs/learn/comment) — the lesson on all three everyday forms - [Documentation](https://rux-lang.dev/docs/learn/documentation) — writing documentation a package publishes - [`rux doc`](https://rux-lang.dev/docs/cli/doc) and [`rux lint`](https://rux-lang.dev/docs/cli/lint) — the tools that read documentation comments # Identifiers An identifier names a binding, function, type, field, module or other declaration. ```text Identifier ::= IdentStart IdentContinue* IdentStart ::= 'A'…'Z' | 'a'…'z' | '_' IdentContinue ::= IdentStart | '0'…'9' ``` An identifier starts with an ASCII letter or an underscore and continues with letters, digits and underscores. It cannot be one of the 38 [keywords](https://rux-lang.dev/docs/lang/lexical/keywords). ```rux let count = 1; let x1 = 2; let _scratch = 3; let parsedValue = 4; ``` ## Rules - **ASCII only.** Letters outside ASCII are not identifier characters: `let café = 1;` stops with `error: unexpected character 'é' (U+00E9)`. Non-ASCII text belongs in [literals](https://rux-lang.dev/docs/lang/lexical/literals) and [comments](https://rux-lang.dev/docs/lang/lexical/comments). - **Case-sensitive.** `count`, `Count` and `COUNT` are three different names. - **No keywords.** A keyword in a name's place is a syntax error, such as `error: expected a pattern after the binding keyword before 'while'` for `let while = 1;`. For `none` and `fail` the message names the problem directly: `error: 'none' is a reserved keyword and cannot name a binding`. - **A lone `_` is not a name.** In a binding or pattern, `_` discards the value instead of naming it: `let _ = Compute();`. See [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring). Primitive type names such as `int`, `bool` and `char8` are not keywords; they are predefined type names, listed in the [Primitive Types](https://rux-lang.dev/docs/lang/appendix/primitives) appendix. A few other words have a meaning only in a particular position; [Keywords](https://rux-lang.dev/docs/lang/lexical/keywords#contextual-words) lists them. ## Naming conventions Rux code uses two spellings, and `rux lint` checks them: | Spelling | Used for | Examples | | ---------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | | PascalCase | Types (structs, enums, variants, unions, interfaces, aliases), enum and variant cases, functions and methods, constants, modules | `HttpRequest`, `ParseInput`, `MaxSize`, `Io` | | camelCase | Local bindings, parameters, loop variables, fields, named variant fields | `userId`, `fileName`, `red` | Neither spelling uses underscores. A name that breaks the convention compiles, and `rux lint` suggests the fix: ```text warning: function name 'parse_input' should be PascalCase help: rename it to 'ParseInput' warning: local variable name 'Result' should be camelCase help: rename it to 'result' ``` The primitive types and their built-in aliases (`int`, `float64`, `byte`) are lower case by long tradition. A declaration that has to keep a foreign spelling — a C type name in a binding, say — can opt out with [`#Allow("naming.type")`](https://rux-lang.dev/docs/lang/attributes/allow) or `#Allow("naming.const")`. ## See also - [Keywords](https://rux-lang.dev/docs/lang/lexical/keywords) — the words an identifier cannot be - [Modules](https://rux-lang.dev/docs/lang/modules/overview) — qualified names such as `Io::PrintLine` - [`rux lint`](https://rux-lang.dev/docs/cli/lint) — the naming checks - [Variable](https://rux-lang.dev/docs/learn/variable) — the lesson on naming values # Keywords A keyword is a word the language reserves. Keywords are lower case and case-sensitive, and none of them can be used as an [identifier](https://rux-lang.dev/docs/lang/lexical/identifiers): `While` is an ordinary name, `while` is not. ## Reserved keywords Rux reserves 38 words: ```text as break catch const continue defer do else enum extend extern fail false for func if import in interface intrinsic is let loop match module none null pub return self struct true type union var variant when while ``` | Keyword | Introduces | | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | [`as`](https://rux-lang.dev/docs/lang/expressions/casts) | A conversion, `value as T` | | [`break`](https://rux-lang.dev/docs/lang/statements/break-continue) | An exit from the nearest enclosing loop | | [`catch`](https://rux-lang.dev/docs/lang/errors/handling) | A handler for the failure of a fallible expression, `x catch { … }` | | [`const`](https://rux-lang.dev/docs/lang/bindings/constants) | A compile-time constant | | [`continue`](https://rux-lang.dev/docs/lang/statements/break-continue) | A jump to the next iteration of a loop | | [`defer`](https://rux-lang.dev/docs/lang/ownership/defer) | A statement run when the enclosing scope exits | | [`do`](https://rux-lang.dev/docs/lang/statements/loops) | A `do { … } while condition;` loop, which tests after the body | | [`else`](https://rux-lang.dev/docs/lang/statements/if) | The alternative of an `if`, the default arm of a `match`, and the `? else` mapping | | [`enum`](https://rux-lang.dev/docs/lang/enums/overview) | An enumeration of named integer values | | [`extend`](https://rux-lang.dev/docs/lang/structs/extensions) | An extension block of methods, constructors and constants for a type | | [`extern`](https://rux-lang.dev/docs/lang/ffi/overview) | A declaration of a function defined outside Rux | | [`fail`](https://rux-lang.dev/docs/lang/errors/overview) | A failure from a fallible function | | [`false`](https://rux-lang.dev/docs/lang/types/booleans) | The boolean value false | | [`for`](https://rux-lang.dev/docs/lang/statements/loops) | A loop over a range, slice, array or iterable | | [`func`](https://rux-lang.dev/docs/lang/functions/declaration) | A function, a method or a function type | | [`if`](https://rux-lang.dev/docs/lang/statements/if) | A conditional statement | | [`import`](https://rux-lang.dev/docs/lang/modules/imports) | Names brought into scope from a package or module | | [`in`](https://rux-lang.dev/docs/lang/statements/loops) | The sequence of a `for` loop, `for item in items` | | [`interface`](https://rux-lang.dev/docs/lang/interfaces/overview) | A set of methods a type can implement | | [`intrinsic`](https://rux-lang.dev/docs/lang/comptime/intrinsics) | A declaration the compiler implements: a primitive type, function or value | | [`is`](https://rux-lang.dev/docs/lang/sums/type-tests) | A type test, `value is T` | | [`let`](https://rux-lang.dev/docs/lang/bindings/overview) | An immutable binding | | [`loop`](https://rux-lang.dev/docs/lang/statements/loops) | A loop with no condition | | [`match`](https://rux-lang.dev/docs/lang/patterns/match) | A selection by pattern | | [`module`](https://rux-lang.dev/docs/lang/modules/overview) | A named module block, `module A::B { … }` | | [`none`](https://rux-lang.dev/docs/lang/optionals/overview) | The absent value of an optional | | [`null`](https://rux-lang.dev/docs/lang/pointers/overview) | The null pointer | | [`pub`](https://rux-lang.dev/docs/lang/modules/visibility) | Visibility outside the declaring package | | [`return`](https://rux-lang.dev/docs/lang/statements/return) | A return from the enclosing function | | [`self`](https://rux-lang.dev/docs/lang/structs/methods) | The receiver parameter of a method | | [`struct`](https://rux-lang.dev/docs/lang/structs/overview) | A structure type | | [`true`](https://rux-lang.dev/docs/lang/types/booleans) | The boolean value true | | [`type`](https://rux-lang.dev/docs/lang/types/aliases) | A type alias, `type Name = T;` | | [`union`](https://rux-lang.dev/docs/lang/unions/overview) | A union type, whose fields share storage | | [`var`](https://rux-lang.dev/docs/lang/bindings/overview) | A mutable binding, and writability in `&var T`, `*var T` and `var T[..]` | | [`variant`](https://rux-lang.dev/docs/lang/variants/overview) | A tagged type whose cases may carry payloads | | [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) | A compile-time conditional | | [`while`](https://rux-lang.dev/docs/lang/statements/loops) | A loop that tests a condition before each iteration | `true` and `false` are lexed as boolean [literals](https://rux-lang.dev/docs/lang/lexical/literals#boolean-literals), and are reserved like the other 36. ## Contextual words These words are ordinary identifiers except in one position, where they take a fixed meaning. Outside that position they can name things, although using them as names makes code harder to read. | Word | Meaning, and where | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `asm` | Directly before `func`: a function whose body is [assembly](https://rux-lang.dev/docs/lang/ffi/assembly) | | `sizeof`, `alignof` | In an expression: the size or alignment of a type, `sizeof(T)`. An expression cannot read a binding with either name. | | `Self` | In an interface or extension: the implementing type, as in `other: &Self` | | `_` | In a binding or pattern: discards the value instead of naming it | | `Success`, `Failure`, `Some` | After a leading `.`: the native constructors and patterns `.Success(v)`, `.Failure(e)` and `.Some(v)` of [fallibles](https://rux-lang.dev/docs/lang/errors/overview) and [optionals](https://rux-lang.dev/docs/lang/optionals/overview) | Primitive type names — `int`, `uint8`, `float64`, `bool`, `char` and the rest — are not keywords either. They are predefined type names, listed in the [Primitive Types](https://rux-lang.dev/docs/lang/appendix/primitives) appendix, and so is `opaque`, the unknown pointee of an untyped pointer `*opaque`. Some words that other languages reserve mean nothing special in Rux: `mut`, `try`, `unsafe`, `use`, `impl` and `fn` are ordinary identifiers. ## See also - [Identifiers](https://rux-lang.dev/docs/lang/lexical/identifiers) — what a name may be - [Token Reference](https://rux-lang.dev/docs/lang/appendix/tokens) — every token, keywords included - [Cheat sheet](https://rux-lang.dev/docs/learn/cheatsheet) — the whole language on one page # Literals A literal is a value written directly in the source. Rux has integer, floating-point, boolean, character and string literals. | Literal | Examples | Type without a suffix or prefix | | -------------- | --------------------------------------- | ------------------------------------------------ | | Integer | `42`, `0xFF`, `0o17`, `0b1010`, `200u8` | `int`, or the type the context requires | | Floating-point | `3.14`, `1e-9`, `0.5f32` | `float64` | | Boolean | `true`, `false` | `bool` | | Character | `'A'`, `'\n'`, `c8'A'`, `c16'Ж'` | `char32`, or `char8` / `char16` from the context | | String | `"text"`, `c16"text"`, `c32"text"` | `char8[..]` | ## Integer literals ```text IntegerLiteral ::= ( Decimal | '0x' Hex | '0o' Octal | '0b' Binary ) IntegerSuffix? Decimal ::= Digit ( '_'? Digit )* ``` An integer literal is written in decimal, or in another base after a prefix. The prefix letter may be upper or lower case, and so may hexadecimal digits. ```rux let decimal = 255; let hex = 0xFF; let octal = 0o377; let binary = 0b1111_1111; let grouped = 8_100_000_000; ``` The first four are the same value, 255; the base is only how the source spells it. An underscore may separate two digits, anywhere in the number, and is ignored. It must stand between digits: `1__0` and `1_` are `error: numeric separator '_' must appear between digits`. A minus sign is not part of the literal — it is the [negation](https://rux-lang.dev/docs/lang/expressions/arithmetic) operator — but a negated literal is range-checked as one value, so `-128i8` and `-9223372036854775808` are accepted. ### Suffixes A suffix fixes the literal's type: | Suffix | Type | Suffix | Type | | ------ | -------- | ------ | --------- | | `i` | `int` | `u` | `uint` | | `i8` | `int8` | `u8` | `uint8` | | `i16` | `int16` | `u16` | `uint16` | | `i32` | `int32` | `u32` | `uint32` | | `i64` | `int64` | `u64` | `uint64` | | `i128` | `int128` | `u128` | `uint128` | | `i256` | `int256` | `u256` | `uint256` | | `i512` | `int512` | `u512` | `uint512` | ```rux let small = 200u8; let mask = 0xFFFF_0000u32; let index = 3u; let huge = 340282366920938463463374607431768211455u128; ``` A float suffix (`f32`, `f64`) on a whole number makes a floating-point literal: `1f32` is the `float32` value `1.0`. In a hexadecimal literal, `a`–`f` are digits, so a trailing `f8` or `f32` is read as more digits: `0xFFf8` is the integer 65528. A suffix after a hexadecimal number has to start with a letter that is not a hexadecimal digit, as `u8` and `i32` do. ### Type of an unsuffixed integer An integer literal without a suffix has no fixed type. It takes the type its context requires: - the declared type of the binding, field, array element, parameter or return value it initialises; - the type of the other operand of an arithmetic, bitwise or comparison operator. With no such context it is an `int`. Either way, the value must fit: ```rux let level: uint8 = 200; let raised = level + 100; // 100 is a uint8; the sum wraps to 44 let plain = 200 + 100; // both are int: 300 ``` ```text let tooBig: uint8 = 256; 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' ``` An integer literal never becomes a float: `let ratio: float64 = 1;` is `error: cannot assign 'int' to 'float64'`. Write `1.0`. ## Floating-point literals ```text FloatLiteral ::= Decimal '.' Decimal Exponent? FloatSuffix? | Decimal Exponent FloatSuffix? | Decimal FloatSuffix Exponent ::= ( 'e' | 'E' ) ( '+' | '-' )? Decimal ``` A floating-point literal has digits on both sides of the point, an exponent, or both. The exponent is a power of ten. ```rux let pi = 3.141592653589793; let light = 2.998e8; let charge = 1.6e-19; let half = 0.5f32; ``` `1.` and `.5` are not literals; write `1.0` and `0.5`. Underscores may separate digits in each part, as in `1_000.25`. | Suffix | Type | | ------ | --------- | | none | `float64` | | `f64` | `float64` | | `f32` | `float32` | The suffixes `f8`, `f16`, `f80`, `f128`, `f256` and `f512` name [reserved](https://rux-lang.dev/docs/lang/types/floating-point#reserved-widths) types, so `1.5f16` is `error: primitive type 'float16' is reserved but is not implemented in this compiler version`. An unsuffixed floating-point literal is always a `float64`. It does not narrow to fit a `float32` context: `let single: float32 = 0.75;` is `error: cannot assign 'float64' to 'float32'`, and `single * 2.0` is a `float64`. Write `0.75f32` and `2.0f32`. ## Boolean literals `true` and `false` are the two boolean literals. Their type is `bool`, which converts implicitly to the other boolean widths. ```rux let ready = true; let wide: bool32 = false; ``` ## Character literals ```text CharLiteral ::= CharPrefix? "'" ( Character | Escape ) "'" CharPrefix ::= 'c8' | 'c16' | 'c32' | 'c64' ``` A character literal is exactly one character, or one [escape](https://rux-lang.dev/#escape-sequences), between single quotes. A prefix, written directly against the quote, chooses the character type: | Literal | Type | Holds | | --------- | -------- | ---------------------------------------------------------- | | `'A'` | `char32` | Any Unicode scalar value; `char8` or `char16` by context | | `c8'A'` | `char8` | One UTF-8 code unit: U+0000 to U+007F | | `c16'Ж'` | `char16` | One UTF-16 code unit that is not a surrogate: up to U+FFFF | | `c32'😀'` | `char32` | Any Unicode scalar value | | `c64'😀'` | `char64` | Any Unicode scalar value, in eight bytes | ```rux let letter = 'A'; let emoji = '😀'; let unit = c8'R'; let word = c16'Ж'; let smile = '\u{263A}'; ``` An unprefixed literal is a `char32`, except that it becomes a `char8` or `char16` when it initialises or is assigned to one: `let initial: char8 = 'R';` works, and so does `return 'Ж';` from a function that returns `char16`. A character that does not fit one code unit of its type is refused, never truncated: ```text 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 ``` | Mistake | Error | | ------------------------- | ----------------------------------------------------------------------- | | `''` | `error: character literal is empty` | | `'ab'` | `error: character literal contains more than one character` | | `'a` at the end of a line | `error: character literal is not terminated before the end of the line` | A prefix is only a prefix when the quote follows at once: `c16 'x'`, with a space, is the identifier `c16` followed by a separate literal. ## String literals ```text StringLiteral ::= StringPrefix? '"' ( Character | Escape )* '"' StringPrefix ::= 'c8' | 'c16' | 'c32' ``` A string literal is a read-only [slice](https://rux-lang.dev/docs/lang/slices/overview) of character code units — Rux has no separate string type. The prefix chooses the encoding: | Literal | Type | Encoding | | ------------ | ------------ | -------- | | `"Hello"` | `char8[..]` | UTF-8 | | `c8"Hello"` | `char8[..]` | UTF-8 | | `c16"Hello"` | `char16[..]` | UTF-16 | | `c32"Hello"` | `char32[..]` | UTF-32 | There is no `c64` string. ```rux let greeting = "Hello, World!"; let path = "C:\\Rux\\Bin"; let quoted = "say \"hi\""; let wide = c16"\u{1F680}"; // two UTF-16 code units ``` `.length` counts code units of the encoding, not characters: `"€".length` is 3, `c16"€".length` is 1. The stored text ends with a NUL code unit that `.length` does not count, so `.data` can be passed to C. [Text](https://rux-lang.dev/docs/lang/types/text) describes string values in full. A string literal stays on one line. A raw line break or tab inside the quotes is an error — write `\n` and `\t` — and so is a missing closing quote: | Mistake | Error | | ----------------------------------- | -------------------------------------------------------------------- | | No closing `"` before the line ends | `error: string literal is not terminated before the end of the line` | | A tab typed inside the quotes | `error: string literal contains unescaped control byte 0x09` | ## Escape sequences Character and string literals share one set of escapes: | Escape | Meaning | Code point | | ------- | ------------------------------------------------------- | ---------- | | `\n` | Line feed | U+000A | | `\r` | Carriage return | U+000D | | `\t` | Horizontal tab | U+0009 | | `\v` | Vertical tab | U+000B | | `\f` | Form feed | U+000C | | `\b` | Backspace | U+0008 | | `\a` | Alert (bell) | U+0007 | | `\0` | NUL | U+0000 | | `\\` | Backslash | U+005C | | `\'` | Single quote | U+0027 | | `\"` | Double quote | U+0022 | | `\u{…}` | The Unicode scalar value with 1 to 8 hexadecimal digits | as written | ```rux let bell = '\a'; let euro = "\u{20AC}"; let rocket = '\u{1F680}'; ``` In a string the `\u{…}` escape is encoded in the literal's encoding: `"\u{20AC}"` stores three UTF-8 bytes, `c16"\u{20AC}"` one UTF-16 unit. | Mistake | Error | | ----------------- | --------------------------------------------------------------------------- | | `"\x41"` | `error: escape sequence '\x' is not recognized` | | `"C:\Rux"` | `error: escape sequence '\R' is not recognized` | | `"\u41"` | `error: Unicode escape requires '{' after '\u'` | | `"\u{}"` | `error: Unicode escape requires at least one hexadecimal digit` | | `"\u{000000041}"` | `error: Unicode escape contains more than eight hexadecimal digits` | | `'\u{D800}'` | `error: Unicode escape U+D800 is a surrogate, not a scalar value` | | `'\u{110000}'` | `error: Unicode escape U+110000 is above the maximum scalar value U+10FFFF` | There is no `\x` escape. A single byte that is not a character is written as an integer, such as `0xE9u8`. ## Other values written as keywords `none` is the absent value of an [optional](https://rux-lang.dev/docs/lang/optionals/overview) and `null` the null [pointer](https://rux-lang.dev/docs/lang/pointers/overview). They are keywords rather than literals, and take their type from the context. Arrays (`[1, 2, 3]`), tuples (`(1, "one")`) and struct values (`Point { x: 1, y: 2 }`) are expressions built from other values; see [Arrays](https://rux-lang.dev/docs/lang/arrays/overview), [Tuples](https://rux-lang.dev/docs/lang/tuples/overview) and [Structs](https://rux-lang.dev/docs/lang/structs/overview). ## See also - [Types](https://rux-lang.dev/docs/lang/types/overview) — the types these literals produce - [Integers](https://rux-lang.dev/docs/lang/types/integers), [Floating-Point](https://rux-lang.dev/docs/lang/types/floating-point), [Characters](https://rux-lang.dev/docs/lang/types/characters), [Text](https://rux-lang.dev/docs/lang/types/text) - [Literal](https://rux-lang.dev/docs/learn/literal) — the lesson on numeric literals - [Character](https://rux-lang.dev/docs/learn/character) and [String literal](https://rux-lang.dev/docs/learn/string-literal) — the lessons on character and text literals # Operators and Punctuation Operators and punctuation are the symbol tokens of Rux. The lexer reads them by the [longest-match rule](https://rux-lang.dev/docs/lang/lexical/source-files#tokens), so `>>=` is one token, not `>>` followed by `=`. This page lists every symbol and what it means; how operators bind is in the [precedence table](https://rux-lang.dev/docs/lang/expressions/overview). ## Arithmetic | Token | Meaning | Rules | | ----- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | | `+` | Addition | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) | | `-` | Subtraction; prefix negation | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) | | `*` | Multiplication; prefix dereference of a pointer; pointer type `*T` | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic), [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) | | `/` | Division | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) | | `%` | Remainder | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) | | `++` | Increment, prefix or postfix | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) | | `--` | Decrement, prefix or postfix | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) | There is no unary `+` and no power operator: `a ** p` is `a * *p`, a multiplication by the value `p` points to. ## Comparison | Token | Meaning | | ----- | --------------------- | | `==` | Equal | | `!=` | Not equal | | `<` | Less than | | `<=` | Less than or equal | | `>` | Greater than | | `>=` | Greater than or equal | See [Comparison](https://rux-lang.dev/docs/lang/expressions/comparison). ## Logical | Token | Meaning | | ----- | ------------------------------------------------ | | `&&` | Logical and, short-circuiting | | `||` | Logical or, short-circuiting | | `!` | Logical not; with a type, `! E` marks a fallible | See [Logical](https://rux-lang.dev/docs/lang/expressions/logical) and, for `T ! E`, [Errors](https://rux-lang.dev/docs/lang/errors/overview). ## Bitwise and shift | Token | Meaning | | ----- | ------------------------------------------------------------------- | | `&` | Bitwise and; in a type, a reference `&T` | | `|` | Bitwise or; in a type, a sum `A | B` | | `^` | Bitwise exclusive or | | `~` | Bitwise not; before a type name in `extend`, a destructor `func ~T` | | `<<` | Shift left | | `>>` | Shift right: arithmetic for signed operands, logical for unsigned | | `>>>` | Logical shift right, filling with zeros | See [Bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise), [Shift](https://rux-lang.dev/docs/lang/expressions/shift), [References](https://rux-lang.dev/docs/lang/references/overview), [Sums](https://rux-lang.dev/docs/lang/sums/overview) and [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors). Prefix `&` is not an expression operator; the address of a place is taken with `@`. ## Assignment | Token | Meaning | Token | Meaning | | ----- | ---------------------------- | ------ | ------------- | | `=` | Assign | `&=` | `x = x & y` | | `<-` | Move; prefix `<-x` moves `x` | `|=` | `x = x | y` | | `+=` | `x = x + y` | `^=` | `x = x ^ y` | | `-=` | `x = x - y` | `<<=` | `x = x << y` | | `*=` | `x = x * y` | `>>=` | `x = x >> y` | | `/=` | `x = x / y` | `>>>=` | `x = x >>> y` | | `%=` | `x = x % y` | | | See [Assignment](https://rux-lang.dev/docs/lang/expressions/assignment) and [Copy and Move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## Optionals, errors and conditions | Token | Meaning | | ----- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `?` | After a type, an optional `T?`. Touching an expression, postfix propagation `x?`. With whitespace before it, the conditional `c ? a : b` | | `??` | Coalescing, `x ?? fallback`. In a type, two optional levels `T??` | `?` is the one token whose meaning depends on whitespace. `value?` propagates and `flag ? a : b` selects, so `flag? a : b` is a syntax error. `??` is always one token: two propagations in a row are written `(x?)?`. See [Conditional](https://rux-lang.dev/docs/lang/expressions/conditional), [Coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing), [Optional propagation](https://rux-lang.dev/docs/lang/optionals/propagation) and [Error propagation](https://rux-lang.dev/docs/lang/errors/propagation). ## Ranges | Token | Meaning | | ----- | -------------------------------------------------------------------------------------------------- | | `..` | Half-open range `a..b`; open ranges `a..`, `..b`, `..`; a slice type `T[..]` | | `..=` | Inclusive range `a..=b`, `..=b` | | `...` | Inclusive range in expressions and patterns `a...b`; spread `args...`; a variadic parameter `T...` | See [Ranges](https://rux-lang.dev/docs/lang/ranges/overview), [Slices](https://rux-lang.dev/docs/lang/slices/overview) and [Parameters](https://rux-lang.dev/docs/lang/functions/parameters). ## Punctuation | Token | Meaning | | ------- | ------------------------------------------------------------------------------------------- | | `(` `)` | Grouping, calls, parameter lists, tuples | | `[` `]` | Array literals, indexing, array and slice types | | `{` `}` | Blocks, declaration bodies, struct literals | | `,` | Separates list items | | `;` | Ends a statement or declaration | | `:` | Separates a name from its type or field value; the conditional's `:` | | `::` | Path separator, `Io::PrintLine`, `Color::Red` | | `.` | Member access `p.x`, tuple index `t.0`, method call `s.Len()`; leading `.` in `.Success(v)` | | `->` | Return type of a function or function type | | `=>` | Separates a `match` arm's pattern from its result | | `@` | Address of a place, `@value`, giving a pointer | | `#` | Attributes `#Link(…)` and compile-time values `#target` | See [Pointers](https://rux-lang.dev/docs/lang/pointers/overview), [Attributes](https://rux-lang.dev/docs/lang/attributes/overview) and [Compile-time evaluation](https://rux-lang.dev/docs/lang/comptime/overview). ## Operators a type can define A type can give operators such as `+`, `-`, `*`, `==` and `<` a meaning of its own by declaring them in an `extend` block, and can define indexing with an indexer. `??` is built in and cannot be declared. See [Operators](https://rux-lang.dev/docs/lang/interfaces/operators) and [Indexers](https://rux-lang.dev/docs/lang/interfaces/indexers). ## See also - [Expressions](https://rux-lang.dev/docs/lang/expressions/overview) — precedence and associativity of every operator - [Token Reference](https://rux-lang.dev/docs/lang/appendix/tokens) — the lexer's name for each token - [Operators](https://rux-lang.dev/docs/learn/operators) and [Precedence](https://rux-lang.dev/docs/learn/precedence) — the lessons # Types Every value in Rux has a type, fixed at compile time. The type decides what the value can hold, how much storage it takes, and which operations apply to it. There are no implicit conversions that can change a value: the few that happen on their own only widen, and every other conversion is written with [`as`](https://rux-lang.dev/docs/lang/expressions/casts). ## Kinds of type | Kind | Examples | Described in | | ----------------- | ----------------------------------------- | ------------------------------------------------------------------------- | | Integer | `int`, `uint8`, `int128` | [Integers](https://rux-lang.dev/docs/lang/types/integers) | | Floating-point | `float64`, `float32` | [Floating-Point](https://rux-lang.dev/docs/lang/types/floating-point) | | Boolean | `bool`, `bool32` | [Booleans](https://rux-lang.dev/docs/lang/types/booleans) | | Character | `char`, `char8`, `char16` | [Characters](https://rux-lang.dev/docs/lang/types/characters) | | Text | `char8[..]`, `char16[..]` | [Text](https://rux-lang.dev/docs/lang/types/text) | | Array | `int[4]`, `float64[3][3]` | [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) | | Slice | `int[..]`, `var int[..]` | [Slices](https://rux-lang.dev/docs/lang/slices/overview) | | Tuple | `(int, char8[..])` | [Tuples](https://rux-lang.dev/docs/lang/tuples/overview) | | Range | `int..int`, `int..=int`, `..` | [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) | | Reference | `&T`, `&var T` | [References](https://rux-lang.dev/docs/lang/references/overview) | | Pointer | `*T`, `*var T`, `*opaque` | [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) | | Optional | `T?` | [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) | | Fallible | `T ! E`, `! E` | [Errors](https://rux-lang.dev/docs/lang/errors/overview) | | Sum | `int | bool` | [Sums](https://rux-lang.dev/docs/lang/sums/overview) | | Function | `func(int) -> int` | [Function types](https://rux-lang.dev/docs/lang/functions/function-types) | | Struct | `struct Point { … }` | [Structs](https://rux-lang.dev/docs/lang/structs/overview) | | Enum | `enum Color { … }` | [Enums](https://rux-lang.dev/docs/lang/enums/overview) | | Variant | `variant Shape { … }` | [Variants](https://rux-lang.dev/docs/lang/variants/overview) | | Union | `union Word { … }` | [Unions](https://rux-lang.dev/docs/lang/unions/overview) | | Interface | `interface Shape { … }` | [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) | | Generic parameter | `T` in `func First(items: T[..]) -> T` | [Generics](https://rux-lang.dev/docs/lang/generics/overview) | The integer, floating-point, boolean and character types are the **primitive** types. Their names are predefined, not keywords, and the [Primitive Types](https://rux-lang.dev/docs/lang/appendix/primitives) appendix lists all of them, with sizes. Text is not a separate type: a string is a slice of characters. Two more types have no family of their own: `opaque`, the unknown pointee of an untyped pointer `*opaque`, and `()`, the unit type of a function that returns nothing. ## Type expressions ```text Type ::= SumType ( '!' SumType )? | '!' SumType SumType ::= RangeType ( '|' RangeType )* RangeType ::= PostfixType ( ( '..' | '..=' ) PostfixType? )? | ( '..' | '..=' ) PostfixType? | 'var' PostfixType PostfixType ::= PrimaryType ( '?' | '[' ']' | '[' Expression ']' | '[' '..' ']' )* PrimaryType ::= Path TypeArguments? | '*' 'var'? PostfixType | '&' 'var'? PostfixType | 'func' '(' … ')' ( '->' Type )? | '(' Type ( ',' Type )* ')' | '(' ')' ``` The forms bind in this order, tightest first: 1. **Postfix suffixes**, applied left to right: `?` optional, `[N]` array, `[..]` slice, and `[]`, the flexible array tail a struct's last field may have (see [Memory Layout](https://rux-lang.dev/docs/lang/memory/layout)). 2. **Range** bounds `T..T`, `T..=T`, and the `var` of a writable slice `var T[..]`. 3. **Sum** `A | B`. 4. **Fallible** `T ! E` — one `!` per type; a leading `! E` is a fallible with no success value. The pointer and reference forms take a postfix type, so `*int?` is a pointer to an optional, `*(int?)`. | Written | Means | | --------------- | ----------------------------------------------- | | `int32?[..]` | A slice of optional `int32` | | `int32[..]?` | An optional slice of `int32` | | `int[3][2]` | An array of 2 elements, each an `int[3]` | | `int??` | An optional of an optional `int` | | `A | B ! E | F` | `(A | B) ! (E | F)` | | `T ! (E?)` | A fallible whose error is an optional — grouped | | `A | (B?)` | A sum with an optional member — grouped | An optional member of a sum and an optional error of a fallible must be written in parentheses, since `A | B?` could mean either `A | (B?)` or `(A | B)?`. Ungrouped, they are `error: an optional sum member must be grouped` and `error: an optional error type must be grouped`. ```rux variant ParseError { Empty, Bad(char8) } func Double(x: int) -> int { return x * 2; } func Check(text: char8[..]) -> ! ParseError { if text.length == 0 { fail ParseError::Empty; } } func Main() -> int { let grid: int[3][2] = [[1, 2, 3], [4, 5, 6]]; let pair: (int, char8[..]) = (1, "one"); let span: int..int = 0..10; let maybe: int? = none; let either: int | bool = 7; let op: func(int) -> int = Double; var storage: int[4] = [1, 2, 3, 4]; let view: var int[..] = storage[..]; let address: *int = @grid[0][0]; let pointerToOptional: *int? = null; view[0] = 10; return grid[1][2] + op(pair.0); // 6 + 2 } ``` ## Type identity Two type expressions denote the same type when they are spelled the same after [aliases](https://rux-lang.dev/docs/lang/types/aliases) are resolved. A `type` alias is another name for its target, never a new type. Each `struct`, `enum`, `variant`, `union` and `interface` declaration, on the other hand, introduces a type distinct from every other, even one with identical fields. A generic type is a different type for each set of type arguments: `Box` and `Box` are unrelated. `int` and `int64` are distinct types — a function can be overloaded on them — that convert to each other implicitly, because `int` is 64 bits wide on every supported target. The same holds for `uint` and `uint64`. ## Implicit conversions A value converts to another type without `as` only when no value can change: | From | To | Example | | ------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | An integer | A wider integer of the same signedness | `int8` → `int32`, `uint32` → `uint` | | `int` / `uint` | `int64` / `uint64`, and back | | | `float32` | `float64` | | | `char32` | `char64` | | | Any boolean width | Any other boolean width | `bool` → `bool32` | | `T[N]` | `T[..]` | An array viewed as a slice | | `var T[..]`, `*var T`, `&var T` | `T[..]`, `*T`, `&T` | Dropping write access | | Any pointer | `*opaque` | | | `T` | `T?`, or a sum that has `T` as a member | See [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) and [Sums](https://rux-lang.dev/docs/lang/sums/overview) | Everything else needs [`as`](https://rux-lang.dev/docs/lang/expressions/casts): narrowing, changing signedness, crossing between integers, floats, characters and booleans, and `float64` to `float32`. Each family page lists its conversions. Unsuffixed integer literals are the one flexible case: they take the type their context needs, as [Literals](https://rux-lang.dev/docs/lang/lexical/literals#type-of-an-unsuffixed-integer) describes. Two integer operands of one signedness meet at the wider type, as an assignment would widen the narrower one. Operands of different signedness have no common type: ```text let u: uint64 = 3; let s: int64 = -5; let mixed = u + s; error: operator '+' cannot combine left operand 'uint64' with right operand 'int64' ``` A result that is wider than its destination is not narrowed back: with `small: int32` and `big: int64`, `let t: int32 = small + big;` is `error: cannot assign 'int64' to 'int32'`. ## Size and alignment `sizeof(T)` and `alignof(T)` give a type's storage size and alignment in bytes, as `uint` values computed at compile time. Every primitive's size is fixed by its name — `int` and `uint` are 8 bytes on every supported target — and [Memory Layout](https://rux-lang.dev/docs/lang/memory/layout) describes how compound types are laid out. ## See also - [Primitive Types](https://rux-lang.dev/docs/lang/appendix/primitives) — every primitive in one table - [Casts](https://rux-lang.dev/docs/lang/expressions/casts) — `as` and `is` - [Types](https://rux-lang.dev/docs/learn/types) — the Learn part on building your own types - [Convert](https://rux-lang.dev/docs/learn/convert) — the lesson on conversions # Integers An integer type holds a whole number in a fixed number of bits. The signed types `int8` to `int512` use two's complement; the unsigned types `uint8` to `uint512` hold zero and up. `int` and `uint` are as wide as a pointer. ## The integer types | Type | Bits | Bytes | Min | Max | Suffix | | --------- | ---- | ----- | --------------------- | ----------------------- | ------ | | `int8` | 8 | 1 | −128 | 127 | `i8` | | `int16` | 16 | 2 | −32,768 | 32,767 | `i16` | | `int32` | 32 | 4 | −2,147,483,648 | 2,147,483,647 | `i32` | | `int64` | 64 | 8 | −263 ≈ −9.22 × 1018 | 263 − 1 | `i64` | | `int128` | 128 | 16 | −2127 ≈ −1.70 × 1038 | 2127 − 1 | `i128` | | `int256` | 256 | 32 | −2255 ≈ −5.79 × 1076 | 2255 − 1 | `i256` | | `int512` | 512 | 64 | −2511 ≈ −6.70 × 10153 | 2511 − 1 | `i512` | | `int` | 64 | 8 | as `int64` | as `int64` | `i` | | `uint8` | 8 | 1 | 0 | 255 | `u8` | | `uint16` | 16 | 2 | 0 | 65,535 | `u16` | | `uint32` | 32 | 4 | 0 | 4,294,967,295 | `u32` | | `uint64` | 64 | 8 | 0 | 264 − 1 ≈ 1.84 × 1019 | `u64` | | `uint128` | 128 | 16 | 0 | 2128 − 1 ≈ 3.40 × 1038 | `u128` | | `uint256` | 256 | 32 | 0 | 2256 − 1 ≈ 1.16 × 1077 | `u256` | | `uint512` | 512 | 64 | 0 | 2512 − 1 ≈ 1.34 × 10154 | `u512` | | `uint` | 64 | 8 | 0 | as `uint64` | `u` | `byte` is a built-in [alias](https://rux-lang.dev/docs/lang/types/aliases#built-in-aliases) of `uint8`, for values that are raw storage rather than numbers. `int` and `uint` are pointer-sized: their width follows the target, and it is 64 bits on every target rux 0.4.0 supports. `int` is the type of an unsuffixed integer literal and of `Main`'s exit status; `uint` is the type of lengths, indices and `sizeof`. When a value must keep a width across targets — a file format, a protocol, a C structure — name a fixed-width type. The 128-, 256- and 512-bit types are ordinary integers: they support every operator and conversion below. ## Associated constants Every integer type has four constants. They are declared in the standard `Core` package, so the type has to be imported from it, and `Core` listed under `[Dependencies]`: | Constant | Type | Value | | -------- | -------- | --------------------- | | `Bits` | `uint` | Width in bits | | `Bytes` | `uint` | Storage size in bytes | | `Min` | the type | Smallest value | | `Max` | the type | Largest value | ```rux import Core::{ int, int8, uint64 }; import Io::PrintLine; func Main() -> int { PrintLine("int8 {} to {}", int8::Min, int8::Max); // -128 to 127 PrintLine("uint64 max {}", uint64::Max); // 18446744073709551615 PrintLine("int {} bits", int::Bits); // 64 return 0; } ``` Without the import, the constant is not found: `error: 'Max' not found in extend for type 'int8'`. An alias carries the constants of its target, so `byte::Max` is 255 once `byte` is imported. ## Literals Integer literals are decimal, hexadecimal (`0x`), octal (`0o`) or binary (`0b`), with `_` between digits, and an optional suffix from the table above. An unsuffixed literal takes the type its context requires — the declared type it initialises, or the other operand's type — and is an `int` otherwise. It must fit that type: ```rux let level: uint8 = 200; let raised = level + 100; // 100 is a uint8 let mask = 0xFF00u16; ``` `let tooBig: uint8 = 256;` is `error: integer literal is out of range for type 'uint8'`. [Literals](https://rux-lang.dev/docs/lang/lexical/literals#integer-literals) gives the full spelling rules. ## Operations | Operators | Result | Rules | | --------------------------------- | ----------------------- | ------------------------------------------------------------- | | `+` `-` `*` | The operand type | Wrap around modulo 2bits | | `/` `%` | The operand type | Truncate toward zero; panic on a zero divisor | | prefix `-` | The operand type | Wraps; on an unsigned type, gives 2bits − x | | `&` `|` `^` `~` | The operand type | Bitwise | | `<<` `>>` `>>>` | The left operand's type | See [Shift](https://rux-lang.dev/docs/lang/expressions/shift) | | `==` `!=` `<` `<=` `>` `>=` | `bool` | Numeric order | | `++` `--` and compound assignment | — | As the operator they stand for | ### Wrapping `+`, `-` and `*` wrap: a result that does not fit keeps its low bits, in every build profile. ```rux var level: uint8 = 250; level += 10; // 4 var small: int8 = 127; small += 1; // -128 ``` When overflow must be detected, use the `Core` functions `AddChecked`, `SubChecked` and `MulChecked`, which report it, or `AddSaturating` and its siblings, which stop at the limits: ```rux import Core::{ AddChecked, AddSaturating }; import Io::PrintLine; func Main() -> int { let level: uint8 = 250; var sum: uint8 = 0; if AddChecked(level, 10u8, @sum) { PrintLine("overflow; wrapped to {}", sum); // 4 } PrintLine("saturated: {}", AddSaturating(level, 10u8)); // 255 return 0; } ``` ### Division `/` truncates toward zero and `%` takes the sign of the dividend: `-7 / 2` is `-3`, and `-7 % 2` is `-1`. A zero divisor stops the program with a panic, on every target and in every build profile. So does the one signed quotient that does not fit, a type's `Min` divided by `-1`, and the matching `%`: ```text Panic: division by zero at Divide (Src/Main.rux:5:14) Panic: division overflow at Main (Src/Main.rux:16:23) ``` ### Mixed operands Two operands of the same signedness meet at the wider type: an `int32` plus an `int64` is an `int64`. Operands of different signedness are an error, since either conversion could change a value — convert one with `as` to choose: ```text error: operator '+' cannot combine left operand 'uint64' with right operand 'int64' error: operator '<' cannot compare left operand 'uint64' with right operand 'int64' ``` ## Conversions An integer widens implicitly to a wider integer of the **same** signedness, and `int` and `int64` (likewise `uint` and `uint64`) convert to each other freely. Everything else is written with `as`: | Conversion | Result | | --------------------------- | ----------------------------------------------------------------------------------------------------------- | | To a narrower integer | Keeps the low bits: `300 as int8` is 44 | | Between signed and unsigned | Keeps the bit pattern: `-1 as uint8` is 255 | | To a wider integer | Sign-extends a signed value: `(-1i32) as uint64` is 264 − 1 | | To a floating-point type | The nearest representable value | | To a boolean type | `false` for zero, `true` for anything else | | To a character type | The character with that code; see [Characters](https://rux-lang.dev/docs/lang/types/characters#conversions) | ```rux let big: int32 = 300; let narrowed = big as int8; // 44 let negative: int32 = -1; let unsigned = negative as uint8; // 255 let ratio = 7 as float64 / 2.0; // 3.5 let flag = 256 as bool; // true ``` A narrowing `as` never fails; it produces a different value. To find out whether a value fits first, compare it with the target's `Min` and `Max`, or call `Core::ConvertChecked`, which returns `true` when the conversion lost the value. Implicit widening refuses what it cannot do losslessly: `let d: int16 = c;` for a `uint8` `c` is `error: cannot assign 'uint8' to 'int16'`, and `let i: int32 = n;` for an `int` `n` is `error: cannot assign 'int' to 'int32'`. ## See also - [Floating-Point](https://rux-lang.dev/docs/lang/types/floating-point) — the other numeric family - [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic), [Bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise), [Shift](https://rux-lang.dev/docs/lang/expressions/shift) — the operators in full - [Casts](https://rux-lang.dev/docs/lang/expressions/casts) — `as` for every type - [Integer](https://rux-lang.dev/docs/learn/integer), [Number limit](https://rux-lang.dev/docs/learn/number-limit), [Wide integer](https://rux-lang.dev/docs/learn/wide-integer) — lessons - [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic), [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic), [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) — lessons on overflow # Floating-Point A floating-point type holds a binary fraction with an exponent, covering a vast range at a fixed number of significant digits. Rux implements the two IEEE 754 formats every supported machine has in hardware. ## The floating-point types | Type | Bits | Bytes | Format | Significant digits | Largest finite value | Suffix | | --------- | ---- | ----- | ----------------- | ------------------ | -------------------- | ------ | | `float32` | 32 | 4 | IEEE 754 binary32 | about 7 | ≈ 3.40 × 1038 | `f32` | | `float64` | 64 | 8 | IEEE 754 binary64 | about 16 | ≈ 1.80 × 10308 | `f64` | `float` is a built-in [alias](https://rux-lang.dev/docs/lang/types/aliases#built-in-aliases) of `float64`, and `float64` is the type of an unsuffixed floating-point literal. Use `float32` where storage or bandwidth matters more than precision, such as large arrays of samples, and `float64` otherwise. ### Reserved widths These widths are named by the language but not implemented in rux 0.4.0: | Type | Bits | Bytes | Intended format | Suffix | | ---------- | ---- | ----- | ------------------------------------- | ------ | | `float8` | 8 | 1 | E4M3 (4 exponent, 3 significand bits) | `f8` | | `float16` | 16 | 2 | IEEE 754 binary16 | `f16` | | `float80` | 80 | 16 | x87 extended precision | `f80` | | `float128` | 128 | 16 | IEEE 754 binary128 | `f128` | | `float256` | 256 | 32 | IEEE 754 binary256 interchange | `f256` | | `float512` | 512 | 64 | IEEE 754 binary512 interchange | `f512` | Using one, as a type or through its suffix, is an error: ```text error: primitive type 'float16' is reserved but is not implemented in this compiler version ``` ## Associated constants Imported from `Core` like the [integer constants](https://rux-lang.dev/docs/lang/types/integers#associated-constants): | Constant | Meaning | `float32` | `float64` | | ------------- | ------------------------------------- | -------------- | ------------------------ | | `Bits` | Width in bits (`uint`) | 32 | 64 | | `Bytes` | Storage size in bytes (`uint`) | 4 | 8 | | `Max` | Largest finite value | 3.4028235e+38 | 1.7976931348623157e+308 | | `Lowest` | Most negative finite value, `-Max` | -3.4028235e+38 | -1.7976931348623157e+308 | | `MinPositive` | Smallest positive normal value | 1.1754944e-38 | 2.2250738585072014e-308 | | `Epsilon` | Gap between 1.0 and the next value up | 1.1920929e-07 | 2.220446049250313e-16 | | `Infinity` | Positive infinity | `Inf` | `Inf` | | `NaN` | A quiet not-a-number | `NaN` | `NaN` | ```rux import Core::float64; import Io::PrintLine; func Main() -> int { PrintLine("{} {}", float64::Max, float64::Epsilon); PrintLine("{} {}", float64::Infinity, -float64::Infinity); // Inf -Inf return 0; } ``` ## Literals A floating-point literal has digits on both sides of a point, an exponent, or a float suffix: `3.14`, `2.998e8`, `1.6e-19`, `0.5f32`, `1f32`. Without a suffix it is a `float64`, whatever the context: ```text let single: float32 = 0.75; error: cannot assign 'float64' to 'float32' ``` Write `0.75f32`. [Literals](https://rux-lang.dev/docs/lang/lexical/literals#floating-point-literals) gives the full grammar. ## Operations | Operators | Result | Rules | | --------------------------- | ---------------- | --------------------------------------------------- | | `+` `-` `*` `/` | The operand type | IEEE 754, rounded to nearest | | `%` | The operand type | Remainder of truncated division: `7.5 % 2.0` is 1.5 | | prefix `-` | The operand type | Flips the sign, including of zero and infinity | | `==` `!=` `<` `<=` `>` `>=` | `bool` | IEEE 754 comparison | Floating-point arithmetic never panics. Results follow IEEE 754: | Expression | Result | | ------------ | ------ | | `1.0 / 0.0` | `Inf` | | `-1.0 / 0.0` | `-Inf` | | `0.0 / 0.0` | `NaN` | A NaN is unordered: every comparison with it is `false` except `!=`, so `x != x` is `true` exactly when `x` is a NaN. The `Core` functions `IsNaN`, `IsInfinite`, `IsFinite`, `IsZero` and `IsNegativeZero` test for special values. The bitwise and shift operators do not apply to floats: `a & 1.0` is `error: operator '&' requires an integer, bool, or character left operand, but found 'float64'`. Most decimal fractions have no exact binary form, so arithmetic on them rounds: `0.1 + 0.2 == 0.3` is `false`. Compare against a tolerance, or count in integer units such as cents. ### Mixed widths A `float32` operand meets a `float64` operand at `float64`. Because an unsuffixed literal is a `float64`, `single * 2.0` is a `float64` too; write `single * 2.0f32` to stay in `float32`. ## Conversions `float32` converts to `float64` implicitly; every `float32` value is exactly a `float64` value. Everything else is written with `as`: | Conversion | Result | | ---------------------- | ----------------------------------------------------------------------------------- | | `float64` to `float32` | Rounded to the nearest `float32`; a value beyond its range becomes an infinity | | To an integer type | Truncated toward zero; beyond the range, saturated at `Min` or `Max`; NaN becomes 0 | | From an integer type | The nearest representable value | | To a boolean type | `false` for zero, `true` for anything else, NaN included | ```rux let ratio = 3.9; let whole = ratio as int32; // 3 let down = -3.9 as int32; // -3 let capped = 1e10 as int32; // 2147483647 let floor = -1.0 as uint8; // 0 let single = 3.141592653589793 as float32; // 3.1415927 let back = 7 as float64; // 7.0 ``` Float-to-integer conversion is the same on every target and whether it runs or is folded at compile time. It never wraps: `1e20 as int32` is `int32::Max`, not its low bits. Round first, with the `Math` package, when rounding rather than truncation is meant. ## See also - [Integers](https://rux-lang.dev/docs/lang/types/integers) — the other numeric family - [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) and [Casts](https://rux-lang.dev/docs/lang/expressions/casts) - [Float](https://rux-lang.dev/docs/learn/float) and [Float special](https://rux-lang.dev/docs/learn/float-special) — lessons on precision, infinities and NaN - [Math](https://rux-lang.dev/docs/learn/math) — rounding, powers and roots # Booleans A boolean holds one of two values, `true` or `false`. Comparisons produce booleans, and `if`, `while` and the logical operators consume them. ## The boolean types | Type | Bits | Bytes | Values | | -------- | ---- | ----- | --------------- | | `bool8` | 8 | 1 | `false`, `true` | | `bool16` | 16 | 2 | `false`, `true` | | `bool32` | 32 | 4 | `false`, `true` | | `bool64` | 64 | 8 | `false`, `true` | `bool` is a built-in [alias](https://rux-lang.dev/docs/lang/types/aliases#built-in-aliases) of `bool8`, and the everyday spelling. The wider types hold the same two values in more storage; they exist to match the layout of foreign code, such as the four-byte `BOOL` of the Windows API, which is a `bool32` in Rux. A compiler message names the canonical type, so a mistake with a `bool` reports `bool8`. ### Reserved widths `bool128`, `bool256` and `bool512` (16, 32 and 64 bytes) are named by the language but not implemented in rux 0.4.0. Using one is `error: primitive type 'bool128' is reserved but is not implemented in this compiler version`. ## Associated constants Imported from `Core`, each boolean type has `Bits` and `Bytes` (both `uint`): `bool32::Bits` is 32 and `bool32::Bytes` is 4. ## Literals `true` and `false` are the only boolean literals. Both are keywords, and their type is `bool`. ```rux let ready = true; let finished: bool = false; let wide: bool32 = ready; ``` ## Operations | Operator | Meaning | | ------------------ | ------------------------------------------------------- | | `!a` | Not | | `a && b` | And; `b` is evaluated only when `a` is `true` | | `a || b` | Or; `b` is evaluated only when `a` is `false` | | `a & b` | And, always evaluating both operands | | `a | b` | Or, always evaluating both operands | | `a ^ b` | Exclusive or: `true` when exactly one operand is `true` | | `a == b`, `a != b` | Equality | ```rux let a = true; let b = false; let both = a && b; // false let either = a || b; // true let differ = a ^ b; // true ``` `&&` binds more tightly than `||`, so `a || b && c` is `a || (b && c)`. See [Logical](https://rux-lang.dev/docs/lang/expressions/logical) for short-circuiting and [Expressions](https://rux-lang.dev/docs/lang/expressions/overview) for precedence. ### Conditions A condition must be a boolean. A number is not treated as one: ```text if 1 { … } error: condition for 'if' must have type 'bool', but found 'int' let flag = !5; error: operator '!' requires a bool operand, but found 'int' ``` Write the comparison you mean, `count != 0`. Comparing a boolean with a literal, `ready == true`, is legal but says nothing `ready` does not. ## Conversions Every boolean width converts implicitly to every other, since all of them hold the same two values: ```rux let flag: bool = true; let wide: bool32 = flag; let back: bool = wide; ``` Between booleans and other types, conversion is written with `as`: | Conversion | Result | | ---------------------------------------- | ------------------------------------------ | | Boolean to an integer or float | `1` for `true`, `0` for `false` | | Integer, float or character to a boolean | `false` for zero, `true` for anything else | ```rux let one = true as int; // 1 let zero = false as uint8; // 0 let set = 256 as bool; // true let some = 0.5 as bool; // true ``` A cast to a boolean tests the whole value for zero; it does not keep low bits. `256 as bool` is `true` even though the low byte of 256 is zero, and the result reads back as exactly 1. Without `as`, a number is never a boolean: `let on: bool = 1;` is `error: cannot assign 'int' to 'bool8'`, and `let n: int = true;` is `error: cannot assign 'bool8' to 'int'`. ## See also - [Logical](https://rux-lang.dev/docs/lang/expressions/logical) and [Comparison](https://rux-lang.dev/docs/lang/expressions/comparison) — the operators that make and use booleans - [if](https://rux-lang.dev/docs/lang/statements/if) and [Loops](https://rux-lang.dev/docs/lang/statements/loops) — where conditions appear - [Boolean](https://rux-lang.dev/docs/learn/boolean) and [Logical](https://rux-lang.dev/docs/learn/logical) — lessons # Characters A character type holds one unit of text. Rux distinguishes two kinds, and the difference decides which values are valid: - A **code unit** is one piece of an encoding. `char8` is one byte of UTF-8 and `char16` one 16-bit unit of UTF-16. Every value that fits the width is a valid code unit, but a code unit is a whole character only when the character is small enough to be encoded in one. - A **scalar value** is one Unicode character: a code point from U+0000 to U+10FFFF, excluding the surrogates U+D800 to U+DFFF. `char32` and `char64` hold scalar values. ## The character types | Type | Bits | Bytes | Holds | Range | Literal prefix | | -------- | ---- | ----- | ---------------------- | -------------------------------------- | -------------- | | `char8` | 8 | 1 | A UTF-8 code unit | 0 to 255 | `c8` | | `char16` | 16 | 2 | A UTF-16 code unit | 0 to 65,535 | `c16` | | `char32` | 32 | 4 | A Unicode scalar value | U+0000 to U+10FFFF, without surrogates | `c32`, or none | | `char64` | 64 | 8 | A Unicode scalar value | U+0000 to U+10FFFF, without surrogates | `c64` | `char` is a built-in [alias](https://rux-lang.dev/docs/lang/types/aliases#built-in-aliases) of `char32`, and the type of an unprefixed character literal. Use `char` for characters, and `char8` or `char16` when working with encoded text one unit at a time — indexing a [string](https://rux-lang.dev/docs/lang/types/text) gives code units, not characters. ### Reserved widths `char128`, `char256` and `char512` (16, 32 and 64 bytes) are named by the language but not implemented in rux 0.4.0. Using one is `error: primitive type 'char128' is reserved but is not implemented in this compiler version`. ## Associated constants Imported from `Core`: | Constant | `char8` | `char16` | `char32` | `char64` | | -------- | ------- | -------- | -------- | -------- | | `Bits` | 8 | 16 | 32 | 64 | | `Bytes` | 1 | 2 | 4 | 8 | | `Min` | 0 | 0 | U+0000 | U+0000 | | `Max` | 255 | 65,535 | U+10FFFF | U+10FFFF | `Min` and `Max` have the character type itself; `char::Max as uint32` is 1114111. ## Literals A character literal is one character or [escape](https://rux-lang.dev/docs/lang/lexical/literals#escape-sequences) in single quotes. The prefix selects the type, and the character must be exactly one unit of it: | Literal | Type | Accepts | | ----------------- | -------- | --------------------------------------------------- | | `c8'A'` | `char8` | U+0000 to U+007F — one UTF-8 byte | | `c16'Ж'` | `char16` | U+0000 to U+FFFF, not a surrogate — one UTF-16 unit | | `'😀'`, `c32'😀'` | `char32` | Any scalar value | | `c64'😀'` | `char64` | Any scalar value | An unprefixed literal initialising or assigned to a `char8` or `char16` takes that type by the same rule, so `let initial: char8 = 'R';` works. A character that does not fit is refused, never truncated: ```text 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 let emoji: char16 = '😀'; error: character '😀' (U+1F600) does not fit one 'char16' code unit ``` `é` takes two bytes of UTF-8 and `😀` two units of UTF-16; text that needs several units is a [string](https://rux-lang.dev/docs/lang/types/text). ```rux let letter = 'A'; let emoji = '😀'; let unit = c8'R'; let word = c16'Ж'; let wide = c64'\u{1F600}'; let initial: char8 = 'R'; ``` ## Operations Characters compare by their numeric value — code-point order for scalar values, unit value for code units — with `==`, `!=`, `<`, `<=`, `>` and `>=`. The order is not alphabetical in any language's sense: `'Z' < 'a'`, because U+005A comes before U+0061. ```rux let c = c8'7'; let isDigit = c >= c8'0' && c <= c8'9'; // true ``` Arithmetic on characters goes through an integer: convert with `as`, compute, and convert back. ```rux let lower = c8'a'; let upper = ((lower as uint8) - 32) as char8; // 'A' let value = (c8'7' as int) - (c8'0' as int); // 7 let next = ('a' as uint32 + 1) as char; // 'b' ``` ## Conversions ### Between character types `char32` widens implicitly to `char64`, since both hold the same scalar values. No other character conversion is implicit: `char8` and `char16` are units of different encodings, and a UTF-8 byte above 0x7F is not the UTF-16 unit with the same number. ```text let unit: char8 = 'a'; let crossed: char16 = unit; error: cannot assign 'char8' to 'char16' let scalar: char32 = 'a'; let narrowed: char16 = scalar; error: cannot assign 'char32' to 'char16' ``` With `as`, a conversion to a wider character keeps the value, and a conversion to a narrower one keeps the low bits. It never re-encodes: for a `face: char32` holding U+1F600, `face as char16` is the unit 0xF600, not a surrogate pair. A constant that does not fit is refused instead, as `'😀' as char16` is with `error: constant cast from 'char32' to 'char16' is outside the target type's range`. To transcode text, use the Unicode and Text packages. ### To and from integers `as` converts a character to any integer type, giving its numeric value, and an integer to any character type: ```rux let code = 'A' as uint32; // 65 let byte = c8'A' as uint8; // 65 let smile = 0x263A as char; // '☺' ``` When the integer is a constant, the compiler checks that the character type can hold it: ```text let past: char8 = 0x100 as char8; error: constant cast from 'int' to 'char8' is outside the target type's range let surrogate: char32 = 0xD800 as char32; error: cast from 'int' to 'char32' uses invalid surrogate code point U+D800 ``` A conversion at run time is not checked: it keeps the low bits of the integer, so a computed number can produce a surrogate or a value above U+10FFFF in a `char32`. Validate such values before treating them as text. ### Other conversions Characters convert with `as` to floating-point types (`'A' as float64` is 65.0) and to booleans (`false` only for U+0000). Without `as`, a character is never a number: `let n: uint8 = c8'a';` is `error: cannot assign 'char8' to 'uint8'`. ## See also - [Text](https://rux-lang.dev/docs/lang/types/text) — strings as slices of code units - [Literals](https://rux-lang.dev/docs/lang/lexical/literals#character-literals) — the full literal grammar - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — matching characters and character ranges - [Character](https://rux-lang.dev/docs/learn/character), [Character pattern](https://rux-lang.dev/docs/learn/character-pattern), [Unicode](https://rux-lang.dev/docs/learn/unicode) — lessons # Text Rux has no built-in string type. Text is a [slice](https://rux-lang.dev/docs/lang/slices/overview) of character code units, and a string literal is a read-only slice of code units stored in the program: | Type | Encoding | Literal | | ------------ | -------- | ---------------------- | | `char8[..]` | UTF-8 | `"Hello"`, `c8"Hello"` | | `char16[..]` | UTF-16 | `c16"Hello"` | | `char32[..]` | UTF-32 | `c32"Hello"` | `char8[..]` is the everyday text type: source files are UTF-8, unprefixed literals are UTF-8, and the standard packages take and return `char8[..]`. The wider encodings exist for foreign APIs that use them, such as UTF-16 on Windows. Owned, growable text — `String`, `StringBuilder` and the validated `StringView` — comes from the [Text package](https://rux-lang.dev/docs/api/text), not from the language. ## Code units, not characters Everything a slice does, it does in code units of its encoding: `.length` counts them, `[i]` returns one, and `for` visits each one. A character outside ASCII takes several UTF-8 units, or two UTF-16 units outside the Basic Multilingual Plane: | Text | `char8[..]` length | `char16[..]` length | `char32[..]` length | | -------------- | ------------------ | ------------------- | ------------------- | | `Hello` | 5 | 5 | 5 | | `€` (U+20AC) | 3 | 1 | 1 | | `🚀` (U+1F680) | 4 | 2 | 1 | ```rux let euro = "€uro"; let units = euro.length; // 6 let first = euro[0] as uint8; // 226, the first UTF-8 byte of € let tail = euro[3..]; // "uro" let wide = c16"\u{1F680}"; let pair = wide.length; // 2, a surrogate pair ``` A sub-slice can therefore cut a character in half. The Text and Unicode packages provide validated text, iteration by character and grapheme boundaries when an operation needs them. ## Members and operations | Form | Type | Meaning | | --------------- | ----------------------- | ------------------------------ | | `text.length` | `uint` | Number of code units | | `text.data` | `*char8` (and so on) | Pointer to the first code unit | | `text[i]` | `char8` (and so on) | The code unit at index `i` | | `text[a..b]` | `char8[..]` (and so on) | A view of units `a` up to `b` | | `for u in text` | — | Visits each code unit in order | These members need no import. An index past the end stops the program with `Panic: index out of range`. [Slices](https://rux-lang.dev/docs/lang/slices/overview) describes indexing and sub-slicing in full. ```rux func CountSpaces(text: char8[..]) -> uint { var spaces: uint = 0; for unit in text { if unit == ' ' { spaces += 1; } } return spaces; } ``` ### Comparing text `==` is not defined on slices, because comparing two views would compare where they point rather than what they hold: ```text error: operator '==' is not defined for slice type 'char8[..]' note: a slice is a view, so comparing the views would compare addresses rather than elements help: declare '==' on 'char8[..]', or compare the elements one at a time ``` Compare the units, or use `StringView::Equals` from the Text package: ```rux func SameText(a: char8[..], b: char8[..]) -> bool { if a.length != b.length { return false; } for i in 0..a.length { if a[i] != b[i] { return false; } } return true; } ``` ## Storage A literal's code units are stored in read-only data, followed by a NUL code unit that `.length` does not count. `"Hello".data` can therefore be passed to a C function that expects a NUL-terminated string. A slice value itself is a pointer and a length — 16 bytes — so passing or copying text never copies its units. A literal is read-only, and so is any `char8[..]`: ```text let word = "word"; word[0] = c8'W'; error: cannot modify elements through read-only slice 'char8[..]' help: use a 'var T[..]' view to write through the sequence ``` To change text, copy it into storage the program owns and take a writable view `var char8[..]` of that: ```rux let word = "word"; var letters: char8[4]; for i in 0..word.length { letters[i] = word[i]; } letters[0] = c8'W'; let changed: var char8[..] = letters[..]; // "Word" ``` ## Constants and parameters A string literal can initialise a constant, a field, or a parameter of the matching slice type: ```rux const Greeting: char8[..] = "Hello"; struct Message { text: char8[..]; tag: int32; } func Shout(text: char8[..]) -> uint { return text.length; } ``` Escapes such as `\n`, `\"` and `\u{1F680}` are encoded in the literal's own encoding. [Literals](https://rux-lang.dev/docs/lang/lexical/literals#string-literals) lists them. ## See also - [Characters](https://rux-lang.dev/docs/lang/types/characters) — the code unit and scalar value types - [Slices](https://rux-lang.dev/docs/lang/slices/overview) — the type text is made of - [Text package](https://rux-lang.dev/docs/api/text) — `String`, `StringBuilder` and friends - [String literal](https://rux-lang.dev/docs/learn/string-literal), [Encoding](https://rux-lang.dev/docs/learn/encoding), [UTF-8](https://rux-lang.dev/docs/learn/utf8), [String view](https://rux-lang.dev/docs/learn/string-view) — lessons # Type Aliases A type alias gives an existing type a second name. It introduces no new type: the alias and its target are the same type, interchangeable everywhere without a conversion. ```text TypeAlias ::= 'pub'? 'type' Identifier '=' Type ';' ``` ```rux type UserId = uint64; type Matrix = float32[2][2]; type Bytes = uint8[..]; type MaybeId = UserId?; ``` An alias is declared at the top level of a file or inside a `module` block. Like any declaration it is private to its package unless marked [`pub`](https://rux-lang.dev/docs/lang/modules/visibility). ## Transparency Because an alias names the same type as its target, values pass between the two in both directions, and a type test answers to either spelling: ```rux type Count = int32; func Main() -> int { let counted: Count = 7; let plain: int32 = counted; // no conversion let back: Count = plain; // and none back let same = counted is int32; // true return back as int; } ``` An alias also carries everything attached to its target. Its [associated constants](https://rux-lang.dev/docs/lang/types/integers#associated-constants) are reachable through it — `Count::Max` is `int32::Max` once `int32` is imported from `Core` — and its methods are callable on it. The reverse holds too: an `extend` block written for an alias extends the target type itself. ```rux type Celsius = float64; extend Celsius { func Fahrenheit(self: &Celsius) -> float64 { return self * 1.8 + 32.0; } } ``` After this, every `float64` has a `Fahrenheit` method, not only values declared as `Celsius`. ## An alias is not a distinct type An alias gives a name, never type safety. Two aliases of the same type are the same type as each other, so nothing stops them being mixed: ```rux type Metres = float64; type Feet = float64; func Main() -> int { let height: Metres = 1.8; let step: Feet = 2.5; let nonsense = height + step; // accepted: both are float64 return 0; } ``` When the compiler should keep two kinds of value apart, wrap the value in a single-field [struct](https://rux-lang.dev/docs/lang/structs/overview); each struct declaration is a type of its own. ```rux struct Metres { value: float64; } struct Feet { value: float64; } ``` ## What can be aliased Any type can be aliased: primitives, slices, arrays, tuples, optionals, sums, fallibles, pointers and references, [function types](https://rux-lang.dev/docs/lang/functions/function-types), user-defined types and instantiations of generic types. ```rux struct Point { x: int; y: int; } struct Box { value: T; } type Position = Point; type IntBox = Box; type Pair = (int, int); type Callback = func(int) -> bool; func Main() -> int { let here = Position { x: 1, y: 2 }; let boxed: IntBox = Box { value: 3 }; let pair: Pair = (4, 5); return here.x + boxed.value + pair.1; // 9 } ``` An alias of a struct can be used to write a struct literal, as `Position { x: 1, y: 2 }` shows. ::note **Limits in rux 0.4.0.**:br rux 0.4.0 resolves an alias where it is declared, so the type it names must be declared above it in the file. It does not yet accept a struct literal written through an alias of a generic instantiation, such as `IntBox { value: 3 }`; write `Box { value: 3 }`. And an alias named through a module path from outside its module, such as `Units::Metres`, is not yet resolved to its target. :: An alias cannot take type parameters. `type Pair = (T, T);` is a syntax error, `error: expected '=' after the type alias name before '<'`; write a generic struct instead. An alias cannot name itself, directly or through other aliases: ```text type A = B; type B = A; error: type alias 'A' has a cyclic definition ``` ## Built-in aliases Four primitive names are aliases, declared in the `Core` package with `pub type`: | Alias | Target | Meaning | | ------- | --------- | --------------------------------------------------- | | `bool` | `bool8` | The everyday boolean | | `byte` | `uint8` | A raw byte of storage, as opposed to a small number | | `char` | `char32` | One Unicode scalar value | | `float` | `float64` | The everyday floating-point type | They behave exactly like their targets — same representation, ABI, constants, conversions and overloads — and compiler messages name the target: a mistake with a `bool` reports `bool8`. The names can be used as types without an import; their associated constants, like any primitive's, need one, such as `import Core::byte;` for `byte::Max`. `int` and `uint` are not aliases. They are distinct primitive types that convert implicitly to and from `int64` and `uint64`; see [Integers](https://rux-lang.dev/docs/lang/types/integers). ## See also - [Types](https://rux-lang.dev/docs/lang/types/overview) — type identity and conversions - [Function types](https://rux-lang.dev/docs/lang/functions/function-types) — naming a callable signature - [Structs](https://rux-lang.dev/docs/lang/structs/overview) — when a distinct type is wanted - [Type alias](https://rux-lang.dev/docs/learn/type-alias) — the lesson # Bindings A *binding* gives a name to a value. Rux has three kinds, each introduced by its own keyword: | Keyword | Binds | Can change | Computed | Page | | ------- | --------------------------- | ---------- | --------------- | -------------------------------------------------------------- | | `let` | an immutable run-time value | no | at run time | this page | | `var` | a mutable run-time value | yes | at run time | this page | | `const` | a compile-time value | no | by the compiler | [Constants](https://rux-lang.dev/docs/lang/bindings/constants) | ```text binding = ("let" | "var") pattern [":" type] [initializer] ";" initializer = "=" expression | "<-" expression ``` The pattern is usually a single name. It can also take a tuple or a structure apart — see [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring). ```rux let answer = 42; var counter = 0; counter += 1; ``` ## let `let` binds a value that never changes. The name keeps the value it was given for as long as it is in scope: ```rux let limit: int32 = 10; limit = 11; ``` ```text error: cannot modify immutable variable 'limit' help: declare 'limit' with 'var' to make it mutable ``` Because a `let` can never be assigned later, it must be initialized where it is declared. `let total: int32;` is `error: immutable variable requires an initializer`. Prefer `let`. A name that cannot change is one less thing for the reader to track, and the compiler rejects an accidental write instead of letting it through. ## var `var` binds a value that may be replaced. Assignment, the compound operators and `++`/`--` all need a `var` (or another mutable place, such as a field of one): ```rux var score: int32 = 10; score = 12; score += 5; score++; ``` A `var` may also be declared without a value and assigned later. Until then it holds nothing, and the compiler makes sure no path reads it too early — see [Initialization](https://rux-lang.dev/docs/lang/bindings/initialization). ## Types and inference With an initializer and no annotation, the binding takes the initializer's type. Literals have a default type: | Initializer | Inferred type | | ------------------------ | --------------------------- | | `42`, `3000000000` | `int` | | `1.5` | `float64` | | `'A'` | `char32` | | `true` | `bool` | | `"text"` | `char8[..]` | | `7u8`, `2.5f32`, `c8'A'` | the suffix or prefix's type | An annotation, `: Type`, chooses the type instead. An unsuffixed integer literal then takes the annotated type, provided its value fits: ```rux let small: uint8 = 200; let wide: int64 = 5; ``` `let small: uint8 = 300;` is `error: integer literal is out of range for type 'uint8'`. A binding's type is fixed once it is declared. A value of another type is not converted to fit, with one exception: an integer widens to a wider integer of the same signedness, because no value can change on the way. Every other change of type is written with [`as`](https://rux-lang.dev/docs/lang/expressions/casts): ```rux let narrow: int32 = 7; let widened: int64 = narrow; let converted = narrow as float64; ``` The reverse, `let back: int32 = widened;`, is `error: cannot assign 'int64' to 'int32'`. ## = and <- An initializer is written with `=` or `<-`, and the two mean different things: | Form | Meaning | | ------------- | ------------------------------------------------------------------- | | `let b = a;` | **copy**: `b` gets its own copy, and `a` is unchanged | | `let b <- a;` | **move**: the value is handed to `b`, and `a` may not be read again | ```rux let first = Buffer { size: 4 }; let second <- first; PrintLine("{}", second.size); ``` Reading `first` after the move is `error: value 'first' is used after it was moved`. A type that cannot be copied, such as one that owns a resource, can only be bound with `<-` from a named source; a freshly made value, such as the result of a call, needs no `<-`. The full rules are in [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## Mutability Mutability belongs to the binding and reaches all the way into the value. A `let` structure cannot have any field changed, at any depth; a `var` one can have every field changed, or be replaced whole: ```rux let fixed = Point { x: 1, y: 2 }; var moving = Point { x: 1, y: 2 }; moving.x = 5; moving = Point { x: 0, y: 0 }; ``` `fixed.x = 5;` is `error: cannot modify immutable variable 'fixed'`. There is no per-field opt-out in either direction. The same holds for tuples and fixed-size arrays. A function changes a caller's value only through a writable reference, `&var T`, or a writable pointer, `*var T` — see [References](https://rux-lang.dev/docs/lang/references/overview) and [Pointers](https://rux-lang.dev/docs/lang/pointers/overview). A parameter itself is always immutable; a function that needs a mutable copy moves the argument into a local, as in `var local <- value;`. ## Scope A binding is visible from its declaration to the end of the block that contains it. Declaring the same name twice in one block is an error: ```rux let total = 1; let total = 2; ``` ```text error: variable 'total' is already declared in this scope ``` Each name a [destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) pattern declares counts separately, so `let (p, p) = (1, 2);` is refused the same way. A block nested inside another — the body of an `if` or a loop — may declare a name that is already used outside it. The inner binding hides the outer one until the block ends. ::note **Reusing an outer name.**:br rux 0.4.0 does not yet keep a hiding binding apart from the one it hides: after the inner block, the outer name can read the inner value. Until that is fixed, give a binding in a nested block a name of its own. :: ## Discarding a value `_` is not a name. `let _ = value;` evaluates `value` and throws it away, and nothing can read `_` afterwards — see [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring#discarding-a-value). ## See also - [Constants](https://rux-lang.dev/docs/lang/bindings/constants) — names the compiler computes - [Initialization](https://rux-lang.dev/docs/lang/bindings/initialization) — declaring a `var` now and giving it a value later - [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) — binding several names from one value - [Assignment](https://rux-lang.dev/docs/lang/expressions/assignment) — `=`, `<-`, compound assignment, `++` and `--` - [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move) — what `=` and `<-` do to the source - Learn: [Variable](https://rux-lang.dev/docs/learn/variable), [Mutable](https://rux-lang.dev/docs/learn/mutable) # Constants A *constant* is a name for a value the compiler computes once, before the program runs. It is declared with `const`, can never change, and can be used wherever the language needs a compile-time value — an array size, for instance. ```text constant = ["pub"] "const" Name [":" type] "=" expression ";" ``` ```rux const MaxConnections = 100; const Ratio: float64 = 1.5; const Greeting: char8[..] = "hello"; ``` The type annotation is optional. Without one, the constant takes its initializer's type, exactly as a [`let`](https://rux-lang.dev/docs/lang/bindings/overview#types-and-inference) would: `MaxConnections` above is an `int`. With one, an unsuffixed literal takes the annotated type, and must fit it — `const Small: uint8 = 300;` is `error: integer literal is out of range for type 'uint8'`. By convention, constant names are PascalCase. ## Where a constant may appear | Place | Example | Named as | | ---------------------- | ------------------------------------ | ------------- | | A module, at top level | `const Limit: uint = 4;` | `Limit` | | A function body | `const Local = Limit * 2;` | `Local` | | An `extend` block | `const Zero = Point { x: 0, y: 0 };` | `Point::Zero` | A module-level constant may be `pub`, like any other declaration — see [Visibility](https://rux-lang.dev/docs/lang/modules/visibility). A constant declared in an `extend` block is an *associated constant* of the type, and is named through it: ```rux extend Point { const Zero = Point { x: 0, y: 0 }; const Dimensions: int = 2; } ``` ```rux PrintLine("{} {}", Point::Zero.x, Point::Dimensions); ``` The primitive types carry associated constants of their own, such as `int8::Min` and `float64::Epsilon`, once their type is imported from Core — see [Integers](https://rux-lang.dev/docs/lang/types/integers). ## What the initializer may contain The initializer is computed by the compiler, so everything in it must be known before the program runs. It may use: - literals, and other constants - the [arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic), [comparison](https://rux-lang.dev/docs/lang/expressions/comparison), [logical](https://rux-lang.dev/docs/lang/expressions/logical), [bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise) and [shift](https://rux-lang.dev/docs/lang/expressions/shift) operators, [casts](https://rux-lang.dev/docs/lang/expressions/casts) and the [conditional](https://rux-lang.dev/docs/lang/expressions/conditional) `? :` - `sizeof(T)` and `alignof(T)` - compile-time values such as `#target` — see [Compile-time context](https://rux-lang.dev/docs/lang/comptime/context) - structure, array, tuple and enum values built from all of these ```rux const Limit: uint = 4; const Area = Limit * Limit + 1; const Mask = (1u32 << 4) - 1; const Origin = Point { x: 0, y: 0 }; const Primes: int32[4] = [2, 3, 5, 7]; const Pair = (1, true); const Favourite = Color::Green; const Bytes = sizeof(Point) + alignof(Point); const OnWindows = #target.os == OperatingSystem::Windows; ``` A function call and a variable hold run-time values, so neither may appear: ```rux const FromCall = Seven(); const FromVariable = counter; ``` ```text error: call to 'Seven' is not a compile-time value help: declare 'FromCall' with 'let' to compute it at run time error: 'counter' is not a compile-time constant help: declare 'FromVariable' with 'let' to compute it at run time ``` The same is true of a parameter. When the value can only be known as the program runs, it belongs in a `let`. ## Constants as compile-time integers An integer constant is a compile-time integer wherever one is required. It can give an array its size and a repeated array literal its count, and another constant can be computed from it: ```rux const Limit: uint = 4; const Area = Limit * Limit + 1; func Main() -> int { const Local = Area * 2; var flags: bool[Limit] = [false; Limit]; let grid: int32[Local] = [0; Local]; PrintLine("{} {}", flags.length, grid.length); return 0; } ``` This prints `4 34`. A `let` holding the same number could not do this: its value exists only at run time. ## A constant cannot change A constant is not a variable, and nothing can assign to it. `MaxConnections = 5;` is `error: cannot modify constant 'MaxConnections'`. ## Constants are not patterns In a [`match`](https://rux-lang.dev/docs/lang/patterns/match) arm, a bare name always binds a new variable, so a constant's name cannot be used to compare against its value. Writing one is an error rather than a silent new binding: ```text error: pattern 'Limit' cannot bind a new variable because 'Limit' already names a constant help: a pattern compares with literal values; write the value of 'Limit', or bind the value and compare it with 'Limit' in a guard such as 'value if value >= Limit' ``` Compare in a [guard](https://rux-lang.dev/docs/lang/patterns/patterns#guards) instead: ```rux match n { v if v == Limit => PrintLine("at the limit"), else => PrintLine("elsewhere") } ``` ## const or let | | `const` | `let` | | ------------------------ | ------------------------ | ----------------------- | | Computed | by the compiler, once | each time the line runs | | Initializer | compile-time values only | any expression | | Module level | allowed | not allowed | | `extend` block | allowed, as `Type::Name` | not allowed | | Array size, repeat count | allowed | not allowed | ## See also - [Bindings](https://rux-lang.dev/docs/lang/bindings/overview) — `let` and `var` - [Compile-time evaluation](https://rux-lang.dev/docs/lang/comptime/overview) — `when` and the compile-time values - [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) — sizes and repeated literals - Learn: [Const](https://rux-lang.dev/docs/learn/const) # Initialization Every binding holds a value before anything reads it. A `let` gets its value where it is declared. A `var` may be declared without one, and what happens then depends on its type: ```text var-declaration = "var" Name ":" type ";" ``` | Declaration | When `T` … | The variable starts as | | --------------- | ----------------------------------- | ------------------------------------------------------ | | `var value: T;` | has an accessible constructor `T()` | the value `T()` returns | | `var value: T;` | has no such constructor | nothing — it must be assigned before it is read | | `let value: T;` | — | an error: `immutable variable requires an initializer` | There is no hidden default. A number declared without a value is not zero; it holds nothing until the program says what it is. ## A default constructor runs When the type has a [constructor](https://rux-lang.dev/docs/lang/structs/constructors) that takes no arguments, `var value: T;` calls it, and the variable is initialized from the start: ```rux struct Tally { count: int32; } extend Tally { func Tally() -> Tally { return Tally { count: 10 }; } } ``` ```rux var tally: Tally; tally.count += 1; PrintLine("{}", tally.count); ``` This prints `11`. The `+=` reads `count` before changing it, and that read is fine: `Tally()` has already run. ## Tracked storage Without such a constructor, the variable holds nothing, and the compiler tracks it. Every path from the declaration to a read must assign it first: ```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; } ``` Each branch assigns `grade`, so the `return` is accepted. Remove the final `else`, and a score below 50 reaches the `return` with nothing assigned: ```text error: value 'grade' may be uninitialized on some control-flow paths help: initialize or preserve 'grade' on every path before this use ``` A read with no assignment before it at all is reported directly: ```rux var total: int32; PrintLine("{}", total); ``` ```text error: variable 'total' is used before it is initialized help: assign a value to 'total' before this use ``` ## One part at a time A value of a plain type — one with nothing to destroy, such as a structure of numbers — may be filled in one part at a time: field by field, tuple element by tuple element. A part that has been written may be read at once. The whole value counts as initialized once every part has been written on every path that reaches the use: ```rux var p: Point; p.x = 1; p.y = 2; PrintLine("{} {}", p.x, p.y); ``` Before `p.y` is written, reading it — or using `p` whole — is refused, with a note that names what has been written: ```text error: variable 'p' is used before it is initialized note: only 'p.x' has been written, not 'p.y' ``` A fixed-size array of plain elements counts as initialized from its declaration, so a loop can fill it: ```rux var values: int32[3]; for i in 0..3 { values[i] = (i as int32) * 10; } ``` ## A value with a destructor is written whole A type that needs destroying — one with a [destructor](https://rux-lang.dev/docs/lang/ownership/destructors), or with a field that has one — cannot be built a part at a time. A part written into a variable that holds no value would belong to no whole value, and nothing would ever destroy it: ```rux var holder: Holder; holder.handle <- Handle { code: 1 }; ``` ```text error: cannot write field 'handle' of 'holder', which holds no value help: initialize 'holder' whole, as in 'holder = Holder { ... }' ``` Assign such a variable whole — `holder = Holder { … };` or `holder <- other;` — and from then on its parts can be replaced like any other place. Taking the address of a part, `@holder.handle`, is refused for the same reason. ## Taking the address initializes Taking the writable address of a whole uninitialized variable with `@` counts as initializing it, because the address exists to be filled through. The compiler trusts the function that receives it to write a complete value: ```rux func Fill(target: *var Point) { *target = Point { x: 3, y: 4 }; } ``` ```rux var q: Point; Fill(@q); PrintLine("{} {}", q.x, q.y); ``` This is how a variable is handed to a C function that fills it in — see [FFI](https://rux-lang.dev/docs/lang/ffi/overview). ## After a move A variable that has been [moved](https://rux-lang.dev/docs/lang/ownership/copy-and-move) from holds nothing again, as if it had just been declared. Reading it is an error — for a variable named `moved`, `error: value 'moved' is used after it was moved` — until it is assigned whole once more. ## See also - [Bindings](https://rux-lang.dev/docs/lang/bindings/overview) — `let`, `var` and their types - [Constructors](https://rux-lang.dev/docs/lang/structs/constructors) — the `T()` that `var value: T;` calls - [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors) — why some values are written whole - [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move) — what a move leaves behind - Learn: [Initialization](https://rux-lang.dev/docs/learn/initialization) # Destructuring A `let` or `var` may bind a [pattern](https://rux-lang.dev/docs/lang/patterns/patterns) instead of a single name. The pattern mirrors the shape of the value and takes it apart, declaring one new binding for each name it contains: ```rux let (quotient, remainder) = Divide(17, 5); let Point { x: px, y: py } = point; ``` ```text binding-pattern = Name | "_" | "(" binding-pattern { "," binding-pattern } ")" | TypeName "{" field-pattern { "," field-pattern } "}" field-pattern = Name [":" binding-pattern] ``` ## Irrefutable patterns only A `let` has no second branch to take when a pattern does not fit, so its pattern must match every value of its type. It is built only from names, `_`, tuple patterns and structure patterns, nested to any depth. A literal, a range, an enum or variant case, a typed pattern, `none` or a presence pattern could fail, and is refused: ```rux let (s, 1) = (1, 1); ``` ```text error: refutable pattern in 'let' binding note: a 'let' pattern must match every value, so each part is a name, '_', a tuple, or a structure help: test the value with 'match' instead ``` To test a value against a pattern that may not match, use [`match`](https://rux-lang.dev/docs/lang/patterns/match). ## Tuple patterns A tuple pattern has one element per member of the tuple, in order: ```rux let (quotient, remainder) = Divide(17, 5); let ((x1, y1), (x2, y2)) = ((0, 0), (6, 8)); ``` The pattern must have exactly as many elements as the tuple. `let (a, b) = (1, 2, 3);` is `error: tuple pattern has 2 elements but type '(int, int, int)' has 3`, and a tuple pattern over anything that is not a tuple, as in `let (x, y) = 5;`, is `error: cannot destructure non-tuple type 'int'`. ## Structure patterns A structure pattern names the type and lists fields by name, in any order. `field: pattern` binds the field through a pattern; `field` alone is shorthand for `field: field`, binding a variable with the field's own name: ```rux let point = Point { x: 3, y: 4 }; let Point { x: px, y: py } = point; let Point { y } = point; ``` A field the pattern does not mention is simply not bound, so there is no need to list every field. Structure and tuple patterns nest inside each other: ```rux let segment = Segment { start: Point { x: 1, y: 2 }, end: Point { x: 5, y: 6 } }; let Segment { start: Point { x: sx }, end } = segment; ``` This binds `sx` to `1` and `end` to the whole end point. The type in the pattern must be the value's type — `let Handle { code } = point;` is `error: struct pattern 'Handle' cannot match value of type 'Point'` — and every field must exist: `let Point { z } = point;` is `error: struct 'Point' has no field 'z'`. ## var patterns With `var`, every name the pattern declares is mutable: ```rux var (low, top) = (1, 9); low -= 1; top += 1; ``` There is no way to make some of the names mutable and others not. ## Annotations A type annotation after the pattern applies to the whole value: ```rux let (pair, count): ((int, int), int) = ((1, 2), 3); ``` A pattern cannot annotate one of its own parts: `let (a: int32, b) = …` is a typed pattern, and typed patterns are refutable. ## Discarding a value `_` matches anything and binds nothing. Inside a pattern it skips a part; on its own, `let _ = value;` evaluates a value and throws it away at once, like a temporary nobody kept: ```rux let (_, high) = Bounds(readings); let _ = Divide(1, 1); ``` `_` names nothing, so it cannot be read afterwards — `return _;` is `error: cannot read '_', because it discards the value it binds` — and several `let _` lines in one block do not clash. A [fallible](https://rux-lang.dev/docs/lang/errors/overview) result is the exception. Binding it to `_` would lose its failure without a word, so binding one to `_` is refused: ```text error: fallible result of type 'int ! ConfigError' is discarded note: binding a fallible to '_' does not handle its failure help: propagate it with '?', recover with 'catch', or match both '.Success' and '.Failure' ``` Handle the failure instead, as [Handling errors](https://rux-lang.dev/docs/lang/errors/handling) shows. ## Copying and moving patterns A pattern initialized with `=` copies the parts it binds and leaves the source as it was. One initialized with `<-` takes the value over: each bound part moves into its binding, and each part bound to `_` or left out of a structure pattern is destroyed on the spot. A type that declares its own [destructor](https://rux-lang.dev/docs/lang/ownership/destructors) is destroyed whole, so it cannot be split into parts: ```rux let File { handle: h } <- file; ``` ```text error: cannot split 'File' with a moving pattern, because it declares destructor '~File' note: '~File' 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 ``` See [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move) for what a move does to the source. ## Patterns only declare Destructuring always declares new names. It cannot assign to names that already exist, so a swap written as `(m, n) = (n, m);` is `error: operator '=' requires an assignable target, but its left operand has type '(int, int)'`. Assign the variables one at a time, through a temporary. Each name in a pattern is its own declaration, so `let (p, p) = (1, 2);` is `error: variable 'p' is already declared in this scope`. A `for` loop variable is a single name, never a pattern — see [Loops](https://rux-lang.dev/docs/lang/statements/loops#for). ## See also - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — every pattern form, refutable ones included - [match](https://rux-lang.dev/docs/lang/patterns/match) — testing a value against patterns that may fail - [Tuples](https://rux-lang.dev/docs/lang/tuples/overview) and [Structures](https://rux-lang.dev/docs/lang/structs/overview) — the values being taken apart - Learn: [Destructure](https://rux-lang.dev/docs/learn/destructure), [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern) # Expressions An *expression* computes a value: a literal, a name, a call, or operators applied to other expressions. This chapter covers the operators; the values they work on are described with their [types](https://rux-lang.dev/docs/lang/types/overview). ```rux let total = price * quantity + shipping; let ready = count > 0 && !paused; let label = n % 2 == 0 ? "even" : "odd"; ``` ## Operators | Kind | Operators | | --------------------------------------------------------------------- | ----------------------------------------- | | [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) | `+` `-` `*` `/` `%`, unary `-` | | [Comparison](https://rux-lang.dev/docs/lang/expressions/comparison) | `==` `!=` `<` `<=` `>` `>=` | | [Logical](https://rux-lang.dev/docs/lang/expressions/logical) | `&&` `||` `!` | | [Bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise) | `&` `|` `^` `~` | | [Shift](https://rux-lang.dev/docs/lang/expressions/shift) | `<<` `>>` `>>>` | | [Assignment](https://rux-lang.dev/docs/lang/expressions/assignment) | `=` `<-` `+=` `-=` … `>>>=`, `++` `--` | | [Conditional](https://rux-lang.dev/docs/lang/expressions/conditional) | `c ? a : b` | | [Casts](https://rux-lang.dev/docs/lang/expressions/casts) | `as` | | [Type tests](https://rux-lang.dev/docs/lang/sums/type-tests) | `is` | | [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) | `..` `..=` `...` | | [Coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing) | `??` | | [Propagation](https://rux-lang.dev/docs/lang/errors/propagation) | postfix `?`, `? else (e => …)` | | [Recovery](https://rux-lang.dev/docs/lang/errors/handling) | postfix `catch { … }` | | [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) | `@` (address of), unary `*` (dereference) | | [Moves](https://rux-lang.dev/docs/lang/ownership/copy-and-move) | unary `<-` | There is no unary `+` and no power operator; [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic#no-power-operator) explains what `**` means instead. ## Precedence When an expression mixes operators, *precedence* decides how it groups: an operator on a higher level takes its operands before one on a lower level. Parentheses override precedence everywhere. Rux has 17 levels: | Level | Operators | Associativity | | ----- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------- | | 17 | postfix: `.field` `.0` `.Method()` `::Name` `Type { … }` `f()` `f()` `a[i]` `x?` `x? else (e => …)` `x catch { … }` `x++` `x--` | left to right | | 16 | prefix: `<-` `!` `-` `~` `*` `@` `++` `--` | right to left | | 15 | `as` `is` | left to right | | 14 | `*` `/` `%` | left to right | | 13 | `+` `-` | left to right | | 12 | `<<` `>>` `>>>` | left to right | | 11 | `<` `<=` `>` `>=` | left to right | | 10 | `==` `!=` | left to right | | 9 | `&` | left to right | | 8 | `^` | left to right | | 7 | `|` | left to right | | 6 | `&&` | left to right | | 5 | `||` | left to right | | 4 | `??` | right to left | | 3 | `c ? a : b` | right to left | | 2 | `a..b` `a..=b` `a...b` `..b` `..=b` `...b` `a..` `..` | does not chain | | 1 | `=` `<-` `+=` `-=` `*=` `/=` `%=` `&=` `|=` `^=` `<<=` `>>=` `>>>=` | right to left | Operators on one level group from the left where the table says so: `100 / 10 / 5` is `(100 / 10) / 5`, which is 2. `??` and `? :` group from the right, so `a ?? b ?? c` is `a ?? (b ?? c)`. A range does not chain: `0..1..2` does not parse. A few entries need a word: - **Postfix `?` and the conditional `?`.** A `?` written directly after an expression, with no space before it, is postfix [propagation](https://rux-lang.dev/docs/lang/errors/propagation). A `?` with whitespace before it opens a [conditional](https://rux-lang.dev/docs/lang/expressions/conditional). - **Casts and tests take a type.** The right side of `as` and `is` is a type, not an expression. It ends before `..`, `|` and `!`, so a sum or fallible target is grouped: `value is (A | B)`. - **The coalescing fallback may leave.** The right side of `??` may be `fail`, `return`, `break` or `continue` instead of a value — see [Coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing). - **Prefix `&` does not exist.** Taking an address is `@`; `&x` is `error: '&' does not take an address; write '@' to take the address of a value`. ## Consequences Most of the order matches ordinary arithmetic and needs no thought: `2 + 3 * 4` is 14, and `age >= 18 && age < 65` compares before it combines. A few places are worth knowing by heart. **Bitwise operators bind more loosely than comparisons.** `x & 1 == 0` is `x & (1 == 0)`, an integer and a `bool`: ```text error: operator '&' cannot combine left operand 'int32' with right operand 'bool8' ``` Write `(x & 1) == 0`. **`as` binds more tightly than arithmetic.** It converts only the operand next to it, so `a as float64 / b as float64` converts both operands before dividing, and `(a / b) as float64` divides integers first. A prefix operator binds more tightly still: `-x as int64` is `(-x) as int64`. **Shifts bind more loosely than `+` and `-`.** `1 << 2 + 1` is `1 << 3`, which is 8. **`==` binds more tightly than `??`.** `value ?? 0 == 0` is `value ?? (0 == 0)`. Write `(value ?? 0) == 0`. **The conditional binds more loosely than almost everything.** `price - member ? 5 : 0` is `(price - member) ? 5 : 0`. A conditional inside a larger expression is wrapped: `price - (member ? 5 : 0)`. ```mermaid flowchart TD e["flags & mask == 0"] --> g["flags & (mask == 0)"] g --> x["error: int & bool"] f["(flags & mask) == 0"] --> ok["a bool, as intended"] ``` ## Evaluation order Operands are evaluated from left to right, and so are the arguments of a call: in `Note(1) + Note(2) * Note(3)`, the three calls run in the order they are written, even though the multiplication happens first. `&&`, `||`, `??` and the conditional evaluate their right side only when it is needed — see [Logical](https://rux-lang.dev/docs/lang/expressions/logical#short-circuit-evaluation) and [Conditional](https://rux-lang.dev/docs/lang/expressions/conditional). ## Operand types Rux never changes a value's type behind your back. The operands of a binary operator must agree on a type, by these rules: | Operands | Result | | ------------------------------------------------ | ------------------------------------------- | | two values of one type | that type | | an unsuffixed integer literal and an integer | the integer's type; the literal must fit it | | two integers of one signedness, different widths | the wider type | | two floating-point values of different widths | the wider type | | a signed and an unsigned integer | an error: convert one with `as` | | an integer and a floating-point value | an error: convert one with `as` | ```rux let small: int32 = 1; let big: int64 = 5000000000; let sum = small + big; let doubled = 2 * small; ``` `sum` is an `int64` and `doubled` an `int32`. Mixing signedness is refused with a note saying why: ```text error: operator '+' cannot combine left operand 'uint64' with right operand 'int64' note: the operands differ in signedness, so neither converts to the other's type help: convert one operand with 'as' to the type the operation should use ``` A literal that does not fit the other operand's type is an error rather than a silent wrap: with `count: uint64`, `count == -1` is `error: integer literal is out of range for type 'uint64'`. ## Statements and values An assignment is an expression in the grammar but produces no value, so it cannot be used inside another expression — see [Assignment](https://rux-lang.dev/docs/lang/expressions/assignment). An expression followed by `;` is an [expression statement](https://rux-lang.dev/docs/lang/statements/overview). ## See also - [Operators and punctuation](https://rux-lang.dev/docs/lang/lexical/operators) — how the operator tokens are spelt - [Statements](https://rux-lang.dev/docs/lang/statements/overview) — where expressions are used - [Operator interfaces](https://rux-lang.dev/docs/lang/interfaces/operators) — giving your own types operators - Learn: [Precedence](https://rux-lang.dev/docs/learn/precedence), [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic) # Arithmetic The arithmetic operators compute with integers and floating-point numbers. | Operator | Operation | Example | Level | | -------- | ----------------- | ------- | ----- | | `-` | negation (prefix) | `-a` | 16 | | `*` | multiplication | `a * b` | 14 | | `/` | division | `a / b` | 14 | | `%` | remainder | `a % b` | 14 | | `+` | addition | `a + b` | 13 | | `-` | subtraction | `a - b` | 13 | ```rux let a: int32 = 17; let b: int32 = 5; PrintLine("{} {} {} {} {} {}", a + b, a - b, a * b, a / b, a % b, -a); ``` This prints `22 12 85 3 2 -17`. Multiplication, division and remainder bind more tightly than addition and subtraction, and each level groups from the left — see [Precedence](https://rux-lang.dev/docs/lang/expressions/overview#precedence). ## Operand types Both operands must agree on a type, following the [operand rules](https://rux-lang.dev/docs/lang/expressions/overview#operand-types): one type, a literal that takes the other operand's type, or two integers of one signedness, where the narrower widens. The result has that type. An unsuffixed integer literal adapts only to another integer. Beside a floating-point value, write a floating-point literal: ```rux let half = 1.5 + 2; ``` ```text error: operator '+' cannot combine left operand 'float64' with right operand 'int' ``` Write `1.5 + 2.0`. A `bool` is not a number, and `flag + flag` is `error: operator '+' cannot combine left operand 'bool8' with right operand 'bool8'`. To compute with a character's code, convert it with [`as`](https://rux-lang.dev/docs/lang/expressions/casts) first, as in `(c as int32) - ('0' as int32)`. ## Integer overflow wraps Integer `+`, `-`, `*` and unary `-` wrap: a result that does not fit the type keeps its low bits, the two's-complement result, in every build profile. ```rux var top: int8 = int8::Max; top += 1; let small: uint8 = 0; let m: uint8 = 200; PrintLine("{} {} {}", top, small - 1, m * 2); ``` This prints `-128 255 144`. Negating an unsigned value wraps the same way. When overflow must be noticed instead, Core's `AddChecked`, `SubChecked` and `MulChecked` report it — see the Learn lessons [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) and [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic). A literal is not allowed to wrap on its way in: `let narrow: uint8 = 0; narrow + 300` is `error: integer literal is out of range for type 'uint8'`. ## Integer division Integer `/` truncates towards zero, and `%` gives the remainder that goes with it, so it has the sign of the dividend: | Expression | Value | | ---------- | ----- | | `7 / 2` | `3` | | `-7 / 2` | `-3` | | `7 % 2` | `1` | | `-7 % 2` | `-1` | | `7 % -2` | `1` | Dividing by zero is never allowed to produce a value. Integer `/` and `%`, and the compound `/=` and `%=`, stop the program when the divisor is zero, on every target and in every build profile: ```rux func Divide(a: int32, b: int32) -> int32 { return a / b; } ``` ```text Panic: division by zero at Divide (Src/Main.rux:4:14) ``` The same happens to the one signed division whose quotient does not fit its type — the type's minimum divided by `-1` — which stops with `Panic: division overflow`. A divisor written as a non-zero literal needs no check, and a release build drops a check it can prove will pass. The panic is the same kind a call to `Panic` produces — see [Panics](https://rux-lang.dev/docs/lang/errors/panics). ## Floating-point arithmetic `float32` and `float64` arithmetic follows IEEE 754. Division by zero does not stop the program; it gives an infinity, or NaN for `0.0 / 0.0`: ```rux PrintLine("{} {}", 1.0 / 0.0, 0.0 / 0.0); PrintLine("{} {}", 7.5 % 2.0, -7.5 / 2.0); ``` This prints `Inf NaN`, then `1.5 -3.75`. `%` on floating-point values is the remainder of truncated division, with the sign of the dividend. See [Floating-point types](https://rux-lang.dev/docs/lang/types/floating-point) for the special values and their comparisons. ## No power operator Rux has no exponentiation operator. `a ** b` is two operators, multiplication and a [dereference](https://rux-lang.dev/docs/lang/pointers/overview), `a * (*b)`, so with integers it fails: ```text error: operator '*' requires a pointer operand, but found 'int' ``` Use [`Pow`](https://rux-lang.dev/docs/api/math/pow) from the Math package for floating-point powers, or a loop for small integer ones. There is no unary `+` either: `+5` does not parse. ## Compound assignment Each binary arithmetic operator has a compound form, `+=` `-=` `*=` `/=` `%=`, that updates a mutable place, and `++` and `--` add or subtract one — see [Assignment](https://rux-lang.dev/docs/lang/expressions/assignment). ## See also - [Integers](https://rux-lang.dev/docs/lang/types/integers) and [Floating-point types](https://rux-lang.dev/docs/lang/types/floating-point) — the types and their limits - [Casts](https://rux-lang.dev/docs/lang/expressions/casts) — converting between numeric types - [Comparison](https://rux-lang.dev/docs/lang/expressions/comparison) — relating the results - Learn: [Arithmetic](https://rux-lang.dev/docs/learn/arithmetic), [Wrapping arithmetic](https://rux-lang.dev/docs/learn/wrapping-arithmetic) # Comparison A comparison tests two values and produces a `bool`. | Operator | Test | Level | | -------- | --------------------- | ----- | | `<` | less than | 11 | | `<=` | less than or equal | 11 | | `>` | greater than | 11 | | `>=` | greater than or equal | 11 | | `==` | equal | 10 | | `!=` | not equal | 10 | ```rux let a = 10; let b = 20; PrintLine("{} {} {} {}", a == b, a != b, a < b, a >= b); ``` This prints `false true true false`. The ordering operators bind more tightly than `==` and `!=`, and all of them bind more tightly than `&&` and `||`, so `offset + length <= capacity && capacity != 0` needs no parentheses. ## Operand types Numbers compare under the same [operand rules](https://rux-lang.dev/docs/lang/expressions/overview#operand-types) as arithmetic: one type, a literal that takes the other operand's type, or two integers of one signedness, where the narrower widens. A signed and an unsigned integer have no common type: ```text error: operator '<' cannot compare left operand 'uint64' with right operand 'int64' note: the operands differ in signedness, so neither converts to the other's type help: convert one operand with 'as' to the type the operation should use ``` A literal must fit the other operand's type, so a test that could never be true is caught: with `count: uint64`, `count == -1` is `error: integer literal is out of range for type 'uint64'`. ## What can be compared | Type | `==` `!=` | `<` `<=` `>` `>=` | | --------------------------------- | --------------------------------------------------- | ------------------------------- | | integers, floating-point numbers | yes | yes | | characters | yes | yes, by code | | `bool` | yes | yes, `false` before `true` | | enums | yes | yes, by the members' values | | pointers | yes, and with `null` | yes, by address | | structures | yes, field by field, when every field supports `==` | only through declared operators | | tuples | yes, element by element | no | | fixed-size arrays | yes, element by element | no | | variants | yes, case and payload | no | | optionals, fallibles and sums | yes, between values of one type | no | | slices, including string literals | no | no | A structure, tuple or variant compares structurally only when every part can be compared. A structure gets ordering only from the operators it declares: `!=` is derived from a declared `==`, `>` from `<`, and `<=` and `>=` from `<` and `==` together — see [Operator interfaces](https://rux-lang.dev/docs/lang/interfaces/operators). Without them: ```text error: operator '<' is not defined for 'Point' note: a struct is compared through the operators it declares, never by its representation help: declare '<' on 'Point' ``` An optional compares with `none` and with a present value directly: `found == none`, `found == 5`. See [Optionals](https://rux-lang.dev/docs/lang/optionals/overview). ::note **Arrays and array literals.**:br rux 0.4.0 does not yet compare a fixed-size array with an array literal correctly: `values == [1, 2]` is `false` even when the elements are equal. Bind the literal to a name first. :: ### Slices A [slice](https://rux-lang.dev/docs/lang/slices/overview) is a view of elements stored elsewhere. Comparing two views would compare the addresses they hold rather than the elements, so Rux refuses it — and a string literal is a `char8[..]` slice: ```rux let same = name == "Rux"; ``` ```text error: operator '==' is not defined for slice type 'char8[..]' note: a slice is a view, so comparing the views would compare addresses rather than elements ``` Compare the elements one at a time, or use a type that declares `==`, such as the Text package's `String`. ## Floating-point comparisons Floating-point comparisons follow IEEE 754. NaN is unordered: it is not equal to anything, itself included, and every ordering test with it is `false`: ```rux let n = float64::NaN; PrintLine("{} {} {}", n == n, n != n, n < 1.0); ``` This prints `false true false`. See [Floating-point types](https://rux-lang.dev/docs/lang/types/floating-point) for testing for NaN and for comparing computed values with a tolerance. ## Comparisons do not chain `a < b < c` is not a range test. It groups as `(a < b) < c`, which compares a `bool` with a number: ```rux let inside = 1 < 2 < 3; ``` ```text error: operator '<' cannot compare left operand 'bool8' with right operand 'int' ``` Combine two comparisons with [`&&`](https://rux-lang.dev/docs/lang/expressions/logical): `low <= value && value <= high`. ## See also - [Logical](https://rux-lang.dev/docs/lang/expressions/logical) — combining comparisons - [Casts](https://rux-lang.dev/docs/lang/expressions/casts) — matching operand types before comparing - [Operator interfaces](https://rux-lang.dev/docs/lang/interfaces/operators) — `==` and `<` for your own types - Learn: [Comparison](https://rux-lang.dev/docs/learn/comparison), [Equatable](https://rux-lang.dev/docs/learn/equatable), [Comparable](https://rux-lang.dev/docs/learn/comparable) # Logical The logical operators combine and invert `bool` values. | Operator | Operation | Result | Level | | -------- | --------- | ------------------------------------ | ----- | | `!` | not | `true` when the operand is `false` | 16 | | `&&` | and | `true` when both operands are `true` | 6 | | `||` | or | `true` when either operand is `true` | 5 | ```rux let isReady = true; let hasWork = false; PrintLine("{} {} {}", isReady && hasWork, isReady || hasWork, !isReady); ``` This prints `false true false`. ## bool operands only Every operand must be a `bool`. A number, a pointer or an optional is never treated as true or false; write the comparison you mean: ```rux let count: int32 = 3; let zero = !count; let both = count && true; ``` ```text error: operator '!' requires a bool operand, but found 'int32' error: operator '&&' requires a bool left operand, but found 'int32' ``` Write `count == 0` and `count != 0 && true`. The same rule applies to the conditions of `if`, `while` and the conditional operator. ## Short-circuit evaluation `&&` and `||` evaluate their left operand first and evaluate the right one only when it can still change the answer: | Expression | The right side runs when | | --------------- | ------------------------ | | `left && right` | `left` is `true` | | `left || right` | `left` is `false` | That makes it safe to guard a test with another one: ```rux if index < values.length && values[index] == target { PrintLine("found"); } ``` `values[index]` is read only after the bounds test has passed. A call on the right side is skipped the same way: ```rux let a = Check("left", false) && Check("right", true); ``` Only `left` is checked; `Check("right", true)` never runs. ## Precedence `!` is a prefix operator, so it applies only to the operand right after it: `!ready && busy` is `(!ready) && busy`, and `!count == 0` is `(!count) == 0`. Negate a larger expression by wrapping it, `!(ready && busy)`. `&&` binds more tightly than `||`, the way `*` binds more tightly than `+`: ```rux PrintLine("{}", true || false && false); ``` This is `true || (false && false)`, which prints `true`. Both bind more loosely than every comparison, so comparisons combine without parentheses. ## Non-short-circuiting forms The [bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise) operators `&`, `|`, `^` and `~` also accept `bool` operands. `&` and `|` compute the same answer as `&&` and `||`, but always evaluate both sides, and `^` is exclusive or: ```rux let c = Check("left", false) & Check("right", true); PrintLine("{}", true ^ true); ``` Both checks run here. Use `&&` and `||` for conditions, and `&` or `|` only when the right side must run for its effect. ## See also - [Comparison](https://rux-lang.dev/docs/lang/expressions/comparison) — producing the `bool` operands - [Booleans](https://rux-lang.dev/docs/lang/types/booleans) — the `bool` types - [if](https://rux-lang.dev/docs/lang/statements/if) — conditions in statements - Learn: [Logical](https://rux-lang.dev/docs/learn/logical) # Bitwise The bitwise operators work on the individual bits of their operands. | Operator | Operation | Compound form | Level | | -------- | ------------ | ------------- | ----- | | `~` | not (prefix) | — | 16 | | `&` | and | `&=` | 9 | | `^` | exclusive or | `^=` | 8 | | `|` | or | `|=` | 7 | ```rux let a: uint8 = 0b00001100; let b: uint8 = 0b00001010; PrintLine("{} {} {} {}", a & b, a | b, a ^ b, ~a); ``` This prints `8 14 6 243`: the bits set in both, in either, in exactly one, and every bit of `a` inverted. ## Operand types | Operand | `&` `|` `^` | `~` | | ---------- | ---------------------------------------------- | ----------------- | | integers | bit by bit | inverts every bit | | `bool` | logical and, or, exclusive or — both sides run | logical not | | characters | bit by bit on the code | — | The two operands follow the [operand rules](https://rux-lang.dev/docs/lang/expressions/overview#operand-types): one type, a literal that takes the other operand's type, or two integers of one signedness, where the narrower widens. A floating-point operand is refused: ```text error: operator '&' requires an integer, bool, or character left operand, but found 'float64' error: operator '~' requires an integer or bool operand, but found 'float64' ``` Unsigned integers are the natural home for bit work, since none of their bits is a sign. On `bool`, `&` and `|` are the non-short-circuiting forms of `&&` and `||` — see [Logical](https://rux-lang.dev/docs/lang/expressions/logical#non-short-circuiting-forms). ## Precedence The binary bitwise operators bind **more loosely than the comparisons**, and among themselves `&` binds most tightly, then `^`, then `|`. A test of a masked value therefore needs parentheses: ```rux let x: int32 = 6; let even = x & 1 == 0; ``` This groups as `x & (1 == 0)`: ```text error: operator '&' cannot combine left operand 'int32' with right operand 'bool8' ``` Write `(x & 1) == 0`. Shifts and arithmetic bind more tightly than all three, so `flags & 1 << 2` is `flags & (1 << 2)`; parentheses still make it easier to read. ## Masks and flags With a [shift](https://rux-lang.dev/docs/lang/expressions/shift) to build the mask, the four single-bit operations are: ```rux var flags: uint32 = 0; flags |= 1 << 3; flags &= ~(1u32 << 3); flags ^= 1 << 2; let isSet = (flags & (1 << 2)) != 0; ``` `|=` sets bit 3, `&=` with the inverted mask clears it, `^=` toggles bit 2, and the last line tests it. The `1u32` in the clearing mask gives `~` a `uint32` to invert. With an unsuffixed `1`, `~(1 << 3)` is an `int`, and `flags &= ~(1 << 3)` is `error: operator '&=' cannot combine left operand 'uint32' with right operand 'int'`. ## Compound forms `&=`, `|=` and `^=` update a mutable place: `flags |= mask` is `flags = flags | mask`. The target must be a `var` or another writable place — see [Assignment](https://rux-lang.dev/docs/lang/expressions/assignment). ## See also - [Shift](https://rux-lang.dev/docs/lang/expressions/shift) — moving bits, and building masks - [Logical](https://rux-lang.dev/docs/lang/expressions/logical) — the short-circuiting `&&` and `||` - [Integers](https://rux-lang.dev/docs/lang/types/integers) — widths and signedness - Learn: [Bitwise](https://rux-lang.dev/docs/learn/bitwise), [Bit operation](https://rux-lang.dev/docs/learn/bit-operation) # Shift A shift moves every bit of an integer left or right by a number of places. Bits that move past the end are dropped, and new bits come in at the other end. | Operator | Operation | Compound form | Level | | -------- | ------------------------------------------------------ | ------------- | ----- | | `<<` | left shift: zeros come in at the bottom | `<<=` | 12 | | `>>` | right shift: arithmetic on signed, logical on unsigned | `>>=` | 12 | | `>>>` | logical right shift: zeros come in at the top | `>>>=` | 12 | ```rux let value: uint8 = 0b00010100; PrintLine("{} {}", value << 2, value >> 2); ``` This prints `80 5`. ## Operand types The left operand is an integer (or a character), and the result has its type: a shift never changes the width of the value being shifted. The right operand, the *count*, is any integer type. It keeps its own type and is not converted to the left operand's: ```rux let one: uint32 = 1; let count: uint8 = 5; PrintLine("{}", one << count); ``` This prints `32`, a `uint32`. A floating-point operand on either side is refused, with `error: operator '<<' requires an integer or character left operand, but found 'float64'` or `error: operator '<<' requires an integer right operand, but found 'float64'`. A left shift keeps only the bits that still fit. A `uint8` holding 200 shifted left by one is `144`, not 400: the top bit fell off. Shift a wider type when the result needs the room. ## Right shifts Shifting right empties the top bits, and there are two ways to fill them: | Left operand | `>>` fills with | `>>>` fills with | | ------------ | ----------------------------------- | ---------------- | | signed | copies of the sign bit (arithmetic) | zeros (logical) | | unsigned | zeros (logical) | — not allowed | ```rux let negative: int8 = -8; PrintLine("{} {}", negative >> 2, negative >>> 2); ``` This prints `-2 62`. The arithmetic shift keeps the sign and divides by four, rounding towards minus infinity — so `-7 >> 1` is `-4`, where `-7 / 2` is `-3`. The logical shift treats the value as plain bits. An unsigned `>>` is already logical, so `>>>` exists only for signed operands: ```text error: operator '>>>' requires a signed integer left operand, but found 'uint8' ``` ## The shift count A count must be at least zero and less than the bit width of the left operand: `0` to `31` for an `int32`, `0` to `7` for a `uint8`. Shifting by the full width or more does not produce a meaningful value; to clear a value, assign zero. ::note **Counts at or past the width.**:br rux 0.4.0 does not yet check this. A shift by the width or more compiles, and what it produces depends on the operand's width, so keep every count below the width yourself. :: ## Precedence Shifts bind more loosely than `+` and `-` and more tightly than the comparisons and the bitwise operators: ```rux PrintLine("{}", 1 << 2 + 1); ``` This is `1 << (2 + 1)`, which prints `8`. Parenthesise a shift that builds a mask, as in `flags & (1 << n)`, even where precedence would already group it that way. ## Compound forms `<<=`, `>>=` and `>>>=` update a mutable place. Like `>>>`, `>>>=` needs a signed target: ```rux var bits: int32 = -16; bits >>>= 1; bits <<= 2; ``` ## Packing fields Shifts and [masks](https://rux-lang.dev/docs/lang/expressions/bitwise) together store several small fields in one integer. Each field is shifted into place and combined with `|`, and read back by shifting it down and masking off the rest: ```rux let red: uint32 = 0xFF; let green: uint32 = 0x80; let blue: uint32 = 0x20; let color = (red << 16) | (green << 8) | blue; let g = (color >> 8) & 0xFF; ``` ## See also - [Bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise) — combining shifted masks - [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) — division and multiplication by powers of two - [Integers](https://rux-lang.dev/docs/lang/types/integers) — widths and signedness - Learn: [Shift](https://rux-lang.dev/docs/learn/shift) # Assignment An assignment stores a new value in a place that already exists — a `var`, a field, an element, or the target of a writable pointer. | Operator | Meaning | | ---------------------------------------- | ----------------------------------------------------------------------- | | `place = value` | copy `value` into `place` | | `place <- value` | move `value` into `place` | | `place += value`, `-=`, `*=`, `/=`, `%=` | `place = place + value`, and so on | | `place &= value`, `|=`, `^=` | the [bitwise](https://rux-lang.dev/docs/lang/expressions/bitwise) forms | | `place <<= value`, `>>=`, `>>>=` | the [shift](https://rux-lang.dev/docs/lang/expressions/shift) forms | | `place++`, `++place` | add one | | `place--`, `--place` | subtract one | ```rux var n: int32 = 10; n += 5; n -= 3; n *= 4; n /= 5; n %= 5; ``` Each line starts from the result of the one before: 15, 12, 48, 9, 4. ## The target The left side must be an assignable place: a variable, a field (`p.x`), a tuple element (`pair.0`), an indexed element (`values[i]`), or a dereferenced pointer (`*ptr`). And it must be mutable — a `var`, or something reached through one, a `&var` reference or a `*var` pointer: ```rux var p = Point { x: 1, y: 2 }; p.x += 10; p.y++; var values: int32[3] = [1, 2, 3]; values[1] *= 7; ``` | Target | Error | | --------------------------- | --------------------------------------------------------------------------------- | | a `let` | `cannot modify immutable variable 'fixed'` | | a constant | `cannot modify constant 'Limit'` | | a value that is not a place | `operator '=' requires an assignable target, but its left operand has type 'int'` | | through a read-only pointer | `cannot modify data through read-only pointer '*int'` | The error for a `let` comes with `help: declare 'fixed' with 'var' to make it mutable`. ## Type rules The value must have the place's type, or widen to it without loss — an integer of the same signedness and a smaller width, as in a [binding](https://rux-lang.dev/docs/lang/bindings/overview#types-and-inference). A compound assignment follows the rules of its operator, and its result must still fit the place: ```rux var total: int32 = 0; let big: int64 = 5; total += big; ``` ```text error: operator '+=' produces 'int64', which cannot be stored in target type 'int32' ``` `total += big as int32` converts first. Mixing kinds is refused just as the plain operator would refuse it: on an `int32`, `score += 1.5` is `error: operator '+=' cannot combine left operand 'int32' with right operand 'float64'`, and on a `uint8`, `u += 300` is `error: integer literal is out of range for type 'uint8'`. ## An assignment produces no value An assignment is a complete action, not a value. The grammar reads `a = b = c` from the right, as `a = (b = c)`, but `b = c` has nothing to give `a`: ```rux first = second = 7; ``` ```text error: an assignment produces no value and cannot be chained help: assign each target in its own statement ``` For the same reason `=` cannot stand in for `==` in a condition: `if a = 5 { … }` is refused, because the condition is not a `bool`. ## ++ and -- `++` adds one and `--` subtracts one. They apply to any integer or floating-point place, and need it to be mutable like any other assignment: on a `let`, `k++` is `error: cannot modify immutable variable 'k'`. On a line of its own, the prefix and postfix forms are the same. Used as a value, they differ in what they hand back: | Form | Value of the expression | Afterwards | | --------- | ----------------------- | ---------- | | `count++` | the old value | one higher | | `++count` | the new value | one higher | | `count--` | the old value | one lower | | `--count` | the new value | one lower | ```rux var count: int32 = 1; let before = count++; let after = ++count; PrintLine("{} {} {}", before, after, count); ``` This prints `1 3 3`. Code that leans on the difference is easy to misread; most Rux code keeps `++` and `--` on lines of their own. Rux has no unary `+`, so `a =+ 3` does not parse; and `a =- 3` is `a = -3`, a valid assignment of minus three. The operator always comes before the `=`. ## Moving with <- `place <- value` moves instead of copying: the value is handed over and its source may not be read again. The place's old value is destroyed as it is replaced, just as with `=`: ```rux let first = Buffer { size: 4 }; var second = Buffer { size: 0 }; second <- first; ``` Reading `first` afterwards is `error: value 'first' is used after it was moved`. A type that cannot be copied is assigned from a named source only with `<-`. When copy and move apply, and what replacing a value destroys, are covered in [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## See also - [Bindings](https://rux-lang.dev/docs/lang/bindings/overview) — declaring the places that are assigned to - [Initialization](https://rux-lang.dev/docs/lang/bindings/initialization) — the first assignment of a `var` declared without a value - [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic) — the operators inside the compound forms - [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move) — `=` and `<-` - Learn: [Assignment](https://rux-lang.dev/docs/learn/assignment) # Conditional The conditional operator chooses one of two values by a condition. It is the only operator with three operands, and is often called the *ternary* operator. ```text conditional = condition "?" then-value ":" else-value then-value = an expression above level 3, or a parenthesised conditional else-value = an expression above level 3, or another conditional ``` ```rux let label = n % 2 == 0 ? "even" : "odd"; ``` When the condition is `true` the result is the first value, otherwise the second. Only the chosen value is evaluated; the other is skipped, just as an untaken `if` block is. ## The space before ? A `?` written directly after an expression is postfix [propagation](https://rux-lang.dev/docs/lang/errors/propagation): `Read()?` passes a failure on. The conditional's `?` is told apart by the whitespace in front of it, so it must not touch the condition: ```rux let a = n > 0 ? 1 : 2; let b = n > 0? 1 : 2; ``` The first line is a conditional. In the second, `0?` is a propagation of `0`, and the line fails to parse with `error: expected ';' after the binding declaration before '1'`. The space after the `?` does not matter, but write one for symmetry. ## The condition The condition must be a `bool`. A number is not true or false by itself: ```text error: condition for '?:' must have type 'bool', but found 'int32' ``` Write the comparison: `n % 2 != 0 ? "odd" : "even"`. ## Branch types The conditional produces one value, so its two branches must have one type. The second converts to the first or the first to the second, as two `match` arms do; two unrelated types are an error: ```rux let answer = ready ? "yes" : 7; ``` ```text error: conditional branch type mismatch: expected 'char8[..]', found 'int' help: make both branches produce the same type ``` Where the context fixes the type — an annotated binding, an assignment, a `return`, a call argument — both branches take it, and an unsuffixed literal in either branch must fit it: ```rux let wide: uint128 = n > 2 ? 18446744073709551616 : 0; ``` A branch that never produces a value, such as a call to `Panic`, does not take part: ```rux func Checked(n: int32) -> int32 { return n >= 0 ? n : Panic("negative"); } ``` `return`, `fail`, `break` and `continue` cannot be a branch — `error: 'return' cannot be used as a value here`. Use an `if`, or the fallback of [`??`](https://rux-lang.dev/docs/lang/optionals/coalescing), which accepts them. ## Nesting The conditional groups from the right, so a chain in the else branch reads as a list of cases tested in order: ```rux let sign = n < 0 ? "negative" : n == 0 ? "zero" : "positive"; ``` A conditional in the **then** branch must be in parentheses. The then branch ends at the first `:`, and an unparenthesised `?` inside it stops the parse with `error: expected ':' between the conditional expression branches before '?'`: ```rux let size = n > 0 ? (n > 5 ? "big" : "small") : "none"; ``` Past two or three cases, an `if`/`else if` chain or a [`match`](https://rux-lang.dev/docs/lang/patterns/match) expression reads better. ## Precedence The conditional is on [level 3](https://rux-lang.dev/docs/lang/expressions/overview#precedence), below every operator but ranges and assignment. Its condition takes in everything before the `?`, including `||` and `??`: ```rux let total = price - (member ? 5 : 0); ``` Without the parentheses, `price - member ? 5 : 0` would subtract a `bool` from `price` and use the result as the condition. Wrap a conditional whenever it sits inside a larger expression. ## See also - [if](https://rux-lang.dev/docs/lang/statements/if) — choosing between statements rather than values - [match](https://rux-lang.dev/docs/lang/patterns/match) — choosing among many values - [Propagation](https://rux-lang.dev/docs/lang/errors/propagation) — the postfix `?` - Learn: [Ternary](https://rux-lang.dev/docs/learn/ternary) # Casts Rux converts a value to another type only when asked. The `as` operator is the request: ```text cast = operand "as" type ``` ```rux let ratio = count as float64 / total as float64; ``` The right side is a type, not an expression. It is a *postfix* type — a name with any `?`, `[]`, `[N]` or `[..]` suffixes — so a sum or fallible target must be grouped: `value as (T ! E)`. Writing `f as int32 ! bool` is `error: a fallible type after 'as' must be grouped`. `as` is on [level 15](https://rux-lang.dev/docs/lang/expressions/overview#precedence), above every binary operator and below the prefix ones. It converts only the operand next to it: `a / b as float64` converts `b` alone, while `-x as int64` is `(-x) as int64`. ## What converts | From | To | Result | | ------------------------- | ------------------------------ | ------------------------------------------------------------------- | | an integer | an integer | the low bits, reinterpreted in the new type | | an integer | a floating-point type | the nearest representable value | | a floating-point type | an integer | truncated towards zero, saturating at the type's limits; NaN is `0` | | a floating-point type | a floating-point type | the nearest representable value | | a number | `bool` | `true` when the value is not zero | | `bool` | a number | `1` or `0` | | `bool` | another `bool` width | the same truth value | | a character or an integer | a character or an integer | the code, as a number, or the number, as a code | | an enum | an integer | the member's value | | an integer | an enum | the member with that value — not checked | | a pointer | a pointer, integer or function | see [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) | ```rux let big: int32 = 300; PrintLine("{} {} {}", big as uint8, -1i32 as uint32, 200u8 as int8); ``` This prints `44 4294967295 -56`. An integer conversion never saturates and never fails: it keeps the bits that fit. ## Floating-point to integer A float converts to an integer by truncating towards zero while the result fits. A value beyond the range saturates — one above `T::Max`, positive infinity included, becomes `T::Max`, and one below `T::Min` becomes `T::Min` — and NaN becomes `0`. The rule is the same for every width, on every target, and whether the compiler folds the cast or it runs: ```rux PrintLine("{} {} {} {}", 3.99 as int32, -3.99 as int32, 1e20 as int32, -1e20 as int32); PrintLine("{} {}", -5.0 as uint8, float64::NaN as int32); ``` This prints `3 -3 2147483647 -2147483648`, then `0 0`: a negative value converts to `0` for an unsigned type. ## bool Any number converts to `bool` by testing it against zero, and a `bool` converts to `1` or `0`: ```rux PrintLine("{} {} {}", 5 as bool, 0 as bool, true as int32); ``` This prints `true false 1`. Where the intent is a test, a comparison says it more plainly: `n != 0`. ## Characters A character converts to its code, and an integer to the character with that code: ```rux PrintLine("{} {} {}", 'A' as int32, 66 as char32, 'λ' as uint32); ``` This prints `65 B 955`. Between character widths, the code is kept. A constant is checked against the target's range: a code that cannot exist is refused rather than wrapped. | Cast | Error | | -------------------- | ------------------------------------------------------------------------- | | `0x110000 as char32` | `constant cast from 'int' to 'char32' is outside the target type's range` | | `-1 as char32` | `constant cast from 'int' to 'char32' is outside the target type's range` | | `300 as char8` | `constant cast from 'int' to 'char8' is outside the target type's range` | | `0xD800 as char32` | `cast from 'int' to 'char32' uses invalid surrogate code point U+D800` | See [Characters](https://rux-lang.dev/docs/lang/types/characters) for what each width holds. ## Enums An enum member converts to the integer it stands for, and an integer back to a member: ```rux enum Level: uint8 { Low = 1, Mid = 5, High = 9 } ``` ```rux PrintLine("{}", Level::Mid as int32); let raw: uint8 = 9; let level = raw as Level; ``` The conversion from an integer is not checked. A number that is no member's value gives a value that equals no member, so convert only numbers known to be valid, or test with a [`match`](https://rux-lang.dev/docs/lang/patterns/match) on the integer first. One enum does not convert to another: `Level::Low as Other` is `error: cannot cast enum 'Level' directly to unrelated enum 'Other'`. Go through an integer when that is really meant. ## What does not convert `as` converts scalars. Text, structures, tuples, arrays and variants have no cast: ```text error: cannot cast value of type 'char8[..]' to 'int' error: cannot cast value of type 'Point' to 'int64' error: cannot cast variant 'Shape' to scalar type 'int32' ``` Parsing text into a number is a function call, not a cast — see Learn: [Parse](https://rux-lang.dev/docs/learn/parse). Building a value of another type from this one is a [constructor](https://rux-lang.dev/docs/lang/structs/constructors). A [sum](https://rux-lang.dev/docs/lang/sums/overview) is not narrowed with `as` either: `value as int32` on an `int32 | bool` is `error: cannot cast value of type 'bool8 | int32' to 'int32'`. Test the member with [`is`](https://rux-lang.dev/docs/lang/sums/type-tests), and reach the value with a [typed pattern](https://rux-lang.dev/docs/lang/patterns/patterns#typed-patterns). ## See also - [Expressions](https://rux-lang.dev/docs/lang/expressions/overview#operand-types) — the conversions Rux does without `as` - [Integers](https://rux-lang.dev/docs/lang/types/integers), [Floating-point types](https://rux-lang.dev/docs/lang/types/floating-point), [Characters](https://rux-lang.dev/docs/lang/types/characters) — the types converted between - [Enums](https://rux-lang.dev/docs/lang/enums/overview) — member values - Learn: [Convert](https://rux-lang.dev/docs/learn/convert), [Checked convert](https://rux-lang.dev/docs/learn/checked-convert) # Statements A function body is a block: a sequence of statements between `{` and `}`, run in order. Every statement is one of these: | Statement | Form | Page | | ---------------------------------- | -------------------------- | ------------------------------------------------------------------------------ | | Binding | `let x = …;` `var x = …;` | [Bindings](https://rux-lang.dev/docs/lang/bindings/overview) | | Constant | `const X = …;` | [Constants](https://rux-lang.dev/docs/lang/bindings/constants) | | Expression | `expression;` | below | | `if` | `if c { … } else { … }` | [if](https://rux-lang.dev/docs/lang/statements/if) | | `when` | `when c { … }` | [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) | | `match` | `match v { … }` | [match](https://rux-lang.dev/docs/lang/patterns/match) | | `while`, `do while`, `loop`, `for` | `while c { … }` … | [Loops](https://rux-lang.dev/docs/lang/statements/loops) | | `break`, `continue` | `break;` `continue label;` | [break and continue](https://rux-lang.dev/docs/lang/statements/break-continue) | | `return` | `return value;` | [return](https://rux-lang.dev/docs/lang/statements/return) | | `fail` | `fail error;` | [Errors](https://rux-lang.dev/docs/lang/errors/overview) | | `defer` | `defer statement` | [defer](https://rux-lang.dev/docs/lang/ownership/defer) | ## Expression statements An expression followed by `;` is a statement, run for its effect: a call, an assignment, an increment. ```rux PrintLine("hello"); total += price; count++; ``` The `;` is required. When something else follows a complete expression, the compiler names it: `error: expected ';' after expression, but found ':'`. A call to a [fallible](https://rux-lang.dev/docs/lang/errors/overview) function cannot be a bare statement, because its failure would be lost; handle it with `?`, `catch` or `match`. A `match` at the start of a statement is a `match` statement; to use a match's value there, bind it or wrap it in parentheses. ## Blocks are bodies Braces appear only as the body of something — a function, an `if` branch, a loop, a `match` arm. A block on its own is not a statement: ```rux let x = 1; { let y = 2; } ``` ```text error: expected an expression before '{' ``` A body is always a block, even when it holds one statement: `if ready return 1;` is `error: expected '{' to start the 'if' body before 'return'`. A value that needs a few statements to compute belongs in a function, or in a `match` arm. ## Conditions `if`, `while`, `do while` and the conditional operator test a condition, and every condition follows the same rules. **It is a `bool`.** A number, a pointer or an optional is never true or false by itself: ```rux let count: int32 = 3; if count { } ``` ```text error: condition for 'if' must have type 'bool', but found 'int32' ``` Write the test: `if count != 0`. **It needs no parentheses.** `if count > 0 { … }` is the usual form. Parentheses are allowed, as around any expression, but add nothing: `if (count > 0) { … }` means the same. **A structure literal inside it is parenthesised.** After `if`, `while`, `for … in`, `match` and `when`, the first `{` that could start the body does start the body. So `Point { x: 1, y: 2 }` cannot appear bare in a condition — its brace would be taken as the body: ```rux if p == (Point { x: 1, y: 2 }) { PrintLine("at the start"); } ``` Without the parentheses the compiler reports `error: expected ';' after expression, but found ':'` at the first field. A structure literal inside a call's argument list needs no extra parentheses, since the call's own parentheses already enclose it. ## See also - [if](https://rux-lang.dev/docs/lang/statements/if), [Loops](https://rux-lang.dev/docs/lang/statements/loops), [break and continue](https://rux-lang.dev/docs/lang/statements/break-continue), [return](https://rux-lang.dev/docs/lang/statements/return) - [match](https://rux-lang.dev/docs/lang/patterns/match) — choosing by the shape of a value - [Expressions](https://rux-lang.dev/docs/lang/expressions/overview) — what statements are built from - Learn: [Control flow](https://rux-lang.dev/docs/learn/control-flow) # if `if` runs a block when a condition is `true`. `else if` tests further conditions in turn, and a final `else` runs when none of them held. The first branch whose condition is `true` runs, and the rest are skipped. ```text if-statement = "if" condition block { "else" "if" condition block } [ "else" block ] ``` ```rux if x > 0 { PrintLine("positive"); } else if x < 0 { PrintLine("negative"); } else { PrintLine("zero"); } ``` ## The condition The condition is a `bool`, written without parentheses, and a structure literal inside it is wrapped in parentheses — the rules every condition follows, set out in [Statements](https://rux-lang.dev/docs/lang/statements/overview#conditions). Combine tests with the [logical operators](https://rux-lang.dev/docs/lang/expressions/logical): ```rux if age >= 18 && hasTicket { Admit(); } ``` A [type test](https://rux-lang.dev/docs/lang/sums/type-tests) is a `bool` too, so `if shape is Circle { … }` reads naturally. ## Bodies Every branch is a block, even for a single statement, and the braces are what end the condition. A binding declared in a branch lasts until that branch's closing brace. ## if is not an expression `if` chooses between statements; it does not produce a value, so `let s = if ready { 1 } else { 2 };` does not parse. To choose a value, use the [conditional operator](https://rux-lang.dev/docs/lang/expressions/conditional) or a [`match`](https://rux-lang.dev/docs/lang/patterns/match) expression: ```rux let label = count == 1 ? "item" : "items"; let size = match count { 0 => "none", 1 => "one", else => "many" }; ``` Or declare a `var` and assign it in each branch; the compiler checks that every branch does — see [Initialization](https://rux-lang.dev/docs/lang/bindings/initialization). ## if and when `when` has the same shape as `if` but is decided by the compiler, which keeps only the branch it selects — see [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional). A chain keeps the keyword it starts with: ```text error: expected 'if' after 'else' in a run-time 'if' chain; 'when' is the compile-time conditional ``` ## See also - [Statements](https://rux-lang.dev/docs/lang/statements/overview) — the rules every condition follows - [Conditional](https://rux-lang.dev/docs/lang/expressions/conditional) — choosing between two values - [match](https://rux-lang.dev/docs/lang/patterns/match) — choosing by the shape of a value - Learn: [If](https://rux-lang.dev/docs/learn/if), [Else if](https://rux-lang.dev/docs/learn/else-if) # Loops Rux has four loops. Each runs a block repeatedly; they differ in when, or whether, they test a condition. | Loop | Tests | The body runs | | ------------------- | ------------------------- | --------------------------- | | `while c { … }` | before each pass | zero or more times | | `do { … } while c;` | after each pass | at least once | | `loop { … }` | never | until a `break` or `return` | | `for x in s { … }` | for a next element of `s` | once per element | ```text while-loop = [label ":"] "while" condition block do-while = [label ":"] "do" block "while" condition ";" loop = [label ":"] "loop" block for-loop = [label ":"] "for" Name "in" expression block ``` Any loop may carry a label, so that a `break` or `continue` inside a nested loop can name it — see [break and continue](https://rux-lang.dev/docs/lang/statements/break-continue). Conditions follow the rules in [Statements](https://rux-lang.dev/docs/lang/statements/overview#conditions): a `bool`, no parentheses needed, a structure literal wrapped in parentheses. ## while `while` tests its condition before every pass, including the first, so the body may never run: ```rux var i = 0; while i < 3 { PrintLine("{}", i); i++; } ``` ## do while `do … while` runs the body first and tests afterwards, so the body always runs at least once. The statement ends with `;`: ```rux var tries = 5; do { tries--; } while tries > 10; ``` The condition is false from the start, but `tries` is still decremented once, to 4. A `continue` in the body goes straight to the test. ## loop `loop` repeats until something leaves it — a [`break`](https://rux-lang.dev/docs/lang/statements/break-continue), a [`return`](https://rux-lang.dev/docs/lang/statements/return), a `fail`, or a panic. It says "until I stop it" more directly than `while true`: ```rux var n = 0; loop { n += 1; if n == 4 { break; } } ``` ## for `for` runs its body once for each element of a sequence, binding the element to the loop variable: ```rux let values = [10, 20, 30]; for v in values { PrintLine("{}", v); } ``` The subject is evaluated once, before the first pass. What `for` can iterate: | Subject | Elements | | ------------------------------------- | --------------------------------------------------------- | | a fixed-size array `T[N]` | each element, in order | | a slice `T[..]` | each element, in order | | a string literal or other `char8[..]` | each `char8` code unit — a byte of UTF-8, not a character | | a range `a..b`, `a..=b`, `a...b` | each integer from `a`, up to `b` exclusive or inclusive | | an open range `a..` | each integer from `a`, until the loop is left | | a type with an iterator | each value its iterator's `Next` returns | ```rux for k in 0..3 { Print("{} ", k); } for k in 0..=3 { Print("{} ", k); } ``` This prints `0 1 2 0 1 2 3`. A range whose start is past its end runs zero times. When both ends are constants, the compiler catches it instead: `for k in 5..2` is `error: range start cannot be greater than its end`. See [Ranges](https://rux-lang.dev/docs/lang/ranges/overview). Any other type is iterated through the iterator convention: a container declares a parameterless `Iterate` returning an iterator, and the iterator declares `func Next(self: &var Iterator) -> Item?`. Each pass calls `Next` once; a present result is the element, and `none` ends the loop. See [Iteration](https://rux-lang.dev/docs/lang/interfaces/iteration). ```rux for left in countdown { Print("{} ", left); } ``` A type with neither is refused: ```text error: cannot iterate over 'int' help: iterate an array, a slice, a range, or a type declaring 'Next' or 'Iterate' ``` ### The loop variable The loop variable is a single name, fresh on every pass and immutable: `i = 10;` inside `for i in 0..3` is `error: cannot modify immutable variable 'i'`. It cannot be declared `var`, and it cannot be a pattern — `for (a, b) in pairs` does not parse. Bind a mutable copy or take an element apart inside the body: ```rux for pair in pairs { let (a, b) = pair; PrintLine("{} {}", a, b); } ``` ## Moving inside a loop A loop body runs again from where its last pass ended, so a value one pass moves out is not there for the next. Moving an outer variable inside a loop is an error unless the same pass gives it a new value before the loop repeats, or leaves the loop after the move: ```rux for i in 0..3 { Consume(<- buffer); } ``` ```text error: value 'buffer' may have been moved on some control-flow paths ``` The loop variable, and bindings declared inside the body, are new on every pass and may be moved freely. See [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## See also - [break and continue](https://rux-lang.dev/docs/lang/statements/break-continue) — leaving a loop early, and labels - [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) — the values `for` counts through - [Iteration](https://rux-lang.dev/docs/lang/interfaces/iteration) — making a type iterable - [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) and [Slices](https://rux-lang.dev/docs/lang/slices/overview) — the sequences `for` walks - Learn: [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), [Iterator](https://rux-lang.dev/docs/learn/iterator) # break and continue `break` leaves a loop at once. `continue` ends the current pass and moves on to the next one. ```text break-statement = "break" [label] ";" continue-statement = "continue" [label] ";" ``` | Statement | In `while` and `loop` | In `do while` | In `for` | | ---------- | -------------------------------------------------------- | --------------------- | ------------------------- | | `break` | leaves the loop | leaves the loop | leaves the loop | | `continue` | goes back to the condition (`while`) or the top (`loop`) | goes to the condition | moves to the next element | ```rux for v in values { if v == 0 { break; } if v < 0 { continue; } sum += v; } ``` Without a label, both act on the innermost loop around them. Outside every loop they are errors: `error: 'break' can only be used inside 'while', 'for', or 'loop'`, and the same for `continue`. `break` takes no value; a loop does not produce one. ## Labels A label names a loop. It is written before the loop keyword, followed by `:`, and any of the four loops may carry one: ```rux outer: for row in 0..3 { for col in 0..3 { if col == 2 { continue outer; } if row == 2 { break outer; } Print("({} {}) ", row, col); } } ``` `continue outer` abandons the inner loop and starts the next pass of `outer`; `break outer` leaves both. This prints `(0 0) (0 1) (1 0) (1 1)`. A label names exactly one loop at a time. An inner loop may not reuse the label of a loop around it, because `break outer` inside it would then read as either one: ```text error: loop label 'outer' shadows an enclosing loop label help: give the inner loop a different label ``` Loops that follow one another, rather than nest, may reuse a label. A label must belong to a loop that encloses the statement: `continue missing;` is `error: 'continue' refers to unknown loop label 'missing'`. Only loops take labels. ## Inside expressions `break` and `continue`, with or without a label, may also stand where an expression would leave rather than produce a value: as a whole `match` arm, or as the fallback of [`??`](https://rux-lang.dev/docs/lang/optionals/coalescing): ```rux for v in values { let step = match v { 0 => break, -1 => continue, else => v }; sum += step; } ``` ```rux for v in values { let x = maybe ?? continue; PrintLine("{}", x); } ``` They cannot be used as an ordinary value: `n > 0 ? n : break` is `error: 'break' cannot be used as a value here`. ## See also - [Loops](https://rux-lang.dev/docs/lang/statements/loops) — the four loops - [return](https://rux-lang.dev/docs/lang/statements/return) — leaving the whole function - [Coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing) — `?? break` and `?? continue` - Learn: [Break](https://rux-lang.dev/docs/learn/break), [Continue](https://rux-lang.dev/docs/learn/continue), [Label](https://rux-lang.dev/docs/learn/label) # return `return` ends the function it is in and hands a value back to the caller. ```text return-statement = "return" [expression] ";" ``` ```rux func Sign(n: int32) -> int32 { if n < 0 { return -1; } match n { 0 => return 0, else => return 1 } } ``` ## With and without a value A function with a return type returns a value of that type; one without a return type returns with a bare `return;`, or simply by reaching the end of its body: ```rux func Log(message: char8[..]) { if message.length == 0 { return; } PrintLine("{}", message); } ``` | Mistake | Error | | ------------------------------------------- | ---------------------------------------------------------------- | | a value of the wrong type | `'return' value must have type 'int', but found 'char8[..]'` | | a value from a function with no return type | `'return' cannot have a value in a function with no return type` | | no value from a function with a return type | `'return' requires a value of type 'int'` | The value takes its type from the function the way an annotated binding does: an unsuffixed literal takes the return type, and a narrower integer widens to it. In a [fallible](https://rux-lang.dev/docs/lang/errors/overview) function, `return value;` is the success and `fail error;` the failure. ## Every path returns A function with a return type must return on every path through its body. The compiler follows each branch, and one that can reach the closing brace is an error: ```rux func Positive(x: int) -> int { if x > 0 { return 1; } } ``` ```text error: function 'Positive' must return a value of type 'int' on every control-flow path ``` A path that cannot continue counts as finished: a `match` whose every arm returns, a `loop` with no `break`, a call to `Panic` or to any function marked [`#NoReturn()`](https://rux-lang.dev/docs/lang/attributes/noreturn): ```rux func Day(n: uint) -> char8[..] { if n < 7 { return "weekday"; } Panic("no such day"); } ``` ## Returning a move-only value `return name;` copies `name` into the result, like any other use by value. A type that cannot be copied is handed out with `<-`: ```rux func Pass(token: Token) -> Token { return <- token; } ``` Without the `<-`: ```text error: move-only value 'token' requires an explicit '<-' in return note: plain by-value use copies its source, but 'Token' prohibits copying help: prefix the return value with '<-', as in 'return <-token' ``` A freshly made value, such as a structure literal or the result of a call, needs no `<-`. See [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## return and defer `return value;` evaluates `value` exactly once, then runs the function's deferred statements, newest first, and only then leaves. Changes a deferred statement makes to a local after that point do not alter the value already captured. See [defer](https://rux-lang.dev/docs/lang/ownership/defer). ## Inside expressions `return`, with or without a value, may also stand where an expression would leave rather than produce one: as a whole `match` arm, as the fallback of [`??`](https://rux-lang.dev/docs/lang/optionals/coalescing), or in an error mapping: ```rux func First(values: int32[..]) -> int32 { let found: int32? = values.length > 0 ? .Some(values[0]) : none; let v = found ?? return -1; return v; } ``` Anywhere else it is `error: 'return' cannot be used as a value here`. ## See also - [Functions](https://rux-lang.dev/docs/lang/functions/declaration) — return types - [main](https://rux-lang.dev/docs/lang/functions/main) — what `Main` returns to the operating system - [Errors](https://rux-lang.dev/docs/lang/errors/overview) — `fail`, the other way out of a fallible function - [defer](https://rux-lang.dev/docs/lang/ownership/defer) — what runs on the way out - Learn: [Return](https://rux-lang.dev/docs/learn/return) # match `match` compares a value, the *subject*, against a list of [patterns](https://rux-lang.dev/docs/lang/patterns/patterns) and runs the first arm whose pattern fits. It is both a statement and an expression. ```text match = "match" subject "{" arm { "," arm } "}" arm = pattern "=>" body | "else" "=>" body body = expression | block | "return" [expression] | "fail" expression | "break" [label] | "continue" [label] ``` ```rux match status { 200 => PrintLine("OK"), 404 => PrintLine("Not Found"), else => PrintLine("Unknown") } ``` ## Arms Each arm is a pattern, `=>`, and a body. Arms are separated by commas, block arms included, and the last arm takes **no** trailing comma: ```text error: trailing comma is not allowed in match blocks ``` The arms are tried from the top, and the first whose pattern fits — and whose [guard](https://rux-lang.dev/docs/lang/patterns/patterns#guards), if it has one, is `true` — is the one that runs. No other arm runs, and nothing falls through. `else` is the default arm. It matches whatever no earlier arm did, and must come last: an arm after it can never be reached, which is `error: match arm is unreachable because an earlier pattern matches every value`. `_` is a pattern, not a default, so `_ =>` as a whole arm is `error: use 'else' for the default match arm`. Two arms with the same pattern are refused rather than one silently hiding the other: `error: duplicate pattern in match`. A runtime arm takes one pattern; several values that share an outcome are written as separate arms, or as a [range](https://rux-lang.dev/docs/lang/patterns/patterns#range-patterns) or a guard. ## The subject The subject is any expression and is evaluated exactly once, before the first arm is tried. Like a condition, it ends at the first `{` that could open the arms, so a structure literal subject is parenthesised: `match (Point { x: 0, y: 0 }) { … }`. A match normally only reads its subject. Written `match <- value { … }`, it takes the value over, so that its arms can move the parts they bind — see [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## Arm bodies | Body | Use | | ------------------------------------- | --------------------------------------------- | | an expression | the arm's value, or a call run for its effect | | a block `{ … }` | several statements, in a match statement | | `return`, `fail`, `break`, `continue` | leave the function or loop from this arm | ```rux match Direction::East { .North => { effect = 1; }, .East => { effect = 2; }, else => {} } ``` ## Statement and expression A `match` at the start of a statement is a **statement**: its arms run for their effect, and it produces no value. Anywhere else — after `=`, after `return`, as an argument — it is an **expression**, and every arm produces the match's value: ```rux let label = match status { 200 => "OK", 404 => "Not Found", else => "Unknown" }; ``` The arms of a match expression must agree on a type, as the branches of a [conditional](https://rux-lang.dev/docs/lang/expressions/conditional) do: ```text error: match arm type mismatch: expected 'char8[..]', found 'int' ``` An arm that leaves — `return`, `fail`, `break`, `continue`, or a call that never returns, such as `Panic` — produces no value and does not take part. Where the context fixes the type, such as an annotated binding or a return, every arm takes it: an unsuffixed literal in an arm must fit it, and `none`, `.Success(…)` and `.Failure(…)` arms are completed by it. See [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) and [Errors](https://rux-lang.dev/docs/lang/errors/handling). A statement cannot be followed by a postfix operator: `match … { … } catch { … }` at the start of a statement is an error. To use the value of a match there, bind it, or wrap the match in parentheses. ## Exhaustiveness A match must not meet a value that no arm accepts. How strictly that is checked depends on the subject's type, and on whether the match is a statement or an expression: | Subject | Covered by | Statement must cover | Expression must cover | | ------------------------------------------------------------- | ---------------------------------------------------------- | -------------------- | --------------------- | | an [enum](https://rux-lang.dev/docs/lang/enums/overview) | an arm per member | yes | yes | | a [variant](https://rux-lang.dev/docs/lang/variants/matching) | an arm per case | yes | yes | | an optional, a fallible or a sum | every level: `none` and present, each channel, each member | yes | yes | | `bool` | a `true` arm and a `false` arm | no | yes | | a tuple | every combination of its elements | no | yes | | an integer, a floating-point number or a character | `else`, or an arm that binds every value | no | yes | | a structure | an arm whose field patterns are all irrefutable, or `else` | no | yes | A missing case is reported by name: ```text error: match on 'Color' is not exhaustive; missing Color::Blue error: match on 'bool8' is not exhaustive; missing false error: match on '(bool8, bool8)' is not exhaustive; missing (true, false) error: match on 'int32?' is not exhaustive; missing none error: match on 'int32' is not exhaustive; its arms do not cover every value ``` Two rules make coverage predictable: - **A guarded arm covers nothing**, because its guard might be `false`. `true => …, false if ready => …` still lacks an unguarded `false`. - **Ranges never replace `else`** for a number. A value-producing match on an integer ends with `else`, even when its ranges happen to reach every value. `else` is allowed on an enum or variant too, but it is a promise made on behalf of cases that do not exist yet: add a case later, and every match that names its cases one by one stops compiling at the spot that needs a decision, while one ending in `else` quietly gives the new case the default answer. ::note **Characters, floating-point numbers and structures.**:br rux 0.4.0 does not yet check a match expression on a character, a floating-point number or a structure for coverage. One with no `else` compiles, and a value that no arm accepts gives a meaningless result. End such a match with `else`. :: ## See also - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — every pattern an arm can use - [Variants](https://rux-lang.dev/docs/lang/variants/matching) — matching cases and their payloads - [Sums](https://rux-lang.dev/docs/lang/sums/patterns) — matching members of a sum - [Conditional](https://rux-lang.dev/docs/lang/expressions/conditional) — a two-way choice of value - Learn: [Match](https://rux-lang.dev/docs/learn/match), [Match expression](https://rux-lang.dev/docs/learn/match-expression), [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) # Patterns A *pattern* describes the shape of a value. Matched against a value, it either fits or does not, and when it fits it may bind names to parts of the value. Patterns appear in three places: | Place | Patterns allowed | | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | a [`match`](https://rux-lang.dev/docs/lang/patterns/match) arm | every form on this page | | a `catch` arm | patterns over the error — see [Handling errors](https://rux-lang.dev/docs/lang/errors/handling) | | a `let` or `var` binding | irrefutable forms only — see [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) | A pattern is *irrefutable* when it fits every value of its type — a name, `_`, and tuples and structures built from them — and *refutable* otherwise. | Form | Example | Fits | | ------------ | --------------------------------------- | ------------------------------------------------------ | | literal | `0`, `-1`, `1.5`, `'A'`, `true`, `null` | a value equal to the literal | | range | `1..5`, `1..=9`, `1...9` | a number in the range | | binding | `n` | anything, and binds it to `n` | | wildcard | `_` | anything, and binds nothing | | tuple | `(0, y)` | a tuple whose elements fit | | structure | `Point { x: 0, y }` | a structure whose named fields fit | | enum member | `.Red`, `Color::Red` | that member | | variant case | `.Round(r)`, `.Gauge { level }` | that case, with a payload that fits | | typed | `c: Circle`, `_: Square` | a sum whose active member has that type | | absence | `none` | an absent optional | | presence | `n?`, `n??`, `.Some(n)` | a present optional whose value fits | | outcome | `.Success(v)`, `.Failure(e)` | that channel of a fallible | | guarded | `n if n > 100` | a value the pattern fits and for which the guard holds | Patterns nest: wherever a pattern appears inside another — a tuple element, a field, a payload — any form can be used. ## Literal patterns An integer, floating-point, character or `bool` literal fits a value equal to it. A negative integer is written with its sign, `-1`. The literal takes the subject's type, so `255` fits a `uint8` field and `'a'` a `char32`; a literal of another kind is refused: ```text error: pattern has type 'char32', but the matched value has type 'int32' ``` `null` fits a null [pointer](https://rux-lang.dev/docs/lang/pointers/overview): ```rux func Read(p: *int32) -> int32 { return match p { null => 0, else => *p }; } ``` A negative floating-point literal cannot be written as a pattern; use a guard. ## Range patterns A range pattern fits a number between two literal bounds. `lo..hi` excludes the upper bound; `lo..=hi` and `lo...hi` include it: ```rux func Classify(n: int32) -> char8[..] { return match n { -1 => "minus one", 0 => "zero", 1..5 => "one to four", 5..=9 => "five to nine", else => "other" }; } ``` Both bounds are needed: `5..` is `error: expected a range pattern end after '..' before '=>'`. Ranges apply to integers and floating-point numbers only — `'a'..='z'` is `error: range pattern cannot match value of type 'char32'`. Test a character with a guard instead: `c if c >= 'a' && c <= 'z'`. ## Binding patterns A name fits any value and binds it, for the rest of the arm. A binding is immutable, like a `let`: ```rux let m = match pair { (a, b) if a > b => a, (_, b) => b }; ``` A bare name always binds; it never compares. A name that already names a constant, a type, a function or a case of the matched type cannot be used: ```text error: pattern 'Limit' cannot bind a new variable because 'Limit' already names a constant help: a pattern compares with literal values; write the value of 'Limit', or bind the value and compare it with 'Limit' in a guard such as 'value if value >= Limit' ``` ## The wildcard `_` fits anything and binds nothing. It skips a part inside a larger pattern, such as a tuple element or a payload: `.Round(_)` matches the case without naming its value. On its own, as a whole arm, it is not the default — write `else` (see [match](https://rux-lang.dev/docs/lang/patterns/match#arms)). ## Tuple patterns A tuple pattern has one pattern per element, in order, and fits when every element fits: ```rux func Classify(pair: (int32, int32)) -> int32 { return match pair { (0, 0) => 0, (x, 0) => 100 + x, (0, y) => 200 + y, (a, b) => a * 10 + b }; } ``` The number of elements must match the tuple's: `error: tuple pattern has 3 elements, but matched tuple has 2`. ## Structure patterns A structure pattern names the type and lists fields by name, in any order. `field: pattern` tests the field; `field` alone binds it to a variable of the same name. Fields left out are not tested: ```rux match point { Point { x: 0, y: 0 } => PrintLine("origin"), Point { x: 0, y } => PrintLine("on the y axis at {}", y), Point { y: 7 } => PrintLine("at height 7"), else => PrintLine("elsewhere") } ``` The literals in field patterns compare at the field's type. A field the type does not have is `error: struct 'Point' has no field 'z'`. ## Enum and variant patterns A member of an [enum](https://rux-lang.dev/docs/lang/enums/overview) is written in full, `Color::Red`, or with a leading dot, `.Red`, which takes the enum from the subject's type. A [variant](https://rux-lang.dev/docs/lang/variants/matching) case is written the same way, followed by patterns for its payload — in parentheses for a positional case, in braces for a case with named fields: ```rux variant Reading { Missing, Gauge { level: int32; unit: char32; }, Pair(int32, int32) } ``` ```rux func Level(r: Reading) -> int32 { return match r { .Missing => 0, .Gauge { level: 0 } => -1, .Gauge { level, unit: 'C' } => level, Reading::Gauge { level: l } => l * 10, .Pair(a, _) => a }; } ``` A payload pattern may itself be refutable — a literal or a range in a field is tested before the arm is taken. The shorthand needs a subject whose type is an enum or variant: on an `int32`, `.Red` is `error: cannot infer enum or variant type for shorthand pattern '.Red' from type 'int32'`. ## Typed patterns A typed pattern, `name: Type`, fits a [sum](https://rux-lang.dev/docs/lang/sums/overview) whose active member has that type, and binds the member, as that type, to the name. `_: Type` tests the member without binding it: ```rux func Area(shape: Circle | Square) -> float64 { return match shape { c: Circle => 3.0 * c.radius * c.radius, s: Square => s.side * s.side }; } ``` The type after `:` is a complete type, so `v: A | B` needs no grouping. See [Sum patterns](https://rux-lang.dev/docs/lang/sums/patterns) for matching groups of members. ## Absence and presence On an [optional](https://rux-lang.dev/docs/lang/optionals/overview), `none` fits the absent value and a *presence* pattern fits a present one. `p?` is shorthand for `.Some(p)`, and `p??` peels two levels at once: ```rux func Describe(value: int32?) -> int32 { return match value { 0? => -1, n? if n > 100 => 100, n? => n, none => 0 }; } ``` The `?` must touch its pattern — `n ?` is `error: a presence suffix must touch its pattern` — and a range bound or a typed pattern cannot take one; write `.Some(1..=9)` or `.Some(v: A)` instead. On a [fallible](https://rux-lang.dev/docs/lang/errors/overview), `.Success(p)` and `.Failure(p)` fit the two channels — see [Handling errors](https://rux-lang.dev/docs/lang/errors/handling). ## Guards `pattern if condition` fits when the pattern fits **and** the condition, a `bool`, is `true`. The condition is checked after the pattern, so it can read the pattern's bindings: ```rux func Bucket(value: int32, small: bool) -> int32 { return match value { 1..=9 if small => 1, 1..=9 => 2, else => 3 }; } ``` A guard applies to the whole pattern before it, a range included. It is usually written at the end of an arm, but may also follow a pattern nested inside a tuple. A guard that is not a `bool` is `error: pattern guard must have type 'bool', but found 'int32'`. A guarded arm does not count towards [exhaustiveness](https://rux-lang.dev/docs/lang/patterns/match#exhaustiveness). ## No alternatives A pattern describes one shape. There is no `|` between patterns in an arm, and no comma-separated list: `1 | 2 => …` does not parse. Write one arm per value, a range, or a guard. ## See also - [match](https://rux-lang.dev/docs/lang/patterns/match) — arms, `else`, and exhaustiveness - [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) — irrefutable patterns in `let` - [Sum patterns](https://rux-lang.dev/docs/lang/sums/patterns), [Variant matching](https://rux-lang.dev/docs/lang/variants/matching), [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) - Learn: [Guard](https://rux-lang.dev/docs/learn/guard), [Range pattern](https://rux-lang.dev/docs/learn/range-pattern), [Tuple pattern](https://rux-lang.dev/docs/learn/tuple-pattern), [Struct pattern](https://rux-lang.dev/docs/learn/struct-pattern), [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern), [Presence](https://rux-lang.dev/docs/learn/presence) # Function declarations A function gives a block of statements a name, a list of typed parameters and, optionally, a result type. Calling it runs the block with the arguments bound to the parameters, and the call is worth the value the block returns. ```text function = [ attributes ] [ "pub" ] "func" name [ type-parameters ] "(" [ parameter { "," parameter } [ "," ] ] ")" [ "->" result ] block parameter = name ":" type [ "=" expression ] | name ":" type "..." result = type | type "!" type | "!" type ``` ```rux func Add(left: int, right: int) -> int { return left + right; } ``` | Part | In `Add` | Meaning | | ----------- | -------------------------- | ---------------------------------------------------------------------- | | Name | `Add` | An identifier; PascalCase by convention | | Parameters | `left: int, right: int` | `name: Type` each, separated by commas; a trailing comma is allowed | | Result type | `-> int` | The type of the value the function returns; omitted when there is none | | Body | `{ return left + right; }` | A block | Parameters, default values and variadic parameters are described in [Parameters](https://rux-lang.dev/docs/lang/functions/parameters). A name in angle brackets after the function name declares a type parameter, which makes the function [generic](https://rux-lang.dev/docs/lang/generics/overview). ## Where functions are declared | Place | Declares | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The top level of a file | A free function of the package's root module | | A `module A::B { }` block | A free function of that module — see [Modules](https://rux-lang.dev/docs/lang/modules/overview) | | An `extend T { }` block | A [method](https://rux-lang.dev/docs/lang/structs/methods), [constructor](https://rux-lang.dev/docs/lang/structs/constructors), operator or destructor of `T` | | An `extern { }` block | A foreign function, without a body — see [FFI](https://rux-lang.dev/docs/lang/ffi/overview) | | `asm func` | A function whose body is assembly — see [Assembly](https://rux-lang.dev/docs/lang/ffi/assembly) | Every other function needs a body. A signature ending in `;` is refused: ```text error: function 'Later' has no body ``` Declaration order does not matter: a function may call one declared further down the file, or in another file of the package. `pub` makes a function visible to other packages; see [Visibility](https://rux-lang.dev/docs/lang/modules/visibility). ## The result | Written | The function | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | | no `->` | Returns nothing. A bare `return;` leaves early; reaching the closing brace returns | | `-> T` | Returns a `T`. Every path through the body must end in `return` with a value | | `-> ()` | Returns the [unit](https://rux-lang.dev/docs/lang/tuples/overview#the-unit-type) value `()` | | `-> T ! E`, `-> ! E` | Is [fallible](https://rux-lang.dev/docs/lang/errors/overview): it returns a `T` (or nothing) or fails with an `E` | Several values are returned as a [tuple](https://rux-lang.dev/docs/lang/tuples/overview), an absent result as an [optional](https://rux-lang.dev/docs/lang/optionals/overview). ```rux func Greet(name: char8[..]) { if name.length == 0 { return; } PrintLine("Hello, {}!", name); } func Divide(dividend: int, divisor: int) -> (int, int) { return (dividend / divisor, dividend % divisor); } ``` The compiler checks that a function with a result type returns on every path, and that a function without one never returns a value: ```text error: function 'Sign' must return a value of type 'int' on every control-flow path error: 'return' cannot have a value in a function with no return type ``` Leaving out the result type is not the same as writing `-> ()`. Only a unit-returning function produces a value of type `()` that can be stored or passed; a call to a function without a result is written as a statement on its own. See [`return`](https://rux-lang.dev/docs/lang/statements/return) for the statement itself. ## Calls A call is the function's name followed by one argument per parameter, in parentheses and in order. Arguments are evaluated from left to right, then bound to the parameters, and the body runs: ```rux func Note(label: char8[..], value: int) -> int { PrintLine("evaluating {}", label); return value; } func Main() -> int { // Prints "evaluating first", then "evaluating second", then 12. PrintLine("{}", Pair(Note("first", 1), Note("second", 2))); return 0; } func Pair(first: int, second: int) -> int { return first * 10 + second; } ``` A call is an expression of the result type and may appear wherever a value of that type fits — as an argument to another call, in an operator, on the right of a binding. Each argument must have its parameter's type. Nothing is converted to make a call fit, with one exception: an unsuffixed integer literal takes the integer type of its parameter, so `Half(9)` works for a `uint8` parameter. A function may call itself, directly or through others; each call has its own parameters and locals. ## Rules and errors | Mistake | Error | | ------------------------------------------------- | --------------------------------------------------------------------------------- | | An argument of another type | `argument 1 to 'Square' has type 'float64', but parameter 'value' requires 'int'` | | Too many or too few arguments | `call to 'Square' expects 1 argument, but 2 were provided` | | A name in the wrong case | `name 'square' is not defined in this scope`, with `did you mean 'Square'?` | | A missing `return` on some path | `function 'Sign' must return a value of type 'int' on every control-flow path` | | A value returned from a function without a result | `'return' cannot have a value in a function with no return type` | | A body-less signature outside `extern` | `function 'Later' has no body` | Names are case-sensitive. Several functions may share a name when their parameters differ; see [Overloading](https://rux-lang.dev/docs/lang/functions/overloading). ## See also - [Parameters](https://rux-lang.dev/docs/lang/functions/parameters) — by-value parameters, defaults and variadics - [Function types](https://rux-lang.dev/docs/lang/functions/function-types) — functions as values - [Main](https://rux-lang.dev/docs/lang/functions/main) — the entry point of a program - [Generics](https://rux-lang.dev/docs/lang/generics/overview) — functions over type parameters - Learn: [Function](https://rux-lang.dev/docs/learn/function), [Return](https://rux-lang.dev/docs/learn/return), [Recursion](https://rux-lang.dev/docs/learn/recursion) # Parameters A parameter is written `name: Type`. Each call binds the parameter to its argument for that one run of the body. A parameter is read-only: the body may read it but never assign to it, and what the caller can see changed depends only on the parameter's type. ```text parameter = name ":" type [ "=" expression ] // an ordinary parameter, optionally with a default | name ":" type "..." // a variadic parameter ``` ## Passing modes The parameter's type decides how the argument is passed. The call site writes nothing extra for any of them, except the arrow that moves a value which cannot be copied. | Parameter | The function receives | Can change the caller's value | The caller passes | | -------------- | ----------------------------------------- | ----------------------------- | ------------------------------------------------------- | | `x: T` | its own copy, read-only | no | any `T`; a move-only value as `<-value` | | `x: &T` | the caller's value, borrowed for reading | no | a named `T` | | `x: &var T` | the caller's value, borrowed for writing | yes | a `var` `T` | | `x: T[..]` | a read-only view of the caller's elements | no | a slice, or an array viewed as one | | `x: var T[..]` | a writable view of the caller's elements | the elements, yes | a writable slice, such as `buffer[..]` of a `var` array | References are described in [References](https://rux-lang.dev/docs/lang/references/overview), views in [Slices](https://rux-lang.dev/docs/lang/slices/overview), and moves in [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## Parameters are read-only Assigning to a parameter is an error, whatever its type: ```rux func Bump(n: int) -> int { n = n + 1; // error return n; } ``` ```text error: cannot modify parameter 'n' note: a parameter is immutable help: take 'n' as '&var int' to change the caller's value, or move it into a 'var' local ``` A function that needs a value it can change declares a local of its own and starts it from the parameter. A function that must change the caller's value takes `&var T`: ```rux func Countdown(from: int) { var remaining = from; while remaining > 0 { Print("{} ", remaining); remaining -= 1; } PrintLine("liftoff"); } func Tick(count: &var int) { count += 1; } ``` `var` is never written before a parameter name. `func Count(var n: int)` is refused with `mutable parameter syntax 'var n: T' has been removed`, and the help names the two forms above. ## Default values A parameter may give a default value after `=`. A call may then stop before it, and the default fills the gap: ```rux func Clamp(value: int, low: int = 0, high: int = 100) -> int { if value < low { return low; } if value > high { return high; } return value; } ``` | Call | `value` | `low` | `high` | Result | | -------------------- | ------- | ----- | ------ | ------ | | `Clamp(150)` | 150 | 0 | 100 | 100 | | `Clamp(42, 50)` | 42 | 50 | 100 | 50 | | `Clamp(150, 0, 255)` | 150 | 0 | 255 | 150 | The rules: - **Defaults come last.** A parameter without a default may not follow one with a default, because arguments always fill parameters from the left: `parameter 'second' without a default value cannot follow a parameter with a default value`. - **There are no named arguments.** A call cannot skip a parameter in the middle; to change `high`, it passes `low` too. `Clamp(150, high: 255)` is a syntax error. - **The default has the parameter's type**: `default value type 'float64' does not match parameter type 'int'`. - **A default is evaluated at each call that omits it**, in the caller's scope, as if the caller had written it. A default with an effect has it once per such call: ```rux func Tick() -> int { PrintLine("tick"); return 7; } func Show(value: int = Tick()) { PrintLine("value {}", value); } func Main() -> int { Show(); // tick, value 7 Show(1); // value 1 — Tick is not called Show(); // tick, value 7 return 0; } ``` - **A default cannot read another parameter**, `self` included, since it is evaluated where no parameter exists yet. Write an overload that passes the value instead: ```rux // func Repeat(text: char8[..], count: uint = text.length) is refused func Repeat(text: char8[..], count: uint) -> uint { return text.length * count; } func Repeat(text: char8[..]) -> uint { return Repeat(text, text.length); } ``` ```text error: a default value cannot refer to parameter 'text' help: add an overload without this parameter that passes the value it should default to ``` ::note **`#source` as a default.**:br A [`#source`](https://rux-lang.dev/docs/lang/comptime/context) value written as a default, such as `line: uint = #source.line`, is expanded where the function is declared in rux 0.4.0, not at the call, so every call sees the declaration's line. :: ## Variadic parameters A last parameter written `name: T...` accepts any number of arguments of type `T`, zero included. Inside the function it is a read-only slice, `T[..]`: ```rux func Sum(values: int...) -> int { var total = 0; for value in values { total += value; } return total; } func Largest(first: int, rest: int...) -> int { var largest = first; for value in rest { if value > largest { largest = value; } } return largest; } ``` | Call | `values` inside `Sum` | Result | | -------------- | --------------------- | ------ | | `Sum()` | empty | 0 | | `Sum(1, 2, 3)` | 1, 2, 3 | 6 | | `Largest(4)` | `rest` is empty | 4 | Ordinary parameters may come before the variadic one, and each still needs its argument: `Largest()` is `call to 'Largest' expects at least 1 argument, but 0 were provided`. The elements are read-only — `values[0] = 1` is `cannot modify elements through read-only slice 'int[..]'`. The element type may be an [interface](https://rux-lang.dev/docs/lang/interfaces/overview), and then each argument may be a different type that implements it. That is how `PrintLine` is declared: `args: Display...` takes an `int`, a `float64` and a string in one call. ### Spread `expression...` as the argument for a variadic parameter passes the elements of a slice or array as the arguments: ```rux let more = [4, 5, 6]; Sum(more...); // 15 Sum(more[1..]...); // 11 ``` A spread must be the only argument for the variadic parameter. Mixing it with separate values is an error: ```text error: spread argument to 'Sum' must be the only argument for variadic parameter 'values' ``` A spread is how one variadic function hands everything it received to another: ```rux func Average(values: int...) -> int { if values.length == 0 { return 0; } return Sum(values...) / (values.length as int); } ``` ::note **Only the last parameter may be variadic.**:br rux 0.4.0 does not check this yet: `func Bad(args: int32..., last: int32)` is accepted, and the build then fails or crashes. Keep the variadic parameter last. :: A variadic parameter may also follow a format string marked [`#Format()`](https://rux-lang.dev/docs/lang/attributes/format), which lets the compiler count placeholders against arguments. ## See also - [Function declarations](https://rux-lang.dev/docs/lang/functions/declaration) — the shape of a function - [Overloading](https://rux-lang.dev/docs/lang/functions/overloading) — how a call chooses among functions with defaults and variadics - [References](https://rux-lang.dev/docs/lang/references/overview) — `&T` and `&var T` parameters - [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move) — passing values that cannot be copied - Learn: [Default argument](https://rux-lang.dev/docs/learn/default-argument), [Variadic](https://rux-lang.dev/docs/learn/variadic), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) # Overloading Several functions in one scope may share a name when their parameter lists differ. Together they form an *overload set*, and each call picks one member by looking at its arguments. Methods and [constructors](https://rux-lang.dev/docs/lang/structs/constructors) overload the same way. ```rux func Describe(value: int) { PrintLine("an integer: {}", value); } func Describe(value: float64) { PrintLine("a number with a fraction: {}", value); } func Area(side: int) -> int { return side * side; } func Area(width: int, height: int) -> int { return width * height; } ``` `Describe(42)` and `Describe(2.5)` run different functions; `Area(4)` and `Area(4, 5)` likewise. ## What may differ Overloads must differ in their parameters — the number of them or their types. The result type takes no part: a call does not say what type it wants back, so two functions that differ only in their result could never be told apart, and the second is refused. ```rux func Half(value: int) -> int { return value / 2; } func Half(value: int) -> float64 { // error return 0.0; } ``` ```text error: function 'Half' has the same parameter signature as an earlier overload ``` Parameter names do not count either. When the difference really is in the result, give the functions different names. ## How a call is resolved Declaration order never decides a call. The compiler finds every overload that could accept the arguments and keeps the best, in these steps: 1. **Fixed parameter lists first.** Overloads without a variadic parameter are tried first; a variadic overload is chosen only when none of them accepts the call. 2. **Exact matches first.** Within that group, overloads whose parameter types are exactly the argument types are tried before overloads that need any conversion. 3. **The most specific overload.** Among the overloads that accept the call, one that is no worse for any argument and better for at least one wins. For each argument, from best to worst: | Rank | The argument's type is… | | ---- | --------------------------------------------------------------------------------------------------------------------- | | 1 | exactly the parameter's type | | 2 | the same type, differing only in a view's writability (`var int[..]` for `int[..]`) | | 3 | the same type reached through a borrow or a read through a reference (`int32` for `&int32`, `&var int32` for `int32`) | | 4 | any other type the argument converts to, such as an unsuffixed integer literal to another integer type | 4. **No default needed.** Among overloads still tied, one that the arguments fill without using a default value wins. 5. **Not generic.** Then a function without type parameters wins over a [generic](https://rux-lang.dev/docs/lang/generics/overview) one. Overloads still tied after that make the call ambiguous, which is an error. ```rux func G(x: int) -> int { return 1; } func G(x: int, y: int = 1) -> int { return 2; } func H(x: T) -> int { return 3; } func H(x: int) -> int { return 4; } func V(args: int...) -> int { return 5; } func V(a: int, b: int) -> int { return 6; } func W(x: float64) -> int { return 7; } func W(x: int32) -> int { return 8; } ``` | Call | Calls | Because | | ------------ | ------------------- | --------------------------------------- | | `G(2)` | `G(x: int)` | step 4 — the other needs its default | | `G(2, 3)` | `G(x: int, y: int)` | only it takes two arguments | | `H(1)` | `H(x: int)` | step 5 — the generic one ties but loses | | `H(true)` | `H(x: T)` | only it accepts a `bool` | | `V(1, 2)` | `V(a: int, b: int)` | step 1 — a fixed list is preferred | | `V(1, 2, 3)` | `V(args: int...)` | no fixed overload takes three arguments | | `W(small)` | `W(x: int32)` | step 2 — `small` is an `int32` | | `W(2.0)` | `W(x: float64)` | only it accepts a `float64` | A literal is not converted to suit an overload, except that an unsuffixed integer literal may become another integer type. `Area(4, 2.0)` matches nothing when the overloads take `(int, int)` and `(float64, float64)`: ```text error: no matching overload for 'Area' with argument types (int, float64) ``` ## Ambiguous calls When two overloads remain after every step, the compiler names both: ```rux func F(x: int8) -> int { return 8; } func F(x: int64) -> int { return 64; } func Main() -> int { return F(5); // error } ``` ```text error: call to 'F' is ambiguous: 2 overloads accept argument types (int) note: candidate 'F(x: int8)' declared at … note: candidate 'F(x: int64)' declared at … help: rename one of the overloads, or remove a default value that makes them overlap ``` The literal `5` converts to both `int8` and `int64` at rank 4, and nothing else separates them. A suffix picks one — `F(5i8)` is an exact match. ## See also - [Parameters](https://rux-lang.dev/docs/lang/functions/parameters) — default values and variadic parameters - [Generics](https://rux-lang.dev/docs/lang/generics/overview) — one generic function instead of a family of overloads - [Constructors](https://rux-lang.dev/docs/lang/structs/constructors) — overloaded constructors - Learn: [Overload](https://rux-lang.dev/docs/learn/overload) # Function types A function is also a value. Its type, a *function type*, records the parameter types and the result type and nothing else, so any function of the same shape fits wherever that type is expected. A function value can be passed as an argument, held in a local or a field, compared, and called. ```text function-type = "func" "(" [ parameter-type { "," parameter-type } ] ")" [ "->" result ] parameter-type = [ name ":" ] type ``` ## The type of a function A function's type is its declaration with the name, the parameter names and the body taken out: | Function | Its type | | -------------------------------------------- | ---------------------------- | | `func Double(value: int32) -> int32` | `func(int32) -> int32` | | `func Ascending(a: int32, b: int32) -> bool` | `func(int32, int32) -> bool` | | `func Report(label: char8[..])` | `func(char8[..])` | | `func Ready() -> bool` | `func() -> bool` | A function type without `->` describes a function with no result. Parameter names may be written inside a function type for readability, `func(a: int32, b: int32) -> bool`, but they are ignored: only the types count. Two function types are the same when their parameter types and result types are identical, in order. Nothing converts — a `func(int) -> int` does not fit a `func(int32) -> int32`, and a function returning `bool` does not fit one returning `int32`. ## Function values A function's name, written without parentheses, is a value of its function type. Writing parentheses calls the function instead. ```rux type Comparator = func(int32, int32) -> bool; struct Sorter { name: char8[..]; before: Comparator; } func Ascending(a: int32, b: int32) -> bool { return a < b; } func Descending(a: int32, b: int32) -> bool { return a > b; } func Sort(items: var int32[..], before: func(int32, int32) -> bool) { for i in 1..items.length { var j = i; while j > 0 && before(items[j], items[j - 1]) { let held = items[j]; items[j] = items[j - 1]; items[j - 1] = held; j -= 1; } } } func Main() -> int { var numbers: int32[5] = [3, 1, 4, 1, 5]; Sort(numbers[..], Ascending); // 1 1 3 4 5 let sorter = Sorter { name: "down", before: Descending }; Sort(numbers[..], sorter.before); // 5 4 3 1 1 var compare: func(int32, int32) -> bool = Ascending; PrintLine("{}", compare(1, 2)); // true compare = Descending; PrintLine("{}", compare(1, 2)); // false PrintLine("{}", compare == Descending); // true return 0; } ``` | Where | Written | Called as | | ----------- | ----------------------------------------- | --------------------- | | A parameter | `before: func(int32, int32) -> bool` | `before(x, y)` | | A local | `var compare: func(int32, int32) -> bool` | `compare(1, 2)` | | A field | `before: Comparator;` | `sorter.before(x, y)` | A local, parameter or field of function type is called exactly like a function, and the call runs whichever function the value holds at that moment. A `var` local or field can be reassigned to another function of the same type. `==` and `!=` compare two function values by identity: they are equal when they hold the same function. A [type alias](https://rux-lang.dev/docs/lang/types/aliases) gives a long function type a name. The alias is the same type, so `Comparator` and `func(int32, int32) -> bool` are interchangeable, as `Sort` and `Sorter` show. ## What a function value is not A function value refers to one declared function and carries no state of its own. - **There are no closures.** There is no lambda or anonymous function expression, and a function value cannot capture a local variable. Data a callback needs is passed to it as an argument, or kept in a struct beside the function field. - **A generic function is not a value.** Its name has no single type until its type parameters are known, so `let pick: func(int, int) -> int = Larger;` is refused with `cannot assign 'func(T, T) -> T' to 'func(int, int) -> int'`. Pass a non-generic function that calls it. - **A method is not a value.** `card.Area` without parentheses is read as a field and fails with `has no field 'Area'`. ::note **Functions inside a block.**:br rux 0.4.0 does not yet reject a `func` declared inside a function body, and a program that calls one builds but cannot start. Declare functions at the top level, in a module, or in an `extend` block. :: ::note **Overloaded names.**:br rux 0.4.0 does not yet choose an overload by the expected function type: the name of an overloaded function is taken as one particular overload, and assigning it to a variable of another overload's type fails with `cannot assign`. Pass functions that are not overloaded. :: ## Rules and errors | Mistake | Error | | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | A function of another shape, `Sort(numbers[..], IsEven)` | `argument 2 to 'Sort' has type 'func(int32) -> bool8', but parameter 'before' requires 'func(int32, int32) -> bool8'` | | Calling instead of passing, `Sort(numbers[..], Ascending(1, 2))` | `argument 2 to 'Sort' has type 'bool8', but parameter 'before' requires 'func(int32, int32) -> bool8'` | | Calling a function value with the wrong arguments, `compare(1)` | `call to 'compare' expects 2 arguments, but 1 was provided` | Error messages print `bool` by its full name, `bool8`. ## See also - [Function declarations](https://rux-lang.dev/docs/lang/functions/declaration) — declaring the functions that become values - [Type aliases](https://rux-lang.dev/docs/lang/types/aliases) — naming a function type - [Structs](https://rux-lang.dev/docs/lang/structs/overview) — fields of function type - Learn: [Callback](https://rux-lang.dev/docs/learn/callback), [Function field](https://rux-lang.dev/docs/learn/function-field) # The Main function An executable package starts at a function named `Main`, declared at the top level of the package — outside every `module` block — and taking no parameters. When `Main` returns, the program ends, and its result becomes the process's *exit status*: 0 for success, anything else for failure. ```rux func Main() -> int { PrintLine("Hello, Rux!"); return 0; } ``` A new executable package keeps `Main` in `Src/Main.rux`, but any file of the package may hold it. Library packages have no `Main`, and `rux run` refuses to run one. ## The three forms | Declaration | Ends by | Exit status | | ------------------------ | ------------------------------ | ----------- | | `func Main() -> int` | `return n;` | `n` | | `func Main() -> ! E` | reaching its end, or `return;` | 0 | | `func Main() -> int ! E` | `return n;` | `n` | | `-> ! E` or `-> int ! E` | `fail`, or a failed `?` | 1 | `E` may be any error type. A fallible `Main` lets [`?`](https://rux-lang.dev/docs/lang/errors/propagation) propagate a failure out of the program: ```rux variant StartError { MissingConfig } func Load(present: bool) -> int ! StartError { if !present { fail StartError::MissingConfig; } return 3; } func Main() -> ! StartError { let count = Load(false)?; // fails: the program ends with status 1 PrintLine("loaded {}", count); } ``` On failure, `Main` runs its ordinary cleanup — deferred statements and destructors — and the process exits with status 1. **The error itself is not printed.** A program that wants the user to see why it stopped prints a message before it fails, for instance in a [`catch`](https://rux-lang.dev/docs/lang/errors/handling) arm that then fails with the same error. A [panic](https://rux-lang.dev/docs/lang/errors/panics) is different: it stops the program at once, without cleanup, whatever form `Main` has. ::note **rux 0.4.0 does not check `Main`'s signature yet.**:br A `Main` with parameters, with no result, or with another result type is accepted, but its exit status is then not defined. Use one of the three forms above. :: ## DllMain A shared library built for Windows (`Type = "SharedLibrary"`) may declare a top-level function named `DllMain`. The Windows loader calls it when a process loads or unloads the library, and the library loads only if it returns a non-zero value. Its signature is the one Windows expects: ```rux func DllMain(instance: *opaque, reason: uint32, reserved: *opaque) -> int32 { return 1; } ``` Without a `DllMain`, the library loads as if one had returned 1. On other systems the name has no special meaning. ## See also - [Function declarations](https://rux-lang.dev/docs/lang/functions/declaration) — functions in general - [Errors](https://rux-lang.dev/docs/lang/errors/overview) — fallible results, `fail` and `?` - [Package types](https://rux-lang.dev/docs/packaging/types) — executables and libraries - Learn: [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) # Structs A `struct` declares a named type made of named *fields*. A value of the type holds every field at once. Two structs are different types even when their fields are identical, so a function that asks for a `Point` cannot be handed something else that happens to have an `x` and a `y`. A struct declares only its data. Methods, constructors, operators and destructors are added in a separate [`extend`](https://rux-lang.dev/docs/lang/structs/methods) block. ```text struct = [ attributes ] [ "pub" ] "struct" name [ type-parameters ] "{" { field } "}" field = [ "pub" ] name ":" type ";" ``` ```rux struct Point { x: int; y: int; } struct Box { corner: Point; width: int; height: int; } struct Empty {} ``` Each field is `name: Type` and ends with a semicolon. A field may have any type that has a known size: a primitive, an array, a slice, a pointer, an optional, a tuple, another struct. By convention the type name is PascalCase and field names are camelCase. ## Struct literals A value is built with a *struct literal*: the type's name and a value for every field, in braces. The fields may come in any order, because each is named: ```rux let origin = Point { x: 0, y: 0 }; let corner = Point { y: 2, x: 5 }; let box = Box { corner: corner, width: 4, height: 3 }; ``` Every field must be given. A field has no default value — `x: int = 0;` in a declaration is a syntax error — so a literal that leaves one out is refused: ```text error: initializer for 'Point' is missing required field 'y' error: struct 'Point' has no field 'z' note: available fields are 'x' and 'y' ``` When building a value takes more than listing its fields, give the type a [constructor](https://rux-lang.dev/docs/lang/structs/constructors): `Point(1, 2)` is a constructor call, not a literal. A literal always remains available where every field is visible. ## Fields A field is read with `.`, and fields of nested structs by chaining: `box.corner.x`. A field is assigned like a variable, and the same rule applies: the value 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; let fixed = Point { x: 1, y: 2 }; fixed.x = 5; // error: cannot modify immutable variable 'fixed' ``` A field can also be read through a [reference](https://rux-lang.dev/docs/lang/references/overview) or a [pointer](https://rux-lang.dev/docs/lang/pointers/overview) to the struct with the same `.`; writing needs a `&var T` or a `*var T`. ## Visibility A field is private to its package unless it is marked `pub`, separately from the struct itself: ```rux pub struct Point { pub x: int; y: int; } ``` Another package that imports `Point` can read and write `x` but not `y`. Because a literal must name every field, it cannot build a `Point` at all; it calls a public constructor instead: ```text error: struct field 'y' is private to package 'Shapes' error: struct 'Point' cannot be initialized outside its package because it has private fields help: use a public constructor instead ``` Within its own package every field is visible. See [Visibility](https://rux-lang.dev/docs/lang/modules/visibility). ## Values, not references A struct is a value. Assigning it, passing it to a by-value parameter and returning it copies every field, and the copy is independent of the original: ```rux var a = Point { x: 1, y: 2 }; var b = a; // a copy b.x = 9; // a.x is still 1 ``` A function that should read a large struct without copying it takes `&Point`; one that should change the caller's struct takes `&var Point`. A struct whose type declares that it cannot be copied is moved instead, with `<-`; see [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## Self-reference A struct cannot contain itself by value, since its size would be infinite. A linked structure holds a [pointer](https://rux-lang.dev/docs/lang/pointers/overview) to the next node instead, usually an optional one: ```rux struct Node { value: int; next: (*Node)?; } ``` ::note **rux 0.4.0 does not report a struct that contains itself.**:br`rux check` passes a field such as `next: Node;`, and `rux build` then never finishes. Use a pointer. :: ## Equality Two values of the same struct type compare with `==` and `!=` field by field, in declaration order. Every field must support `==` itself; a field such as a `char8[..]` slice, which has no `==`, makes the comparison unavailable: ```rux let same = origin == Point { x: 0, y: 0 }; // true ``` ```text error: structural equality for 'Named' is unavailable because element type 'char8[..]' has no '==' operator ``` A struct that declares its own `==` in an `extend` block uses that instead; see [Operators](https://rux-lang.dev/docs/lang/interfaces/operators). Structs have no built-in ordering: `<` needs a declared operator. ## Generic structs A struct may take type parameters, written after its name. Each use names the type arguments, in the type and in a literal: ```rux struct Pair { first: T; second: T; } let pair = Pair { first: 1, second: 2 }; ``` See [Generic types](https://rux-lang.dev/docs/lang/generics/types). ## See also - [Methods](https://rux-lang.dev/docs/lang/structs/methods) — functions that belong to a struct - [Constructors](https://rux-lang.dev/docs/lang/structs/constructors) — building values through a function - [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) and [patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — taking a struct apart - [Memory layout](https://rux-lang.dev/docs/lang/memory/layout) — field order, alignment and padding - Learn: [Struct](https://rux-lang.dev/docs/learn/struct), [Structural equality](https://rux-lang.dev/docs/learn/structural-equality) # Methods An `extend` block adds functions to a type. A function in it whose first parameter is named `self` is a *method*, called on a value with `.`: `counter.Advance()`. A function without `self` is a *static function* of the type, called through its name with `::`: `Counter::Zero(5)`. ```text extension = "extend" type [ ":" interface ] "{" { function } "}" receiver = "self" ":" ( type | "&" type | "&" "var" type ) ``` ```rux struct Counter { count: int; step: int; } extend Counter { func Zero(step: int) -> Counter { return Counter { count: 0, step: step }; } func Value(self: &Counter) -> int { return self.count; } func Advance(self: &var Counter) { self.count += self.step; } func Advanced(self: Counter) -> Counter { var next = self; next.Advance(); return next; } } func Main() -> int { var counter = Counter::Zero(5); counter.Advance(); counter.Advance(); let ahead = counter.Advanced(); PrintLine("{} {}", counter.Value(), ahead.Value()); // 10 15 return 0; } ``` A type may have any number of `extend` blocks, in any file of its package. The type's fields stay in the `struct` declaration; an `extend` block holds only functions. `extend` works for every named type — structs, [enums](https://rux-lang.dev/docs/lang/enums/overview), [variants](https://rux-lang.dev/docs/lang/variants/overview), [unions](https://rux-lang.dev/docs/lang/unions/overview) — and for built-in types, as [Extensions](https://rux-lang.dev/docs/lang/structs/extensions) shows. ## Receivers The receiver is always named `self`, always comes first, and has its type written out like any other parameter. The type is the extended type, or a reference to it, and decides what the method may do: | Receiver | The method gets | May change the caller's value | Callable on | | -------------------- | ----------------------------- | ----------------------------- | ----------------- | | `self: &Counter` | the caller's value, to read | no | any `Counter` | | `self: &var Counter` | the caller's value, to change | yes | a `var` `Counter` | | `self: Counter` | a copy, read-only | no | any `Counter` | A `&T` receiver is the usual choice for a method that only reads; `&var T` for one that changes the value in place; `T` for small values such as numbers and enum cases, or for a method that wants its own copy to work on, as `Advanced` does. Inside a method, fields are always reached through `self`. There is no implicit scope: a bare `count` is an unknown name. A receiver is read-only like every parameter, and a `&T` receiver cannot be written through: ```text error: cannot modify immutable receiver 'self' help: take the receiver as 'self: &var Counter' to change the caller's value error: cannot modify data through immutable reference '&Counter' ``` ## Calling methods The value before the dot becomes `self`. It is borrowed when the receiver is a reference and copied when it is a value; the call site writes nothing extra in either case. The remaining arguments go in the parentheses. A method with a `&var T` receiver needs a value it can write to: ```rux let fixed = Counter::Zero(1); fixed.Value(); // fine fixed.Advance(); // error ``` ```text error: cannot call 'Advance' on immutable 'fixed' note: 'Advance' declares a writable receiver '&var Counter' help: declare 'fixed' with 'var' to make it mutable ``` A method that reads may also be called on a value that has no name, such as a literal: `Counter { count: 9, step: 1 }.Value()`. | Kind | Declared | Called as | | --------------- | ------------------------------------ | ------------------ | | Method | `func Value(self: &Counter) -> int` | `counter.Value()` | | Static function | `func Zero(step: int) -> Counter` | `Counter::Zero(5)` | | Constructor | `func Counter(step: int) -> Counter` | `Counter(5)` | A method is reached only through a value. It is not in scope as a free function, and it cannot be called through the type path: ```text error: name 'Value' is not defined in this scope error: call to 'Counter::Value' expects 0 arguments, but 1 was provided ``` Each type has its own methods, so `Rectangle` and `Circle` may both have an `Area` without clashing; the type of the value before the dot decides which runs. Methods of one type overload like functions, by their parameters after `self`; see [Overloading](https://rux-lang.dev/docs/lang/functions/overloading). ## Rules and errors | Mistake | Error | | --------------------------------------------------- | --------------------------------------------------------------------------------------------- | | The receiver given another name, `this: &Counter` | it is an ordinary parameter; `counter.Value()` then `expects 1 argument, but 0 were provided` | | `self` after another parameter | `receiver 'self' must be the first parameter of method 'Value'` | | `self` without a type, `Value(self)` | `expected ':' and the receiver type after 'self' before ')'` | | A receiver of another type | `receiver type '&Other' does not name the extended type 'Counter'` | | A method named without parentheses, `counter.Value` | `struct 'Counter' has no field 'Value'` | ## What else goes in extend | Declaration | Page | | -------------------------------------------------------- | ------------------------------------------------------------------- | | `func T(...) -> T` | [Constructors](https://rux-lang.dev/docs/lang/structs/constructors) | | `func ~T(self: &var T)` | [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors) | | `func +(self: T, other: T) -> T` and the other operators | [Operators](https://rux-lang.dev/docs/lang/interfaces/operators) | | `func [](self: &T, index: uint) -> E` | [Indexers](https://rux-lang.dev/docs/lang/interfaces/indexers) | | `extend T : Interface { … }` | [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) | A generic type's `extend` block names its type parameters, `extend Pair { … }`, and a method may have type parameters of its own; see [Generic methods](https://rux-lang.dev/docs/lang/generics/methods). `pub` on each function makes it visible to other packages — `pub` on the struct does not reach its methods, and calling one that lacks it from another package is `method 'Secret' is private to package 'Shapes'`. ## See also - [Structs](https://rux-lang.dev/docs/lang/structs/overview) — the data a method works on - [Extensions](https://rux-lang.dev/docs/lang/structs/extensions) — methods on built-in types - [References](https://rux-lang.dev/docs/lang/references/overview) — what `&T` and `&var T` receivers borrow - Learn: [Method](https://rux-lang.dev/docs/learn/method), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) # Constructors A *constructor* is a function that builds a value of its type. It is declared in the type's `extend` block with the type's own name, takes no receiver, and returns exactly the type. It is called by the type's name, like a function: `Time(9, 30)`. ```text constructor = [ "pub" ] "func" TypeName "(" [ parameters ] ")" "->" TypeName block // inside extend TypeName ``` ```rux struct Time { hours: int; minutes: int; } extend Time { func Time() -> Time { return Time { hours: 0, minutes: 0 }; } func Time(totalMinutes: int) -> Time { let inDay = totalMinutes % (24 * 60); return Time { hours: inDay / 60, minutes: inDay % 60 }; } func Time(hours: int, minutes: int) -> Time { return Time(hours * 60 + minutes); } } func Main() -> int { let start = Time(7, 95); // 8:35 let later = Time(start.hours * 60 + start.minutes + 135); // 10:50 var midnight: Time; // Time() — 0:00 return 0; } ``` A constructor ends by building the value itself — usually with a [struct literal](https://rux-lang.dev/docs/lang/structs/overview#struct-literals) or by calling another constructor. It does not replace literals; it decides what goes into one, so a rule such as "minutes are below 60" is written once instead of at every literal. | Kind of function | Name | First parameter | Called as | | ---------------- | ------------------- | ------------------------------------ | ------------------ | | Method | anything | `self: &Time`, `&var Time` or `Time` | `start.Show()` | | Static function | anything but `Time` | not `self` | `Time::Midnight()` | | Constructor | `Time`, always | not `self` | `Time(9, 30)` | ## Overloads A type may have any number of constructors, told apart by their parameters like any [overload set](https://rux-lang.dev/docs/lang/functions/overloading). Above, the two-argument constructor converts to minutes and passes the total to the one-argument constructor, so the wrapping past midnight is written once. A call with arguments that no constructor takes is an error naming the candidates, such as `call to 'Time' expects 2 arguments, but 1 was provided` when only the two-argument constructor exists. ## Default construction `var value: T;` with no initializer calls `T()` when the type has a constructor that can be called without arguments: ```rux var midnight: Time; // runs Time() ``` With no such constructor the declaration is still allowed, but it leaves the storage uninitialized, and the compiler refuses to read it before it has been assigned: ```text error: variable 'q' is used before it is initialized ``` See [Initialization](https://rux-lang.dev/docs/lang/bindings/initialization) for how such a local becomes initialized, field by field or as a whole. If more than one constructor can be called with no arguments — `Time()` and `Time(hours: int = 0)` — the declaration is ambiguous: ```text error: default construction of 'Time' is ambiguous note: more than one constructor can be called without arguments help: remove defaults so exactly one constructor accepts no arguments ``` ## Generic types A generic type's constructor is declared in its generic `extend` block and called with the type arguments written out: ```rux struct Pair { first: T; second: T; } extend Pair { func Pair(both: T) -> Pair { return Pair { first: both, second: both }; } } let twins = Pair(7); ``` ## A convenience, not a lock A type with constructors can still be built with a literal wherever its fields are visible, so a constructor's rules can be bypassed inside its package. Making the fields private to the package, and the constructor `pub`, leaves other packages only the constructor; see [Visibility](https://rux-lang.dev/docs/lang/modules/visibility). ## Rules and errors | Mistake | Error | | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | Calling a type that has no constructor | `type 'Point' cannot be called because it has no declared constructor` | | Declaring the constructor outside `extend` | `name 'Time' cannot be declared as a function because it is already a type in this scope` | | A result other than the type | `constructor 'Time' must return exactly 'Time'` | | Two constructors callable with no arguments, and `var t: Time;` | `default construction of 'Time' is ambiguous` | The first error's help names both ways out: `declare a receiverless constructor in its extend block or use explicit initialization`. A function that can fail to build its value — one returning `T?` or `T ! E` — is not a constructor; give it a descriptive name and declare it as a static function, called as `T::Parse(…)`. ## See also - [Structs](https://rux-lang.dev/docs/lang/structs/overview) — struct literals - [Methods](https://rux-lang.dev/docs/lang/structs/methods) — the `extend` block - [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors) — the other end of a value's life - Learn: [Constructor](https://rux-lang.dev/docs/learn/constructor), [Initialization](https://rux-lang.dev/docs/learn/initialization) # Extensions `extend` is not limited to the types a package declares. It can add methods to a built-in type, and from then on every value of that exact type has them: `answer.IsEven()`, `scores[..].Sum()`. ```rux extend int32 { func IsEven(self: int32) -> bool { return self % 2 == 0; } } extend int[..] { func Sum(self: int[..]) -> int { var total = 0; for value in self { total += value; } return total; } } extend var int[..] { func Fill(self: var int[..], value: int) { for i in 0..self.length { self[i] = value; } } } extend char8[..] { func Initial(self: char8[..]) -> char8 { return self[0]; } } func Main() -> int { let n: int32 = 6; PrintLine("{} {}", n.IsEven(), 7i32.IsEven()); // true false let scores = [3, 4, 5]; PrintLine("{} {}", scores[..].Sum(), scores[1..].Sum()); // 12 9 var buffer: int[3] = [0, 0, 0]; buffer[..].Fill(2); PrintLine("{}", "rux".Initial()); // r return 0; } ``` The receiver follows the rules of [Methods](https://rux-lang.dev/docs/lang/structs/methods). Numbers and slices are usually taken by value — `self: int32`, `self: int[..]` — because copying them is as cheap as borrowing them. A slice is a view, so a by-value slice receiver still sees the caller's elements, and a `var T[..]` receiver can write them. ## What can be extended | Type | Extendable | Example | | ------------------------------------------------------------ | ---------------------------- | ---------------------------------------- | | A primitive: integer, float, `bool`, character | yes | `extend int32 { … }` | | A slice of one element type | yes | `extend int[..] { … }` | | A writable slice | yes | `extend var int[..] { … }` | | A struct, enum, variant or union | yes | `extend Point { … }` | | A [type alias](https://rux-lang.dev/docs/lang/types/aliases) | the aliased type is extended | `extend Celsius { … }` adds to `float64` | | An optional, fallible, sum or `()` | no | `cannot extend native type 'int32?'` | | A tuple | no | `extend (int, int)` is refused | | A slice of a type parameter | no | `extend T[..]` is refused | An alias names its type and hides nothing, so `extend Celsius` with `type Celsius = float64;` gives every `float64` in the package those methods. Methods meant for one kind of value belong on a type of its own, such as a struct with a single field. A native optional, fallible, sum or unit has no declaring package to own its methods: ```text error: cannot extend native type 'int32?' note: a sum, optional, fallible, or unit type has no declaring package to own methods or interface implementations help: write a generic function that takes the native type as a parameter ``` `extend T[..]` for every element type is not available; extend each slice type separately, or write a [generic function](https://rux-lang.dev/docs/lang/generics/overview) that takes a `T[..]`: ```text error: cannot extend slice type 'T[..]' because element type 'T' is not defined help: extend a slice of one concrete element type, for example 'extend int[..]' ``` ::note **Fixed-size arrays.**:br rux 0.4.0 passes `extend int[3] { … }` through `rux check`, but the build then fails. Extend the slice type instead, and call the method on a view of the array, `numbers[..]`. :: ## One exact type Methods belong to exactly the type that was extended. `extend int32` does not reach `int` or `int64`, `extend int[..]` does not reach `uint8[..]`, and an array is not a slice: ```text error: type 'int32' has no field 'Double' error: type 'int[3]' has no field 'Sum' ``` An array reaches slice methods through a view of itself: `scores[..].Sum()`, or `scores[2..].Sum()` for part of it. In rux 0.4.0 writability is part of the type for method lookup too. A view of a `var` array is a `var int[..]`, which finds the methods of `extend var int[..]` but not those of `extend int[..]`: ```text error: slice type 'var int[..]' has no member 'Sum' note: available slice members are 'data' and 'length' ``` Bind such a view to a read-only slice first, `let all: int[..] = buffer;`, and call the read-only methods on that. ## Interfaces An extension written `extend T : Interface { … }` makes the type implement an interface, and works for built-in types as for declared ones; see [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview). ## See also - [Methods](https://rux-lang.dev/docs/lang/structs/methods) — receivers and calls - [Slices](https://rux-lang.dev/docs/lang/slices/overview) — read-only and writable views - [Type aliases](https://rux-lang.dev/docs/lang/types/aliases) — what an alias does and does not create - Learn: [Extension](https://rux-lang.dev/docs/learn/extension) # Enums An `enum` declares a closed set of named values, its *cases*, stored as an integer. A `Direction` is `North`, `East`, `South` or `West` and nothing else, and the compiler knows every case, so a `match` can be checked for completeness. An enum case carries no data; cases with data belong to a [variant](https://rux-lang.dev/docs/lang/variants/overview). ```text enum = [ attributes ] [ "pub" ] "enum" name [ ":" integer-type ] "{" [ case { "," case } ] "}" case = name [ "=" constant-expression ] ``` ```rux enum Direction { North, East, South, West } enum Status: uint16 { Ok = 200, Created, NotFound = 404, ServerError = 500 } ``` Cases are separated by commas, with no comma after the last (`trailing comma is not allowed in enum declarations`). By convention the type and its cases are PascalCase. ## Backing type and values The integer type after the colon is the enum's *backing type*: every value of the enum is stored as one value of it, so `sizeof(Status)` is 2. It must be an integer type — `enum 'Wide' base type must be an integer type` otherwise. Without one, the backing type is `int`. Each case has an integer value. A case may give it with `=`; a case without one takes the previous case's value plus one, and the first case defaults to 0: | Case | Value | | ------------------ | ----- | | `Direction::North` | 0 | | `Direction::West` | 3 | | `Status::Ok` | 200 | | `Status::Created` | 201 | | `Status::NotFound` | 404 | Values may be negative when the backing type is signed: ```rux enum Ordering: int8 { Less = -1, Equal, Greater } ``` Once other programs read these numbers — a file format, a network protocol, a C library — they are part of the program's interface. Inserting an unnumbered case in the middle renumbers every unnumbered case after it, so give each case whose value matters an explicit `=`. ::note **Values must fit the backing type.**:br rux 0.4.0 does not check this yet: `Big = 300` in a `uint8` enum, or an implicit successor of 255, is silently truncated. Keep every value inside the backing type's range. :: ## Naming a case A case is named through its type: `Direction::North`. Inside a pattern whose subject is already known to be a `Direction`, the short form `.North` is allowed; everywhere else the case is written in full: ```rux var heading = Direction::North; let back = heading == Direction::North; // true ``` ```text error: '.North' must be written in full, as in 'Direction::North' ``` A case on its own, `North`, is an unknown name. ## Conversions `as` converts in both directions between an enum and any integer type: ```rux let code = Status::NotFound as uint16; // 404 let received: uint16 = 404; let status = received as Status; // Status::NotFound ``` No other conversion applies: an enum does not convert to a float, and a case is not its number, so `Status::Ok == 200` is `operator '==' cannot compare left operand 'Status' with right operand 'int'`. **`as` does not check.** Converting an integer that is no case's value gives an enum value that is none of the cases. Nothing goes wrong until a `match` meets it: a match without an `else` arm then stops the program with `Panic: no match arm matched value of 'Status'`. Check a number from outside the program before converting it. ## Comparison Enum values compare with `==`, `!=`, `<`, `<=`, `>` and `>=`, which order them by their values: ```rux let failed = status >= Status::NotFound; // true for 404 and 500 ``` ## Matching A `match` on an enum must cover every case, or end with an `else` arm. An arm names one case: ```rux func TurnRight(direction: Direction) -> Direction { return match direction { .North => Direction::East, .East => Direction::South, .South => Direction::West, .West => Direction::North }; } ``` ```text error: match on 'Direction' is not exhaustive; missing Direction::West ``` Leaving `else` out when every case matters means adding a case later makes every incomplete `match` stop compiling, at the place that needs the new arm. See [`match`](https://rux-lang.dev/docs/lang/patterns/match). ## Methods An enum can be extended with methods like any other type. A receiver taken by value, `self: Direction`, is natural for something as small as a number: ```rux extend Direction { func Opposite(self: Direction) -> Direction { return (((self as int) + 2) % 4) as Direction; } } ``` An enum value cannot be printed with `{}` directly; give it a method that returns its name. ## Rules and errors | Mistake | Error | | ---------------------------------------- | -------------------------------------------------------------------------------------------- | | A case with a payload, `Circle(float64)` | `enum 'Shape' cannot declare payloads`, with `help: use 'variant' for cases that carry data` | | Type parameters, `enum Gen` | `enum 'Gen' cannot declare type parameters` | | A non-integer backing type | `enum 'Wide' base type must be an integer type` | | A case declared twice | `duplicate enumerator 'A' in enum 'Twice'` | | The short form outside a pattern | `'.North' must be written in full, as in 'Direction::North'` | | A missing case in a `match` | `match on 'Direction' is not exhaustive; missing Direction::West` | ## See also - [Variants](https://rux-lang.dev/docs/lang/variants/overview) — cases that carry data - [Casts](https://rux-lang.dev/docs/lang/expressions/casts) — `as` in general - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — case patterns - Learn: [Enum](https://rux-lang.dev/docs/learn/enum), [Enum value](https://rux-lang.dev/docs/learn/enum-value) # Variants A `variant` declares a type whose value is exactly one of a closed list of *cases*, and each case may carry data of its own. A thermometer reading is missing, or an exact temperature, or a range between two: three cases, two of them with numbers. A struct holds all of its fields at once; a variant holds one case at a time, and only that case's data. ```text variant = [ attributes ] [ "pub" ] "variant" name [ type-parameters ] "{" [ case { "," case } ] "}" case = name // unit | name "(" type { "," type } ")" // positional | name "{" { name ":" type ";" } "}" // named ``` ```rux variant Reading { Missing, Exact(float64), Between { low: float64; high: float64; } } ``` Cases are separated by commas, with no comma after the last. Named fields inside a case end with `;`, as struct fields do. | Case | Shape | Carries | Built as | | --------- | ------------------------------------- | ---------------- | -------------------------------------------- | | `Missing` | unit — a bare name, like an enum case | nothing | `Reading::Missing` | | `Exact` | positional — values by position | one `float64` | `Reading::Exact(21.5)` | | `Between` | named — fields by name | `low` and `high` | `Reading::Between { low: 20.0, high: 24.0 }` | A positional case may carry any number of values, `Jump(int, int)`. The three shapes may be mixed freely in one variant. ## Building a case A case is built through its type's name, with the syntax of its shape: nothing for a unit case, call syntax for a positional one, struct-literal syntax for a named one. The fields of a named case may come in any order, and every field must be given: ```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 }; } ``` A unit case may also be written with empty parentheses, `Reading::Missing()`. The payload is checked against the case's declaration like a call's arguments: ```text error: argument 1 to variant case 'Reading::Exact' has type 'int', but field 1 requires 'float64' error: initializer for 'Reading::Between' is missing required field 'high' ``` ::note **Named cases take named fields.**:br rux 0.4.0 does not check this yet: a named case can be built, and matched, positionally, as in `Reading::Between(20.0, 24.0)`. Use the field names. :: ## Reading the data A variant has no fields of its own to read: the data belongs to one case, and which case is held is known only at run time. `reading.0` is refused with `type 'Reading' has no field '0'`. The data is taken out with [`match`](https://rux-lang.dev/docs/lang/variants/matching), which tests the case and binds its payload in one step: ```rux func Describe(reading: Reading) -> float64 { return match reading { .Missing => 0.0, .Exact(value) => value, .Between { low, high } => (low + high) / 2.0 }; } ``` ## Equality Two variant values are equal when they hold the same case and that case's payloads are equal, compared in declaration order. Different cases are never equal, whatever they carry: ```rux let precise = Reading::Exact(21.5); let same = precise == Reading::Exact(21.5); // true let other = precise == Reading::Missing; // false ``` Every payload type of every case must support `==`. A variant with a case carrying a `char8[..]`, which has no `==`, cannot be compared at all: ```text error: variant equality for 'Token' is unavailable because payload type 'char8[..]' in case 'Token::Word' has no '==' operator ``` Variants have no built-in ordering. ## Generic variants A variant may take type parameters, used in its payloads: ```rux variant Lookup { Found(T), Missing } ``` A case with a payload infers the type arguments from it: `Lookup::Found(5)` is a `Lookup`. A case without one takes them from the type it is assigned, passed or returned as, or names them itself: ```rux let hit = Lookup::Found(5); // Lookup let miss: Lookup = Lookup::Missing; let empty = Lookup::Missing(); ``` ```text error: variant case 'Lookup::Missing' requires 1 type argument, but 0 were provided help: annotate the destination, as in 'let value: Lookup<...> = Lookup::Missing;', or write 'Lookup::Missing<...>()' ``` An annotation wins over the payload: `let small: Lookup = Lookup::Found(7);` makes the `7` a `uint8`. See [Generic types](https://rux-lang.dev/docs/lang/generics/types). ## The tag is private A variant value stores a *tag* that says which case it holds, followed by storage for the largest case. The tag belongs to the compiler: a variant has no backing type and no case values, and it does not convert to or from an integer. | Written | Error | | ------------------------------- | ----------------------------------------------------- | | `variant Code: uint8 { A, B }` | `variant 'Code' cannot specify a base type` | | `variant Numbered { A = 1, B }` | `variant 'Numbered' cannot assign case discriminants` | | `precise as int` | `cannot cast variant 'Reading' to scalar type 'int'` | The tag's numbers are not part of any contract and must never be written to a file or sent to another program. Data that crosses a program's boundary as numbers is encoded with an [enum](https://rux-lang.dev/docs/lang/enums/overview), whose values are chosen in the source, and translated to and from the variant. A variant is copyable when every payload is, and is moved otherwise; destroying one destroys only the active case's payload. See [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## Recursive variants A variant cannot contain itself by value, but a case may hold a pointer to another value of the same variant: ```rux variant Tree { Leaf(int), Node { left: *Tree; right: *Tree; } } ``` ## Variants, enums, structs and unions | Declaration | Holds | Data per alternative | Which one is held is… | | ----------- | ------------------- | -------------------- | --------------------------------------- | | `struct` | every field at once | — | not a question: all are | | `enum` | one case | none | its integer value, chosen in the source | | `variant` | one case | any | a private tag, checked by `match` | | `union` | one member's bytes | any | not recorded; the program must know | ## See also - [Matching variants](https://rux-lang.dev/docs/lang/variants/matching) — patterns, bindings and exhaustiveness - [Enums](https://rux-lang.dev/docs/lang/enums/overview) — cases without data, with integer values - [Sum types](https://rux-lang.dev/docs/lang/sums/overview) — a value of one of several types, without case names - [Unions](https://rux-lang.dev/docs/lang/unions/overview) — overlapping storage without a tag - Learn: [Variant](https://rux-lang.dev/docs/learn/variant), [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Generic type](https://rux-lang.dev/docs/learn/generic-type) # Matching variants A variant's payload can only be read once it is known which case is held, and `match` does both in one step. A *case pattern* fits only values of its case, and when it fits, the names inside it are new bindings holding that case's data. No pattern can both fit a `Turn` and bind a `Forward`'s data, which is the guarantee a variant gives. ```rux variant Command { Forward(int), Turn, Jump(int, int), Stop } func Cost(command: Command) -> int { return match command { .Forward(steps) => steps, .Turn => 1, .Jump(across, _) => across * 2, .Stop => 0 }; } ``` This page covers what is particular to variants. The `match` expression and statement, arms, guards and every other kind of pattern are described in [`match`](https://rux-lang.dev/docs/lang/patterns/match) and [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns). ## Case patterns ```text case-pattern = ( "." | VariantType "::" ) case-name [ payload-pattern ] payload-pattern = "(" pattern { "," pattern } ")" // positional case | "{" field-pattern { "," field-pattern } "}" // named case field-pattern = name [ ":" pattern ] ``` | Pattern | Fits | Binds | | ------------------------ | -------------------------------- | --------------------------------- | | `.Stop` | the unit case `Stop` | nothing | | `.Forward(steps)` | every `Forward` | `steps`, its one value | | `.Jump(across, _)` | every `Jump` | `across`; `_` ignores the second | | `.Jump(across, 0)` | a `Jump` whose second value is 0 | `across` | | `.Say { text }` | every `Say` | the field `text`, as `text` | | `.Say { text: message }` | every `Say` | the field `text`, as `message` | | `.Say { loud: true }` | a `Say` whose `loud` is `true` | nothing; other fields are ignored | | `Command::Stop` | the unit case `Stop` | nothing | - **The short form `.Case`** is allowed wherever the subject's type is known to be the variant. `Type::Case` names the type as well, and is needed where the subject is a [sum](https://rux-lang.dev/docs/lang/sums/overview) containing the variant. - **A positional payload lists every value**, each as a pattern: a name binds it, `_` ignores it, a literal or a nested pattern tests it. The count must match the case: `pattern for 'Command::Jump' expects 2 fields, but found 1`. - **A named payload may leave fields out**; an omitted field matches anything. A field written alone binds a variable of its own name, and `field: pattern` binds or tests it under another name. The fields may come in any order. - **A unit case is written without parentheses**, `.Stop`. Patterns nest. A payload pattern can be another case pattern, an [enum](https://rux-lang.dev/docs/lang/enums/overview) case, a tuple or a struct pattern: ```rux enum Direction { North, East, South, West } variant Command { Move(Direction, int), Jump(int, int), Say { text: char8[..]; loud: bool; }, Stop } func Describe(command: &Command) -> char8[..] { return match command { .Move(.North, 0) => "face north", .Move(_, steps) if steps > 10 => "a long walk", .Move(_, _) => "a walk", .Jump(across, 0) => across > 3 ? "a long flat jump" : "a flat jump", .Jump(_, _) => "a jump", .Say { loud: true } => "a shout", .Say { text } => text, Command::Stop => "stop" }; } ``` Arms are tried from top to bottom, and the first that fits runs, so a specific arm goes above the general one for the same case. An arm may add a guard, `if condition`, which is checked after the pattern fits and may use its bindings; when the guard is false the next arm is tried. ## Bindings A binding exists only inside its own arm: `steps` is unknown in the `.Turn` arm, and using it there is `name 'steps' is not defined in this scope`. When the subject is a value the `match` owns, the bindings take the payload over. When the subject is a reference, as `command: &Command` above, the case is matched in place and the bindings read the payload where it lies; nothing is copied out of the caller's value. A bare name in a pattern always binds a new variable. A name that is a case of the subject is refused rather than silently matching everything: ```text error: pattern 'Stop' cannot bind a new variable because 'Stop' is a case of variant 'Command' help: write 'Command::Stop' to select the case ``` ## Exhaustiveness A `match` on a variant — an expression or a statement — must cover every case. A missing case is named: ```text error: match on 'Command' is not exhaustive; missing Command::Stop ``` A case counts as covered by an arm that fits **every** value of it: a unit pattern, or a payload pattern made only of bindings and `_`, without a guard. An arm with a guard, or one that tests a payload value such as `.Move(_, 0)`, covers only part of the case, so the case still needs a general arm below it. ::note **Payload tests do not combine.**:br In rux 0.4.0, `.Light(true)` and `.Light(false)` together do not count as covering `Light`, although together they match every value — unlike the same two tests on a [tuple](https://rux-lang.dev/docs/lang/tuples/overview). End the case with a general arm such as `.Light(_)`. :: An `else` arm covers whatever the arms above it leave, and makes a match exhaustive. Prefer naming every case: with `else`, a case added to the variant later falls silently into the `else` arm, while without it every incomplete `match` stops compiling at the place that needs the new arm. The default arm is spelled `else`; `_ =>` is `use 'else' for the default match arm`. Two arms with the same pattern are `duplicate pattern in match`. ## See also - [Variants](https://rux-lang.dev/docs/lang/variants/overview) — declaring and building cases - [`match`](https://rux-lang.dev/docs/lang/patterns/match) — the expression and statement - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — every kind of pattern - Learn: [Variant match](https://rux-lang.dev/docs/learn/variant-match), [Guard](https://rux-lang.dev/docs/learn/guard), [Exhaustive](https://rux-lang.dev/docs/learn/exhaustive) # Unions A `union` declares a type whose *members* all share one piece of storage. A value holds the bytes of one member, and reading any member reads those same bytes as that member's type — nothing is converted and nothing is checked. A union records no tag, so the program, or the foreign interface it talks to, is responsible for knowing which member is meaningful. ```text union = [ attributes ] [ "pub" ] "union" name "{" [ member { "," member } [ "," ] ] "}" member = name ":" type ``` ```rux union Bits { asFloat: float32, asUnsigned: uint32, asBytes: uint8[4] } ``` Members are separated by commas, unlike struct fields; a `;` after a member is `expected ',' between union fields before ';'`. ## Building a union A union literal names exactly one member and gives its value: ```rux let one = Bits { asFloat: 1.0f32 }; ``` Naming none, or more than one, is an error: `union initializer for 'Bits' must select exactly one field, but 2 were provided`. `var bits: Bits;` declares a union without a value. Writing any one member initializes it, and from then on every member may be read. ## Reading and writing members Members are read and written with `.`, like struct fields. Every member starts at the same address, so a write through one member is seen through all the others, reinterpreted: ```rux let one = Bits { asFloat: 1.0f32 }; PrintLine("{:x}", one.asUnsigned); // 3f800000, the bit pattern of 1.0 PrintLine("{}", one.asBytes[3]); // 63, its high byte on a little-endian target var bits = Bits { asUnsigned: 0x40000000u32 }; PrintLine("{}", bits.asFloat); // 2.0 bits.asBytes[3] = 0x3Fu8; PrintLine("{}", bits.asFloat); // 0.5 ``` Reading a member other than the one last written is not an error; it is the reason a union exists. The result depends on the target's byte order and on the members' representations, so code that relies on it is code about a particular layout. A member narrower than the union leaves the rest of the storage as it was: writing a `uint8` member of a union that also holds a `uint64` changes only the low byte. ## Layout A union is as large as its largest member, rounded up to its alignment, and as aligned as its most-aligned member. `sizeof(Bits)` is 4. See [Memory layout](https://rux-lang.dev/docs/lang/memory/layout). A union is a value: assigning it copies its bytes, and the copy is independent of the original. ## No ownership A union does not know which member it holds, so it never runs a destructor for any member. A member whose type has a destructor is stored without it ever running; keep such values out of unions, or destroy them by hand. ## When to use a union | Use | Instead | | -------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | A C `union` in a foreign interface | — | | Examining the bits of a value of another type | — | | One of several alternatives, known by the compiler | a [variant](https://rux-lang.dev/docs/lang/variants/overview), whose tag is checked by `match` | | A value of one of several types | a [sum type](https://rux-lang.dev/docs/lang/sums/overview) | ## See also - [Variants](https://rux-lang.dev/docs/lang/variants/overview) — alternatives with a tag - [FFI](https://rux-lang.dev/docs/lang/ffi/overview) — unions in foreign interfaces - [Memory layout](https://rux-lang.dev/docs/lang/memory/layout) — sizes and alignment - Learn: [Union](https://rux-lang.dev/docs/learn/union) # Tuples A tuple groups a fixed number of values, possibly of different types, into one value without declaring a type for it. The elements have positions but no names. The type is written like the value: a parenthesized, comma-separated list. ```text tuple-type = "(" ")" | "(" type "," ")" | "(" type "," type { "," type } [ "," ] ")" tuple-value = "(" ")" | "(" expression "," ")" | "(" expression "," expression { "," expression } [ "," ] ")" ``` ```rux let pair: (int, float64) = (1, 2.5); let triple = ("Rux", 3, true); // (char8[..], int, bool) let one = (5,); // a tuple of one element ``` A one-element tuple needs a trailing comma; `(5)` is just `5` in parentheses. ## Elements Elements are read by position, counting from zero: `.0`, `.1`, and so on. On a `var` tuple they can be assigned the same way. Tuples nest, and so do the accesses: ```rux var point = (0, 0); point.0 = 4; point = (point.0, 9); // (4, 9) let nested: (int, (char8[..], bool)) = (1, ("deep", true)); let word = nested.1.0; // "deep" ``` An index beyond the last element is a compile-time error: `tuple index 2 is out of range for a tuple with 2 elements`. ## Type identity A tuple type is its element types, in order. `(int, float64)` and `(float64, int)` are different types, and two tuples with the same element types are the same type wherever they were written — a tuple has no name to tell it apart: ```text error: cannot assign '(int, float64)' to '(float64, int)' ``` A tuple is a value. Assigning it, passing it and returning it copies every element, and the copy is independent of the original. A tuple is how a function returns several values: ```rux func Divide(dividend: int, divisor: int) -> (int, int) { return (dividend / divisor, dividend % divisor); } ``` The usual way to use such a result is to take it apart at once, `let (quotient, remainder) = Divide(17, 5);`; see [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring). ## The unit type `()` is the tuple with no elements, the *unit* type, and its only value is also written `()`. It is the success type of a fallible function that produces nothing on success: `! E` is exactly `() ! E`. A function may return `()` explicitly, `func Reset() -> ()`, which is not the same as leaving out the result type; see [Function declarations](https://rux-lang.dev/docs/lang/functions/declaration#the-result). `sizeof(())` is 0. ## Equality Two tuples of the same type compare with `==` and `!=`, element by element from left to right, stopping at the first difference. Every element type must support `==`: ```rux let same = (1, 'a') == (1, 'a'); // true let differ = (1, 2) != (1, 3); // true ``` ```text error: structural equality for '(int, char8[..])' is unavailable because element type 'char8[..]' has no '==' operator ``` Tuples have no built-in ordering. `(1, 2) < (1, 3)` is `operator '<' is not defined for tuple '(int, int)'`, with the note `tuples have structural equality but no built-in ordering`. ## Matching A tuple pattern takes a tuple apart in a `match` arm and tests its elements at the same time. A `match` on a tuple is exhaustive when the combinations of its arms cover every value, so the four `(bool, bool)` combinations need no `else`: ```rux func Describe(flags: (bool, bool)) -> char8[..] { return match flags { (true, true) => "both", (true, false) => "first", (false, true) => "second", (false, false) => "neither" }; } ``` See [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns). ## Tuples or structs Both group several values. A tuple's elements are anonymous, so nothing at the use says what `.1` means; a struct's are named, and the struct is a type of its own that cannot be mixed up with another of the same shape. | A tuple fits when… | A struct fits when… | | ------------------------------------------------------- | ------------------------------------------------- | | two or three values, their meaning obvious from context | more values, or meanings that need labels | | the grouping is local, such as a function's results | the type is used across the program | | no behaviour is attached | the type needs methods, constructors or operators | | any `(int, int)` will do | a `Point` must not be confused with a `Size` | Tuples cannot be extended with methods; see [Extensions](https://rux-lang.dev/docs/lang/structs/extensions). ## Layout A tuple's elements are stored in order, each at the next offset its alignment allows, like the fields of a struct: `sizeof((uint8, uint32))` is 8. See [Memory layout](https://rux-lang.dev/docs/lang/memory/layout). ## See also - [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) — unpacking a tuple into bindings - [Structs](https://rux-lang.dev/docs/lang/structs/overview) — named fields and named types - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — tuple patterns - Learn: [Tuple](https://rux-lang.dev/docs/learn/tuple), [Destructure](https://rux-lang.dev/docs/learn/destructure), [Tuple pattern](https://rux-lang.dev/docs/learn/tuple-pattern) # Arrays An *array* `T[N]` is a fixed number `N` of values of one type `T`, stored side by side. The count is part of the type, so `int32[3]` and `int32[4]` are different types, and an array never grows or shrinks. An array *is* its elements: it holds them inline, with no pointer, header or allocation, and it lives wherever its binding lives — in a local, inside a struct, inside another array. ```text array-type = type "[" constant-expression "]" array-literal = "[" [ expression { "," expression } [ "," ] ] "]" repeat-literal = "[" expression ";" constant-expression "]" ``` ```rux let primes: int32[4] = [2, 3, 5, 7]; let rgb: uint8[3] = [255, 128, 0]; let zeros: float64[8] = [0.0; 8]; ``` ## The type `N` must be a non-negative integer known at compile time: a literal or a [constant](https://rux-lang.dev/docs/lang/bindings/constants). A run-time value is rejected: ```rux const Limit: uint = 100; var flags: bool[Limit] = [false; Limit]; ``` ```text error: array length must be a non-negative compile-time integer ``` A range where the length goes, as in `int[0..4]`, gets the same error with the help line `write 'T[..]' for a slice` — `T[..]` is the [slice](https://rux-lang.dev/docs/lang/slices/overview) type, a view of some number of elements. The element type can be any type, including another array. Each `[N]` wraps everything written before it, so a nested array type reads inside out: `int32[4][3]` is three rows of four `int32` values. Indexing reads outside in: `grid[row]` is an `int32[4]`, and `grid[row][column]` is one `int32`. ```rux let grid: int32[4][3] = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ]; ``` ## Array literals `[a, b, c]` lists the elements in order. Its type is `T[N]`, where `N` is the number of elements and `T` comes from the context — an annotation, a parameter, a field — or, without one, from the first element: ```rux let inferred = [2, 3, 5, 7]; // int[4] let reals = [1.5, 2.5]; // float64[2] let bytes: uint8[3] = [1, 2, 255]; // the context types every element ``` Every element must convert to one element type, and the count must match the type exactly: ```text error: array element 2 has type 'bool8', but element 1 established element type 'int' error: cannot assign 'int[3]' to 'int32[4]' ``` ### Repeated literals `[value; count]` builds an array of `count` copies of one value. The value is evaluated **exactly once**, and the result is copied into every element, so `[Roll(); 4]` calls `Roll` once and its four elements are always equal. `count` follows the same rule as `N`: a non-negative compile-time integer. ```rux let zeros: int32[16] = [0; 16]; let rolls = [Roll(); 4]; // one call, four copies var board: int32[3][3] = [[0; 3]; 3]; // nests: three copies of a row ``` ```text error: array repeat count must be a non-negative compile-time integer ``` The element type must be copyable; move-only elements are listed, each handed over with `<-`, or written in a loop. A count of zero still evaluates the value once, and destroys it if its type needs destruction. ::note **Moving into an annotated literal.**:br rux 0.4.0 refuses a move into an array literal that has a type annotation: with a move-only `k`, `let b: Key[1] = [<-k];` reports `value 'k' is used after it was moved`. The same literal without the annotation, `let b = [<-k];`, is accepted. :: ## Value semantics An array is a value, like a [struct](https://rux-lang.dev/docs/lang/structs/overview) or a [tuple](https://rux-lang.dev/docs/lang/tuples/overview). Assigning it, passing it by value and returning it copy **every element**, and the two arrays share nothing afterwards: ```rux var scores: int32[3] = [10, 20, 30]; var copy = scores; copy[0] = 99; // scores is still [10, 20, 30] ``` The binding decides mutability for the whole array: through a `let` binding no element can change, while a `var` binding allows both element writes and whole-array assignment. ```text error: cannot modify immutable variable 'primes' help: declare 'primes' with 'var' to make it mutable ``` To hand an array to a function without copying it, take a [reference](https://rux-lang.dev/docs/lang/references/overview) to it, `&T[N]` or `&var T[N]`, or pass a [slice](https://rux-lang.dev/#arrays-as-slices), which also accepts every length. ```rux func Bump(values: &var int32[4]) { values[0] += 100; } ``` Two arrays of the same type are equal when every element is: `==` and `!=` compare element by element. Arrays have no ordering. ::note **Comparing wide arrays.**:br rux 0.4.0 compares an array wider than 16 bytes incorrectly — only its first eight bytes take part — and accepts `<`, `<=`, `>` and `>=` on arrays, comparing their bytes as one number. Compare such arrays element by element. :: ## Length `.length` is the element count `N`, a `uint`. It is fixed by the type, so it never changes and costs nothing to read: ```rux let buffer: float64[8] = [0.0; 8]; let count = buffer.length; // 8 ``` `.length` is an array's only member. An array has no `.data`: its first element's address is `@a[0]`, and `a[..].data` is the data pointer of a view of it. ## Indexing `a[i]` is the element at position `i`, counting from zero, so valid indexes run from `0` to `a.length - 1`. The index may have any integer type. The result is a place: it can be read, written with `=`, `<-` or a compound assignment when the array is writable, borrowed, and have its address taken with `@`. ```rux var row: int32[3] = [10, 20, 30]; let first = row[0]; row[2] = 99; row[1] += 5; ``` Every index is checked, on every target and in every build profile: - A **constant** index into an array is checked when the program is compiled: ```text error: index 7 is out of range for an array of 4 elements help: valid indexes are 0 through 3 ``` - An index computed at run time is checked as the program runs. One that does not name an element — a negative index included — stops the program instead of reading the memory beyond the array, and the message names the function, file, line and column of the subscript: ```rux func At(values: int32[4], i: uint) -> int32 { return values[i]; } func Main() -> int { let primes: int32[4] = [2, 3, 5, 7]; return At(primes, 4) as int; } ``` ```text Panic: index out of range at At (Src/Main.rux:2:18) ``` :brSee [Panics](https://rux-lang.dev/docs/lang/errors/panics). A release build removes a check it can prove always passes. The index must be an integer or a [range](https://rux-lang.dev/docs/lang/ranges/overview); anything else is `index for type 'int[2]' must be an integer or range, but has type 'float64'`. Indexing with a range produces a slice — see [below](https://rux-lang.dev/#arrays-as-slices). ## Iteration `for` walks an array's elements in order, binding each one to the loop variable. To know the position as well, walk the indexes with a range: ```rux var sum: int32 = 0; for prime in primes { sum += prime; } for i in 0..primes.length { PrintLine("primes[{}] = {}", i, primes[i]); } ``` A nested array yields whole rows, each an ordinary array. See [Loops](https://rux-lang.dev/docs/lang/statements/loops). ## Arrays as slices An array is contiguous elements with a known count, so it can always be viewed as a [slice](https://rux-lang.dev/docs/lang/slices/overview) — a pointer to the first element and a length, with nothing copied. - **Implicitly, read-only.** A `T[N]` converts to a read-only `T[..]` wherever one is expected: an argument, a binding, a return. One function taking a slice therefore serves arrays of every length. - **Explicitly, with a range.** `a[..]` views the whole array, and `a[i..j]`, `a[i..]`, `a[..j]` and `a[..=j]` view part of it. The view is writable, `var T[..]`, when the array place is — a `var` local, or an array reached through `&var` — and read-only otherwise. ```rux func Total(values: int32[..]) -> int32 { var sum: int32 = 0; for value in values { sum += value; } return sum; } func Fill(values: var int32[..], value: int32) { for i in 0..values.length { values[i] = value; } } func Main() -> int { var storage: int32[5] = [1, 2, 3, 4, 5]; let whole = Total(storage); // the array, viewed read-only let tail = Total(storage[2..]); // 3 + 4 + 5 Fill(storage[..2], 0); // a writable view of the first two return 0; } ``` A writable parameter never takes the array itself, which only converts to a read-only view; hand it `storage[..]`: ```text error: no matching overload for 'Fill' with argument types (int32[5], int) ``` ## Flexible tails `T[]`, with no count, is a *flexible tail*. It is allowed in exactly one place — as the last field of a struct — and describes elements that follow the struct in memory, in storage allocated larger than the struct itself: ```rux struct Packet { length: uint32; bytes: uint8[]; } ``` The tail contributes its alignment to the struct but no storage, so `sizeof(Packet)` is 4. It has no length and no members of its own — the struct records the count, here in `length`. Reach the elements by taking the tail's address, a `*var (uint8[])`, converting it to an element pointer, and viewing as many elements as were allocated: ```rux import Memory::{ Alloc, Free }; func Main() -> int { let count: uint = 3; let packet = Alloc(sizeof(Packet) + count) as *var Packet; if packet == null { return 1; } defer Free(packet); packet.length = 3; let bytes = (@packet.bytes as *var uint8)[..count]; bytes[0] = 7; return 0; } ``` A flexible tail anywhere else — a field that is not last, a union member, a local, a parameter, a return type, an operand of `sizeof` — is an error: ```text error: flexible array type is only allowed as the final field of a struct ``` ::note **Indexing a tail directly.**:br rux 0.4.0 accepts `packet.bytes[i]`, checks `i` against a length of zero, and so stops the program with `Panic: index out of range` for every index. Index the view built from the tail's address, as above. :: ## See also - [Slices](https://rux-lang.dev/docs/lang/slices/overview) — the `T[..]` view an array is passed as - [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) — the bounds used to slice an array and to walk its indexes - [Tuples](https://rux-lang.dev/docs/lang/tuples/overview) — a fixed sequence of values of different types - [Layout](https://rux-lang.dev/docs/lang/memory/layout) — the size and alignment of an array and of a struct with a flexible tail - Learn: [Array](https://rux-lang.dev/docs/learn/array), [Array repeat](https://rux-lang.dev/docs/learn/array-repeat), [Array nested](https://rux-lang.dev/docs/learn/array-nested) # Slices A *slice* is a view of a run of contiguous elements that belong to something else — an array, an allocation, the text of a string literal. It holds two things: where the first element is, and how many elements there are. It owns none of them, so copying a slice copies only those two words, whatever its length. There are two slice types: | Type | Name | Through the view you may | | ----------- | --------------- | --------------------------- | | `T[..]` | read-only slice | read the elements | | `var T[..]` | writable slice | read and write the elements | ```text slice-type = [ "var" ] type "[" ".." "]" slicing = expression "[" range-expression "]" ``` The `..` reads as "some number of": the element type is fixed, the count is carried in the value. One function taking an `int32[..]` therefore accepts every length. ```rux func Total(values: int32[..]) -> int32 { var sum: int32 = 0; for value in values { sum += value; } return sum; } ``` ## Making a slice | From | Written | Gives | | ------------------------------------------------------------------------ | ------------------------------- | ---------------------------------------------------------- | | an [array](https://rux-lang.dev/docs/lang/arrays/overview), implicitly | `Total(numbers)` | `T[..]` of the whole array | | an array, with a range | `numbers[..]`, `numbers[1..4]` | `var T[..]` from a writable array place, `T[..]` otherwise | | another slice, with a range | `view[1..]` | the same writability as `view` | | a [pointer](https://rux-lang.dev/docs/lang/pointers/slicing) and a count | `p[..n]`, `p[a..b]` | `var T[..]` from `*var T`, `T[..]` from `*T` | | a [string literal](https://rux-lang.dev/docs/lang/types/text) | `"hello"` | `char8[..]`, always read-only | | an array literal | `let v: int32[..] = [1, 2, 3];` | a view of the literal's elements | | the empty literal | `let e: var int32[..] = [];` | a view of nothing, with `null` data | A string literal is a `char8[..]` — or a `char16[..]` or `char32[..]` with a `c16` or `c32` prefix — over text stored in a read-only section of the program. Its `.length` counts code units, not characters. See [Text](https://rux-lang.dev/docs/lang/types/text). ## Slicing Indexing an array, a slice or a pointer with a [range](https://rux-lang.dev/docs/lang/ranges/overview) produces a slice of part of it. An omitted start means the first element, an omitted end the last: | Expression | Elements of `[10, 20, 30, 40, 50]` | Meaning | | ---------------- | ---------------------------------- | --------------------------------- | | `numbers[1..4]` | 20, 30, 40 | indexes 1 up to, not including, 4 | | `numbers[1..=3]` | 20, 30, 40 | indexes 1 to 3 inclusive | | `numbers[2..]` | 30, 40, 50 | from index 2 to the end | | `numbers[..2]` | 10, 20 | from the start up to 2 | | `numbers[..=1]` | 10, 20 | from the start to 1 inclusive | | `numbers[..]` | all five | the whole sequence | A range held in a variable slices the same way: with `let r: int..int = 1..4;`, `numbers[r]` is `numbers[1..4]`. A slice of a slice counts from the start of the **view**, not of the storage beneath it: ```rux let numbers: int32[5] = [10, 20, 30, 40, 50]; let middle = numbers[1..4]; // 20, 30, 40 let inner = middle[1..]; // 30, 40 ``` Nothing is copied: every one of these views points into `numbers`. A range subscript is checked like an index. `s[start..end]` needs `start <= end <= s.length`, and an inclusive end must be below the length. Bounds written as literals that run backwards are rejected while compiling; anything else that falls outside the sequence stops the program: ```text error: range start cannot be greater than its end ``` ```text Panic: index out of range ``` ::note **A constant end past the array.**:br rux 0.4.0 does not yet check a constant end against a fixed array while compiling: `numbers[1..9]` on an `int32[5]` compiles and stops the program when it runs. :: ## Members | Member | Type | Meaning | | --------- | ------------------------------------------ | -------------------------------- | | `.length` | `uint64` | the number of elements | | `.data` | `*T` for `T[..]`, `*var T` for `var T[..]` | the address of the first element | `.data` is a raw [pointer](https://rux-lang.dev/docs/lang/pointers/overview), for code that has to leave the language's checks behind — handing a buffer to a [foreign function](https://rux-lang.dev/docs/lang/ffi/overview), for one. The data of an empty slice may be `null`. ## Indexing and iteration `s[i]` is the element at position `i` of the view, counting from zero, with an index of any integer type. It is checked against `.length` exactly as an [array index](https://rux-lang.dev/docs/lang/arrays/overview#indexing) is, and one past the end stops the program with `Panic: index out of range`. `for` walks the elements in order: ```rux func Largest(values: int32[..]) -> int32 { var best = values[0]; for value in values { if value > best { best = value; } } return best; } ``` A `for` loop visits exactly `.length` elements of the view it was given. Spreading a slice with `...` passes its elements one by one to a [variadic parameter](https://rux-lang.dev/docs/lang/functions/parameters): with `func Sum(args: int32...)`, the call `Sum(values...)` gives `Sum` every element of `values`. ## Writability belongs to the view Whether elements may be written depends on the **view's type**, never on the binding that holds it: ```rux func Fill(values: var int32[..], value: int32) { for i in 0..values.length { values[i] = value; } } func Main() -> int { var storage: int32[4] = [1, 2, 3, 4]; let writable = storage[..]; // var int32[..]: the array place is writable writable[0] = 10; // a 'let' binding, but a writable view var cursor: int32[..] = storage; cursor = storage[2..]; // a 'var' binding may be pointed elsewhere Fill(storage[1..3], 0); // a writable view of part of the array return 0; } ``` The binding decides whether the variable can be pointed at another view; the view decides whether the elements can change. So a `var` binding of a read-only view still cannot write an element, and a `let` binding of a writable view cannot be repointed: ```text error: cannot modify elements through read-only slice 'int32[..]' help: use a 'var T[..]' view to write through the sequence error: cannot modify immutable variable 'view' help: declare 'view' with 'var' to make it mutable ``` Writability flows in one direction: - A `var T[..]` converts implicitly to a `T[..]` of the same elements, wherever a read-only view is expected. The reverse is rejected: `cannot assign 'int32[..]' to 'var int32[..]'`. - A view of an array is writable only when the array place is. With `let fixed: int32[3] = [1, 2, 3];`, `fixed[..]` is an `int32[..]`, and `Fill(fixed[..], 0)` fails with `argument 1 to 'Fill' has type 'int32[..]', but parameter 'values' requires 'var int32[..]'`. - A sub-slice keeps its parent's writability, and a view of a pointer takes the pointer's. - A string literal's bytes are read-only, always: ```text error: cannot assign string literal to 'var char8[..]' because literal text is stored in a read-only section ``` A writable parameter is given an explicit view, `storage[..]`; an array passed on its own only becomes a read-only one. ::note **A method call with an array argument.**:br A method that takes a `var T[..]` must be given a view, too, but rux 0.4.0 does not report an array passed to it at the call: the mistake surfaces later, as `cannot determine the type of this expression` where the result is used. Pass `buffer[..]`. :: ## A view does not keep its storage alive A slice only points at storage, and the storage must outlive every view of it. A view of a local array is valid until that array goes out of scope; a view of an allocation, until the allocation is freed. String literals live for the whole program, so a view of one may be returned freely. ```rux func Platform() -> char8[..] { return "Windows"; // static storage: fine to return } ``` ::note **Returning a view of a local.**:br Returning a slice of a local array, such as `return letters[..2];`, leaves the caller holding a view of storage that no longer exists. rux 0.4.0 does not yet check view lifetimes and accepts it; never return a view of a local. :: ## Comparison `==` and the other comparison operators are not defined on slices. Comparing two views would compare where they point rather than what they contain: ```text error: operator '==' is not defined for slice type 'int32[..]' note: a slice is a view, so comparing the views would compare addresses rather than elements help: declare '==' on 'int32[..]', or compare the elements one at a time ``` ## Methods on a slice type An [extension](https://rux-lang.dev/docs/lang/structs/extensions) may add methods to a slice of one concrete element type, `extend int[..]` or `extend char8[..]`; each element type has its own method set. A generic slice extension is rejected — a reusable algorithm over slices is an ordinary [generic](https://rux-lang.dev/docs/lang/generics/overview) function: ```text error: cannot extend slice type 'T[..]' because element type 'T' is not defined help: extend a slice of one concrete element type, for example 'extend int[..]' ``` ## Layout A slice is 16 bytes, aligned to 8, on every supported target: `.data` at offset 0 and `.length` at offset 8. Both `T[..]` and `var T[..]` have this layout; the elements are not part of it. See [Layout](https://rux-lang.dev/docs/lang/memory/layout). ## See also - [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) — the storage a slice most often views - [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) — the bounds a slice is cut with - [Pointer slicing](https://rux-lang.dev/docs/lang/pointers/slicing) — a view of raw memory, built from a pointer and a count - [Text](https://rux-lang.dev/docs/lang/types/text) — string literals as `char8[..]` - Learn: [Slice](https://rux-lang.dev/docs/learn/slice), [Writable slice](https://rux-lang.dev/docs/learn/writable-slice), [Variadic](https://rux-lang.dev/docs/learn/variadic) # Ranges A *range* is an interval of numbers written with `..`, `..=` or `...`. It is an ordinary value with a type of its own, and it has three everyday jobs: driving a [`for`](https://rux-lang.dev/docs/lang/statements/loops) loop, cutting a [slice](https://rux-lang.dev/docs/lang/slices/overview) out of a sequence, and matching an interval in a [pattern](https://rux-lang.dev/docs/lang/patterns/patterns). ```rux for i in 0..5 { PrintLine("{}", i); // 0, 1, 2, 3, 4 } ``` ## The six forms The start, when there is one, is always included. The end is excluded by `..` and included by `..=`. Either bound may be left out: | Expression | Type | Contains | Members | | ---------- | ----------- | ------------------------------ | ---------------- | | `1..4` | `int..int` | 1, 2, 3 | `.start`, `.end` | | `1..=4` | `int..=int` | 1, 2, 3, 4 | `.start`, `.end` | | `1..` | `int..` | 1 and every value after it | `.start` | | `..4` | `..int` | every value before 4 | `.end` | | `..=4` | `..=int` | every value up to 4, inclusive | `.end` | | `..` | `..` | everything | none | ```text range-expression = expression ( ".." | "..=" | "..." ) expression | expression ".." | ( ".." | "..=" | "..." ) expression | ".." range-type = type ( ".." | "..=" ) type | type ".." | ( ".." | "..=" ) type | ".." ``` In expressions and patterns `...` is a second spelling of the inclusive `..=`: `1...4` is `1..=4`, and `...4` is `..=4`. Types use `..=` only. (After an argument, `values...` before `)` or `,` is a spread, not a range — see [Slices](https://rux-lang.dev/docs/lang/slices/overview#indexing-and-iteration).) A range type is written with the same punctuation as its values, so a range can be stored, passed and returned like any other value: ```rux func Width(span: int..=int) -> int { return span.end - span.start + 1; } func Main() -> int { let bounded: int..int = 2..4; let closed = 1...3; // int..=int let from: int.. = 5..; let upTo: ..=int = ..=4; let everything: .. = ..; return Width(closed) - 3; } ``` ## Bounds The bounds of a range are numbers — integers or floating-point values — and both bounds of a two-sided range have one type. An unsuffixed literal takes the type of the other bound, so with `n: uint`, `0..n` is a `uint..uint` and a loop over it counts in `uint`. A bound of any other type is an error: ```text error: range bounds must be numeric ``` ::note **Bounds of two widths.**:br rux 0.4.0 does not yet reject a range whose bounds have different types. It gives the range the start's type and converts the end silently, so with `a: int32` and `b: int64`, `a..b` is an `int32..int32` and an end above `int32::Max` is truncated. Convert one bound with `as` first. :: A range counts upwards. When both bounds are literals the compiler checks their order: ```text error: range start cannot be greater than its end ``` With bounds computed while the program runs nothing is checked, and a range whose start is past its end is empty: a `for` over it runs zero times. ### Precedence The range operators bind more loosely than every other operator except assignment — more loosely even than `?:` and `??`. So `n + 1..n * 3` is `(n + 1)..(n * 3)` and `flag ? 1 : 2..5` is `(flag ? 1 : 2)..5`, while a range inside a comparison needs parentheses: `r == 0..5` parses as `(r == 0)..5`. Ranges do not chain — `1..2..3` is a syntax error. See [Expressions](https://rux-lang.dev/docs/lang/expressions/overview). ## Members A range that has a start exposes it as `.start`, and one that has an end exposes it as `.end`; both have the bound type. Asking a range for a bound it does not have is an error: ```text error: range type '..int' has no member 'start' note: range members are 'start' and 'end' when that bound is present ``` The members need no import. A range held in a `var` binding can have its bounds assigned, as fields can. ## In a `for` loop `for` walks a range from its start upwards in steps of one, binding each value to the loop variable, whose type is the bound type: ```rux for i in 1..=5 { PrintLine("{}", i); // 1, 2, 3, 4, 5 } ``` - `a..b` visits `a` up to `b - 1`, and `a..=b` visits `a` up to `b`. An inclusive range ending at its type's maximum visits that value last and stops, so `for v in start..=255` over `uint8` never wraps around. - `a..` has no end, so the loop runs until it leaves with [`break`](https://rux-lang.dev/docs/lang/statements/break-continue) or `return`. - A range with no start has no first value and cannot be walked: ```text error: range type '..int' has no initial value and is not iterable ``` The loop variable is immutable; a loop that must control its own stepping is a [`while`](https://rux-lang.dev/docs/lang/statements/loops). ## In slicing Indexing an array, a slice or a pointer with a range produces a [slice](https://rux-lang.dev/docs/lang/slices/overview#slicing). Every form is allowed on an array or a slice: an omitted start means 0 and an omitted end means `.length`. A pointer has no length, so slicing one requires an end — `p[..n]`, `p[a..b]` or `p[..=n]`; see [Pointer slicing](https://rux-lang.dev/docs/lang/pointers/slicing). ```rux let values: int[6] = [10, 20, 30, 40, 50, 60]; let middle = values[2..4]; // 30, 40 let head = values[...2]; // 10, 20, 30 let span: int..=int = 1..=3; let held = values[span]; // 20, 30, 40 ``` ## In patterns A range pattern matches every value from its low bound to its high bound. Both bounds are required, and both must be literals of the subject's type; the subject must be a number: ```rux func Grade(score: int) -> char8[..] { return match score { 0..50 => "fail", 50...69 => "pass", 70..=89 => "merit", 90..=100 => "distinction", else => "invalid" }; } ``` A pattern with one bound missing, such as `90.. =>`, is a syntax error, and a range pattern over a character is rejected with `range pattern cannot match value of type 'char32'`. See [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns). A type can also accept a range in its own [indexer](https://rux-lang.dev/docs/lang/interfaces/indexers), such as `func [](self: &Vect, span: int..int) -> int[..]`. ## See also - [Loops](https://rux-lang.dev/docs/lang/statements/loops) — `for` over a range - [Slices](https://rux-lang.dev/docs/lang/slices/overview) — the views a range cuts - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — range patterns in `match` - Learn: [Range](https://rux-lang.dev/docs/learn/range), [For](https://rux-lang.dev/docs/learn/for), [Range pattern](https://rux-lang.dev/docs/learn/range-pattern) # References A *reference* lets code use a value that belongs to someone else, without copying it and without taking it over. `&T` is a **shared** reference: it can read the value. `&var T` is an **exclusive** reference: it can read and write it. A reference never owns its referent, is never null, and lives no longer than the call or the block that made it. ```text reference-type = "&" type | "&" "var" type ``` ```rux struct Account { owner: char8[..]; balance: int; } func Report(account: &Account) { PrintLine("{}: {}", account.owner, account.balance); } func Deposit(account: &var Account, amount: int) { account.balance += amount; } func Main() -> int { var alice = Account { owner: "alice", balance: 10 }; Deposit(alice, 5); // borrowed for writing: alice.balance is now 15 Report(alice); // borrowed for reading return 0; } ``` References are the safe way to share. A [raw pointer](https://rux-lang.dev/docs/lang/pointers/overview) is an address the program vouches for itself; a reference is one the compiler checks, under the rules on [Exclusivity](https://rux-lang.dev/docs/lang/references/exclusivity). ## Borrowing is implicit There is no borrow operator. A reference is made — the value is *borrowed* — when a place is given where a reference is expected: - **an argument** for a parameter of type `&T` or `&var T`; - **a receiver**: calling a method declared with `self: &T` or `self: &var T` borrows the value it is called on; - **a binding** annotated with a reference type: `let shared: &Account = alice;`. The call site looks the same as passing by value; the parameter's type decides. `&` is never an operator, and writing it before a value is an error that points to `@`, which takes a raw address: ```text error: '&' does not take an address; write '@' to take the address of a value ``` What is borrowed must be a place — a variable, a field, an element, or the referent of another reference. A literal or a freshly built value has nowhere to be borrowed from, so `Report(Account { owner: "x", balance: 1 })` and `Bump(5)` — `Bump` is defined [below](https://rux-lang.dev/#writing-through-var) — are rejected: ```text error: argument 1 to 'Report' has type 'Account', but parameter 'account' requires '&Account' error: argument 1 to 'Bump' has type 'int', but parameter 'count' requires '&var int' ``` An exclusive borrow also needs a place that may be written. With `let savings = Account { … };`, `Deposit(savings, 10)` is rejected: ```text error: argument 1 to 'Deposit' cannot borrow immutable 'savings' as '&var Account' help: declare 'savings' with 'var' to make it mutable ``` A `&var T` is accepted wherever a `&T` is expected: an exclusive reference can always lend reading. ## Using a reference A reference is used as if it were the value. Fields, methods, indexing and `.length` all reach through it with no `*` and no arrow: ```rux func First(values: &int[3]) -> int { return values[0] + values.length as int; } ``` A reference to a scalar — an integer, a float, a `bool`, a character — supplies the scalar's value wherever a value is expected: in arithmetic and comparisons, in a condition, in a cast, in an assignment or a typed binding, as a by-value argument: ```rux func Twice(value: &int) -> int { return value * 2; } ``` To copy a scalar out of a reference, give the binding its type: `let saved: int = value;`. `*` is a raw-pointer operator and never applies to a reference: ```text error: operator '*' requires a pointer operand, but found '&var int' note: a reference reads and writes its referent without '*' help: write 'r' in place of '*r', as in 'r += 1' ``` ## Writing through `&var` Through a `&var T` the referent can be written in three ways. **Fields and elements** are written as usual: `account.balance += 5`, `values[i] = 0`. **A scalar referent** is written by assigning to the reference itself. `=`, `<-`, the compound assignments and `++`/`--` all store into the caller's value: ```rux func Bump(count: &var int) { count += 1; } func Main() -> int { var total = 4; Bump(total); // total is now 5 let alias: &var int = total; alias = 20; alias++; // total is now 21 return 0; } ``` **Any other referent** — a struct, a tuple, an array, a value of a type parameter — is replaced whole with `=` or `<-`. This is how a method replaces the value it was called on: ```rux extend Account { func Reset(self: &var Account) { self = Account { owner: self.owner, balance: 0 }; } } ``` The write follows the [assignment rules](https://rux-lang.dev/docs/lang/ownership/copy-and-move) for `T` exactly as an assignment to a `var` local would: the new value is produced first, the old one is destroyed, and the new one is installed; a move-only value needs `<-`. Compound assignment and `++`/`--` apply to scalar referents only. Through a shared reference every write is an error: ```text error: cannot modify data through immutable reference '&Account' ``` ### Rebinding a reference A reference binding declared with `let` always writes through. One declared with `var` is different for plain `=`, which points it at other storage instead: ```rux var first = 1; var second = 2; var current: &int = first; current = second; // current now refers to second; first is unchanged ``` Assigning a value of the referent's type to such a binding is therefore an error. With `var counter: &var int = total;`, `counter = 5;` fails: ```text error: cannot assign 'int' to '&var int' note: '=' points the 'var' reference 'counter' at other storage help: declare 'counter' with 'let' to write through it ``` ## Where a reference may appear A reference is a non-owning alias for the length of a call or a block, so it may appear in exactly three places: a **parameter**, a **receiver**, and a **local binding**. It cannot be stored in a field, returned, or moved out of — a field `r: &int` in a `struct Holder`, a function returning `&Account`, and `let mine <- account;` on a reference parameter are rejected in turn: ```text error: field 'r' in struct 'Holder' cannot store reference type '&int' note: references are non-owning aliases and cannot escape into aggregate storage help: store the owned value or a raw pointer when an address must outlive the borrow error: function return type cannot store reference type '&Account' error: cannot move a non-owning reference note: references borrow storage but do not own the value they address ``` A reference cannot destroy its referent either: replacing a value through `&var T` installs a new one in its place. When an address must be stored or must outlive the call, use a [raw pointer](https://rux-lang.dev/docs/lang/pointers/overview). ## References and pointers | | `&T`, `&var T` | `*T`, `*var T` | | --------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------- | | Made by | an implicit borrow | `@place` | | Null | never | `null` is a value | | Read and write | as the value itself | `*p`; `p.field` and `p[i]` reach through | | Arithmetic | none | `p + n`, `p - n`, `p[i]` | | Stored in a field, returned | no | yes | | Checked | [exclusivity](https://rux-lang.dev/docs/lang/references/exclusivity), writability | writability only | A reference is 8 bytes, the size of the address it holds. ## See also - [Exclusivity](https://rux-lang.dev/docs/lang/references/exclusivity) — how many references may exist at once - [Parameters](https://rux-lang.dev/docs/lang/functions/parameters) — `&T` and `&var T` parameters - [Methods](https://rux-lang.dev/docs/lang/structs/methods) — receivers declared as `self: &T` and `self: &var T` - [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) — raw addresses, for what a reference cannot do - Learn: [Reference](https://rux-lang.dev/docs/learn/reference), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference), [Mutating method](https://rux-lang.dev/docs/learn/mutating-method) # Exclusivity At any moment a value may be borrowed by **any number of shared references**, `&T`, or by **one exclusive reference**, `&var T` — never both. While a `&var` borrow is in use, the value is reached only through it; while `&` borrows are in use, nothing changes the value under them. The compiler checks this for every [reference](https://rux-lang.dev/docs/lang/references/overview), so a reader never sees a value change mid-read and two writers never overlap. | While the value is borrowed by | Reading the value directly | Writing it directly | Another `&` borrow | Another `&var` borrow | | ------------------------------ | -------------------------- | ------------------- | ------------------ | --------------------- | | one or more `&T` | allowed | error | allowed | error | | one `&var T` | error | error | error | error | The rule covers everything that borrows: an argument passed to a reference parameter, a method call through a `self: &T` or `self: &var T` receiver, and a local binding of reference type. ## A borrow lasts until its last use A borrow made for a call lasts for that call. A borrow bound to a local lasts from the binding to the **last use** of that local — not to the end of the block. Once the reference is used for the last time the value is free again: ```rux struct Account { balance: int; } func Main() -> int { var alice = Account { balance: 10 }; let wallet: &var Account = alice; wallet.balance += 5; // last use of wallet: the borrow ends here alice.balance -= 1; // fine: alice is no longer borrowed return 0; } ``` ```mermaid flowchart LR b["let wallet: &var Account = alice"] --> u["wallet.balance += 5
(last use)"] u --> f["alice is free again"] b -. "alice reachable only through wallet" .-> u ``` Moving the direct access before the borrow's last use is what makes the program wrong: ```rux let wallet: &var Account = alice; let before = alice.balance; // error: wallet is still to be used wallet.balance += 5; ``` ```text error: cannot read 'alice.balance' while 'wallet' holds an exclusive borrow help: read through 'wallet' or wait until its last use ``` Read through the reference instead, or move the read after the reference's last use. ## What overlaps Two accesses conflict only when they reach the same storage. The compiler compares the places as written: - A whole value overlaps each of its fields and elements, and they overlap it. - **Different fields** of one struct do not overlap, so one can be borrowed exclusively while another is read or borrowed. - Elements overlap when their indexes are the same constant or the same expression: `v[0]` and `v[0]`, `v[i]` and `v[i]`. ```rux struct Pair { x: int; y: int; } func Swap(a: &var int, b: &var int) { let saved: int = a; a = b; b = saved; } func Main() -> int { var pair = Pair { x: 1, y: 2 }; Swap(pair.x, pair.y); // two fields: no overlap var values = [1, 2, 3]; Swap(values[0], values[2]); // two different elements return 0; } ``` ## The errors Each conflict has its own message. All of them carry a note giving the line and column where the conflicting borrow began, and a help line naming the reference to use instead. **Reading a value while a `&var` borrow of it is in use.** ```text error: cannot read 'alice.balance' while 'wallet' holds an exclusive borrow help: read through 'wallet' or wait until its last use ``` **Writing a value while a borrow of it is in use.** A shared borrow forbids writes because a reader expects the value to hold still; an exclusive one forbids every direct access: ```text error: cannot modify 'alice.balance' while it is immutably borrowed error: cannot modify 'alice' while it is exclusively borrowed ``` **Borrowing for writing while a reader is in use**, including through a method with a `self: &var T` receiver: ```text error: cannot borrow exclusively 'alice' while it is immutably borrowed help: use 'view' for the access or wait until its last use ``` **Borrowing at all while a writer is in use**, including by calling a method with a `self: &T` receiver. Such a call is reported both as a read and as a borrow: ```text error: cannot read 'alice' while 'wallet' holds an exclusive borrow help: read through 'wallet' or wait until its last use error: cannot borrow 'alice' while it is exclusively borrowed help: use 'wallet' for the access or wait until its last use ``` **One value in two arguments** of the same call, when at least one of them is `&var`. Two `&T` arguments may share a value; an exclusive one must have it to itself: ```text error: call arguments create overlapping exclusive borrows of 'alice' help: split the accesses into non-overlapping calls ``` The same message names a field or an element when that is what is passed twice: `Swap(pair.x, pair.x)` reports `'pair.x'`, and `Swap(values[0], values[0])` reports `'values[0]'`. ## Outside the rule [Raw pointers](https://rux-lang.dev/docs/lang/pointers/overview) are not borrows. Taking an address with `@`, holding several `*var T` to one value, and writing through them are all accepted; keeping such accesses apart is the program's responsibility. A [slice](https://rux-lang.dev/docs/lang/slices/overview) is a view built on a pointer and is not tracked as a borrow either. ## See also - [References](https://rux-lang.dev/docs/lang/references/overview) — `&T` and `&var T` - [Ownership](https://rux-lang.dev/docs/lang/ownership/overview) — who owns a value, and when it is destroyed - [Methods](https://rux-lang.dev/docs/lang/structs/methods) — receivers that borrow the value they are called on - Learn: [Exclusivity](https://rux-lang.dev/docs/learn/exclusivity), [Reference](https://rux-lang.dev/docs/learn/reference), [Mutable reference](https://rux-lang.dev/docs/learn/mutable-reference) # Pointers A *raw pointer* holds the address of a value. `*T` is a **read-only** pointer: the value it points at can be read through it. `*var T` is a **writable** pointer: the value can also be written. A pointer may be `null`, may be stored in a field, returned, offset and compared, and may outlive what it points at — the compiler checks only what the pointer type allows, never whether the address is still good. That is the price of the things only a pointer can do: [foreign functions](https://rux-lang.dev/docs/lang/ffi/overview), manual allocation, stored links between values, and walking memory by address. ```text pointer-type = "*" type | "*" "var" type address-of = "@" place dereference = "*" expression ``` ```rux var score = 10; let writer = @score; // *var int: score is a var *writer += 5; // score is now 15 let reader: *int = @score; // a read-only view of the same int let current = *reader; // 15 ``` For safe, checked access to a value someone else owns, use a [reference](https://rux-lang.dev/docs/lang/references/overview) instead; a pointer is the deliberate step outside those checks. ## Taking an address The prefix `@` takes the address of a place: a variable, a field, an element, or the target of another pointer. `@x.field` and `@a[i]` address one part of a value. The pointer's writability comes from the place: | Place | `@place` has type | | --------------------------------------------- | ----------------- | | a `var` binding, or a part of one | `*var T` | | a `let` binding, or a part of one | `*T` | | an element of a `var T[..]` view | `*var T` | | an element of a `T[..]` view | `*T` | | a field or element reached through a `*var T` | `*var T` | ```text error: cannot assign '*int' to '*var int': '@limit' yields a read-only '*T'; declare 'limit' with 'var' for a '*var T' ``` `&` never takes an address in Rux: `&x` is rejected with `'&' does not take an address; write '@' to take the address of a value`. A pointer is made only from an address, so a plain value where a pointer is expected is the error `cannot assign 'int' to '*int'`. Taking the writable address of a variable declared without a value counts as initialising it, because the address exists to be filled through: `var info: SystemInfo; GetSystemInfo(@info);` leaves `info` usable. See [Initialisation](https://rux-lang.dev/docs/lang/bindings/initialization). ## Reading and writing through a pointer The prefix `*` *dereferences* a pointer: `*p` is the value it points at, a place that can be read, and written when `p` is a `*var T`: ```rux func Store(target: *var int, value: int) { *target = value; } ``` ```text error: cannot modify data through read-only pointer '*int' ``` The pointee type, not the variable it points at, decides what is allowed: a `*int` cannot write even when it points at a `var`. A write through a pointer **initialises** the storage it addresses and destroys nothing. The pointer cannot tell whether that storage holds a value yet, so `*p = value`, `*p <- value`, `p[i] = value` and a field write through a `*var T` never run a destructor for what was there before. Code that replaces a value through a pointer destroys or moves out the old one first: ```rux struct Noisy { id: int; } extend Noisy { func ~Noisy(self: &var Noisy) { PrintLine("destroy {}", self.id); } } func Main() -> int { var slot = Noisy { id: 1 }; slot = Noisy { id: 2 }; // assignment: destroys 1 let p = @slot; *p = Noisy { id: 3 }; // through a pointer: 2 is never destroyed return 0; // destroys 3 } ``` ```text destroy 1 destroy 3 ``` Moving a value out through a pointer is rejected, because the pointer does not own it: `let mine <- *p;` fails with `cannot move '*p' out of borrowed pointer storage`. ## Fields, elements and methods `.` reaches through a pointer to a struct, and `[i]` through a pointer to an element; there is no arrow operator. Reads and writes go straight to the pointed-at value: ```rux struct Point { x: int; y: int; } func Main() -> int { var point = Point { x: 1, y: 2 }; let p = @point; p.x = 7; // point.x is now 7 let sum = p.x + p.y; return 0; } ``` A method is not called through a raw pointer, because its receiver is a reference and a pointer proves nothing about the address it holds. Dereference first, `(*p).Shift(3)`, to vouch for it explicitly: ```text error: cannot create safe receiver '&var Point' from raw pointer '*var Point' note: raw pointers do not prove a valid non-null borrow help: call the method on an owning value ``` Pointers nest: `**T` is a pointer to a pointer, and each `*` removes one level, so with `pp: **var int`, `**pp = 1` writes the `int`. ## Conversions | From | To | How | | --------- | --------- | ---------------------------------------------- | | `*var T` | `*T` | implicitly | | `*T` | `*opaque` | implicitly; `*var T` likewise to `*var opaque` | | `*opaque` | `*T` | `as *T` | | `*T` | `*U` | `as *U` | | a pointer | `uint` | `as uint` — the address as a number | | `uint` | a pointer | `as *T` | Losing write access is always allowed; gaining it is not: `cannot assign '*int' to '*var int'`. A cast with `as` is the explicit, unchecked way to retype an address — see [Casts](https://rux-lang.dev/docs/lang/expressions/casts). ::note **Casting to a writable pointer.**:br A cast from `*T` to `*var T` is meant to be refused, so that a read-only address can never gain write access. rux 0.4.0 does not yet check this: `@x as *var int` on a `let x` is accepted, and a write through it changes `x`. Never cast write access into a pointer. :: ## `null` `null` is the pointer that points at nothing. It converts to every pointer type, and it is the only value a pointer has that is not an address. Compare with `==` and `!=` before dereferencing a pointer that might be `null`: ```rux struct Node { value: int; next: *Node; } func Main() -> int { var tail = Node { value: 3, next: null }; var head = Node { value: 1, next: @tail }; var cursor: *Node = @head; while cursor != null { PrintLine("{}", cursor.value); cursor = cursor.next; } return 0; } ``` Reading or writing through `null` is not checked: it compiles, and crashes the program when it runs. `null` is a pointer value, not absence. It is not a value of an [optional](https://rux-lang.dev/docs/lang/optionals/overview) type, and where an optional of a pointer is expected it is a **present** null pointer: | Type | Meaning | | ------- | ----------------------------------------- | | `(*T)?` | an optional pointer: `none`, or a pointer | | `*T?` | a pointer to an optional, `*(T?)` | ```rux let present: (*int)? = null; // present, holding a null pointer let absent: (*int)? = none; // absent ``` ## `*opaque` `*opaque` and `*var opaque` point at bytes of no particular type, as C's `void *` does. Allocation functions return them, and foreign functions take and return them. Any typed pointer converts to one implicitly; to use the memory, cast it back to a typed pointer: ```rux import Memory::{ Alloc, Free }; func Main() -> int { let block = Alloc(4 * sizeof(int)) as *var int; if block == null { return 1; } defer Free(block); block[0] = 1; return 0; } ``` Arithmetic on an `*opaque` moves by single bytes. ## Comparison Two pointers compare with `==` and `!=`, which compare addresses, and with `<`, `<=`, `>` and `>=`, which order them — useful when walking a buffer up to an end pointer. See [Pointer arithmetic](https://rux-lang.dev/docs/lang/pointers/arithmetic). ## What a pointer does not have A pointer has no length: `p.length` is `type '*var int' has no field 'length'`. It cannot be iterated with `for`, and slicing it needs an explicit end. To treat raw memory as a sequence, build a [view](https://rux-lang.dev/docs/lang/pointers/slicing) of it with a count, `p[..n]`. ::note **`for` over a pointer.**:br A pointer cannot be iterated, but rux 0.4.0 does not yet reject `for value in p`: the loop compiles and runs zero times. Iterate a view, `for value in p[..n]`. :: A pointer is 8 bytes on every supported target, whatever it points at. ## See also - [Pointer arithmetic](https://rux-lang.dev/docs/lang/pointers/arithmetic) — moving a pointer by whole elements - [Pointer slicing](https://rux-lang.dev/docs/lang/pointers/slicing) — a bounded view of the memory a pointer addresses - [References](https://rux-lang.dev/docs/lang/references/overview) — checked, non-null access to a value - [Foreign functions](https://rux-lang.dev/docs/lang/ffi/overview) — how pointers cross into C - [Layout](https://rux-lang.dev/docs/lang/memory/layout) — the size and alignment of what a pointer addresses - Learn: [Pointer](https://rux-lang.dev/docs/learn/pointer), [Out parameter](https://rux-lang.dev/docs/learn/out-parameter), [Raw memory](https://rux-lang.dev/docs/learn/raw-memory), [Optional pointer](https://rux-lang.dev/docs/learn/optional-pointer) # Pointer arithmetic Adding an integer to a pointer moves it by whole **elements**, not bytes. If `p` is a `*T`, then `p + 1` is the address of the next `T` — `sizeof(T)` bytes further on — whatever `T` is. This is how a program walks memory that has no length attached: an allocation, a buffer from a foreign function, the elements behind a view's `.data`. | Expression | Type | Meaning | | ---------- | -------------------- | ------------------------------------------------ | | `p + n` | the type of `p` | `n` elements after `p` | | `n + p` | the type of `p` | the same as `p + n` | | `p - n` | the type of `p` | `n` elements before `p` | | `p += n` | | moves the `var` pointer `p` forward `n` elements | | `p -= n` | | moves it back `n` elements | | `p[i]` | the pointee, a place | the element `i` elements after `p`: `*(p + i)` | `n` may have any integer type. The result keeps the pointer's writability: an offset `*var T` is still a `*var T`. ```rux var values: int64[4] = [10, 20, 30, 40]; let first = @values[0]; let third = first + 2; // 16 bytes on: sizeof(int64) is 8 let a = *third; // 30 let b = *(third - 1); // 20 let c = first[3]; // 40 let d = third[-1]; // 20: an index may be negative first[1] = 25; // values[1] is now 25 ``` Indexing and arithmetic agree — `@p[i]` is the same address as `p + i` — and both scale by the pointee's size, including a struct's size with its padding. See [Layout](https://rux-lang.dev/docs/lang/memory/layout). `*` binds more tightly than `+`, so the parentheses in `*(p + 2)` matter: `*p + 2` reads the first element and adds 2 to it. `p[2]` says the same as `*(p + 2)` with no parentheses. ## Nothing is checked A pointer carries no length, so neither arithmetic nor `p[i]` is bounds-checked. An offset past the end of the memory a pointer addresses produces an address that is not one, and reading or writing through it is undefined: the program may crash, or may silently read or overwrite something else. The caller vouches for every offset. When the count is known, build a [view](https://rux-lang.dev/docs/lang/pointers/slicing), `p[..n]`, whose indexing **is** checked. ## Walking a buffer Pointers order with `<`, `<=`, `>` and `>=`, so a loop can walk from a start pointer to an end pointer one past the last element. `+=` steps the cursor; there is no `++` or `--` on a pointer: ```rux func Main() -> int { var values: int64[4] = [10, 20, 30, 40]; let start = @values[0]; let end = start + values.length; // one past the last element: never read var cursor = start; var sum: int64 = 0; while cursor < end { sum += *cursor; cursor += 1; } PrintLine("{}", sum); // 100 return 0; } ``` Compare with `<`, not `<=`: the end pointer marks where to stop, and reading it reads past the array. ## Pointer subtraction Subtracting one pointer from another is not defined: ```text error: operator '-' cannot combine left operand '*var int64' with right operand '*var int64' ``` To count the elements between two pointers into the same block, convert both to addresses and divide the distance in bytes by the element size: ```rux let count = ((end as uint) - (start as uint)) / sizeof(int64); // 4 ``` ## `*opaque` A `*opaque` has no element type, so arithmetic on it moves by single bytes: `block + 8` is eight bytes on. To step by elements, cast it to a typed pointer first. Indexing an `*opaque` yields no usable value; cast it before reading or writing. ## See also - [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) — `*T`, `*var T`, `@` and `null` - [Pointer slicing](https://rux-lang.dev/docs/lang/pointers/slicing) — a checked view built from a pointer and a count - [Layout](https://rux-lang.dev/docs/lang/memory/layout) — the element sizes that arithmetic scales by - Learn: [Pointer arithmetic](https://rux-lang.dev/docs/learn/pointer-arithmetic), [Raw memory](https://rux-lang.dev/docs/learn/raw-memory) # Pointer slicing Indexing a pointer with a range turns raw memory into a [slice](https://rux-lang.dev/docs/lang/slices/overview): a view with a length, whose indexing and iteration are checked from then on. Because a pointer has no length of its own, the range must say where the view ends. | Expression | Elements, counting from `p` | Length | | ---------- | ----------------------------- | ----------- | | `p[..n]` | 0 up to, not including, `n` | `n` | | `p[..=n]` | 0 to `n` inclusive | `n + 1` | | `p[a..b]` | `a` up to, not including, `b` | `b - a` | | `p[a..=b]` | `a` to `b` inclusive | `b - a + 1` | The view's type follows the pointer's writability: a `*var T` gives a `var T[..]`, and a `*T` gives a `T[..]`. A view of a `null` pointer with a length of zero is an empty view. ```rux import Memory::{ Alloc, Free }; func Fill(values: var int[..], start: int) { for i in 0..values.length { values[i] = start + i as int; } } func Total(values: int[..]) -> int { var sum = 0; for value in values { sum += value; } return sum; } func Main() -> int { let count: uint = 4; let block = Alloc(count * sizeof(int)) as *var int; if block == null { return 1; } defer Free(block); let all = block[..count]; // var int[..], four elements Fill(all, 10); // 10, 11, 12, 13 let total = Total(block[1..3]); // 11 + 12 return 0; } ``` ## The end is required A pointer does not know how far its memory extends, so a range with no end — `p[..]` or `p[a..]` — is rejected: ```text error: cannot slice pointer '*var int' without an end bound help: write 'p[..n]' or 'p[a..b]' to give the slice a length ``` Once built, the view checks every index against the length it was given. That length is the program's promise, and nothing checks it against the memory: `block[..100]` over a four-element block compiles, and its indexes are then checked against 100, not 4. Build every view from the count the memory was allocated with. ## Precedence `@` is a prefix operator and indexing is postfix, so postfix binds first: `@a[0][..2]` means `@(a[0][..2])` — a slice of the element `a[0]`, which is not a sequence: ```text error: cannot slice value of type 'int' help: declare 'func []' taking a range on 'int' ``` Parenthesise the address to slice the memory it points at: ```rux var numbers: int[6] = [1, 2, 3, 4, 5, 6]; let window = (@numbers[2])[..3]; // var int[..]: 3, 4, 5 ``` A view of an array is more simply `numbers[2..5]`; slicing a pointer is for memory that arrives as an address, such as an allocation, a buffer from a [foreign function](https://rux-lang.dev/docs/lang/ffi/overview), or a [flexible tail](https://rux-lang.dev/docs/lang/arrays/overview#flexible-tails). ## Views of literal text The `.data` of a string literal is a `*char8`, and slicing it gives back a read-only view, as any read-only pointer does: `"abc".data[..2]` is a `char8[..]` of `a` and `b`. ## See also - [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) — `*T`, `*var T` and `@` - [Pointer arithmetic](https://rux-lang.dev/docs/lang/pointers/arithmetic) — unchecked offsets and indexing - [Slices](https://rux-lang.dev/docs/lang/slices/overview) — what a view can do once it has a length - Learn: [Pointer slice](https://rux-lang.dev/docs/learn/pointer-slice), [Fixed buffer](https://rux-lang.dev/docs/learn/fixed-buffer) # Optionals An *optional* `T?` holds either a present value of type `T` or nothing — *absence*, written `none`. It is a compiler-owned type: no package declares it, it has no methods, and the compiler makes sure absence is dealt with before the value inside is used. ```text optional-type = type "?" ``` ## The type `?` is a postfix type suffix. Like `[]`, `[N]` and `[..]`, it binds tighter than every other type operator, so it applies to the type immediately before it. Group with parentheses to put it anywhere else: | Type | Meaning | | ----------------- | ---------------------------------------------------------------------- | | `int32?` | an optional `int32` | | `int32??` | an optional of an optional `int32` — two levels | | `int32?[..]` | a slice of optional `int32` values | | `int32[..]?` | an optional slice | | `*int32?` | a pointer to an optional, `*(int32?)` | | `(*int32)?` | an optional pointer | | `(int32 | bool)?` | an optional [sum](https://rux-lang.dev/docs/lang/sums/overview) | | `int32 | (bool?)` | a sum with an optional member | | `(T ! E)?` | an optional [fallible](https://rux-lang.dev/docs/lang/errors/overview) | | `T ! (E?)` | a fallible whose error is optional | A `?` never reaches across `|` or `!`, so the last two pairs must be grouped. Without the parentheses the compiler stops: ```text error: an optional sum member must be grouped help: write 'A | (B?)' for an optional member, or '(A | B)?' for an optional sum error: an optional error type must be grouped help: write 'T ! (E?)' for an optional error, or '(T ! E)?' for an optional result ``` Any type can be the payload: a struct, a slice, a pointer, a fallible, the unit `()` (a `()?` is just "present or not"), and another optional. ## Levels never collapse `T??` is not `T?`. Each level has its own absence, so an `int32??` has three distinct values: | Value | Meaning | | --------------------------- | ----------------------------------- | | `none` | absent | | `.Some(none)` | present, holding an absent `int32?` | | `.Some(.Some(20))`, or `20` | present, holding a present `20` | This is what lets a lookup tell "there is no such key" from "the key is there, and its value is absent", and an iterator tell "there are no more items" from "the next item is an absent value". ## Writing an optional value There are three ways to produce an optional, and every nested state can be written directly, without a temporary: ```rux let count: int32? = 4; // a plain value where an optional is expected is present let same: int32? = .Some(4); // presence written out let missing: int32? = none; // absence let inferred = .Some(4i32); // .Some alone takes its payload's type: int32? let wrapped: int32?? = count; // present, holding count: .Some(.Some(4)) ``` - **A plain value.** Where an optional is expected — an annotated `let`, an assignment, an argument, a `return` in a function returning `T?` — a value of type `T` is made present. The same happens one level up: an `int32?` placed where an `int32??` is expected becomes `.Some` of it. - **`.Some(value)`** selects presence explicitly. It is required where a plain value would be ambiguous, and it is how a present absence, `.Some(none)`, is written. - **`none`** is the absence of the *outer* level of the optional the context expects, never of a level inside it. In a function returning `int32? ! E` (a fallible whose success is optional), `return none;` succeeds with an absent value. `none` carries no type of its own, so it needs a context: ```text error: cannot infer the type of 'absent' from 'none' help: annotate the optional type, as in 'let value: int32? = none;' ``` An optional payload can also widen: an `A?` converts to `(A | B)?`, keeping absence absent. The [sum](https://rux-lang.dev/docs/lang/sums/overview) rules apply to the payload. ::note **Absence in a match expression.**:br An annotation is meant to give its type to every arm of a `match` expression. rux 0.4.0 does not yet do that when one arm is `none`: `let r: int32? = match n { 0 => none, else => n };` is rejected with `'none' needs an expected optional type, but found 'int32'`. Write the present arm as `.Some(n)`. :: ### `null` is not absence `null` is the null raw [pointer](https://rux-lang.dev/docs/lang/pointers/overview), and it is not a value of any optional type: ```text error: 'null' is not a value of type 'int32?' help: write 'none' for an absent optional ``` A raw pointer may be the payload of an optional, `(*T)?`, when a pointer that might not be there should be checked like any other optional. See [Optional pointer](https://rux-lang.dev/docs/learn/optional-pointer). ## Matching An optional is opened with an ordinary [`match`](https://rux-lang.dev/docs/lang/patterns/match). Its patterns select one level at a time: | Pattern | Matches | | ---------- | ---------------------------------------------------------------------- | | `v?` | a present value, binding its payload to `v` — shorthand for `.Some(v)` | | `.Some(p)` | a present value whose payload matches the pattern `p` | | `none` | absence of the level being matched | | `0?` | a present value equal to `0` — any pattern can carry the `?` suffix | | `v??` | two present levels, binding the inner payload — `.Some(.Some(v))` | | `none?` | a present absence — `.Some(none)` | | `v: T` | a present value whose payload has type `T`, binding it (one level) | ```rux func Find(values: int32[..], wanted: int32) -> uint? { for i in 0..values.length { if values[i] == wanted { return i; } } return none; } func Describe(found: uint?) -> char8[..] { return match found { 0? => "at the front", index? => "further in", none => "missing" }; } ``` Every level must be covered. A nested optional needs an arm for each of its states: ```rux func Lookup(key: int32) -> int32?? { if key == 0 { return none; } if key == 1 { return .Some(none); } return .Some(.Some(key * 10)); } func Classify(found: int32??) -> char8[..] { return match found { value?? => "a value", none? => "a present absence", none => "absent" }; } ``` A typed presence pattern takes exactly one level and leaves the rest to the arm: ```rux func Typed(found: int32??) -> int32 { return match found { stored: int32? => stored ?? -1, none => -2 }; } ``` A match that leaves a state out names it: ```text error: match on 'int32?' is not exhaustive; missing none error: match on 'int32?' is not exhaustive; missing .Some(_) error: match on 'int32??' is not exhaustive; missing .Some(none) ``` ## Using the value An optional is not its payload. It has no arithmetic, no fields, and no `Display`, so `known * 2` with `known: int32?` is `error: operator '*' cannot combine left operand 'int32?' with right operand 'int'`. The value inside is reached in one of four ways: | Tool | Use it when | | ----------------------------------------------------------- | ------------------------------------------------------------------ | | `match` | each state needs its own code | | [`??`](https://rux-lang.dev/docs/lang/optionals/coalescing) | absence has a stand-in value, or should leave the loop or function | | [`?`](https://rux-lang.dev/docs/lang/optionals/propagation) | absence should make the whole function's result absent | | [`is`](https://rux-lang.dev/docs/lang/sums/type-tests) | only presence matters: `reading is int32` is `true` when present | ## Equality `==` and `!=` compare optionals level by level: absent equals absent, and two present values are equal when their payloads are. Both operands must have the same optional type. `none`, `.Some(…)` and an unsuffixed literal take the other operand's type, but a value with a type of its own is never wrapped for a comparison: ```rux let known: int32? = 5; let a = known == 5; // true: the literal is made present let b = known == .Some(5); // true let c = known == none; // false ``` `known == 5i32` is rejected: ```text error: operator '==' cannot compare 'int32?' with 'int32' note: both operands of a native comparison have the same type; a comparison never injects, widens, or wraps an operand help: write '.Some(...)' or another constructor, bind the member with a typed pattern, or match the value ``` ## Iteration The iterator convention is built on optionals: an iterator's `Next` returns `Item?`, and a `for` loop ends at the first outer absence. Because levels never collapse, an iterator whose items are themselves optional returns `Item??`, and an absent item does not end the loop. See [Iteration](https://rux-lang.dev/docs/lang/interfaces/iteration). ## No methods An optional is a compiler-owned form with no declaring package, so it cannot be extended and implements no interface — an `int32?` is not `Display`, even though `int32` is: ```text error: cannot extend native type 'int32?' note: a sum, optional, fallible, or unit type has no declaring package to own methods or interface implementations help: write a generic function that takes the native type as a parameter ``` A reusable operation on optionals is a generic free function, such as `func OrZero(value: T?, zero: T) -> T`. Inference reaches through `T?` as it does through any generic type. ## Layout An optional is stored as an 8-byte tag followed by its payload, aligned for the payload; a zero-sized payload takes no room. `sizeof(int32?)` and `sizeof(int64?)` are 16, `sizeof(int32??)` is 24 and `sizeof(()?)` is 8. The tag values are not a stable ABI. See [Layout](https://rux-lang.dev/docs/lang/memory/layout). ## See also - [Coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing) — `??`, a fallback for absence - [Optional propagation](https://rux-lang.dev/docs/lang/optionals/propagation) — `?`, passing absence to the caller - [Sum types](https://rux-lang.dev/docs/lang/sums/overview) and [Errors](https://rux-lang.dev/docs/lang/errors/overview) — the other native outcome forms - [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) — the full pattern grammar - Learn: [Optional](https://rux-lang.dev/docs/learn/optional), [Presence](https://rux-lang.dev/docs/learn/presence), [Nested optional](https://rux-lang.dev/docs/learn/nested-optional) # Coalescing The *coalescing operator* `??` opens one level of an [optional](https://rux-lang.dev/docs/lang/optionals/overview). When the optional is present, the result is its payload; when it is absent, the result is the fallback. ```text coalesce-expr = expr "??" fallback fallback = expr | "return" [ expr ] | "break" [ label ] | "continue" [ label ] | "fail" expr ``` ## Semantics ```rux func Lookup(key: int32) -> int32? { return key % 2 == 0 ? .Some(key * 10) : none; } func Slow() -> int32 { PrintLine(" (computing the fallback)"); return -1; } ``` ```rux PrintLine("{}", Lookup(4) ?? Slow()); // 40 PrintLine("{}", Lookup(3) ?? Slow()); // (computing the fallback), then -1 ``` - The left operand is evaluated exactly once, and must be a native optional `T?`. - The result has the payload type `T` — not `T?`. - The fallback is checked at compile time but **evaluated only when the optional is absent**. A slow or visible fallback, such as `Slow()` above, costs nothing when a value is present. - The fallback must convert to `T`, or leave (below). `a ?? b` is shorthand for a match: | With `??` | Means | | ---------------- | ---------------------------------------- | | `Lookup(k) ?? 0` | `match Lookup(k) { v? => v, none => 0 }` | ### One level at a time `??` opens only the outer level. On an `int32??`, the result is an `int32?`, and a present absence is a present value, not absence: ```rux let nested: int32?? = .Some(none); let once = nested ?? .Some(1i32); // none: the outer level was present ``` To open both levels, coalesce twice with explicit grouping: `(nested ?? none) ?? 0`, or two `let`s. ## Chaining `??` is right-associative, so a chain tries each optional from the left, and the first present one wins: ```rux PrintLine("{}", Lookup(3) ?? Lookup(5) ?? Lookup(6) ?? 0); // 60 ``` This is `Lookup(3) ?? (Lookup(5) ?? (Lookup(6) ?? 0))`: every operand but the last is an optional, and each later one runs only if everything before it was absent. ## A fallback that leaves Instead of a value, the fallback may leave: `return`, `break`, `continue`, `fail error`, a call to [`Panic`](https://rux-lang.dev/docs/lang/errors/panics), or a call to any [`#NoReturn()`](https://rux-lang.dev/docs/lang/attributes/noreturn) function. A leaving fallback is the whole right operand, and it never decides the result type — the result is still the payload: ```rux // Skip an absent value and try the next key. func FirstEven(keys: int32[..]) -> int32 { for key in keys { let value = Lookup(key) ?? continue; return value; } return 0; } // Leave the function early. func OrZero(key: int32) -> int32 { let value = Lookup(key) ?? return 0; return value + 1; } // Turn absence into a failure. func Required(key: int32) -> int32 ! NotFound { return Lookup(key) ?? fail NotFound { key: key }; } // Absence here is a bug. func Trusted(key: int32) -> int32 { return Lookup(key) ?? Panic("the key is always even"); } ``` | Fallback | On absence | Requires | | --------------- | ------------------------------------- | --------------------------------------------------------------------- | | `?? value` | the expression's result is `value` | `value` converts to the payload type | | `?? return e` | the function returns `e` | `e` fits the function's return type | | `?? break` | the innermost (or labelled) loop ends | an enclosing loop | | `?? continue` | the next iteration starts | an enclosing loop | | `?? fail e` | the function fails with `e` | a [fallible](https://rux-lang.dev/docs/lang/errors/overview) function | | `?? Panic(msg)` | the program stops | — | Each leaving form follows its own rules, so `?? continue` outside a loop is `error: 'continue' can only be used inside 'while', 'for', or 'loop'`, and `?? fail` in a function that cannot fail is `error: 'fail' needs an enclosing fallible function, but this function returns 'int'`. `?? return none` needs a function that returns an optional. `?? fail` is the standard way to turn absence into an error; see [Error propagation](https://rux-lang.dev/docs/lang/errors/propagation). ## Precedence `??` sits between the conditional operator and `||` in the [precedence table](https://rux-lang.dev/docs/lang/expressions/overview): | Tighter than `??` | Looser than `??` | | -------------------------------------------------------------- | ------------------------------- | | `||`, `&&`, comparison, arithmetic, `as`, `is`, unary, postfix | `c ? a : b`, assignment, ranges | So every comparison and arithmetic operator next to a `??` belongs to the fallback: ```rux let mild = (Lookup(3) ?? 0) == 0; ``` Without the parentheses, `Lookup(3) ?? 0 == 0` means `Lookup(3) ?? (0 == 0)`, a `bool` fallback for an `int32` payload: ```text error: coalescing fallback has type 'bool8', but the optional payload is 'int32' ``` Likewise `flag ?? false || true` is `flag ?? (false || true)`. ## Restrictions - **The left operand must be a native optional.** A plain value is never absent: `count ?? 0` is `error: operator '??' requires an optional left operand, but found 'int32'`. A raw pointer is rejected the same way, even though it may be null; make it an optional pointer `(*T)?` first. - **A fallible is rejected**, because coalescing would silently throw its error away: ```text error: operator '??' cannot take 'int32 ! E' note: coalescing tests one optional level and would silently discard an error help: recover the error with 'catch', or propagate it with '?' ``` :brThe fallible counterpart of `??` is `catch { else => fallback }`; see [Handling failures](https://rux-lang.dev/docs/lang/errors/handling). - **There is no `??=`.** `cached ??= 5;` does not parse; write `cached = cached ?? 5;`. - `??` is compiler-owned control flow. It cannot be declared in an `extend` block or overloaded. ## Ownership `??` consumes the optional it is given. A named optional of a copyable type is copied and stays usable. A named optional of a move-only type must be moved explicitly, and so must a move-only fallback: ```rux let h = (<-maybe) ?? <-fallback; ``` Without the `<-` the compiler stops with `error: move-only value 'h' requires an explicit '<-' in coalescing operand`. The fallback is moved only when it is used, so after this line `fallback` is *possibly* moved, and is destroyed at the end of its scope if it was not. A borrowed optional — a `&T?` — is never an operand; match it instead. See [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## See also - [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) — the type `T?` and its patterns - [Optional propagation](https://rux-lang.dev/docs/lang/optionals/propagation) — `?`, when absence should reach the caller - [Handling failures](https://rux-lang.dev/docs/lang/errors/handling) — `catch`, the fallback for a fallible - Learn: [Coalesce](https://rux-lang.dev/docs/learn/coalesce), [Coalesce exit](https://rux-lang.dev/docs/learn/coalesce-exit), [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) # Optional Propagation Postfix `?` on an [optional](https://rux-lang.dev/docs/lang/optionals/overview) removes one level of it. When the optional is present, `value?` is its payload and evaluation carries on. When it is absent, the enclosing function returns absence at once, and nothing after the `?` runs. ```text propagate-expr = postfix-expr "?" ``` The `?` is written tight against its operand. With whitespace on both sides, `?` is the conditional operator `c ? a : b` instead; see [Conditional](https://rux-lang.dev/docs/lang/expressions/conditional). ## Semantics ```rux func Parse(text: char8[..]) -> int32? { if text.length == 0 { return none; } var value = 0i32; for c in text { if c < c8'0' || c > c8'9' { return none; } value = value * 10 + ((c - c8'0') as int32); } return value; } func Sum(left: char8[..], right: char8[..]) -> int32? { return Parse(left)? + Parse(right)?; } ``` `Sum("12", "30")` is `42`. `Sum("12", "x")` is `none`: the second `?` finds absence and returns it. Each `?` operand is evaluated exactly once, from left to right, so in `Sum("x", "30")` the second `Parse` never runs. `value?` means the same as this match, with the absent arm leaving the function: ```rux match value { v? => v, none => return none } ``` Leaving through `?` is an ordinary `return`: [deferred statements](https://rux-lang.dev/docs/lang/ownership/defer) run and live locals are destroyed. ## The enclosing function Absence has to go somewhere, so the function containing the `?` must be able to return it. Two return types qualify: | The function returns | Absence leaves as | | -------------------- | ------------------------------------------------------------ | | `U?` | `none` | | `U? ! F` | a *successful* `none` — a fallible whose success is optional | `U` need not be the payload type of the operand; only the absence is passed on. The second form lets one function propagate both absence and failure: ```rux func ReadLine(ok: bool) -> char8[..]? ! IoError { if !ok { fail IoError { code: 5 }; } return "42"; } func FirstNumber(ok: bool) -> int32? ! IoError { let line = ReadLine(ok)?; // a failure leaves as a failure let text = line?; // absence leaves as a successful none return Parse(text); } ``` Absence never becomes an error. In a function returning a plain `int32 ! NotFound`, `?` on an optional is rejected, because there is no absent value to return and `?` will not invent an error: ```text error: '?' propagates the absence of 'int32?', but the enclosing function returns 'int32 ! NotFound' note: absence leaves as an optional's 'none', or as the successful 'none' of 'U? ! F'; '?' never invents an error for it help: declare an optional result, as in '-> T?', or supply a fallback with '??' ``` To turn absence into a failure, say which failure: `value ?? fail NotFound {}`. See [Coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing#a-fallback-that-leaves). The same message, with `'int'` at the end, is what `Main` gets when it returns a plain `int`. ## One level at a time `?` removes exactly one level. On an `int32??` the result is an `int32?`: a present absence is present, so it continues as `none` instead of leaving. ```rux func Inner(present: bool) -> int32?? { return present ? .Some(none) : none; } func Peel(present: bool) -> int32? { let once: int32? = Inner(present)?; return once; } ``` Two levels need two propagations, and they must be grouped. `??` is a single token — the [coalescing operator](https://rux-lang.dev/docs/lang/optionals/coalescing) — so `Inner(present)??` is a `??` missing its right operand (`error: expected an expression after '??' before ';'`). Write: ```rux func PeelBoth(present: bool) -> int32? { return (Inner(present)?)?; } ``` `?` and `??` compose: `Inner(present)? ?? 17` returns absence for an outer `none`, and uses `17` for a present absence. ## Restrictions - The operand must be a native optional or a native [fallible](https://rux-lang.dev/docs/lang/errors/propagation). Anything else is `error: 'int32' cannot be propagated with '?' because it is neither a native fallible nor an optional`. - A borrowed optional is never an operand — `?` moves the payload onward, and a reference owns nothing to move: ```text error: '?' cannot consume the borrowed value '&(int32?)' help: match the borrowed value to inspect it, or propagate an owned value ``` - `?` consumes its operand. A named copyable optional is copied; a named move-only one is written `(<-value)?`. See [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). ## See also - [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) — the type and its patterns - [Coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing) — `??` when absence has a stand-in - [Error propagation](https://rux-lang.dev/docs/lang/errors/propagation) — the same `?` on a fallible - Learn: [Optional propagate](https://rux-lang.dev/docs/learn/optional-propagate), [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error) # Errors Rux has no exceptions. A function that can fail says so in its return type with a *fallible* `T ! E`: a successful `T` or a failed `E`. The caller receives the failure as an ordinary value and has to deal with it — the compiler rejects code that drops it on the floor. ```text fallible-type = [ type ] "!" type ``` | Form | Meaning | | ------- | ------------------------------------------------------------------- | | `T ! E` | a success holding a `T`, or a failure holding an `E` | | `! E` | exactly `() ! E`: successful completion with no value, or a failure | The two sides are called *channels*. They stay distinct even when `T` and `E` are the same type: an `int32 ! int32` is either a successful `5` or a failed `5`, never just `5`. Conditions that no caller could recover from — a broken invariant, a bug — are not errors in this sense. They stop the program with a [panic](https://rux-lang.dev/docs/lang/errors/panics). ## The type `!` is the loosest type operator. The postfix suffixes `?`, `[]`, `[N]` and `[..]` bind first, then `|` (a [sum](https://rux-lang.dev/docs/lang/sums/overview)), then `!`: | Type | Means | | -------------------------------- | ---------------------------------------------------------- | | `int32 | bool ! IoError` | `(int32 | bool) ! IoError` | | `int32 ! ParseError | IoError` | `int32 ! (ParseError | IoError)` — an error sum | | `int32? ! IoError` | a fallible whose success is optional | | `int32 ! (IoError?)` | a fallible whose error is optional — the group is required | | `(int32 ! IoError)?` | an optional fallible | | `(int32 ! ParseError) ! IoError` | a nested fallible — the group is required | A type holds at most one unparenthesized `!`: ```text error: a type contains at most one unparenthesized '!' help: group the nested fallible, as in '(T ! E) ! F' or 'T ! (E ! F)' ``` ## Producing a result Inside a function that returns `T ! E`: | Write | Produces | | ------------------- | -------------------------------------------------- | | `return value;` | a success holding `value` | | `fail error;` | a failure holding `error`, and leaves the function | | `return;` | a success, when the success type is exactly `()` | | falling off the end | a success, when the success type is exactly `()` | ```rux variant DivideError { ByZero, Inexact(int) } func ExactDivide(a: int, b: int) -> int ! DivideError { if b == 0 { fail DivideError::ByZero; } if a % b != 0 { fail DivideError::Inexact(a % b); } return a / b; } func Save(ok: bool) -> ! IoError { if !ok { fail IoError { code: 28 }; } PrintLine("saved"); } ``` `return` is the success channel and `fail` the failure channel, and each checks its own operand: `return DivideError::ByZero;` is `error: 'return' value must have type 'int ! DivideError', but found 'DivideError'`, and `fail 30;` is `error: 'fail' value must have type 'DivideError', but found 'int'`. In a `! E` function there is no value to return, so `return 0;` is `error: 'return' value must have type '! IoError', but found 'int'`. `fail` is a statement, and it is only allowed in a function with an error channel: ```text error: 'fail' needs an enclosing fallible function, but this function returns 'int' help: declare the function's error channel, as in '-> T ! E' or '-> ! E' ``` `fail` leaves like `return`: the error is evaluated first, then [deferred statements](https://rux-lang.dev/docs/lang/ownership/defer) run and live locals are destroyed. Only a success type of exactly `()` completes without a value. A function with no return type is a void function, not a unit-returning one, and a type that merely contains the unit, such as `()?`, still needs a value: `return;` there is `error: 'return' requires a value of type '()?'`. ## Constructing either channel `.Success(value)` and `.Failure(error)` build a fallible as a value, without leaving the function. They can be stored, passed, compared and returned like anything else: ```rux let accepted: int ! DivideError = .Success(3); let rejected: int ! DivideError = .Failure(DivideError::ByZero); let plain: int ! DivideError = 3; // a plain value is a success let unit: ! IoError = .Success(()); // the unit success let same: int32 ! int32 = .Failure(5); ``` A plain value of the success type becomes a success wherever a fallible is expected, as `plain` shows; a failure always needs `.Failure` or `fail`. A constructor needs an expected type for whatever it does not fix itself: ```text error: cannot infer the type of 'promised' from a native constructor with an unknown channel ``` `return .Failure(error);` and `fail error;` produce the same result; `fail` is the usual spelling, and `.Failure` is for a failure that is stored rather than returned. ## Error types An error is an ordinary value of an ordinary type. There is no base error type, no error interface, and no implicit conversion between error types. Common choices: | Error type | Use it when | | --------------------------------------------------------------- | ------------------------------------------------------------------------- | | a `struct` | one kind of failure, with details: `IoError { code: 28 }` | | a [`variant`](https://rux-lang.dev/docs/lang/variants/overview) | several named kinds, each with its own payload: `DivideError::Inexact(1)` | | an [`enum`](https://rux-lang.dev/docs/lang/enums/overview) | several kinds with no payload | | a [sum](https://rux-lang.dev/docs/lang/sums/overview), `A | B` | the failures of several steps, each kept with its own type | | `()` | only the fact of failure matters: `int32 ! ()`, `fail ();` | An error sum widens implicitly: a `fail` or `?` with any member — or with a smaller sum of members — fits. A public function usually names a [`variant`](https://rux-lang.dev/docs/lang/variants/overview) instead, so that its error does not change every time an internal step gains a failure; a [type alias](https://rux-lang.dev/docs/lang/types/aliases) of a sum hides none of its members. ## Reading the result A fallible is not its success value: `ExactDivide(12, 2) / 2` is `error: operator '/' cannot combine left operand 'int ! DivideError' with right operand 'int'`. The value inside is reached by one of these: | Tool | Who decides what a failure means | | --------------------------------------------------------------------------------------------- | ---------------------------------------- | | [`match`](https://rux-lang.dev/docs/lang/errors/handling#matching) on `.Success` / `.Failure` | this function, case by case | | [`catch { … }`](https://rux-lang.dev/docs/lang/errors/handling#catch) | this function, with a recovery value | | [`?`](https://rux-lang.dev/docs/lang/errors/propagation) | the caller — the failure is passed on | | [`? else (e => …)`](https://rux-lang.dev/docs/lang/errors/propagation#error-mapping) | the caller, after the error is converted | ```rux func Show(outcome: int ! DivideError) { match outcome { .Success(value) => PrintLine("= {}", value), .Failure(DivideError::ByZero) => PrintLine("division by zero"), .Failure(DivideError::Inexact(rest)) => PrintLine("remainder {}", rest) } } ``` A fallible cannot be ignored. Calling `ExactDivide(12, 4);` as a statement is an error; see [Discarding a result](https://rux-lang.dev/docs/lang/errors/handling#discarding-a-result). ## Nesting Levels never collapse. In a `(int32 ! ParseError) ! IoError`, a failed inner result held as a success is *data* — the outer operation worked, and what it produced is a failure — not a failure of the outer level: ```rux func Layered(depth: int32) -> (int32 ! ParseError) ! IoError { if depth == 0 { return .Success(.Success(1)); } if depth == 1 { return .Success(.Failure(ParseError { position: 2 })); } fail IoError { code: 3 }; } ``` `?`, `catch` and a `.Success`/`.Failure` pattern each handle exactly one level. Where a value could become either a success or a failure of the outer level, the compiler refuses to guess: with `type R = int32 ! ParseError;`, `return value;` in a function returning `(int32 | R) ! ParseError` could store `value` as successful data or forward its failure. ```text error: conversion from 'int32 ! ParseError' to '(int32 | (int32 ! ParseError)) ! ParseError' is ambiguous; write '.Success(...)', '.Some(...)', or '?' to choose one, or annotate an intermediate type ``` `return .Success(value);` stores it, and `return value?;` forwards it. ## Equality `==` and `!=` compare the channel first, then the active payload with the payload's own `==`. A success and a failure holding equal values are different, and two unit successes are equal. ## A fallible Main [`Main`](https://rux-lang.dev/docs/lang/functions/main) may return `! E` or `int ! E`, so the top level of a program can use `?`: | `Main` returns | Exit status on success | On failure | | -------------- | ---------------------- | ---------- | | `! E` | `0` | `1` | | `int ! E` | the returned `int` | `1` | A failure runs the ordinary cleanup — defers and destructors — and exits with status 1 **without printing anything**. Report what the user needs to know before failing. ## No methods A fallible is a compiler-owned form. It cannot be extended and implements no interface; operations on fallibles are generic free functions, such as [`Core::Succeeded` and `Core::Failed`](https://rux-lang.dev/docs/lang/errors/handling#asking-without-handling). ## Layout A fallible is stored as an 8-byte tag (success or failure) followed by the payload of the active channel, so `sizeof(int32 ! int64)` is 16. A zero-sized payload takes no room: a `! E` is the tag and an `E`, and `sizeof(! ())` is 8. The tag values are not a stable ABI. See [Layout](https://rux-lang.dev/docs/lang/memory/layout). ## See also - [Handling failures](https://rux-lang.dev/docs/lang/errors/handling) — `match`, `catch`, and the discard rules - [Error propagation](https://rux-lang.dev/docs/lang/errors/propagation) — `?`, `? else`, and `?? fail` - [Panics](https://rux-lang.dev/docs/lang/errors/panics) — failures that stop the program - [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) — absence, which is not an error - Learn: [Fallible](https://rux-lang.dev/docs/learn/fallible), [Fail](https://rux-lang.dev/docs/learn/fail), [Unit fallible](https://rux-lang.dev/docs/learn/unit-fallible), [Outcome](https://rux-lang.dev/docs/learn/outcome), [Error variant](https://rux-lang.dev/docs/learn/error-variant), [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Nested fallible](https://rux-lang.dev/docs/learn/nested-fallible), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) # Handling Failures A function that receives a [fallible](https://rux-lang.dev/docs/lang/errors/overview) `T ! E` either handles the failure itself or passes it on. This page covers handling it: a `match` over both channels, postfix `catch`, and deliberately discarding a result. Passing it on is [Error propagation](https://rux-lang.dev/docs/lang/errors/propagation). ## Matching An ordinary [`match`](https://rux-lang.dev/docs/lang/patterns/match) selects a channel with the patterns `.Success(p)` and `.Failure(p)`. The inner pattern can be anything that matches the payload — a binding, a literal, a variant case, a typed pattern for an error sum, or another channel pattern for a nested fallible: ```rux func Percent(text: char8[..]) -> int { return match ParsePercent(text) { .Success(value) => value, .Failure(_: DigitError) => -1, .Failure(error: RangeError) => -error.value }; } ``` | Pattern | Matches | | ----------------------- | ------------------------------------------------------- | | `.Success(p)` | a success whose value matches `p` | | `.Success(())` | the success of a `! E` | | `.Failure(p)` | a failure whose error matches `p` | | `.Failure(e: A)` | a failure whose error is the member `A` of an error sum | | `.Failure(E::Case(x))` | a failure holding one case of a variant error | | `.Success(.Failure(e))` | an inner failure held as the outer success | Coverage is checked across both channels and every level, and the diagnostic names what is missing: ```text error: match on 'int ! NobodyToShare' is not exhaustive; missing .Failure(_) error: match on 'int ! (DigitError | RangeError)' is not exhaustive; missing .Failure(_: RangeError) error: match on 'int? ! SensorError' is not exhaustive; missing .Success(none) ``` A `.Success` pattern always has one field. For a `! E` it is the unit: `.Success => …` is `error: pattern '.Success' expects 1 field, but found 0`; write `.Success(())` or `.Success(_)`. ## catch `catch` recovers from the failure of one fallible. It is postfix, written right after the fallible expression, and its arms match the **error** only: ```text catch-expr = postfix-expr "catch" "{" match-arm { "," match-arm } [ "," ] "}" ``` ```rux variant DigitError { Blank, Letter(char) } func Lenient(c: char) -> int { return ReadDigit(c) catch { DigitError::Blank => 0, DigitError::Letter('O') => 0, DigitError::Letter(_) => -1 }; } ``` - The subject is evaluated once. A success passes through unchanged and no arm runs; the expression's value is the success value. - A failure is matched against the arms, which use ordinary match-arm grammar — guards and an `else` arm included — and must cover every error value: `error: match on 'DigitError' is not exhaustive; missing DigitError::Blank`. - Each arm either produces a value of the **success type** or leaves the function. `DigitError::Blank => "blank"` in the example is `error: 'catch' arm produces 'char8[..]', but the recovered value has type 'int'`. - The result of `catch` is a plain `T` — the fallible is fully handled. An arm leaves with `fail`, `return`, `break`, `continue`, a call to [`Panic`](https://rux-lang.dev/docs/lang/errors/panics) or a [`#NoReturn()`](https://rux-lang.dev/docs/lang/attributes/noreturn) function. A leaving arm does not decide the result type, and its `fail` or `?` is an ordinary exit from the enclosing function — the same `catch` never sees it again: ```rux func Strict(c: char) -> int ! IoError { let digit = ReadDigit(c) catch { e => fail IoError { code: 22 } }; return digit * 2; } let trusted = ReadDigit('8') catch { e => Panic("a literal digit always reads") }; ``` `catch` applies only to a native fallible. On an optional or a plain value it is `error: 'catch' recovers a native fallible, but the subject has type 'int?'`; the optional counterpart is [`??`](https://rux-lang.dev/docs/lang/optionals/coalescing). ### A fallback for every failure `catch { else => fallback }` is the fallible counterpart of `??`: the success value, or `fallback` for any failure. ```rux let digit = ReadDigit(c) catch { else => 0 }; ``` Unlike `??`, `catch` has no brace-less form; the braces are always written. ### Block arms An arm whose body is a block `{ … }` completes with `()`. It is therefore valid only where the success type is `()` — a `! E` — unless the block ends by leaving: ```rux Close(false) catch { e => { PrintLine("close failed with {}", e.code); } }; ``` On a fallible with a value, an empty block is rejected rather than inventing one: ```text error: a block arm completes with '()', but 'catch' must recover a value of type 'int32' help: give the arm a value, or leave it with 'fail', 'return', or a call to 'Panic' ``` An expression arm has to have the success type too. `e => PrintLine("…")` in a `! E` catch is an error, because `PrintLine` returns `IoError?`, not `()`; put the call in a block. ### Binding `catch` binds tighter than every binary operator, to the postfix expression right before it, and a postfix chain continues after the closing brace: ```rux PrintLine("{}", 1 + ReadDigit('x') catch { else => 10 }); // 11: only ReadDigit is recovered PrintLine("{}", ReadDigit('4') catch { else => 0 } * 10); // 40 ``` A `match` expression may be the subject: `let picked = match key { … } catch { else => -1 };`. A match statement takes no postfix operator. `catch` handles one level. On a `(int32 ! ParseError) ! IoError`, only the outer `IoError` reaches the arms; an inner failure is part of the success and passes through. `catch` consumes its subject. A named copyable fallible is copied; a named move-only one is written `(<-outcome) catch { … }`. An arm that binds the error owns it, and an error no arm binds is destroyed when its arm is chosen. ## Discarding a result A failure that nothing looks at is lost, so the compiler rejects the ways a fallible can be silently thrown away: | Code | Result | | ---------------------------------------------------------- | --------------------------------------- | | `Save(true);` — an expression statement | error | | `let _ = Save(true);` | error | | a match statement arm whose bare expression is a fallible | error | | `let r = Save(true);` and `r` is never read | warning | | `Save(true) catch { else => {} };` | a deliberate discard — only for a `! E` | | a `match` with `.Success(_) => {}` and `.Failure(_) => {}` | a deliberate discard of any fallible | ```text error: fallible result of type '! IoError' is discarded note: a failure that nothing handles is lost help: propagate it with '?', recover with 'catch', or match both '.Success' and '.Failure' ``` For `let _`, the note reads `binding a fallible to '_' does not handle its failure`. The unread local is `warning: fallible local 'r' is never read; its failure is never handled`. Deliberate discards, for cleanup whose outcome does not matter: ```rux Close(false) catch { else => {} }; // a ! E match ReadDigit('5') { // a fallible with a value .Success(_) => {}, .Failure(_) => {} } ``` These checks are practical, not a proof that every error is handled. Reading, passing, returning, storing or overwriting a fallible counts as a use, and nothing is followed further — through fields, containers or later writes. ::note **PrintLine is not fallible.**:br`Io::PrintLine` returns `IoError?` — an optional that reports a console write problem. An optional may be ignored, which is why `PrintLine(…);` is a valid statement while a bare call to a fallible function is not. :: ## Asking without handling `Core` provides two generic functions for code that needs only the answer, such as a branch or an assertion: ```rux import Core::{ Failed, Succeeded }; ``` | Function | Returns | | ----------------------------------------- | --------------------------------- | | `Succeeded(outcome: T ! E) -> bool` | whether `outcome` holds a success | | `Failed(outcome: T ! E) -> bool` | whether `outcome` holds a failure | Both consume their argument; the payload is destroyed after the question is answered. `is` does not test channels — `outcome is int32` on a fallible is `error: 'is' cannot test the channel of fallible 'int32 ! IoError'`. ## Idioms | Wanted | Write | | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | a fallback value for any failure | `outcome catch { else => fallback }` | | a fallback per kind of failure | `outcome catch { Kind::A => …, Kind::B => … }` | | an expected success, where failure is a bug | `outcome catch { e => Panic("why it cannot fail") }` | | translate the failure and keep failing | `outcome catch { e => fail Wrap(e) }`, or [`? else`](https://rux-lang.dev/docs/lang/errors/propagation#error-mapping) | | pass the failure on unchanged | [`outcome?`](https://rux-lang.dev/docs/lang/errors/propagation) | | ignore a unit fallible | `outcome catch { else => {} };` | `??` is not one of them: `ReadDigit('7') ?? 5` is `error: operator '??' cannot take 'int ! DigitError'`, because coalescing would discard the error. ## See also - [Errors](https://rux-lang.dev/docs/lang/errors/overview) — the fallible type and how failures are produced - [Error propagation](https://rux-lang.dev/docs/lang/errors/propagation) — handing a failure to the caller - [Match](https://rux-lang.dev/docs/lang/patterns/match) and [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) - Learn: [Outcome](https://rux-lang.dev/docs/learn/outcome), [Discard](https://rux-lang.dev/docs/learn/discard), [Catch](https://rux-lang.dev/docs/learn/catch), [Catch fallback](https://rux-lang.dev/docs/learn/catch-fallback) # Error Propagation Often the function that receives a failure is not the one that should decide what it means. Postfix `?` hands it to the caller: on success, `outcome?` is the success value and evaluation carries on; on failure, the enclosing function fails at once with the same error. ```text propagate-expr = postfix-expr "?" map-expr = postfix-expr "?" "else" "(" binder "=>" expr ")" binder = identifier | "_" ``` ## `?` on a fallible ```rux func ParseNumber(text: char8[..]) -> int ! DigitError { var value = 0; for i in 0..text.length { let c = text[i]; if c < c8'0' || c > c8'9' { fail DigitError { position: i }; } value = value * 10 + ((c - c8'0') as int); } return value; } func Area(width: char8[..], height: char8[..]) -> int ! DigitError { return ParseNumber(width)? * ParseNumber(height)?; } ``` `outcome?` is shorthand for this match, with the failure arm leaving the function: ```rux match outcome { .Success(value) => value, .Failure(error) => fail error } ``` ```mermaid flowchart LR step["ParseNumber(width)?"] --> q{"Which channel?"} q -- ".Success(value)" --> go["the expression is value;
Area carries on"] q -- ".Failure(error)" --> out["Area fails at once
with the same error"] ``` - The operand is evaluated exactly once. Several `?` in one expression run from left to right, and the first failure leaves before the rest are evaluated. - Leaving through `?` is an ordinary return: the error is captured first, then [deferred statements](https://rux-lang.dev/docs/lang/ownership/defer) run in reverse order and live locals are [destroyed](https://rux-lang.dev/docs/lang/ownership/destructors) — including the completed fields of an aggregate whose construction the `?` interrupted. - `?` removes one level. The success value continues as it is, even when it is itself a fallible: in a `(int32 ! ParseError) ! IoError`, `?` forwards only the outer `IoError`, and an inner failure continues as data. ## Requirements ### The function must be fallible The failure leaves through the enclosing function's own failure channel, so that function must return `U ! F` (or `! F`): ```text error: '?' propagates native fallible 'int ! DigitError', but the enclosing function returns 'int' note: '?' leaves through the outer failure channel of a fallible function help: declare the function's error channel, as in '-> T ! E', or handle the failure with 'match' ``` The success types need not match — only the error is passed on. An optional return type is not a target for a failure, and a [fallible `Main`](https://rux-lang.dev/docs/lang/errors/overview#a-fallible-main) lets the top level of a program use `?`. ### The error must fit `?` never converts an error. An error of type `E` leaves a function that fails with `F` only when: | `E` and `F` | Example | | ----------------------------------------- | ------------------------------------------------------------------ | | the same type | `DigitError` into `DigitError` | | `E` is a member of the sum `F` | `DigitError` into `DigitError | RangeError` | | `E` is a sum whose members are all in `F` | `DigitError | RangeError` into `DigitError | IoError | RangeError` | ```rux func ParsePercent(text: char8[..]) -> int ! (DigitError | RangeError) { let value = ParseNumber(text)?; if value > 100 { fail RangeError { value: value }; } return value; } ``` Any other pair is rejected: ```text error: '?' propagates error type 'DigitError', but the enclosing function fails with 'SettingError' note: '?' moves an error into the outer failure only by identity, sum member injection, or subset widening; it never converts an error help: map the error to 'SettingError' with '? else (e => ...)', or match the value ``` ## Error mapping `value? else (e => mapper)` propagates like `value?`, but converts the error first. On success the mapper never runs. On failure, `e` is bound to the complete error, the mapper runs once, and the function fails with its result: ```rux func ParseWidth(text: char8[..]) -> int ! SettingError { let width = ParseNumber(text)? else (e => SettingError { name: "width", column: e.position + 1 }); return width; } ``` The mapper is the place to add context the error does not carry — here, the name of the setting and a 1-based column. - The parentheses hold one binder and one expression. The binder may be `_` when the error is not needed and is copyable. A move-only error must be bound and moved, `(e => Wrap(<-e))`; `_` there is `error: the error 'Owned' cannot be discarded with '_' because it is move-only`. - The mapped value must convert to the enclosing function's error type: `ParseNumber(width)? else (e => 5)` is `error: the mapped error has type 'int', but the enclosing function fails with 'SettingError'`. - A mapped value is never propagated again, so a mapper that returns a fallible is a type error, not a second `?`. - The mapper may leave on its own with `fail`, `return` or a call to `Panic`. A `return` there takes the normal exit instead of failing. - The mapper is not a closure. It reads and moves the surrounding locals under the ordinary rules, on a path that always leaves the function; a local moved only inside the mapper is still owned on the continuing path. - `? else` applies only to a native fallible, in a fallible function. On an optional it is `error: '? else' maps the error of a native fallible, but the operand has type 'int?'` — absence has no error to map. A mapper that converts different members differently is a `match`, usually in a helper function: ```rux func ToConfig(error: ParseError | IoError, line: int32) -> ConfigError { return match error { parse: ParseError => ConfigError { kind: 1, detail: parse.position * 100 + line }, io: IoError => ConfigError { kind: 2, detail: io.code * 100 + line } }; } func Load(text: int32, line: int32) -> int32 ! ConfigError { let value = Read(text)? else (e => ToConfig(e, line)); return value + 1; } ``` ## From absence to failure `?` on an [optional](https://rux-lang.dev/docs/lang/optionals/propagation) passes on absence, never an error; in a function that fails with `F`, it is rejected. When absence should be a failure, the [coalescing](https://rux-lang.dev/docs/lang/optionals/coalescing) fallback says which one: ```rux func PriceOf(code: int) -> int? { return code == 1 ? .Some(250) : none; } func Total(code: int, count: int) -> int ! UnknownProduct { let price = PriceOf(code) ?? fail UnknownProduct { code: code }; return price * count; } ``` A function that returns `U? ! F` can pass on both: `?` on a fallible forwards the failure, and `?` on an optional returns a successful `none`. ## Ownership `?` and `? else` consume their operand. A named copyable fallible is copied and stays usable; a named move-only one must be moved explicitly: ```text error: move-only value 'h' requires an explicit '<-' in propagation operand help: prefix the outcome with '<-', as in '(<-h)?' ``` A borrowed fallible is never an operand; match it instead. The continuing value and the outgoing error are moved with their move operations. ## See also - [Errors](https://rux-lang.dev/docs/lang/errors/overview) — the fallible type - [Handling failures](https://rux-lang.dev/docs/lang/errors/handling) — deciding here instead of passing on - [Optional propagation](https://rux-lang.dev/docs/lang/optionals/propagation) — the same `?` on an optional - Learn: [Propagate](https://rux-lang.dev/docs/learn/propagate), [Error mapping](https://rux-lang.dev/docs/learn/error-mapping), [Error sum](https://rux-lang.dev/docs/learn/error-sum), [Absence to error](https://rux-lang.dev/docs/learn/absence-to-error), [Fallible main](https://rux-lang.dev/docs/learn/fallible-main) # Panics A *panic* stops the program at once. It is for conditions that cannot happen in a correct program — a broken invariant, an impossible case — where no caller could sensibly recover. A failure that a working program can meet, such as bad input or a missing file, is an [error](https://rux-lang.dev/docs/lang/errors/overview) instead: a fallible the caller handles. | | A fallible `T ! E` | A 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 | | Cleanup | defers and destructors run | nothing runs | ## Panic `Core::Panic` stops the program with a message: ```rux import Core::Panic; import Io::PrintLine; func Main() -> int { defer PrintLine("deferred"); PrintLine("before"); Panic("gave up"); return 0; } ``` ```text before Panic: gave up at Main (Src/Main.rux:7:5) ``` The report goes to the standard error stream: `Panic: `, the message, and the function, file, line and column of the call. The message is a `char8[..]`; to include a value in it, print the value first. `Panic` never returns, so it may stand wherever a value is expected — as a match or `catch` arm, a `??` fallback, or a `? else` mapper — without affecting the type of the result: ```rux let trusted = ReadDigit('8') catch { e => Panic("a literal digit always reads") }; let value = Lookup(key) ?? Panic("the key is always present"); ``` ## Assert and DebugAssert An assertion is a panic with a condition attached. When the condition holds nothing happens; when it does not, the program stops: ```rux import Core::Assert; import Io::PrintLine; func Median(scores: int[..]) -> int { Assert(scores.length > 0, "a median needs at least one score"); return scores[scores.length / 2]; } func Main() -> int { let scores = [3, 5, 8]; PrintLine("{}", Median(scores)); PrintLine("{}", Median(scores[0..0])); return 0; } ``` ```text 5 Assertion failed: a median needs at least one score at Median (Src/Main.rux:5:5) ``` The condition must be a `bool`: `Assert(scores.length, "…")` is `error: argument 1 to 'Assert' has type 'uint64', but parameter 'condition' requires 'bool8'`. Write the message as what was expected — the failing value is not printed. `DebugAssert`, also imported from `Core`, is the same check, kept only in builds with debug assertions: | | `Assert` | `DebugAssert` | | ----------------------------------- | -------- | --------------------------------------------- | | Debug build (`rux run`) | checked | checked | | Release build (`rux run --release`) | checked | removed — **its arguments are not evaluated** | A `DebugAssert` in a release build is gone entirely: a condition that calls a function does not call it, so any side effect of the condition disappears too. Keep the work outside the assertion and assert on its result. Whether debug assertions are on is the compile-time value `#build.debugAssertions`; see [Context values](https://rux-lang.dev/docs/lang/comptime/context). ## Functions that never return A function of your own can promise never to return with the [`#NoReturn()`](https://rux-lang.dev/docs/lang/attributes/noreturn) attribute. Like `Panic`, a call to it may then stand where a value is expected: ```rux #NoReturn() func Unreachable(what: char8[..]) { PrintLine("reached {}", what); Panic("unreachable code was reached"); } func Days(month: int) -> int { return match month { 2 => 28, 4 => 30, 6 => 30, 9 => 30, 11 => 30, 1..=12 => 31, else => Unreachable("a month outside 1 to 12") }; } ``` A `#NoReturn()` function declares no return type (`error: '#NoReturn' function cannot declare a return type`) and may not contain `return` (`error: return is not allowed in a '#NoReturn' function`). Without the attribute, the `else` arm above would be an ordinary call that completes with `()`, and the match would be `error: match arm type mismatch: expected 'int', found '()'`. ::note **Falling off the end.**:br A `#NoReturn()` function's body must end by panicking or by calling another function that never returns. rux 0.4.0 does not check this yet: a body that reaches its end stops the program there with no message. :: ## Run-time checks Some operations check their operands while the program runs, on every target and in every build profile, and panic when the check fails. Each report names the function, file, line and column of the operation: | Report | Raised by | | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `Panic: division by zero` | integer `/`, `%`, `/=` or `%=` with a zero divisor | | `Panic: division overflow` | signed `/` or `%` of the type's minimum by `-1`, whose quotient does not fit | | `Panic: index out of range` | `a[i]` on an array or slice with `i` not below the length; `a[start..end]` unless `start <= end <= a.length` | | `Panic: no match arm matched value of 'T'` | a `match` that the compiler accepted as exhaustive meets a value outside it, such as an enum value made with `as` from an integer that names no case | ```rux import Io::PrintLine; func At(values: int[..], i: uint) -> int { return values[i]; } func Main() -> int { let values = [1, 2, 3]; PrintLine("{}", At(values, 3)); return 0; } ``` ```text Panic: index out of range at At (Src/Main.rux:4:18) ``` A check the compiler can decide is decided at compile time instead. A constant index into a fixed array is checked when the program is compiled — `fixed[7]` on an `int32[4]` is `error: index 7 is out of range for an array of 4 elements` — and a division by a nonzero literal needs no check at all, and a release build removes checks it proves always pass. Raw-pointer indexing is never checked. Integer `+`, `-` and `*` wrap rather than panic; see [Arithmetic](https://rux-lang.dev/docs/lang/expressions/arithmetic). ## What a panic does not do A panic does not unwind. After the report the program stops on the spot: - no [deferred statement](https://rux-lang.dev/docs/lang/ownership/defer) runs — the `deferred` line in the first example is never printed; - no [destructor](https://rux-lang.dev/docs/lang/ownership/destructors) runs, for any value in any function; - no caller observes the panic, and nothing can catch it. That is deliberate: once a case that "cannot happen" has happened, the program's invariants no longer hold, and running cleanup code written on the assumption that they do can turn one failure into several. Anything that must be released on a failing path belongs on a fallible path, where the caller sees the failure and cleanup runs. ## Exit status A panic ends the process with an illegal-instruction trap, not through a normal exit. On Windows the exit status is `0xC000001D` (`STATUS_ILLEGAL_INSTRUCTION`); on Linux, macOS and FreeBSD the process is killed by `SIGILL`, which a POSIX shell reports as status `132`. Either way the status is non-zero, so a script or test runner sees the program as failed. A [fallible `Main`](https://rux-lang.dev/docs/lang/errors/overview#a-fallible-main) that fails is different: it is an ordinary exit with status `1`, after cleanup. ## See also - [Errors](https://rux-lang.dev/docs/lang/errors/overview) — failures the caller can handle - [`#NoReturn`](https://rux-lang.dev/docs/lang/attributes/noreturn) — the attribute for functions that never return - API: [`Panic`](https://rux-lang.dev/docs/api/core/panic), [`Assert`](https://rux-lang.dev/docs/api/core/assert) - Learn: [Panic](https://rux-lang.dev/docs/learn/panic), [Assert](https://rux-lang.dev/docs/learn/assert) # Sum Types A *sum type* `A | B` holds one value whose type is one of a set of distinct types, its *members*. A value of `int32 | bool` is either an `int32` or a `bool`, and it remembers which. Like optionals and fallibles, sums are compiler-owned: they need no declaration, and the members can be any types — primitives, structs, variants, slices, other native forms. ```text sum-type = type "|" type { "|" type } ``` ```rux struct Circle { radius: float64; } struct Square { side: float64; } struct Rectangle { width: float64; height: float64; } type Box = Square | Rectangle; type Shape = Circle | Box; func Area(shape: Shape) -> float64 { return match shape { c: Circle => 3.14159 * c.radius * c.radius, s: Square => s.side * s.side, r: Rectangle => r.width * r.height }; } ``` A sum is taken apart only by a [pattern](https://rux-lang.dev/docs/lang/sums/patterns), and tested with [`is`](https://rux-lang.dev/docs/lang/sums/type-tests). ## Sums, variants and unions Rux has three ways to say "one of several": | Form | Alternatives are told apart by | Tag | | --------------------------------------------------------------- | ------------------------------ | ----------- | | a sum `A | B` | their types | yes, hidden | | a [`variant`](https://rux-lang.dev/docs/lang/variants/overview) | case names, declared once | yes, hidden | | a [`union`](https://rux-lang.dev/docs/lang/unions/overview) | nothing — fields overlap | none | Use a sum when the alternatives are already types and their types say everything — `ParseError | IoError`, `Options | Defaults`. Use a `variant` when two alternatives could have the same payload type, or when the set needs a name of its own that callers depend on. ## The type is a set A sum is a set of resolved types, not a list. The compiler normalizes every sum it sees: | Rule | So | | -------------------------------------- | ---------------------------------------------- | | aliases are resolved | `bool | int32` is `bool8 | int32` | | order does not matter | `int32 | bool` and `bool | int32` are one type | | duplicates are removed | `bool | int32 | bool` is `bool8 | int32` | | nested sums are flattened | `int32 | (bool | int32)` is `bool8 | int32` | | a single remaining member is that type | `int32 | int32` is `int32` | | flattening stops at `?` and `!` | `int32 | ((bool | int32)?)` has two members | Members are kept in a canonical order derived from their fully qualified names, which is why diagnostics print `bool8 | int32` whichever order the source used. `|` binds looser than the postfix suffixes and tighter than `!`: `int32 | bool?` is rejected until it is grouped as `int32 | (bool?)` or `(int32 | bool)?`, and `Options | Defaults ! IoError` is `(Options | Defaults) ! IoError`. ## Making a sum value A value enters a sum in one of two ways, where a sum is expected — an annotated `let`, an assignment, an argument, a return value: - **Injection.** A value whose type is exactly one of the members becomes that member. - **Widening.** A value of a smaller sum whose members are all members of the target keeps its active member. ```rux func Tile(side: float64) -> Box { return Square { side: side }; // injection: Square is a member of Box } let shape: Shape = Tile(3.0); // widening: Square | Rectangle into Circle | Rectangle | Square ``` No other conversion is searched. A member is never converted to make it fit, so `let s: int32 | bool = 2.5;` is `error: cannot assign 'float64' to 'bool8 | int32'`, and a wider sum never narrows: ```text error: cannot assign 'A | B | C' to 'A | B' ``` Knowing the value is an `A` right now is not enough; take it out with a [subset pattern](https://rux-lang.dev/docs/lang/sums/patterns#subset-patterns) instead. An unsuffixed integer or float literal targets the one member of its kind, whatever its value. With two integer members it is ambiguous: ```text error: integer literal is ambiguous for 'int32 | int64', which has several integer members; add a suffix or a cast ``` Write `5i64` or `5 as int32`. Every other literal is injected only when its own type is a member. ## Using a sum A sum has none of its members' operations — not even those they all share. Arithmetic, field access, method calls and `Display` all need the member first: ```text error: operator '+' cannot combine left operand 'bool8 | int32' with right operand 'int' error: argument 2 to 'PrintLine' has type 'bool8 | int32', but variadic parameter 'args' requires 'Display' ``` What a sum does have: | Operation | Meaning | | --------------------------------------------------------- | --------------------------------------------------------------- | | a [`match`](https://rux-lang.dev/docs/lang/sums/patterns) | selects the member and binds it | | [`is`](https://rux-lang.dev/docs/lang/sums/type-tests) | tests which member is active, without taking it out | | `==`, `!=` | equal when the same member is active and the payloads are equal | | assignment, passing, returning | moves or copies the whole value, as for any type | `==` needs two operands of the same sum type. An unsuffixed literal adopts the sum's type, so `port == 8080` works for `port: int32 | bool`, but a value with a type of its own is never injected for a comparison: ```text error: operator '==' cannot compare 'bool8 | int32' with 'bool8' note: both operands of a native comparison have the same type; a comparison never injects, widens, or wraps an operand ``` Give the value the sum type first — `let off: int32 | bool = false;` — and compare two sums. Two values holding different members are unequal even when the values look alike: `7i8` and `7i64` in an `int8 | int64 | bool` differ. Like the other native forms, a sum cannot be extended and implements no interface, even when every member implements it. A [type alias](https://rux-lang.dev/docs/lang/types/aliases) of a sum names exactly its members and hides none of them. ## Generic sums A sum may be written over [type parameters](https://rux-lang.dev/docs/lang/generics/overview), `T | U`, and is normalized again for every instantiation. When the arguments coincide, the sum collapses: ```rux func Choose(left: bool, a: T, b: U) -> T | U { if left { return a; } return b; } let same: int32 = Choose(false, 1, 2); // T | U is int32 let mixed = Choose(false, 1, true); // bool8 | int32 ``` A `match` over `T | U` stays valid at every instantiation by ending in `else`; see [Sum patterns](https://rux-lang.dev/docs/lang/sums/patterns#generic-sums). ## Layout A sum is stored as an 8-byte tag naming the active member, followed by that member's payload: `sizeof(int32 | bool)` is 16, and `sizeof(Circle | Rectangle)` is 24. The tag values are not a stable ABI. See [Layout](https://rux-lang.dev/docs/lang/memory/layout). ## See also - [Sum patterns](https://rux-lang.dev/docs/lang/sums/patterns) — taking a member out - [Type tests](https://rux-lang.dev/docs/lang/sums/type-tests) — `is` - [Variants](https://rux-lang.dev/docs/lang/variants/overview) and [Unions](https://rux-lang.dev/docs/lang/unions/overview) — the named and the untagged alternatives - [Errors](https://rux-lang.dev/docs/lang/errors/overview) — error sums `T ! (A | B)` - Learn: [Sum type](https://rux-lang.dev/docs/learn/sum-type), [Sum widening](https://rux-lang.dev/docs/learn/sum-widening), [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) # Sum Patterns The only way to take a value out of a [sum](https://rux-lang.dev/docs/lang/sums/overview) is a pattern in a [`match`](https://rux-lang.dev/docs/lang/patterns/match) (or a [`catch`](https://rux-lang.dev/docs/lang/errors/handling#catch) arm, for an error sum). A sum pattern selects members by their type. ```text typed-pattern = ( identifier | "_" ) ":" type ``` | Pattern | Matches | Binds | | --------------- | ----------------------------------------- | ------------------------------ | | `v: T` | the member `T` | `v` as a `T` | | `_: T` | the member `T` | nothing | | `v: A | B` | any of the members `A`, `B` | `v` as the smaller sum `A | B` | | `Type::Case(p)` | the variant member `Type`, holding `Case` | whatever `p` binds | | `else` | everything left | nothing | Guards, `else` and every other match feature work as usual. See [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) for the full grammar. ## Typed patterns `v: T` selects the member `T` and binds it at the member's type, so the arm can use everything a `T` has: ```rux func Describe(shape: Circle | Square | Rectangle) -> char8[..] { return match shape { c: Circle if c.radius == 0.0 => "a point", _: Circle => "a circle", box: Square | Rectangle => match box { _: Square => "a square box", _: Rectangle => "a rectangular box" } }; } ``` The type must name a member exactly — an `int32` member is not matched by `n: int64`: ```text error: type 'int64' is not a member or subset of sum 'bool8 | int32' ``` A bare type name is not a pattern. An identifier in a pattern binds a new name, and a name that already means something else is rejected rather than silently binding the whole subject: ```text error: pattern 'int32' cannot bind a new variable because 'int32' already names a type help: write 'int32Value: int32' to select the member, 'int32 { ... }' to destructure it, or 'Type::Case' to select a case ``` ## Subset patterns `v: A | B` selects several members at once and binds them as the smaller sum `A | B`. The binding is still a sum: to reach a member's fields, match it again, as `box` is matched above. `box.side` in that arm is an error, because a `Rectangle` has no `side`: ```text error: type 'Rectangle | Square' has no field 'side' ``` A subset pattern is how a value narrows. A plain assignment from a wider sum to a smaller one is rejected; an arm that binds the subset produces a value of the smaller type that can be passed on. ## Variant members When a member is a [`variant`](https://rux-lang.dev/docs/lang/variants/overview), a qualified case pattern selects the member and its case in one step: ```rux variant Token { Number(int32), Name(char8[..]) } func Kind(value: Token | bool | int32) -> int32 { return match value { Token::Number(n) => n, Token::Name(_) => -1, flag: bool => flag ? 1 : 0, else => 99 }; } ``` The qualification is required. An unqualified `.Case` pattern works on a variant subject, but on a sum it is ambiguous by construction: ```text error: case pattern '.Missing' cannot select from sum 'DecodeError | IoError' help: write 'DecodeError::Missing' to select the member and its case ``` A qualified case is ambiguous, too, when two members are instantiations of the same generic variant: ```text error: case pattern on 'Slot' is ambiguous in 'Slot | Slot': more than one member is that variant help: select one instantiation with a typed pattern, as in 'v: Slot', and match it separately ``` ## Coverage A match on a sum must cover every member. The diagnostic names the first one missing: ```text error: match on 'bool8 | char8[..] | int32' is not exhaustive; missing _: char8[..] ``` An arm whose members are all covered by earlier unguarded arms is unreachable, and so is an error: ```rux func Pick(v: A | B | C) -> int32 { return match v { ab: A | B => 1, a: A => 2, // error: every A went to the first arm _: C => 3 }; } ``` ```text error: match arm is unreachable because earlier arms already match every value it matches ``` An `else` arm is the one exception: it is never reported as unreachable, even when the arms before it already cover every member. That keeps generic code valid (below). ## Borrowed subjects A match on a borrowed sum inspects it in place, and the subject stays with its owner. Through an exclusive borrow `&var`, a typed pattern binding one member writes through to the original: ```rux func Grow(shape: &var (Circle | Square)) { match shape { c: Circle => { c.radius = c.radius * 2.0; }, else => {} } } ``` A subset binding of a borrowed subject can be read and matched, but not borrowed again, stored or moved. A match on an owned sum consumes it: arms that bind a member own it, and an arm that binds nothing destroys it. See [Ownership](https://rux-lang.dev/docs/lang/ownership/overview). ## Generic sums A pattern over `T | U` is checked again at every instantiation. When `T` and `U` are the same type, the sum collapses to it, so `_: T` already covers everything — the closing `else` covers nothing, which is allowed: ```rux func Left(value: T | U) -> bool { return match value { _: T => true, else => false }; } ``` `Left(true)` is `false`; `Left(5)` is `true`. ::note **A concrete type in a generic match.**:br A typed pattern that names a concrete member, such as `n: int32 =>` on a `T | U` subject, is meant to select that member after substitution. rux 0.4.0 accepts it but fails while lowering the program, with `error: cannot lower the selection of 'int32' from 'T | U'`. Name the type parameters in the patterns instead. :: ::note **Arms that build a sum.**:br An annotation is meant to give its type to every arm of a `match` expression, so that `let r: int32 | bool = match n { 0 => false, else => n };` injects each arm into the sum. rux 0.4.0 does not do this yet and reports `match arm type mismatch: expected 'bool8', found 'int32'`. Return the value from a function whose return type is the sum, where each `return` is injected on its own. :: ## See also - [Sum types](https://rux-lang.dev/docs/lang/sums/overview) — the type and how values enter it - [Type tests](https://rux-lang.dev/docs/lang/sums/type-tests) — `is`, when only the member matters - [Match](https://rux-lang.dev/docs/lang/patterns/match) and [Patterns](https://rux-lang.dev/docs/lang/patterns/patterns) - Learn: [Typed pattern](https://rux-lang.dev/docs/learn/typed-pattern), [Subset pattern](https://rux-lang.dev/docs/learn/subset-pattern), [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) # Type Tests `value is Type` answers a question about the type of `value` and produces a `bool`. What the question is depends on the operand: on a [sum](https://rux-lang.dev/docs/lang/sums/overview) it asks which member is active, on an [optional](https://rux-lang.dev/docs/lang/optionals/overview) whether a value is present, and on anything else it compares exact types at compile time. ```text type-test = expr "is" postfix-type ``` ```rux struct Plus {} struct Minus {} struct Number { value: int32; } type Token = Plus | Minus | Number; func IsOperator(token: Token) -> bool { return token is (Plus | Minus); } func Count(tokens: Token[..]) -> uint { var numbers: uint = 0; for token in tokens { if token is Number { numbers += 1; } } return numbers; } ``` ## What is tested The tested type is resolved first; together with the operand's type it selects the meaning: | Operand | `value is T` is `true` when | | ------------------ | --------------------------------------------------------------------------------------------- | | a sum `A | B | C` | the active member is `T`, or one of the members of a tested subset `(A | B)` | | an optional `P?` | the value is present and its payload is `T` — or, for a sum payload, a member or subset of it | | a fallible `T ! E` | — rejected; match `.Success(…)` or `.Failure(…)` instead | | any other type | the operand's type is exactly `T`, after aliases are resolved — decided at compile time | ```rux let reading: int32? = 7; let missing: int32? = none; let a = reading is int32; // true: present let b = missing is int32; // false: absent let nested: int32?? = .Some(none); let c = nested is int32?; // true: one level is present let maybe: (int32 | bool)? = true; let d = maybe is bool; // true: present, and the payload's member is bool let count: int32 = 7; let e = count is int32; // true, known before the program runs ``` A test inspects one optional level only. On an `int32??`, the payload is an `int32?`, so `nested is int32` is rejected: ```text error: type 'int32' is neither the payload of optional 'int32??' nor a member of it help: 'is' tests one presence level; match '.Some(...)' to inspect deeper levels ``` ## A test that cannot be true is an error A test whose answer is known to be `false` is a mistake, so the compiler rejects it rather than folding it to a constant: ```text error: type 'bool8' is not a member or subset of sum 'Minus | Number | Plus' error: 'is int64' can never be true for a value of type 'int32' help: an 'is' test on a value that is not a sum or an optional compares its exact type ``` On a value that is neither a sum nor an optional, `is` compares types by name and identity, never by layout: a struct with the same fields under another name is a different type. An `is` test on such a value can only be `true`, which makes it useful mostly in generic code. A fallible has no type to test — its question is which channel, which is a match: ```text error: 'is' cannot test the channel of fallible 'int32 ! E' help: match '.Success(...)' or '.Failure(...)' instead ``` ## `is` never narrows A test does not change the static type of its operand. After `token is Number` succeeds, `token` is still the whole sum, and its member's fields are still out of reach: ```rux if token is Number { PrintLine("{}", token.value); // error } ``` ```text error: type 'Minus | Number | Plus' has no field 'value' ``` To use the member, take it out with a [pattern](https://rux-lang.dev/docs/lang/sums/patterns): ```rux match token { n: Number => PrintLine("{}", n.value), else => {} } ``` `is` also never consumes, moves or binds anything. A borrowed operand is tested through the borrow. ## Grouping The right operand is a postfix type, so a sum or fallible type must be grouped: ```text error: a sum type after 'is' must be grouped help: write 'value is (Plus | Minus)' ``` `is` shares its [precedence](https://rux-lang.dev/docs/lang/expressions/overview) with `as`: it binds tighter than every binary operator, so `count is int32 && count > 0` needs no parentheses. Group it when it is the operand of a prefix operator: `!(missing is int32)`. ## Generic code A test whose operand or tested type mentions a type parameter is checked again for every instantiation. `value is T` on a `T | U` stays valid when `T` and `U` are the same type and the sum collapses — it is then a plain type comparison, and `true`: ```rux func IsLeft(value: T | U) -> bool { return value is T; } ``` `IsLeft(true)` is `false`, and `IsLeft(5)` is `true`. ## Interfaces `is` does not test whether a value implements an [interface](https://rux-lang.dev/docs/lang/interfaces/overview). That would be a run-time question about the value's type, and it is reported as unavailable rather than answered wrongly: ```text error: type test 'is Shape' is unavailable: interface checks are not implemented ``` Membership is not interface implementation either: knowing that every member of a sum implements an interface, or that a test succeeded, makes no method available on the sum. ## See also - [Sum patterns](https://rux-lang.dev/docs/lang/sums/patterns) — taking the member out - [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) — presence and absence - [Casts](https://rux-lang.dev/docs/lang/expressions/casts) — `as`, which shares the precedence of `is` - Learn: [Is](https://rux-lang.dev/docs/learn/is) # Ownership Every value in Rux has exactly one **owner**: a binding, a parameter, a field or element of a larger value, or a temporary that nothing has named yet. The owner decides when the value's life ends, and at that point the compiler [destroys](https://rux-lang.dev/docs/lang/ownership/destructors) it — once. A value passes from one owner to another in one of two ways. A **copy**, written `=`, makes a second, independent value and leaves the source as it was. A **move**, written `<-`, hands the value itself over and leaves the source empty. A [reference](https://rux-lang.dev/docs/lang/references/overview) is the third way to reach a value — it borrows without owning, so nothing changes hands. ## Syntax ```text let name = source; // bind a copy let name <- source; // bind by moving target = source; // copy assignment target <- source; // move assignment F(source) // pass a copy F(<-source) // move into the parameter return source; // return a copy return <-source; // move out of the function Type { field: <-source } // move into an aggregate ``` `<-` is the move operator in every position: as an assignment operator between two places, and as a prefix on an argument, a return value, a field or element value, a conditional arm, or a `match` subject. ## Copy or move A named source is **copied** unless it is written with `<-`. Copying never touches the source: both values exist afterwards, and changing one never shows in the other. Moving invalidates the source, suppresses its destruction — the new owner destroys the value instead — and makes every later read of it an error. ```rux struct Token { id: int32; } extend Token { // No copies: a Token can only be moved. func =(self: &var Token, other: &Token); } func Consume(token: Token) { PrintLine("consumed {}", token.id); } func Main() -> int { let first = Token { id: 1 }; let second <- first; // first is now empty Consume(<-second); // second is now empty return 0; } ``` ```mermaid flowchart LR a(["a: a value
with a name"]) a -- "let b = a
copy" --> c["two values
a and b both usable"] a -- "let b <- a
move" --> m["one value, now b's
a is empty"] a -- "let b: &T = a
borrow" --> r["one value, still a's
b refers to it"] ``` | | Copy `=` | Move `<-` | Borrow `&T` / `&var T` | | ------------ | -------------------- | ------------------ | ---------------------- | | Values after | two | one | one | | The source | unchanged and usable | empty; reads fail | still the owner | | Destroyed by | each owner, its own | the new owner only | the original owner | Whether a type can be copied or moved at all, and what a copy does, is up to the type: see [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move). Moving works for every type that does not prohibit it, a copyable one included — `let m <- n;` with an `int` `n` leaves `n` empty too. ## Fresh temporaries A value nobody has named — the result of a call, a literal, a constructor call, or the value a conditional or `match` produces — has no other owner, so it transfers directly. It needs no `<-`, and no copy runs: ```rux func Make(id: int32) -> Token { return Token { id: id }; // a literal: no '<-' } func Main() -> int { let token = Make(7); // a call result: no '<-' Consume(Make(8)); return 0; } ``` The arrow is for values that have a name. It marks, on the line where it happens, which names stop working. ## After a move Reading a moved-from name is a compile-time error. The note points at the move: ```text error: value 'first' is used after it was moved note: 'first' was moved at … help: clone 'first' before moving it if both uses are required ``` A moved-from `var` is empty, not ruined. Assigning it a whole new value makes it usable again: ```rux var token = Token { id: 1 }; let held <- token; token <- Token { id: 2 }; PrintLine("token is {} again", token.id); ``` ## Moves on some paths The compiler tracks moves through branches and loops. A value moved on one path of an `if` is unavailable after the `if`, because the compiler cannot know which path ran: ```text error: value 'token' may have been moved on some control-flow paths note: one unavailable path for 'token' originates at … help: initialize or preserve 'token' on every path before this use ``` A loop body runs again from where its last pass ended, so a value one pass moves out is not there for the next. Moving an outer local inside a loop body is an error unless the same pass gives it a new value before the loop repeats, or leaves the loop with `break`, `return` or `fail` after the move. The loop's own `for` variable and the locals declared inside the body are fresh on every pass. ```rux var slot = Token { id: 3 }; for pass in 0..2 { let taken <- slot; PrintLine("pass {} took {}", pass, taken.id); slot <- Token { id: 10 + pass as int32 }; // refilled before the next pass } ``` At run time a value moved on only some paths is tracked by a drop flag, so it is still destroyed exactly once — see [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors#drop-flags). ## What cannot be moved | Written | Error | | ----------------------- | ---------------------------------------------------------- | | `return <-reference;` | `cannot move a non-owning reference` | | `let a <- value.field;` | `cannot move field 'field' out of droppable value 'value'` | | `value <- value;` | `cannot move 'value' into itself` | | `let b <- pinned;` | `moving type 'Pinned' is prohibited` | A reference owns nothing, so there is nothing to hand over. A single part cannot leave a value on its own; the value is moved whole or [taken apart](https://rux-lang.dev/docs/lang/ownership/copy-and-move#partial-moves). A type can [prohibit moving](https://rux-lang.dev/docs/lang/ownership/copy-and-move#prohibiting-moves) altogether. ## See also - [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move) — how a type copies, and how it forbids copying or moving - [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors) — what happens when an owner lets go - [Defer](https://rux-lang.dev/docs/lang/ownership/defer) — cleanup that belongs to a scope rather than a value - [References](https://rux-lang.dev/docs/lang/references/overview) — reaching a value without owning it - Learn: [Ownership](https://rux-lang.dev/docs/learn/ownership), [Copy](https://rux-lang.dev/docs/learn/copy), [Move](https://rux-lang.dev/docs/learn/move) # Copy and Move Copying and moving are operations a type has or does not have. By default the compiler **generates** both, structurally: a structure is copied or moved field by field, an array element by element, a tuple, variant or optional part by part. A type changes that by declaring the operation in an `extend` block — with a body to supply its own, or without one to prohibit it. ## The two special operations ```text extend T { func =(self: &var T, other: &T) { … } // custom copy func =(self: &var T, other: &T); // copying prohibited func <-(self: &var T, other: T) { … } // custom move func <-(self: &var T, other: T); // moving prohibited } ``` | Declared in `extend T` | A copy of `T` | A move of `T` | | --------------------------------------- | --------------------------- | --------------- | | nothing | copies each part | moves each part | | `func =(self: &var T, other: &T) { … }` | runs the body | unchanged | | `func =(self: &var T, other: &T);` | an error — `T` is move-only | unchanged | | `func <-(self: &var T, other: T) { … }` | unchanged | runs the body | | `func <-(self: &var T, other: T);` | unchanged | an error | A generated operation exists only when every part supports it. A structure with a move-only field is move-only itself, because copying it would copy the field: ```text error: move-only value 'booking' requires an explicit '<-' in initialization note: plain by-value use copies its source, but 'Booking' prohibits copying help: write 'let destination <- booking' to transfer ownership ``` ### Signatures The signatures are fixed, and each is checked where it is declared: | Mistake | Error | | ------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | `func =(self: &var Sheet, other: Sheet)` | `copy special operation for type 'Sheet' must have signature 'func =(self: &var Sheet, other: &Source)'` | | `func <-(self: &var Sheet, other: &Sheet);` | `move special operation for type 'Sheet' must have signature 'func <-(self: &var Sheet, other: Sheet)'` | | `func =(…);` outside `extend` | `special operation '=' may only be declared in an extend block` | The parameters must be named `self` and `other`, and neither operation takes type parameters, default values or a result. A bodyless declaration in an `extend` block is a prohibition; a bodyless function in an [interface](https://rux-lang.dev/docs/lang/interfaces/overview) is an ordinary requirement. ## Custom copy A copy with a body runs for every copy of the type. `self` is the new value, in storage the compiler provides; `other` is the source, borrowed read-only, so the copy can neither change nor consume it. Once the body exists, nothing is copied field by field any more: every field the copy should have, the body sets. ```rux struct Sheet { text: char8[..]; generation: int32; } extend Sheet { func =(self: &var Sheet, other: &Sheet) { self.text = other.text; self.generation = other.generation + 1; } } func Main() -> int { let original = Sheet { text: "Minutes", generation: 1 }; let copy = original; // runs '=': generation 2 var board = Sheet { text: "Agenda", generation: 1 }; board = copy; // runs '=': generation 3 PrintLine("{} {} {}", original.generation, copy.generation, board.generation); return 0; } ``` A copy runs only when the source **keeps** its value afterwards — a named place, borrowed storage, or a reference. A fresh temporary and a source handed over with `<-` are not copied: they transfer exactly as they would initialize a binding, and the custom `=` is not called. | Written | Runs `=`? | | ----------------------- | ---------------------- | | `let copy = original;` | yes | | `Show(copy)` (by value) | yes | | `board = copy;` | yes | | `board = Sheet { … };` | no — a fresh temporary | | `board = Make();` | no — a fresh temporary | | `Show(<-copy)` | no — a move | Assigning over a live value is a replacement. The new value is produced first — by the custom `=` when it copies — then the old value is [destroyed](https://rux-lang.dev/docs/lang/ownership/destructors), and then the new one is installed. A structure whose field has a custom `=` copies that field with it, as part of its generated copy. ### Copying from another type A copy with a body may take a different source type: `func =(self: &var Celsius, other: &int32) { … }`. It is used by an assignment to an existing `Celsius`, `reading = degrees;`. It is not a conversion: `let reading: Celsius = degrees;` is still `cannot assign 'int32' to 'Celsius'`. Only the exact `T`-from-`T` form has a bodyless, prohibiting meaning. ## Prohibiting copies A bodyless `=` makes a type **move-only**. That is the right choice for any type that owns a resource — memory, a file, a handle — and does not implement a real independent copy, since two copies would each release the same resource. ```rux struct RoomKey { room: int32; } extend RoomKey { func =(self: &var RoomKey, other: &RoomKey); } ``` Every by-value use of a named move-only value then needs `<-`, and the error names the position and the spelling to use: | Where | Refused | Written with a move | Error ends with | | ------------- | ---------------------- | ------------------------ | ------------------- | | A binding | `let spare = key;` | `let spare <- key;` | `in initialization` | | An argument | `CheckOut(key)` | `CheckOut(<-key)` | `in argument` | | A return | `return key;` | `return <-key;` | `in return` | | A field value | `Booking { key: key }` | `Booking { key: <-key }` | `in aggregate` | ```text error: move-only value 'key' requires an explicit '<-' in argument note: plain by-value use copies its source, but 'RoomKey' prohibits copying help: prefix the argument with '<-', as in 'Take(<-key)' ``` Assignment to a move-only `var` always uses `<-`, even from a fresh temporary — `key = other;` and `key = RoomKey { room: 4 };` both fail with `copying type 'RoomKey' is prohibited`. Write `key <- other;` or `key <- RoomKey { room: 4 };`. Initialization from a temporary needs no arrow: `let key = RoomKey { room: 4 };` is fine. ## Prohibiting moves A bodyless `<-` prohibits moving. A value of such a type stays in the storage it was created in. Every `<-` on it is an error: ```text error: moving type 'Stay' is prohibited note: the type declares its canonical move operation without a body help: borrow the value or construct a distinct replacement instead ``` A type that keeps its copy can still be copied with `=`. A type that prohibits **both** cannot receive a value from any expression, a literal included — `let pinned = Pinned { id: 1 };` is the same error — so it is built where it lives, one part at a time: ```rux struct Pinned { id: int32; } extend Pinned { func =(self: &var Pinned, other: &Pinned); func <-(self: &var Pinned, other: Pinned); } func Peek(pinned: &Pinned) -> int32 { return pinned.id; } func Main() -> int { var pinned: Pinned; pinned.id = 4; PrintLine("{}", Peek(pinned)); return 0; } ``` ## Custom move A move with a body runs for every move of the type. `other` is the source, taken **by value**: the operation owns it, and like any by-value parameter it is destroyed when the body ends. The body therefore writes the new state into `self` from `other`, and whatever `other` still holds is released with it. ```rux struct Cell { id: int32; } extend Cell { func <-(self: &var Cell, other: Cell) { self.id = other.id; PrintLine("moved {}", other.id); } } ``` ## Partial moves A field, a tuple element or an array element cannot be moved out of a value on its own. A value that is partly there would leave nothing able to say what to destroy when its owner's scope ends, so a value is moved whole or not at all: ```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 ``` The same error names a tuple element (`field '0'`) and an array element (`indexed element [0]`). What works instead is taking the **whole** value apart with a moving [destructuring pattern](https://rux-lang.dev/docs/lang/bindings/destructuring), so that every part gets exactly one new owner: ```rux struct Item { name: char8[..]; } extend Item { func =(self: &var Item, other: &Item); func ~Item(self: &var Item) { PrintLine("{} destroyed", self.name); } } struct Parcel { wrapping: Item; gift: Item; } func Unwrap(parcel: Parcel) -> Item { let Parcel { wrapping: _, gift: gift } <- parcel; return <-gift; } ``` - A part bound to a name belongs to that binding and is destroyed with it. - A part bound to `_`, or a structure field the pattern leaves out, has no owner left, so it is destroyed on the spot — at the `let`, or at the top of a `match` arm — in the order the parts appear in the pattern. A destructuring `let` owns what it takes apart, so a move-only value is handed to it with `<-`; `= parcel` fails with `move-only value 'parcel' requires an explicit '<-' in initialization`. Tuples may always be split this way. A structure may be split only when it declares no destructor of its own, because a destructor runs on the whole value: ```text error: cannot split 'Sealed' with a moving pattern, because it declares destructor '~Sealed' note: '~Sealed' 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 part, each to one owner | `let Parcel { wrapping: _, gift: gift } <- parcel;` | the structure has no destructor | | to read a field | `parcel.gift.name` | always — reading moves nothing | ## Matching and ownership A `match` that binds parts of its subject takes them, the same way a destructuring `let` does. `match <-value { … }` hands the subject over; a temporary subject is taken directly. A match that binds from a named copyable value without `<-` matches a **copy** of it, which its arms own; the original stays with its owner. A match that binds nothing, or matches through a reference, takes nothing. A move-only named subject has no copy to match, so an arm that binds from it needs the subject handed over; `match box { .Full(item) => … }` fails with `move-only value 'box' requires an explicit '<-' in match subject`, and the help says `transfer the subject with 'match <-box'`. ## See also - [Ownership](https://rux-lang.dev/docs/lang/ownership/overview) — `=`, `<-` and fresh temporaries - [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors) — what replacement and splitting destroy - [Destructuring](https://rux-lang.dev/docs/lang/bindings/destructuring) — the patterns a `let` takes apart - [Constructors](https://rux-lang.dev/docs/lang/structs/constructors) — making a value, as opposed to copying one - Learn: [Non-copyable types](https://rux-lang.dev/docs/learn/no-copy), [Custom copy](https://rux-lang.dev/docs/learn/custom-copy), [Partial move](https://rux-lang.dev/docs/learn/partial-move) # Destructors A **destructor** is code the compiler runs when a value's life ends. It is declared in an `extend` block, named after its type with a leading `~`, and it borrows the dying value mutably. Nothing calls it by name: the compiler invokes it exactly once for each initialized value that still owns its state, and never for a value that was moved away or never initialized. ## Syntax ```text extend T { func ~T(self: &var T) { … } } ``` ```rux struct Guest { name: char8[..]; } extend Guest { func ~Guest(self: &var Guest) { PrintLine("{} leaves", self.name); } } ``` The shape is fixed: one receiver, `self: &var T`, no other parameters, no result, no type parameters, and a body. For a generic type the name carries no type arguments: `func ~Store(self: &var Store)` inside `extend Store`. | Mistake | Error | | -------------------------------- | --------------------------------------------------------------------------------- | | `func ~Guest(self: &Guest)` | `destructor for type 'Guest' must have signature 'func ~Guest(self: &var Guest)'` | | `func ~Visitor(self: &var Host)` | `destructor '~Visitor' must be named '~Host' for type 'Host'` | | `func ~Ghost(self: &var Ghost);` | `destructor '~Ghost' must have a body` | | a destructor outside `extend` | `destructor '~Item' may only be declared in an extend block` | | `seat.~Guest();` | `expected a field name or tuple index after '.' before '~'` | A destructor cannot be called. To end a value's life early, replace it or move it into a function that lets it go. ## When destruction runs | Event | What is destroyed | | --------------------------------------------------------------------- | ------------------------------------------------- | | The owning scope ends — a block, a function, each pass of a loop body | every value it still owns | | `return`, `break`, `continue`, `fail`, `?` propagation | the values of every scope being left | | `=` or `<-` into a live place | the old value, after the new one is produced | | A destructuring `let` or `match` arm | each part bound to `_` or left out of the pattern | | A by-value parameter | the parameter, when the function returns | | Not destroyed | Why | | --------------------------------------------------------- | ------------------------------------------------------- | | A value moved away with `<-` | its new owner destroys it | | A value returned with `return <-value;` | the caller owns it now | | Storage declared without a value and never assigned | it holds nothing | | The old contents of storage written through a raw pointer | a pointer cannot tell whether the storage holds a value | | Anything, when the program panics or exits | panics do not unwind | A write through a raw pointer — `*p <- value`, `p[i] = value`, or a field reached through `*var T` — initializes the storage it addresses and destroys nothing. Code that replaces a value through a pointer destroys or moves out the old one first. ## Order Values that end together are destroyed in **reverse order of creation**, newest first: a value made later may depend on one made earlier, so it has to go while the earlier one is still there. A value's own destructor runs first, while its fields are still intact. Then the compiler destroys its contents — fields, tuple and array elements, and the payload of a variant case or optional — in reverse construction order: the last field first, the last element first. ```rux struct Part { name: char8[..]; } extend Part { func ~Part(self: &var Part) { PrintLine("~Part {}", self.name); } } struct Machine { first: Part; second: Part; } extend Machine { func ~Machine(self: &var Machine) { PrintLine("~Machine"); } } func Build() { let machine = Machine { first: Part { name: "first" }, second: Part { name: "second" } }; let a = Part { name: "a" }; let b = Part { name: "b" }; } ``` `Build` prints `~Part b`, `~Part a`, `~Machine`, `~Part second`, `~Part first`. A type with no destructor of its own still has its droppable parts destroyed: the compiler generates that cleanup for every structure, tuple, array, variant and optional that contains something to destroy. ## Replacement Assigning to a place that holds a value replaces it in three steps: the new value is produced — a [custom copy](https://rux-lang.dev/docs/lang/ownership/copy-and-move#custom-copy) runs here — then the old value is destroyed, then the new one is installed. A field, a tuple element and an element of an array or slice are places like any other, whether they belong to a local or are reached through a `&var` reference: ```rux var seat = Guest { name: "Bob" }; seat = Guest { name: "Cy" }; // Bob leaves here ``` A part of a local holds a value only while the local holds one. A droppable local declared without a value, or moved from, is assigned **whole** before any part of it is written, because a part written into empty storage would never be destroyed: ```text error: cannot write field 'guest' of 'table', which holds no value note: 'table' was declared without a value at … note: 'Table' needs destruction, and a value written into a part of storage that holds none would never be destroyed help: initialize 'table' whole, as in 'table = Table { ... }' ``` A type with nothing to destroy may still be filled one part at a time; see [Initialization](https://rux-lang.dev/docs/lang/bindings/initialization). ## Drop flags Whether a local still owns its value can depend on the path taken. When a value is moved on only some paths, the compiler keeps a **drop flag** for it, and the flag — not the source text — decides at the end of the scope whether the value is destroyed: ```rux func Visit(away: bool) { let guest = Guest { name: "Dee" }; if away { let taken <- guest; // destroyed here, as 'taken' return; } } // destroyed here only when not moved ``` Either way `Dee leaves` is printed once. A local's flag covers the whole value, which is why a part cannot be [moved out](https://rux-lang.dev/docs/lang/ownership/copy-and-move#partial-moves) on its own. ## Copies and destructors A copy is a second value with its own end, so a copyable type with a destructor runs it once per copy. For a type that releases a resource — frees memory, closes a file — that means releasing it twice. Such a type prohibits copying with a bodyless `func =(self: &var T, other: &T);`, or implements a copy that duplicates the resource; see [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move#prohibiting-copies). ## No unwinding A [panic](https://rux-lang.dev/docs/lang/errors/panics) stops the program where it happens. Nothing is unwound: no destructor runs and no [deferred statement](https://rux-lang.dev/docs/lang/ownership/defer) runs, in the panicking function or in any caller. The same holds for ending the process. Cleanup that must survive a failure belongs on an error path — `fail` and `?` do run destructors. ## See also - [Defer](https://rux-lang.dev/docs/lang/ownership/defer) — cleanup tied to a scope, and how it is ordered against destructors - [Copy and move](https://rux-lang.dev/docs/lang/ownership/copy-and-move) — prohibiting copies of a type that owns a resource - [Extensions](https://rux-lang.dev/docs/lang/structs/extensions) — the `extend` block a destructor lives in - [Panics](https://rux-lang.dev/docs/lang/errors/panics) — failures that end the program without cleanup - Learn: [Destructor](https://rux-lang.dev/docs/learn/destructor) # Defer A `defer` statement registers a statement now and runs it when the enclosing scope ends, by whichever way it ends. Cleanup written once, on the line after the work it undoes, then covers every exit — including ones added later. ## Syntax ```text defer statement ``` The deferred part is exactly one statement: an expression statement such as a call, an assignment, or a statement with its own body such as `if`. A bare block is not a statement, so `defer { … }` does not parse: ```text error: expected an expression before '{' ``` To defer several steps, write several defers — remembering that they run in reverse — put the steps in a function and defer the call, or defer an `if` whose body holds them. ```rux 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(); if litres > 100 { return false; } PrintLine("filling {} litres", litres); return true; } ``` Both exits close both valves, the drain first. ## Semantics **Registration happens at run time.** A `defer` counts only once execution reaches it. A `return` before the `defer` line leaves without running it, and a `defer` inside a branch that is not taken never registers. **It belongs to its scope.** A deferred statement runs when the block it is written in ends: the function body, the body of an `if`, or one pass of a loop body — so a `defer` in a loop runs once per pass, not once after the loop. The scope may end by reaching its closing brace, by `return`, or by `fail` and `?` propagation. **Last registered, first run.** Several defers in one scope run in reverse registration order, and the defers of nested scopes run innermost first. **It is evaluated when it runs.** A deferred statement reads its variables at the end of the scope, not at the `defer` line, so it sees every change made in between. **Each function has its own.** A function's defers run when that function's scopes end; calling a function never runs, or inherits, the caller's defers. Each instantiation of a generic function has its own as well. ## Return and defer `return expr;` evaluates `expr` exactly once and keeps the result — performing the copy or move into the return value — **before** any deferred statement runs. Whatever the defers change afterwards, the caller receives the kept value: ```rux func Countdown() -> int32 { var remaining: int32 = 3; defer PrintLine("deferred code sees {}", remaining); defer remaining = 0; return remaining; } ``` `Countdown()` returns `3`, and the deferred `PrintLine` prints `deferred code sees 0`. The same holds for every part of an aggregate return value. A `return` with no value runs the same defers without capturing anything. That ordering makes "hand out the current value, then advance" a two-line method: ```rux struct Dispenser { next: int32; } extend Dispenser { func Take(self: &var Dispenser) -> int32 { defer self.next += 1; return self.next; } } ``` ## Defers and destructors When a scope ends, its deferred statements run first, newest first, and then its values are [destroyed](https://rux-lang.dev/docs/lang/ownership/destructors), newest first. Registration and declaration order are not interleaved: ```rux func Party() { let ada = Guest { name: "Ada" }; defer PrintLine("lights off"); let bob = Guest { name: "Bob" }; defer PrintLine("music off"); } ``` ```mermaid flowchart LR r["return value
captured"] --> d["defers
newest first"] d --> x["destructors
newest first"] x --> c["control reaches
the caller"] ``` `Party` prints `music off`, `lights off`, `Bob leaves`, `Ada leaves`. On a `return`, the full order is: the return value is captured, the defers run, the locals are destroyed. | | Destructor `~T` | `defer` | | ---------- | -------------------------- | ------------------------------ | | Belongs to | a type — every value of it | one piece of work in one scope | | Written | once, in `extend T` | where 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. ## Panics A [panic](https://rux-lang.dev/docs/lang/errors/panics) does not unwind, so no deferred statement runs — not in the panicking function and not in its callers. ::note **`break` and `continue` skip a loop body's defers.**:br A pass of a loop body that ends with `break` or `continue` should run the defers that pass registered. rux 0.4.0 does not yet run them on those exits — it runs only the destructors — so a deferred statement in a loop body runs only on passes that reach the end of the body, or leave by `return` or `fail`. :: ::note **Do not defer a `return`.**:br A deferred statement runs while the function is already returning, so it cannot return in turn. rux 0.4.0 does not yet reject `defer return …;`, and a program containing it crashes when built. :: ## See also - [Destructors](https://rux-lang.dev/docs/lang/ownership/destructors) — cleanup that belongs to a value - [Return](https://rux-lang.dev/docs/lang/statements/return) — leaving a function with a value - [Propagation](https://rux-lang.dev/docs/lang/errors/propagation) — `?` and `fail`, which run defers on the way out - Learn: [Defer](https://rux-lang.dev/docs/learn/defer), [Defer return](https://rux-lang.dev/docs/learn/defer-return) # Interfaces An **interface** names a set of functions that a type must provide. It has no fields and no code of its own: it lists function headers, and each type that **implements** it supplies the bodies in an `extend` block. Once a type implements an interface, its values can be held, passed and stored as [interface values](https://rux-lang.dev/docs/lang/interfaces/interface-values) with dynamic dispatch, and generic code can require the interface as a [bound](https://rux-lang.dev/docs/lang/generics/bounds). ## Declaring an interface ```text interface Name { func Requirement(parameters) -> Result; func Requirement(self: &Self, parameters) -> Result; func Requirement(self: &var Self, parameters) -> Result; } ``` ```rux interface Shape { func Area() -> float64; func Name() -> char8[..]; } ``` Each **requirement** is a function header ending in `;`. An interface may declare any number of requirements, including none, and is private to its module unless declared `pub interface`. A requirement says how it reaches the implementing value: | Requirement written | The implementation receives | Callable through | | ---------------------------------------- | ----------------------------- | --------------------- | | `func Area() -> float64;` (no receiver) | `self: &T` — it reads | `&I`, `&var I`, `I` | | `func Peek(self: &Self) -> int32;` | `self: &T` — it reads | `&I`, `&var I`, `I` | | `func Turn(self: &var Self, by: int32);` | `self: &var T` — it may write | `&var I`, a `var` `I` | A receiver taken by value is refused, because an interface reaches the implementing value through a reference: ```text error: receiver of requirement 'Consume' in interface 'Sink' must be '&Self' or '&var Self' note: a requirement reaches the implementing value through the data half of an interface view help: write 'self: &var Self' if 'Consume' writes through its receiver, or 'self: &Self' if it only reads it ``` ### Self Inside an interface, `Self` names the type that implements it. It is how an interface says that an operand or a result has the implementer's own type — `Core::Equatable` declares `func Equals(other: &Self) -> bool;`, so a `Fraction` is compared only with another `Fraction`. `Self` exists only inside an interface; anywhere else it is `type 'Self' is not defined in this scope`. ### No type parameters An interface takes no type parameters. `interface Box { … }` does not parse: ```text error: expected '{' to start the interface body before '<' ``` A requirement that would need one is written with `Self`, or the generic part moves to the functions that use the interface, as [bounds](https://rux-lang.dev/docs/lang/generics/bounds). This is also why `Core::Iterator` lists no requirements: an iterator's item type differs per implementation, and an interface has no way to name it. ### No bodies A requirement has no body and an interface supplies no default implementations: every implementing type writes every requirement. ::note **A body on a requirement is ignored.**:br rux 0.4.0 does not yet reject `func Area() -> float64 { return 1.0; }` inside an interface. The body is never used, and an implementation still has to provide `Area`. :: ## Implementing an interface ```text extend Type : Interface { methods } extend Interface for Type { methods } ``` The two spellings are equivalent. Inside the block, every requirement appears as an ordinary [method](https://rux-lang.dev/docs/lang/structs/methods), with the receiver of the concrete type in front of the requirement's own parameters: ```rux struct Circle { radius: float64; } extend Circle : Shape { func Area(self: &Circle) -> float64 { return 3.14 * self.radius * self.radius; } func Name(self: &Circle) -> char8[..] { return "circle"; } } struct Square { side: float64; } extend Shape for Square { func Area(self: &Square) -> float64 { return self.side * self.side; } func Name(self: &Square) -> char8[..] { return "square"; } } ``` The rules: - **Every requirement must be present.** A missing one is reported on the `extend` line: `implementation of interface 'Shape' for type 'Circle' is missing method 'Name'`. - **The parameters and the result must match the requirement exactly.** Only the receiver is added. - **The receiver must not write more than the requirement allows.** An implementation of a reading requirement that takes `self: &var T` fails with `method 'Adjust' of 'Dial' writes through its receiver, but requirement 'Adjust' of interface 'Gauge' only reads it`. The opposite is allowed: an implementation of a `&var Self` requirement may take `self: &T` if it does not need to write. - **The receiver is a reference.** `self: T` by value is refused in a block that implements an interface. - **One interface per block.** `extend Circle : Shape, Named` does not parse; a type that implements several interfaces has one `extend` block for each. - **The block may add methods** that the interface does not require. They belong to the type, not to the interface. Methods that implement an interface are ordinary methods of the type, and are called on it like any other: `wheel.Area()`. ::note **Requirement signatures are not checked yet.**:br rux 0.4.0 compares only the names of an implementation's methods with the requirements. A method with different parameters or a different result type is accepted, and calling it through an interface value reads its result as the wrong type. Copy each header from the interface exactly. :: ### Which types can implement Any type with a declaring package can implement an interface: a structure, an enum, a variant, a primitive such as `int32`, a slice type such as `char8[..]`, and a generic type through `extend Box : Shape` (see [Generic types](https://rux-lang.dev/docs/lang/generics/types#implementing-interfaces)). A native form — an optional, a sum, a fallible or the unit type — has no declaring package to own the implementation: ```text error: cannot extend native type 'int32?' note: a sum, optional, fallible, or unit type has no declaring package to own methods or interface implementations help: write a generic function that takes the native type as a parameter ``` An interface from another package is imported before it is implemented: `import Core::Equatable;`, then `extend Fraction : Equatable { … }`. ### Declared, not inferred Implementing an interface is a declaration. A plain `extend Circle { … }` that happens to contain the right methods does not make `Circle` a `Shape` value: `let shape: Shape = wheel;` then fails with `cannot assign 'Circle' to 'Shape'`. Generic [bounds](https://rux-lang.dev/docs/lang/generics/bounds) are the exception — they are satisfied by any type that has the required methods. ## Using an interface | Written | What it is | Page | | --------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------ | | `let shape: Shape = wheel;` | an interface value holding a copy | [Interface values](https://rux-lang.dev/docs/lang/interfaces/interface-values) | | `func Show(shape: &Shape)` | a borrow of any implementing value | [Interface values](https://rux-lang.dev/docs/lang/interfaces/interface-values) | | `func Best(a: T, b: T)` | a generic accepting implementing types | [Bounds](https://rux-lang.dev/docs/lang/generics/bounds) | The standard packages define a handful of interfaces the language and libraries build on — equality, ordering, hashing, text output and iteration; see [Core interfaces](https://rux-lang.dev/docs/lang/interfaces/core-interfaces). Operators, indexing and `for` loops are not interfaces: a type provides them by declaring [operator functions](https://rux-lang.dev/docs/lang/interfaces/operators), [indexers](https://rux-lang.dev/docs/lang/interfaces/indexers) and the [iteration methods](https://rux-lang.dev/docs/lang/interfaces/iteration). ## See also - [Interface values](https://rux-lang.dev/docs/lang/interfaces/interface-values) — holding and borrowing values through an interface - [Core interfaces](https://rux-lang.dev/docs/lang/interfaces/core-interfaces) — `Equatable`, `Comparable`, `Hashable`, `Display` and the rest - [Bounds](https://rux-lang.dev/docs/lang/generics/bounds) — interfaces as requirements on type parameters - [Extensions](https://rux-lang.dev/docs/lang/structs/extensions) — the `extend` block - Learn: [Interface](https://rux-lang.dev/docs/learn/interface), [Interface parameter](https://rux-lang.dev/docs/learn/interface-parameter) # Interface Values An interface is a type in its own right. A value of an interface type holds a value of **any** type that implements the interface, together with a note of which type that is, and a call through it runs that type's method. Choosing the method while the program runs, by the value inside, is **dynamic dispatch**. ## Forms | Written | What it is | Changes reach the original? | | --------------------------- | -------------------------------------------- | --------------------------- | | `let shape: Shape = wheel;` | an interface value: a copy of `wheel`, owned | no | | `let shape: Shape <- key;` | an interface value: `key` moved in | — `key` is gone | | `shape: &Shape` | a read-only borrow of an implementing value | it cannot change anything | | `shape: &var Shape` | a writable borrow of an implementing value | yes | | `shapes: Shape[2]` | an array of interface values | no | | `args: Shape...` | a variadic parameter of interface values | no | In each case the conversion from the concrete type happens where the value is bound, assigned or passed. No `&`, cast or call is written. ## Holding a value The annotation is what turns a concrete value into an interface value. Without it, `var shape = wheel;` is simply another `Circle`. ```rux var shape: Shape = wheel; PrintLine("{} {}", shape.Name(), shape.Area()); shape = block; // a Square now: the same calls run Square's code PrintLine("{} {}", shape.Name(), shape.Area()); ``` ```mermaid flowchart LR call["shape.Area()"] --> note{"Which type is
inside shape now?"} note -- "a Circle" --> c["Circle's Area"] note -- "a Square" --> s["Square's Area"] ``` An interface value **holds a copy**, made by the [copy rules](https://rux-lang.dev/docs/lang/ownership/copy-and-move) of the concrete type, or the value itself when it is moved in with `<-`. Changing the original afterwards leaves the interface value alone, and a writing requirement called on a `var` interface value changes the copy inside, not the original. ::note **The held value is not destroyed yet.**:br An interface value owns what it holds, and should destroy it when its own life ends. rux 0.4.0 does not yet do so: the destructor of a value stored in an interface value never runs. :: ## Many types in one array An array has one element type, so values of different types share one only as interface values. The annotation gives every element the interface type: ```rux let shapes: Shape[2] = [wheel, block]; var total = 0.0; for each in shapes { total += each.Area(); } ``` Without it, the first element decides the element type, and the second is refused: `array element 2 has type 'Square', but element 1 established element type 'Circle'`. ## Borrowing through an interface A parameter of type `&I` or `&var I` borrows the caller's own value, whatever its type, as long as it implements `I`. Nothing is copied, and the borrow follows the usual [reference rules](https://rux-lang.dev/docs/lang/references/overview): ```rux interface Gauge { func Read() -> int32; func Adjust(self: &var Self, amount: int32); } struct Dial { level: int32; } extend Dial : Gauge { func Read(self: &Dial) -> int32 { return self.level; } func Adjust(self: &var Dial, amount: int32) { self.level += amount; } } func Show(gauge: &Gauge) { PrintLine("reads {}", gauge.Read()); } func TurnUp(gauge: &var Gauge, amount: int32) { gauge.Adjust(amount); } func Main() -> int { var dial = Dial { level: 4 }; Show(dial); TurnUp(dial, 5); PrintLine("dial.level is {}", dial.level); // 9 return 0; } ``` A requirement declared with `self: &var Self` writes, so it is callable only through a writable view: ```text error: cannot call 'Adjust' through immutable reference '&Gauge' note: 'Adjust' declares a writable receiver '&var Self' help: declare the reference as '&var Gauge' ``` A writable borrow needs writable storage: passing a `let` binding to a `&var Gauge` parameter fails with `argument 1 to 'TurnUp' cannot borrow immutable 'fixed' as '&var Gauge'`. A writable concrete borrow, `&var Dial`, converts to a read-only `&Gauge` as well. ## Only the interface is visible Through an interface value or borrow, only the interface's requirements can be reached. The value inside might be of any implementing type the next time the line runs, so fields and the type's other methods are out of reach: ```text error: interface type 'Gauge' has no member 'level' ``` ## Interface values and bounds An interface value and a [bound](https://rux-lang.dev/docs/lang/generics/bounds) both use an interface, in different ways: | | Interface value: `shape: Shape` | Bound: `` | | ------------------- | -------------------------------------------- | ----------------------------------- | | Which type | any implementing type, decided while running | one concrete type per instantiation | | A method call | looked up through the value at run time | direct, as if written for that type | | What comes back | the interface; the concrete type is hidden | the same `T` that went in | | Mixed types at once | yes — one array, one parameter list | no — every `T` is the same type | | Conformance | declared with `extend T : Shape` | any type with the required methods | ## See also - [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) — declaring and implementing an interface - [References](https://rux-lang.dev/docs/lang/references/overview) — the borrow rules `&I` and `&var I` follow - [Bounds](https://rux-lang.dev/docs/lang/generics/bounds) — the statically dispatched alternative - Learn: [Interface value](https://rux-lang.dev/docs/learn/interface-value), [Interface parameter](https://rux-lang.dev/docs/learn/interface-parameter) # Core Interfaces A few interfaces are shared by the whole standard library. They are ordinary interfaces declared in packages — the compiler gives none of them special treatment — and a type opts into each one with an `extend` block like any other. | Interface | Package | Requirement | Used by | | ------------ | ------- | -------------------------------------------------------------------------------- | ----------------------------------- | | `Equatable` | Core | `func Equals(other: &Self) -> bool;` | searching, deduplication, hash keys | | `Comparable` | Core | `func Compare(other: &Self) -> Ordering;` | sorting, binary search, extremes | | `Hashable` | Core | `func Hash() -> uint64;` | hash tables | | `Iterator` | Core | none | names the iterator role | | `Iterable` | Core | none | names the container role | | `Display` | Text | `func WriteDisplay(writer: &var TextWriter, spec: FormatSpec) -> ! FormatError;` | `{}` placeholders, `PrintLine` | | `Debug` | Text | `func WriteDebug(writer: &var TextWriter, spec: FormatSpec) -> ! FormatError;` | inspection output | Each is imported by name — `import Core::{ Comparable, Equatable, Ordering };`, `import Text::{ Display, FormatError, FormatSpec, TextWriter };` — and the package is listed under `[Dependencies]` in `Rux.toml`. ## Equatable ```rux pub interface Equatable { func Equals(other: &Self) -> bool; } ``` `Equals` says whether two values of one type stand for the same thing. Because the operand is `&Self`, a value is only ever compared with another of its own type. An implementation must be an **equivalence**: | Law | Meaning | | ---------- | --------------------------------------------------- | | Reflexive | `a.Equals(a)` is `true` | | Symmetric | `a.Equals(b)` is `b.Equals(a)` | | Transitive | `a.Equals(b)` and `b.Equals(c)` imply `a.Equals(c)` | A type that cannot promise all three — one with a value that does not equal itself, as a floating-point NaN does not — should not implement `Equatable`. Generic code that relies on it is entitled to assume the laws hold. `Equals` is a method, not an operator: implementing `Equatable` leaves `==` as it was, which for a structure is [structural equality](https://rux-lang.dev/docs/lang/interfaces/operators#structural-equality). To give `==` the same meaning, declare `==` as well. ## Comparable and Ordering ```rux pub interface Comparable { func Compare(other: &Self) -> Ordering; } ``` `Compare` answers which of two values comes first with a three-way `Ordering`, so one call settles "before, same or after". An implementation must be a **total order**: for any pair exactly one of `Less`, `Equal` and `Greater` holds; swapping the operands swaps `Less` and `Greater`; and the relation is transitive. A type implementing both `Comparable` and `Equatable` keeps them in step: `Compare` reports `Equal` exactly when `Equals` reports `true`. Implementing `Comparable` adds no `<`; declare the [operators](https://rux-lang.dev/docs/lang/interfaces/operators) for that. `Ordering` is an enum stored in an `int8`, so its discriminant is the sign of the comparison: ```rux pub enum Ordering: int8 { Less = -1, Equal = 0, Greater = 1 } ``` | Member | Result | | ------------------------------------------- | ------------------------------------------ | | `IsLess(self: &Ordering) -> bool` | `true` for `Less` | | `IsEqual(self: &Ordering) -> bool` | `true` for `Equal` | | `IsGreater(self: &Ordering) -> bool` | `true` for `Greater` | | `IsLessOrEqual(self: &Ordering) -> bool` | `true` for `Less` or `Equal` | | `IsGreaterOrEqual(self: &Ordering) -> bool` | `true` for `Greater` or `Equal` | | `Reverse(self: Ordering) -> Ordering` | `Less` and `Greater` swapped, `Equal` kept | ## Hashable ```rux pub interface Hashable { func Hash() -> uint64; } ``` `Hash` returns a 64-bit summary of the value. Equal values must hash equally — a table is wrong otherwise — while unequal values may collide. A type that implements `Hashable` implements `Equatable` consistently with it, and the same value always gives the same hash within a run. ## Iterator and Iterable ```rux pub interface Iterator {} pub interface Iterable {} ``` Both are empty. A `for` loop is driven by **shape** — a `Next` method on an iterator, an `Iterate` method on a container — and the item type differs per iterator, which an interface cannot name. What these interfaces add is the name: `extend Countdown : Iterator` records that a type means to be an iterator, and a bound written `` documents what a generic expects. Neither is required to iterate, and a bound on either grants no operations. See [Iteration](https://rux-lang.dev/docs/lang/interfaces/iteration) for the protocol itself. ## Display and Debug ```rux pub interface Display { func WriteDisplay(writer: &var TextWriter, spec: FormatSpec) -> ! FormatError; } pub interface Debug { func WriteDebug(writer: &var TextWriter, spec: FormatSpec) -> ! FormatError; } ``` `Display` is the text a value has for a reader: a number is its digits, a string its characters, nothing quoted. `Debug` is the text a value has for whoever is inspecting a program: it may quote, escape and name parts. Both write into a destination rather than returning a string, so rendering allocates nothing. | Parameter | Meaning | | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `writer: &var TextWriter` | the destination — the console, a string builder, a buffer; `TextWriter` is itself an interface with one requirement, `func Write(self: &var Self, bytes: char8[..]) -> ! FormatError;` | | `spec: FormatSpec` | what the placeholder asked for — width, alignment, precision; `{}` passes `FormatSpec::Plain()` | | `-> ! FormatError` | a write can fail; an implementation stops at the first failed write and reports it | ### How PrintLine uses Display `PrintLine` and `Print` from the Io package take their arguments as a [variadic parameter](https://rux-lang.dev/docs/lang/functions/parameters) of `Display` interface values: ```text pub func PrintLine(#Format() format: char8[..], args: Display...) -> IoError? ``` Each argument is converted to a `Display` value at the call, and each `{}` placeholder calls its `WriteDisplay`. The primitive numbers, characters and booleans and the string types implement `Display` in the standard packages, which is why they print; a type of your own prints once it implements `Display` too. An argument that does not is refused: ```text error: argument 2 to 'PrintLine' has type 'Money', but variadic parameter 'args' requires 'Display' ``` No placeholder selects `Debug`. Debug text is written explicitly, with `Text::WriteDebugValue(writer, value, spec)` or through a bound such as ``; `Text::WriteValue` is the matching entry point for `Display`. ## Example ```rux import Core::{ Comparable, Equatable, Hashable, Ordering }; import Io::PrintLine; import Text::{ Display, FormatError, FormatSpec, TextWriter, WriteBytes, WriteValue }; struct Version { major: int32; minor: int32; } extend Version : Equatable { func Equals(self: &Version, other: &Version) -> bool { return self.major == other.major && self.minor == other.minor; } } extend Version : Comparable { func Compare(self: &Version, other: &Version) -> Ordering { if self.major != other.major { return self.major < other.major ? Ordering::Less : Ordering::Greater; } if self.minor != other.minor { return self.minor < other.minor ? Ordering::Less : Ordering::Greater; } return Ordering::Equal; } } extend Version : Hashable { func Hash(self: &Version) -> uint64 { return (self.major as uint64) << 32 | (self.minor as uint64); } } extend Version : Display { func WriteDisplay(self: &Version, writer: &var TextWriter, spec: FormatSpec) -> ! FormatError { WriteBytes(writer, "v")?; WriteValue(writer, self.major, spec)?; WriteBytes(writer, ".")?; return WriteValue(writer, self.minor, spec); } } func Main() -> int { let old = Version { major: 1, minor: 9 }; let fresh = Version { major: 1, minor: 10 }; PrintLine("{} equals {}: {}", old, fresh, old.Equals(fresh)); PrintLine("older: {}", old.Compare(fresh).IsLess()); PrintLine("newer: {}", old.Compare(fresh).Reverse().IsLess()); return 0; } ``` ## See also - [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) — implementing an interface - [Operators](https://rux-lang.dev/docs/lang/interfaces/operators) — `==` and `<`, which these interfaces do not provide - [Iteration](https://rux-lang.dev/docs/lang/interfaces/iteration) — the protocol `Iterator` and `Iterable` name - Learn: [Equatable](https://rux-lang.dev/docs/learn/equatable), [Comparable](https://rux-lang.dev/docs/learn/comparable), [Display](https://rux-lang.dev/docs/learn/display) # Operators A type gives a binary operator a meaning by declaring a function whose name **is** the operator, in an `extend` block. `a + b` with an `a` of that type is then a call to that function. Operators are not interfaces: there is no `Addable` to implement, only the operator function itself. ## Declaring an operator ```text extend T { func op(self: &T, right: R) -> Result { … } } ``` ```rux struct Money { cents: int64; } extend Money { func +(self: &Money, other: Money) -> Money { return Money { cents: self.cents + other.cents }; } func *(self: &Money, count: int64) -> Money { return Money { cents: self.cents * count }; } func ==(self: &Money, other: Money) -> bool { return self.cents == other.cents; } func <(self: &Money, other: Money) -> bool { return self.cents < other.cents; } } ``` - `self` is the **left** operand, borrowed. The one other parameter is the right operand, of any type: `Money * int64` above, with no `Money * Money` and no `int64 * Money`. - The result is any type. Comparisons conventionally return `bool`. - Overloads of one operator are separated by the right operand's type, like any [overload](https://rux-lang.dev/docs/lang/functions/overloading). - A right operand taken **by value** accepts anything, including a value built on the spot. One taken by reference, `other: &Money`, needs a named value: `rent == Money { cents: 5 }` then fails with `cannot pass 'Money' to parameter of type '&Money'`. | You write | The compiler calls | `self` | Right operand | | -------------- | ------------------ | ------ | ------------- | | `rent + food` | `+` | `rent` | `food` | | `rent * 3` | `*` | `rent` | `3` | | `rent == food` | `==` | `rent` | `food` | | `food < rent` | `<` | `food` | `rent` | | `sum += food` | `+`, then assigns | `sum` | `food` | A compound assignment `a op= b` uses the declared `op` and assigns its result to `a`, so declaring `+` makes `+=` work as well. An operator that is not declared does not exist for the type, and the error names both operands: ```text error: operator '/' cannot combine left operand 'Money' with right operand 'int' ``` ## Which operators | Overloadable | Operators | | ------------------- | ----------------------------------------------------------------------------------- | | Arithmetic | `+` `-` `*` `/` `%` | | Bitwise | `&` `|` `^` | | Shift | `<<` `>>` `>>>` | | Comparison | `==` `!=` `<` `<=` `>` `>=` | | Logical | `&&` `||` | | Compound assignment | `+=` `-=` … — through the binary operator | | Indexing | `[]` and `[]=` — see [Indexers](https://rux-lang.dev/docs/lang/interfaces/indexers) | An overloaded `&&` or `||` is an ordinary call: both operands are evaluated, with no short-circuit. | Not overloadable | Why | | ----------------------------------------- | ---------------------------------------------------------------------------------------------- | | unary `-`, `!`, `~`, `*`, `@`, `++`, `--` | built in for primitives and pointers only | | `=` and `<-` | copy and move are [special operations](https://rux-lang.dev/docs/lang/ownership/copy-and-move) | | `??` | `operator '??' is built in and cannot be declared or overloaded` | | `as`, `is`, `..`, `?`, `.` | part of the language, not of any type | A unary operator on a structure is refused even if a function named after it is declared: `-money` fails with `operator '-' requires a numeric operand, but found 'Money'`. ## Derived comparisons A structure declares at most two comparison operators. From `==` the compiler derives `!=`, and from `<` together with `==` it derives the other three: | You write | The compiler uses | | --------- | ----------------- | | `a != b` | `!(a == b)` | | `a > b` | `b < a` | | `a <= b` | `a < b || a == b` | | `a >= b` | `b < a || a == b` | ```mermaid flowchart LR eq["== (declared)"] --> ne["!="] lt["< (declared)"] --> gt[">"] lt --> le["<="] eq --> le lt --> ge[">="] eq --> ge ``` - Each operand is evaluated **once**, even though the derivation names it twice. - `<=` is `a < b || a == b`, not `!(b < a)`. For a pair that is neither ordered nor equal, only the first is right, so a partial order stays correct. - A declared operator wins over its derivation. Declaring `!=`, `>`, `<=` or `>=` by hand is allowed, and is the one way the six can come to disagree. - Derivation works inside generic code too, after the type parameter is substituted. When neither the operator nor what it derives from is declared, the error says so — here for a `Money` that declares `==` but not `<`: ```text error: operator '>' is not defined for 'Money' note: a struct is compared through the operators it declares, never by its representation help: declare '>' on 'Money', or the '<' it is derived from ``` ## Structural equality Without any declaration, `==` and `!=` already work on structures, tuples, fixed arrays and variants, and on any nesting of them. Two values are equal when they are equal part by part: | Kind | Equal when | | --------- | --------------------------------------------------- | | Structure | every field is equal, compared in declaration order | | Tuple | every element is equal | | Array | every element is equal | | Variant | the case is the same, and so is its payload | Each part is compared with its own `==` — a declared one where its type declares it, structural otherwise — so a structure holding a `Money` uses Money's `==` for that field. Floating-point fields compare by value: a NaN field makes two values unequal, and `0.0` equals `-0.0`. Padding bytes play no part. A declared `==` on a structure replaces its structural equality. Every part must have an `==` of its own; a slice has none, so a structure with a `char8[..]` field has no structural equality: ```text error: structural equality for 'Named' is unavailable because element type 'char8[..]' has no '==' operator help: declare '==' on 'char8[..]' or compare the supported elements explicitly ``` There is no structural **ordering**. `<` and the rest are defined only by declaration: `operator '<' is not defined for 'Point'` for a structure, `operator '<' is not defined for variant 'Shape'` for a variant, and `operator '<' is not defined for tuple '(int, int)'` for a tuple. ## See also - [Comparison](https://rux-lang.dev/docs/lang/expressions/comparison) — the built-in comparison operators - [Indexers](https://rux-lang.dev/docs/lang/interfaces/indexers) — `[]` and `[]=` - [Core interfaces](https://rux-lang.dev/docs/lang/interfaces/core-interfaces) — `Equatable` and `Comparable`, which are methods rather than operators - Learn: [Operator overload](https://rux-lang.dev/docs/learn/operator-overload), [Derived operator](https://rux-lang.dev/docs/learn/derived-operator), [Structural equality](https://rux-lang.dev/docs/learn/structural-equality) # Indexers Arrays, slices and pointers are indexed by the language. Any other type indexes itself by declaring the **indexing operators** in an `extend` block, the same way it declares `==` or `+`: `[]` reads one element and `[]=` writes one. ## Syntax ```text extend T { func [](self: &T, index: I) -> E { … } func []=(self: &var T, index: I, value: E) { … } } ``` `[]` and `[]=` are written with no space inside the brackets and none before the `=`. `v[i]` on such a type is a call to `[]`, and `v[i] = x` is a call to `[]=`. ```rux struct Vect { data: int32[10]; } extend Vect { func [](self: &Vect, index: uint) -> int32 { return self.data[index]; } func []=(self: &var Vect, index: uint, value: int32) { self.data[index] = value; } func [](self: &Vect, span: int..int) -> int32[..] { return self.data[span]; } } func Main() -> int { var v = Vect { data: [0; 10] }; v[2] = 7; v[3] = v[2] + 1; let middle = v[2..5]; PrintLine("{} {} {}", v[2], v[3], middle.length); return 0; } ``` ## Shape Each operator has exactly one shape, checked where it is declared: | Operator | Receiver | Other parameters | Result | | -------- | -------------- | ------------------------- | ----------- | | `[]` | `self: &T` | the index | the element | | `[]=` | `self: &var T` | the index, then the value | none | Neither takes type parameters, a variadic or a default value. Anything else is refused with the expected shape: ```text error: the '[]' operator on 'Bad' must have signature 'func [](self: &Bad, index: I) -> E' note: an index reads one element and evaluates to it help: the index and element types are the author's to choose; overloads are separated by the index type ``` ## Index types Nothing fixes the index type: an integer, a range, an enum, a tuple, a structure. Overloads of either operator are separated by their index type, so one type may answer several index forms — `v[i]` and `v[a..b]` above. An index that no overload accepts is an error: `no '[]' on 'Vect' accepts an index of type 'char8[..]'`. An unsuffixed literal index takes the parameter's type, as in any call. A generic type substitutes its type arguments into the operators, so `func [](self: &Box, index: uint) -> T` returns the element type of each instantiation. ## Reading and writing are separate The two operators are independent. A type may declare only `[]` and be read but not written, only `[]=` and be written but not read, or both: | Declared on `Grid` | `grid[i]` | `grid[i] = x` | | ------------------ | ------------------------------- | --------------------------------------------------- | | `[]` only | reads | `cannot assign through the '[]' operator on 'Grid'` | | `[]=` only | `type 'Grid' cannot be indexed` | writes | | both | reads | writes | Declaring both does not make the pair act as one place. `[]` returns a **value**, not a place — a reference cannot be returned — so: | Rejected, for a `Grid` of `Cell`s | Error | Write instead | | --------------------------------- | --------------------------------------------------------------------------------- | -------------------------- | | `grid[i] += x`, `grid[i]++` | `operator '+=' cannot read and write through the '[]' operator on 'Grid' at once` | `grid[i] = grid[i] + x` | | `grid[i].mark = x` | `cannot assign through the '[]' operator on 'Grid'` | read, change, write back | | `let r: &Cell = grid[i];` | `cannot assign 'Cell' to '&Cell'` | a copy: `let c = grid[i];` | Combining the read and the write would evaluate the receiver and the index twice, so the language leaves the two steps to be written out. ## Borrowing Either call borrows the receiver whole for its duration, exactly as a method call does. `[]=` needs a receiver it may write, so a `let` binding or a `&T` access is refused — `cannot modify immutable variable 'fixed'` — while `[]` works on either. The value written by `v[i] = x` reaches `[]=` as an ordinary by-value argument, so it is copied, or with `v[i] <- x` moved. A read never counts as a partial move: its result is a fresh value. In `v[keys[j]] = x`, only the outer subscript writes; `keys[j]` is a read. ## Built-in indexing stays built in Arrays, slices and pointers keep their own indexing, which no `extend` block can displace. Built-in indexing of an array or slice is bounds-checked on every target and in every build profile; see [Arrays](https://rux-lang.dev/docs/lang/arrays/overview). A declared indexer checks whatever its body checks. ## See also - [Operators](https://rux-lang.dev/docs/lang/interfaces/operators) — the other operators a type can declare - [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) and [Slices](https://rux-lang.dev/docs/lang/slices/overview) — built-in indexing - [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) — range types as index types - Learn: [Indexer](https://rux-lang.dev/docs/learn/indexer) # Iteration A [`for` loop](https://rux-lang.dev/docs/lang/statements/loops) walks an array, a slice or a range directly. Any other subject is driven by a convention of two method shapes: an **iterator** declares `Next`, which hands out one item per call, and a **container** declares `Iterate`, which hands out a fresh iterator. ## The protocol ```text extend It { func Next(self: &var It) -> Item? { … } // an iterator } extend Container { func Iterate(self: &Container) -> It { … } // a container } ``` | Subject of `for item in subject` | What the loop does | | -------------------------------- | --------------------------------------------------------- | | an array, a slice, a range | built in | | a type declaring `Iterate` | calls `Iterate` once, then drives the iterator it returns | | a type declaring `Next` | drives the subject itself | | anything else | `cannot iterate over 'T'` | The loop calls `Next` once per pass. A present result runs the body with its payload bound to the loop variable; `none` ends the loop. ```mermaid flowchart LR start["for item in subject"] --> it["subject.Iterate()
(containers only)"] it --> next["call Next()"] next --> q{"an item,
or none?"} q -- "an item" --> body["run the body
with item bound"] body --> next q -- "none" --> done["leave the loop"] ``` The same walk written by hand is a `loop` with a [coalescing exit](https://rux-lang.dev/docs/lang/optionals/coalescing): ```rux var countdown = Countdown { remaining: 3 }; loop { let second = countdown.Next() ?? break; PrintLine("{}", second); } ``` ## Iterators ```rux struct Countdown { remaining: int32; } extend Countdown : Iterator { 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 second in (Countdown { remaining: 5 }) { Print(" {}", second); } PrintLine(" liftoff"); return 0; } ``` `Next` must take a **writable** receiver, since advancing an iterator changes it, and must return a native optional. The item type is whatever the optional carries: ```text error: iterator method 'Next' on 'A' must take a mutable receiver note: advancing an iterator writes it, so 'Next' cannot borrow its receiver read-only help: write the receiver as 'self: &var A' ``` A `Next` returning something else is not an iterator — `cannot iterate over 'B'`, with the note `type 'B' declares 'Next', but not as 'func Next(self: &var B) -> T?' returning a native optional`. A `Next` returning a variant with `Some` and `None` cases is rejected where it is declared, because a loop ends only at the absence of a native optional: ```text error: iterator method 'Next' on 'It' must return a native optional help: return 'Item?' and report the end with 'none' ``` An iterator is consumed as it is read. Once `Next` has reported `none`, it should keep reporting `none`; nothing promises that an iterator can be restarted. A struct literal written directly after `in` must be parenthesised, as above. Without the parentheses its `{` is taken for the start of the loop body, and the parser stops with `expected ';' after expression, but found ':'`. ::note **A loop over a named iterator advances a copy.**:br In rux 0.4.0, `for second in countdown` walks a copy of `countdown`, so the variable itself is not moved along: after a loop that stops early, `countdown` is where it started. To resume a walk later, call `Next` directly, as in the `loop` above. :: ## Containers A container is not an iterator itself. It hands one out, so two loops over the same container walk independently, and the container is unchanged afterwards: ```rux struct Span { limit: int32; } extend Span : Iterable { func Iterate(self: &Span) -> Countdown { return Countdown { remaining: self.limit }; } } func Main() -> int { let span = Span { limit: 3 }; for n in span { Print(" {}", n); } for n in span { Print(" {}", n); // starts again from 3 } PrintLine(); return 0; } ``` `Iterate` takes no parameters besides the receiver. It borrows the container read-only — handing out an iterator changes nothing — and the iterator it returns usually borrows from the container in turn, so the container must outlive it and must not be changed while it is in use. `Iterator` and `Iterable`, from Core, are empty [marker interfaces](https://rux-lang.dev/docs/lang/interfaces/core-interfaces#iterator-and-iterable). Writing `: Iterator` or `: Iterable` records the role; the loop goes by the method names and shapes alone and works without them. ## Items An item may itself be an optional, a fallible, a sum or the unit type. Only the **outer** absence that `Next` returns ends the loop; an absent or failed item is an ordinary value the body receives: ```rux struct Readings { index: int32; } extend Readings { // Each item is an int32?, so Next returns int32??. func Next(self: &var Readings) -> int32?? { self.index += 1; if self.index > 3 { return none; } if self.index == 2 { let missing: int32? = none; return missing; } let present: int32? = self.index * 10; return present; } } func Main() -> int { for reading in (Readings { index: 0 }) { PrintLine("{}", reading ?? -1); // 10, -1, 30 } return 0; } ``` No error channel is added to iteration: a fallible item is data, and the loop continues past a failed one. ## Moves inside a loop The loop variable and the locals declared in the body are fresh on every pass and may be moved freely. Moving an outer local inside the body is subject to the rule in [Ownership](https://rux-lang.dev/docs/lang/ownership/overview#moves-on-some-paths): the same pass must refill it, or leave the loop, after the move. ## See also - [Loops](https://rux-lang.dev/docs/lang/statements/loops) — `for`, `while` and `loop` - [Optionals](https://rux-lang.dev/docs/lang/optionals/overview) — the `T?` that `Next` returns - [Core interfaces](https://rux-lang.dev/docs/lang/interfaces/core-interfaces) — `Iterator` and `Iterable` - Learn: [Iterator](https://rux-lang.dev/docs/learn/iterator), [Iterable](https://rux-lang.dev/docs/learn/iterable) # Generics A **generic** declaration takes **type parameters**: names in angle brackets that stand for types the user supplies. A generic function is written once and works for every type it is used with; a generic type is a pattern from which `Pair`, `Pair` and so on are stamped out. Functions, methods, structures and variants can be generic. Nothing is decided at run time. The compiler produces a separate **instantiation** for each distinct set of type arguments a program uses, as if each had been written by hand, with its own layout, its own code and its own symbol. ## Generic functions ```text func Name(parameters) -> Result { … } func Name(parameters) -> Result { … } ``` ```rux func Larger(first: T, second: T) -> T { if first > second { return first; } return second; } ``` Each type parameter is in scope in the parameter list, the result type and the body. A parameter may carry [bounds](https://rux-lang.dev/docs/lang/generics/bounds), interfaces its type arguments must implement. ## Type arguments A call supplies the type arguments explicitly, after the name, or leaves the compiler to **infer** them from the arguments: ```rux PrintLine("{}", Larger(3, 9)); // T = int, inferred PrintLine("{}", Larger(2.5, 1.5)); // T = float64 PrintLine("{}", Larger(200, 7) + 100); // T = uint8: prints 44 ``` An explicit type argument also types the arguments: with ``, the literals `200` and `7` become `uint8` values, and so does the result, which is why adding `100` wraps. Without it, unsuffixed literals make `T` an `int`. Inference reads type arguments out of the argument types, wherever the parameter mentions them: | Parameter written | Argument | Infers | | --------------------- | -------------------------------- | ------------------------ | | `value: T` | `7i32` | `T = int32` | | `value: &T`, `&var T` | a local `counter: Counter` | `T = Counter` — borrowed | | `p: *T` | `@counter` | `T = Counter` | | `values: T[..]` | an `int32[3]` array or a slice | `T = int32` | | `pair: Pair` | a `Pair` | `T = int64` | | `f: func(T) -> U` | a function `func(int32) -> bool` | `T = int32`, `U = bool` | | `outcome: T ! E` | an `int32 ! Fault` | `T = int32`, `E = Fault` | | `value: T?` | an `int32?` | `T = int32` | A type parameter that no argument determines must be written: `Size()`. A sum of type parameters is not inferred either, since nothing says which member is `T`: `Side(v)` with `func Side(value: T | U)` is refused, and `Side(v)` is the call. Inference does not look at the result type or the destination, so `let f: int32? = Make();` with `func Make() -> T?` is `function 'Make' requires 1 type argument, but 0 were provided`. Inference never relaxes a parameter's other requirements: a `&var T` parameter still needs a writable argument. Two arguments that disagree about one parameter are refused — `Larger(3, 2.5)`: ```text error: argument 1 to 'Larger' has type 'int', but parameter 'first' requires 'T' ``` ## Checking a generic body A generic body is checked in two stages. **At the declaration**, everything that does not depend on what `T` is: names, statements, and every use of `T` that needs a capability. A method call on a `T`, or passing a `T` where an interface is required, needs a [bound](https://rux-lang.dev/docs/lang/generics/bounds) that provides it: ```text error: argument 2 to 'PrintLine' has type 'T', but variadic parameter 'args' requires 'Display' ``` **At each instantiation**, the operators applied to `T`. `first > second` in `Larger` is checked once `T` is known, so `Larger(3, 9)` and `Larger('a', 'z')` compile and `Larger("abc", "abd")` does not. The error points into the generic body, and a note names the call responsible: ```text error: operator '>' is not defined for slice type 'char8[..]' note: a slice is a view, so comparing the views would compare addresses rather than elements note: in 'Larger' instantiated with T = char8[..] by the call at … ``` Without a bound, a body may store a `T`, pass it on, return it, copy or move it, take its size with `sizeof(T)`, and apply operators that every instantiation will have to support. ## Generic functions are not values A function value has one concrete signature, and a generic function has one per instantiation, so a generic function cannot be stored in a [function-typed](https://rux-lang.dev/docs/lang/functions/function-types) variable or passed as a callback: `let f: func(int32) -> int32 = Identity;` fails with `cannot assign 'func(T) -> T' to 'func(int32) -> int32'`. Wrap the instantiation in an ordinary function instead. A generic function may freely **take** a function value whose type mentions its parameters, as `Apply(value: T, f: func(T) -> T)` does. ## Overloading Generic functions overload like any other, by arity and by parameter types. When a generic and a non-generic overload accept a call equally well, the non-generic one wins: ```rux func Describe(value: T) -> char8[..] { return "generic"; } func Describe(value: int32) -> char8[..] { return "int32"; } ``` `Describe(5i32)` is `int32`; `Describe(true)` is `generic`. See [Overloading](https://rux-lang.dev/docs/lang/functions/overloading) for the full ranking. ## Native forms Optionals, sums and fallibles are built into the language and have no declaring package, so `extend` cannot give them methods. Generic functions whose parameters spell the form out take their place, and work for every payload and error type at once: ```rux func ValueOr(outcome: T ! E, fallback: T) -> T { return outcome catch { else => fallback }; } func Both(first: T?, second: U?) -> (T, U)? { return (first?, second?); } ``` `Core::Succeeded(outcome: T ! E) -> bool` and `Core::Failed` are written this way. A sum of type parameters collapses when its members coincide: `T | U` with `T = U = int32` is plain `int32`. A `match` over a `T | U` therefore ends in `else`, which stays valid for every instantiation, where a second typed arm would be unreachable for the collapsed one. ::note **Typed arms over a generic sum.**:br rux 0.4.0 does not yet lower a typed arm that selects a concrete type, such as `n: int32 =>`, from a sum of type parameters; the build fails with `cannot lower the selection of 'int32' from 'T | U'`. Match on the type parameters themselves, `first: T =>`, with an `else` arm. :: ## Kinds of generic declaration | Declaration | Generic? | Page | | ------------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | function | yes | this page | | structure, variant | yes | [Generic types](https://rux-lang.dev/docs/lang/generics/types) | | method | yes — its own parameters, as well as its type's | [Generic methods](https://rux-lang.dev/docs/lang/generics/methods) | | enum | no — `enum 'Level' cannot declare type parameters` | [Enums](https://rux-lang.dev/docs/lang/enums/overview) | | union, type alias | no | | | interface | no — see [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview#no-type-parameters) | | ## See also - [Generic types](https://rux-lang.dev/docs/lang/generics/types) — structures and variants with type parameters - [Generic methods](https://rux-lang.dev/docs/lang/generics/methods) — methods of generic types, and methods with their own type parameters - [Bounds](https://rux-lang.dev/docs/lang/generics/bounds) — what a type argument must implement - [Function types](https://rux-lang.dev/docs/lang/functions/function-types) — callbacks in generic signatures - Learn: [Generic](https://rux-lang.dev/docs/learn/generic), [Generics](https://rux-lang.dev/docs/learn/generics), [Generic outcome](https://rux-lang.dev/docs/learn/generic-outcome), [Generic sum](https://rux-lang.dev/docs/learn/generic-sum) # Generic Types A structure or a variant may declare type parameters after its name. The declaration is a pattern, not yet a type: `Pair` and `Pair` are two distinct types stamped out of it, each with its own layout, sized from its own type arguments. ## Declaration ```text struct Name { fields } variant Name { cases } struct Name { fields } ``` ```rux struct Pair { first: T; second: T; } struct Entry { key: K; value: V; } variant Reading { Exact(T), Between(T, T), Missing } ``` The parameters are in scope in the fields and case payloads. Enums cannot be generic — `enum 'Level' cannot declare type parameters`, with the help to use a `variant` — and neither can unions, type aliases or interfaces. ## Instantiation A generic type is always named with its type arguments, filled in order: `Pair`, `Entry`, `Reading`. They may themselves be instantiations, and nested argument lists may close on one `>>` or `>>>`: `Pair>`. | Written | Type arguments come from | | ------------------------------------- | -------------------------------------------------------------------------------------------- | | `let p: Pair = …;` | the annotation | | `Pair { first: 3, second: 4 }` | the literal — always written | | `Pair(5)` | the [constructor](https://rux-lang.dev/docs/lang/structs/constructors) call — always written | | `Reading::Exact(7)` | the payload, unless an annotation gives them | | `Reading::Missing` | the destination: an annotation, a parameter or a return type | | `Reading::Missing()` | the case itself | ```rux let point = Pair { first: 3, second: 4 }; let entry = Entry { key: "age", value: 7 }; let temperature = Reading::Between(18.5, 21.0); // Reading let floor: Reading = Reading::Exact(7); // the annotation wins: 7 is an int32 let lost: Reading = Reading::Missing; let other = Reading::Missing(); ``` A structure literal never infers its type arguments from its fields, and a field declared `T` must then have exactly that type: | Written | Error | | ------------------------------------------ | ----------------------------------------------------------------------------------------------------- | | `Pair { first: 3, second: 4 }` | `struct initializer for 'Pair' requires 1 type argument, but 0 were provided` | | `Entry { key: 1, value: 2 }` | `struct initializer for 'Entry' requires 2 type arguments, but 1 was provided` | | `Pair(5)` | `constructor for 'Pair' requires 1 type argument, but 0 were provided` | | `let lost = Reading::Missing;` | `variant case 'Reading::Missing' requires 1 type argument, but 0 were provided` | | `Pair { first: 3, second: "four" }` | `field 'second' in initializer for 'Pair' has type 'char8[..]', but its declaration requires 'int32'` | A [generic function](https://rux-lang.dev/docs/lang/generics/overview) taking a generic type infers its own parameters from the argument, so the caller of `func Swapped(pair: Pair) -> Pair` writes `Swapped(point)`. ## Layout Every instantiation is laid out from its own type arguments, so `Pair` is twice the size of `Pair`, and a `Box` reserves the full width of `Wide` for its field. Two instantiations are unrelated types: a `Pair` is not a `Pair`, and neither converts to the other. ## Methods and extensions Methods, constructors, operators, destructors and the copy and move operations of a generic type live in an `extend` block that names the type with **its own** parameter names: ```rux extend Pair { func Pair(both: T) -> Pair { return Pair { first: both, second: both }; } func Swapped(self: &Pair) -> Pair { return Pair { first: self.second, second: self.first }; } } ``` One block serves every instantiation. The parameter names must match the declaration's: `extend Pair { … }` is `struct type 'Pair' requires 1 type argument, but 0 were provided`, and `extend Pair` for a type declared `Pair` is `type 'U' is not defined in this scope`. See [Generic methods](https://rux-lang.dev/docs/lang/generics/methods) for calling them, and for methods with type parameters of their own. An `extend` block may also name one instantiation, `extend Pair { … }`, to give that instantiation alone extra methods. ::note **Methods of one instantiation are not yet restricted to it.**:br rux 0.4.0 does not yet check the receiver's type arguments: a method declared in `extend Pair` can be called on a `Pair`, and reads it as a `Pair`. Call such a method only on the instantiation it was written for. :: ## Bounds on type parameters A type parameter of a structure may carry [bounds](https://rux-lang.dev/docs/lang/generics/bounds). Every instantiation is checked against them where it is written, and methods of the type may call the bounds' requirements on its fields: ```rux interface Scored { func Score() -> int32; } struct Board { leader: T; } extend Board { func Top(self: &Board) -> int32 { return self.leader.Score(); } } ``` ```text error: type argument 'float64' does not satisfy interface bound 'Scored' on type parameter 'T' note: interface 'Scored' requires method 'Score', which type 'float64' does not implement note: type parameter 'T' of struct 'Board' is bound by 'Scored' help: implement the interface, as in 'extend float64: Scored { ... }' ``` ## Implementing interfaces A generic type implements an interface for every instantiation at once, with `extend Box : Named { … }`. Each instantiation then satisfies a `Named` [bound](https://rux-lang.dev/docs/lang/generics/bounds). ::note **Generic instantiations do not yet convert to interface values.**:br rux 0.4.0 does not yet turn a `Box` into an [interface value](https://rux-lang.dev/docs/lang/interfaces/interface-values) of type `Named`, or into a `&Named` borrow, even when `Box` implements `Named`; the conversion is refused as a type mismatch. Bounds work. :: ::note **Swapped type parameters.**:br A function whose parameter names a generic type with its arguments swapped, such as `func Take(entry: Entry)`, makes rux 0.4.0 crash during `rux check`. Give the function's parameters names that differ from the type's, as in `func Take(entry: Entry)`. :: ## See also - [Generics](https://rux-lang.dev/docs/lang/generics/overview) — generic functions and inference - [Generic methods](https://rux-lang.dev/docs/lang/generics/methods) — methods of generic types - [Structures](https://rux-lang.dev/docs/lang/structs/overview) and [Variants](https://rux-lang.dev/docs/lang/variants/overview) — the non-generic forms - Learn: [Generic type](https://rux-lang.dev/docs/learn/generic-type) # Generic Methods A method can be generic in two ways. A method of a [generic type](https://rux-lang.dev/docs/lang/generics/types) uses the type's parameters, fixed when the value was made. And any method — on a generic type or not, with a receiver or without — may declare **type parameters of its own**, chosen afresh at each call. ## Methods of a generic type Inside `extend Box`, `T` means whatever the receiver's instantiation says. Receivers, parameters and results are written with it: ```rux struct Labeled { label: char8[..]; value: T; } extend Labeled { 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; } } ``` ```rux var count = Labeled("count", 7); count.Set(15); let n = count.Get(); // an int32 ``` The type arguments are written on the type name for a constructor or an associated call, `Labeled("count", 7)`, and come from the receiver for an instance call. Each method is compiled for each instantiation that calls it. ## Methods with their own type parameters A method declares its own type parameters after its name, exactly as a generic function does. They are independent of the type's: ```rux extend Labeled { func With(self: &Labeled, other: U) -> (T, U) { return (self.value, other); } func Map(self: &Labeled, change: func(T) -> U) -> Labeled { return Labeled { label: self.label, value: change(self.value) }; } } ``` | Parameter | Declared on | Fixed when | In `count.With("apples")` | | --------- | ---------------------- | ----------------- | ------------------------- | | `T` | the type, `Labeled` | the value is made | `int32` | | `U` | the method, `With` | each call | `char8[..]` | A method's own parameters are inferred from its arguments, or written after the method name: ```rux let fruit = count.With("apples"); // U = char8[..], inferred let flag = count.With(true); // U = bool, written let half = count.Map(Half); // U from Half's result type ``` A method's result may be a different instantiation of its own type, as `Map` returns `Labeled`. ## Associated calls A method without a receiver is called through its type. On a non-generic type, the method's own type arguments follow the method name: ```rux struct Factory { bias: int; } extend Factory { func Width() -> uint { return sizeof(T); } func Echo(value: T) -> T { return value; } func Read(self: &Factory, value: T) -> T { return value; } } ``` ```rux let w = Factory::Width(); // 4: nothing to infer from, so written let e = Factory::Echo(42i32); // T inferred from the argument let factory = Factory { bias: 5 }; let r = factory.Read(true); // an instance call, written ``` On a **generic** type, an associated call has two sets of parameters to fill: the receiver type's and the method's. They are written in one list after the method name — the receiver type's arguments first, then the method's: ```rux struct Holder { value: T; } extend Holder { func OtherWidth() -> uint { return sizeof(T) + sizeof(U); } func Pair(self: &Holder, other: U) -> (T, U) { return (self.value, other); } } ``` ```rux let total = Holder::OtherWidth(); // T = int32, U = int64: 12 ``` A list that does not fill both fails with `cannot resolve type arguments for method 'OtherWidth'`. An instance call needs no such list for the type's parameters: `holder.Pair(34)` takes `T` from `holder` and `int64` for the method's own parameter. ## Bounds on method parameters A method's own type parameters may carry [bounds](https://rux-lang.dev/docs/lang/generics/bounds), checked at each call like a function's: ```rux interface Scored { func Score() -> int; } extend Factory { func Score(self: &Factory, value: &T) -> int { return self.bias + value.Score(); } } ``` ```text error: type argument 'int' does not satisfy interface bound 'Scored' on type parameter 'T' note: interface 'Scored' requires method 'Score', which type 'int' does not implement note: type parameter 'T' of method 'Score' is bound by 'Scored' help: implement the interface, as in 'extend int: Scored { ... }' ``` ## What cannot be generic The special methods have fixed shapes with no type parameters of their own: [destructors](https://rux-lang.dev/docs/lang/ownership/destructors), the copy and move operations `=` and `<-`, and the [indexers](https://rux-lang.dev/docs/lang/interfaces/indexers) `[]` and `[]=`. They may still use the type parameters of a generic type they belong to. ## See also - [Generic types](https://rux-lang.dev/docs/lang/generics/types) — declaring and extending generic types - [Methods](https://rux-lang.dev/docs/lang/structs/methods) — receivers and method calls - [Bounds](https://rux-lang.dev/docs/lang/generics/bounds) — constraining type parameters - Learn: [Generic method](https://rux-lang.dev/docs/learn/generic-method) # Bounds An unconstrained type parameter promises nothing: the body may store, pass and return a `T`, but cannot call a method on it. A **bound** makes the promise. `T: Scored` admits only type arguments that provide the methods of the [interface](https://rux-lang.dev/docs/lang/interfaces/overview) `Scored`, and in return the body may call those methods on any `T`. ## Syntax ```text ``` A bound follows its parameter after a colon; several bounds are joined with `+`. A comma always starts the next type parameter, so `` declares a second parameter named `Display` rather than a second bound. Bounds may appear on the type parameters of functions, methods and [generic types](https://rux-lang.dev/docs/lang/generics/types). ```rux interface Scored { func Score() -> int32; } interface Named { func Name() -> char8[..]; } func Best(first: T, second: T) -> T { if second.Score() > first.Score() { return second; } return first; } func Announce(value: &T) { PrintLine("{} has {}", value.Name(), value.Score()); } ``` ## Inside the body A bounded `T` is exactly what its bounds say, and nothing more. Every requirement of every bound is callable on it; nothing else is: ```text error: no interface bound on type parameter 'T' provides method 'Score' note: type parameter 'T' has no interface bounds help: add a bound whose interface declares 'Score', as in 'T: SomeInterface' ``` The same holds for passing a `T` where an interface is expected: a `T` prints with `{}` only under a `Display` bound. Inside the body, `Self` in a bound's requirements stands for the type argument, so with `Core::Equatable` as the bound, `left.Equals(right)` takes another `T` — and a requirement returning `Self` returns a `T`. Results stay concrete. `Best` called with two `Player` values returns a `Player`, with every field and method of `Player` available — it never becomes "some `Scored`". ## Satisfying a bound A type argument satisfies a bound when it **has every method the interface requires**. Implementing the interface with `extend T : Scored { … }` is the usual way to provide them, but a bound is structural: a type whose plain `extend` block declares the required methods satisfies it too. ```rux struct Player { name: char8[..]; points: int32; } extend Player : Scored { func Score(self: &Player) -> int32 { return self.points; } } extend Player : Named { func Name(self: &Player) -> char8[..] { return self.name; } } struct Team { total: int32; } // No ': Scored', but Score is there, so Team satisfies the bound. extend Team { func Score(self: &Team) -> int32 { return self.total; } } ``` [Interface values](https://rux-lang.dev/docs/lang/interfaces/interface-values) are stricter: only a declared implementation converts to one. An interface that requires nothing, such as `Core::Iterator`, is satisfied by every type and only documents intent. ### Primitives A primitive type has no methods of its own, so it satisfies a bound only once an `extend` block gives it the methods: ```rux extend int32 : Scored { func Score(self: &int32) -> int32 { return self; } } ``` After this `Best(3, 9)` is accepted, while `Best(2.5, 1.5)` is not — `float64` has `>` but no `Score`. Note that bare literals make `T` an `int`, not an `int32`: `Best(3, 9)` fails naming `'int'`, and the explicit `` is what makes `3` and `9` `int32` values. ## Checked at each use A bound is checked where a type argument meets it — at each call of a bounded function or method, and at each written instantiation of a bounded type — never somewhere inside the body. The type arguments are inferred first, then every bound is checked on its own, and the failing one is reported with the missing method: ```text error: type argument 'float64' does not satisfy interface bound 'Scored' on type parameter 'T' note: interface 'Scored' requires method 'Score', which type 'float64' does not implement note: type parameter 'T' of function 'Best' is bound by 'Scored' help: implement the interface, as in 'extend float64: Scored { ... }' ``` A type that meets one bound of `Scored + Named` but not the other is refused just the same. ## Bounds travel Inside a generic body, a type parameter satisfies exactly the bounds it was declared with. A generic that calls a bounded generic must therefore promise at least what the callee demands: ```rux func Margin(first: T, second: T) -> int32 { let winner = Best(first, second); // T is Scored here, as Best requires return winner.Score() * 2 - first.Score() - second.Score(); } ``` Without the bound on `Margin`, the call to `Best` is refused: ```text error: type argument 'T' does not satisfy interface bound 'Scored' on type parameter 'T' note: type parameter 'T' is not constrained by 'Scored' note: type parameter 'T' of function 'Best' is bound by 'Scored' help: add the bound to the enclosing declaration, as in 'T: Scored' ``` ## Static dispatch Each instantiation calls the method of its own type argument directly, resolved when it is compiled: no interface value is built, and nothing is looked up at run time. The comparison with dynamic dispatch through an interface value is tabled in [Interface values](https://rux-lang.dev/docs/lang/interfaces/interface-values#interface-values-and-bounds). ## See also - [Interfaces](https://rux-lang.dev/docs/lang/interfaces/overview) — declaring the interfaces bounds name - [Generics](https://rux-lang.dev/docs/lang/generics/overview) — type parameters and inference - [Interface values](https://rux-lang.dev/docs/lang/interfaces/interface-values) — the dynamically dispatched alternative - Learn: [Generic bound](https://rux-lang.dev/docs/learn/generic-bound), [Multiple bounds](https://rux-lang.dev/docs/learn/multiple-bounds) # Packages and Modules Rux organises code at two levels. A **package** is the unit that is compiled, versioned and shared: a `Rux.toml` manifest and the source files under `Src/`. A **module** is a named namespace *inside* a package, declared with `module A::B { … }`. Packages are described by the [packaging guide](https://rux-lang.dev/docs/packaging); this chapter covers what the source sees of them — modules, [imports](https://rux-lang.dev/docs/lang/modules/imports) and [visibility](https://rux-lang.dev/docs/lang/modules/visibility). ```text module-decl = [ "pub" ] "module" identifier { "::" identifier } "{" { declaration } "}" ``` ## Files are not modules Every `.rux` file under `Src/` belongs to the package, and the compiler reads them all as one unit. A file adds nothing to any name: its own name means nothing to the compiler, and a package may be split across as many files as is convenient — or none of them besides `Src/Main.rux`. There are no `mod` lists or file-level `module X;` headers; where a declaration lives is decided only by the `module { }` blocks around it. ```text error: expected '{' to start the module body before ';' ``` is what a file-level `module Text;` produces. Write the block form. ## The package root A declaration that is not inside any `module` block sits at the **package root**. Every root item of a package is one namespace, whichever file declares it — two files cannot both declare a root function with the same signature: ```text error: function 'Area' has the same parameter signature as an earlier overload ``` Seen from another package, a root item is reached as `Package::Name`, where `Package` is the package's import name (see [Imports](https://rux-lang.dev/docs/lang/modules/imports)). ## Module declarations `module` opens a namespace and holds any declarations: functions, types, constants, `extend` blocks, `extern` blocks, [imports](https://rux-lang.dev/docs/lang/modules/imports), [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) blocks and further modules. A module may appear at the package root, inside another module, or inside a `when` branch — never inside a function. A path declares the nested modules in one step. These two are the same declaration: ```rux module Shape::Circle { func Area(radius: float64) -> float64 { return 3.14159 * radius * radius; } } module Shape { module Circle { func Area(radius: float64) -> float64 { return 3.14159 * radius * radius; } } } ``` (Written together, as here, they would clash: both declare `Shape::Circle::Area` with the same signature.) A module is **open**: every `module` block with the same path adds to the same module, in the same file or in different ones. Two functions called `Area` do not clash when they are in different modules, because a module's name is part of the full name of everything in it. ## Example A package called `Geometry` with two files. `Src/Shapes.rux` declares a root constant and two modules under a shared parent: ```rux // The package root: these declarations are in no module. const Pi: float64 = 3.14159; module Shape::Circle { func Area(radius: float64) -> float64 { return Pi * radius * radius; } } module Shape { module Rectangle { func Area(width: float64, height: float64) -> float64 { return width * height; } } } ``` `Src/Main.rux` reaches them in two ways: ```rux import Io::PrintLine; import Geometry::Shape::Circle; func Main() -> int { PrintLine("circle {}", Circle::Area(2.0)); PrintLine("rectangle {}", Shape::Rectangle::Area(3.0, 4.0)); return 0; } ``` ```text circle 12.56636 rectangle 12.0 ``` ```mermaid flowchart LR pkg(["package Geometry"]) --> pi["Pi
(root)"] pkg --> main["Main
(root)"] pkg --> shape["module Shape"] shape --> circle["module Circle
Area"] shape --> rect["module Rectangle
Area"] ``` ## Name lookup inside a package A name is looked up in the innermost module that encloses its use, then in each enclosing module, and finally at the package root. So `Circle::Area` above can use the root constant `Pi` unqualified, and code inside `Shape::Rectangle` could call `Circle::Area` through their shared parent. Every root item — including each top-level module — is visible throughout the package without an import, which is why `Main` can write `Shape::Rectangle::Area(…)`. A nested module or an item inside one is reached either by its path from the root or by importing it: `import Geometry::Shape::Circle;` makes `Circle` a name of its own. An import always starts with a package name, the package's own included — see [Imports](https://rux-lang.dev/docs/lang/modules/imports). Inside one package every declaration is usable from every file, `pub` or not. `pub` only matters to the packages that depend on this one — see [Visibility](https://rux-lang.dev/docs/lang/modules/visibility). ## See also - [Imports](https://rux-lang.dev/docs/lang/modules/imports) — bringing names from a package or module into scope - [Visibility](https://rux-lang.dev/docs/lang/modules/visibility) — what `pub` shows to dependent packages - [Packaging](https://rux-lang.dev/docs/packaging) — manifests, [directory layout](https://rux-lang.dev/docs/packaging/layout) and [dependencies](https://rux-lang.dev/docs/packaging/dependencies) - Learn: [Module](https://rux-lang.dev/docs/learn/module), [Package](https://rux-lang.dev/docs/learn/package) # Imports An `import` declaration brings names from a package into the current scope. Every path starts with a **package**, and nothing from another package — not even a fully qualified path — can be named without an import. ```text import-decl = "import" package { "::" segment } [ "::" ( "*" | "{" name { "," name } [ "," ] "}" ) ] ";" segment = identifier | "#" identifier name = identifier | "#" identifier ``` ## Forms | Form | Brings in | Used as | | -------------------------------- | ---------------------------------------------------- | ------------------- | | `import Pkg::Item;` | one root item of `Pkg` | `Item` | | `import Pkg::Mod;` | a module of `Pkg` | `Mod::Item` | | `import Pkg::Mod::Item;` | one item of a module | `Item` | | `import Pkg::{ A, B, #target };` | several items or modules from one place | `A`, `B`, `#target` | | `import Pkg::Mod::{ A, B };` | several items of one module | `A`, `B` | | `import Pkg::*;` | every public root item and top-level module of `Pkg` | each by its name | | `import Pkg::Mod::*;` | every public item of a module | each by its name | | `import Pkg;` | `Pkg`'s module named `Pkg`, when it has one | `Pkg::Item` | ```rux import Io::PrintLine; // a root item import Core::{ #target, int8 }; // a compile-time value and a primitive's constants import Tally::{ Counter, Level }; // two items from one package import Shapes::Square; // a whole module import Shapes::Square::Perimeter; // one item of a module import Shapes::Circle::*; // every public item of a module func Main() -> int { var counter = Counter(3); counter.Tick(); PrintLine("{} {}", counter.Count(), Level::Low == Level::Low); PrintLine("{} {} {}", Square::Area(5), Perimeter(5), Diameter(2)); PrintLine("{} {}", int8::Max, #target.pointerBits); return 0; } ``` Importing a module rather than its items keeps calls qualified, which is the better choice whenever a bare name would be unclear or would collide: `Square::Area(5)` says which `Area` is meant. `import Pkg;` on its own names a module, not the package: it works only when `Pkg` declares a module called `Pkg` (`pub module Pkg { … }`). Otherwise it is an error — import an item or a module instead: ```text error: import 'Io' does not name a module help: import an item instead, for example 'import Io::Name' ``` ## The first segment is a package The first segment of every import is a package's **import name**: - the current package's own `Name` from `Rux.toml` — a package imports its own nested modules this way, as in `import Geometry::Shape::Circle;`; - or a key under `[Dependencies]`. The key is usually the package's name, but it may differ, which renames the package for imports: with `Shapes = { Package = "Geometry2", Path = "../Geometry2" }`, the example above imports from `Shapes`. A first segment that is neither fails before analysis starts: ```text error: package 'Shape' is not listed in [Dependencies] of '…\Rux.toml' note: the import requires a package dependency with the same import name help: add the package under [Dependencies] or correct the import path ``` The remaining segments walk the package's modules. An item or module that is not there is reported as `name 'Gauge' was not found in package 'Tally'` or `module 'Counter' was not found in package 'Tally'`. ## Qualified paths need an import A path in an expression or a type starts from a name already in scope. Another package's name is never in scope by itself, so a fully qualified call does not work: ```rux func Main() -> int { return Tally::Clamp(12); // error: name 'Tally' is not defined in this scope } ``` Import the item, or the module that contains it, and qualify from there. Inside one package, every root item and every top-level module is already in scope, so `Shape::Circle::Area(2.0)` works without an import from anywhere in the package that declares `Shape` — see [Name lookup](https://rux-lang.dev/docs/lang/modules/overview#name-lookup-inside-a-package). ## Compile-time values and primitive constants Imports also reach two kinds of name that look built in but are declared by the `Core` package: - **Compile-time values and directives** are imported by their `#` names: `import Core::{ #target, #build, #Error };`. Without the import, `#target.os` fails with `error: name '#target' is not defined in this scope`. See [Compile-time context](https://rux-lang.dev/docs/lang/comptime/context). - **Associated constants of primitive types** — `int8::Max`, `uint::Bits`, `float64::NaN` — come from `Core`'s declarations of those types, so the type must be imported: `import Core::int8;`. The primitive type itself is usable without any import; only its constants need one. Without it: ```text error: 'Max' not found in extend for type 'int8' ``` The full list of primitive constants is in [Primitive types](https://rux-lang.dev/docs/lang/appendix/primitives); how a package declares them is in [Intrinsics](https://rux-lang.dev/docs/lang/comptime/intrinsics). ## Where imports go An import is a declaration. It may appear at the package root, inside a `module` body, and inside a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) branch; in a `when` match arm the trailing `;` is optional. An import inside a module applies to that module and the modules nested in it. It cannot appear inside a function body: ```text error: expected an expression before 'import' ``` By convention each file starts with the imports it uses. ::note **Root imports are shared between files.**:br rux 0.4.0 places an import written at the top of a file into the package root, so the other files of the package can see it too. Do not rely on that: write in each file the imports that file uses. Associated constants are already strict about it — `int8::Max` is found only in a file that itself imports `int8`. :: ## Glob imports `import Pkg::*;` and `import Pkg::Mod::*;` bring in every **public** name at that level — items, and with the package form also its public top-level modules. Private declarations are skipped silently, so a glob never fails on them; naming a private item explicitly does fail: ```text error: function 'Clamp' is private to package 'Tally' help: add 'pub' to the declaration of 'Clamp' ``` A glob makes it hard to see where a name came from, and grows when the package does. Prefer a list. ## Conflicts An imported function joins the overload set of a function with the same name already in scope. Two candidates that accept the same arguments make every call ambiguous: ```text error: call to 'Validate' is ambiguous: 2 overloads accept argument types () help: rename one of the overloads, or remove a default value that makes them overlap ``` Import the module instead and qualify the call. ## See also - [Packages and modules](https://rux-lang.dev/docs/lang/modules/overview) — what a path walks through - [Visibility](https://rux-lang.dev/docs/lang/modules/visibility) — which names another package can import - [Dependencies](https://rux-lang.dev/docs/packaging/dependencies) — the `[Dependencies]` table that supplies import names - Learn: [Module](https://rux-lang.dev/docs/learn/module), [Dependency](https://rux-lang.dev/docs/learn/dependency) # Visibility Every declaration is **package-private** unless it starts with `pub`. Package-private means usable from every file and every module of the declaring package; it does not mean file-private or module-private. `pub` marks what a package shows to the packages that **depend** on it. ```text visibility = [ "pub" ] ``` Inside one package `pub` changes nothing. It matters only at the border between packages, where everything without it is refused. ## What takes `pub` `pub` is written before the declaration it applies to, and each declaration chooses for itself: | Declaration | Default | Notes | | -------------------------------------------------------------------------- | --------------- | ------------------------------------------------------ | | `func`, `const`, `type`, `struct`, `union`, `enum`, `variant`, `interface` | package-private | at the root or in a module | | `module` | package-private | `pub module A::B` makes both `A` and `B` public | | struct and union fields | package-private | each field separately | | methods, constructors, operators in `extend` | package-private | each member separately; `extend` itself takes no `pub` | | `extern func`, `extern` block members | package-private | `pub` before a member, or `pub extern { … }` for all | | enum members, variant cases | inherit | public exactly when their type is | | interface requirements | inherit | public exactly when the interface is | A type being public does not make its members public, and a public member of a private type is still unreachable, because the type cannot be named. ## Example A library package `Tally`, depended on by a program. The promise it keeps — the count never passes its limit — rests on what is private: ```rux pub struct Counter { pub step: int; count: int; } extend Counter { pub func Counter(step: int) -> Counter { return Counter { step: step, count: 0 }; } pub func Tick(self: &var Counter) { self.count = Clamp(self.count + self.step); } pub func Count(self: &Counter) -> int { return self.count; } } const Limit: int = 10; func Clamp(value: int) -> int { return value > Limit ? Limit : value; } ``` The program sees `Counter`, its `step` field and its three functions, and nothing else: ```rux import Io::PrintLine; import Tally::Counter; func Main() -> int { var counter = Counter(4); counter.Tick(); counter.Tick(); counter.Tick(); PrintLine("step {}, count {}", counter.step, counter.Count()); // step 4, count 10 return 0; } ``` Every attempt to reach past the public surface is refused, for reading as much as for writing: | Attempt in the program | Error | | ----------------------------------- | ------------------------------------------------------------------------------------------ | | `import Tally::{ Counter, Clamp };` | `function 'Clamp' is private to package 'Tally'` | | `import Tally::Limit;` | `constant 'Limit' is private to package 'Tally'` | | `let n = counter.count;` | `struct field 'count' is private to package 'Tally'` | | a private method `counter.Reset()` | `method 'Reset' is private to package 'Tally'` | | `Counter { step: 1, count: 0 }` | `struct 'Counter' cannot be initialized outside its package because it has private fields` | Each error comes with a help line naming the fix — "add 'pub' to the declaration of 'Clamp'", or for the struct literal, "use a public constructor instead". ## Construction A struct literal must name every field, so outside its package a struct with any private field can only be made by a public [constructor](https://rux-lang.dev/docs/lang/structs/constructors) or another public function. That is the usual way to protect an invariant: keep the representation private and publish the operations. ## Private modules An item is **effectively public** only when it and every module that contains it are public. A `pub` function inside a private module stays package-private: ```rux module Hidden { pub func Inside() -> int { return 4; } } ``` ```text error: module 'Hidden' is private to package 'Lib' help: add 'pub' to the declaration of 'Hidden' ``` `pub module Text::Utf8 { … }` publishes both `Text` and `Utf8`; the items inside still choose individually. ## Public signatures A public declaration may not expose a private type. A public function's parameters and result, a public field's type, a public alias, constant, extern, generic bound, variant payload or interface requirement must all name public types, down to nested generic arguments. Publishing an item never re-exports the private types its signature uses; the compiler rejects the leak instead: ```rux struct Secret { value: int; } pub func Reveal() -> Secret { return Secret { value: 1 }; } pub struct Holder { pub inner: Secret; } ``` ```text error: public function 'Reveal' exposes private type 'Secret' help: make 'Secret' public or remove it from the public signature error: public field 'Holder.inner' exposes private type 'Secret' help: make 'Secret' public or remove it from the public signature ``` ## Interfaces and overloads A private method may still satisfy a public interface. Code in another package can call it through the [interface value](https://rux-lang.dev/docs/lang/interfaces/interface-values) but not directly on the concrete type: ```rux pub interface Named { func Name(self: &Self) -> char8[..]; } extend Counter : Named { func Name(self: &Counter) -> char8[..] { return "counter"; } } ``` From the program, `Describe(counter)` with a `&Named` parameter calls `Name`, while `counter.Name()` fails with `method 'Name' is private to package 'Tally'`. When a function name has public and private overloads, another package's overload resolution sees only the public ones, and its diagnostics do not mention the private candidates. A [glob import](https://rux-lang.dev/docs/lang/modules/imports#glob-imports) silently skips private declarations. ## Other rules - Compiler-generated copy and move operations are available wherever their type is; a custom copy or move, like any operator, must be `pub` to be used from another package. Destructors are called by the compiler whatever their visibility, and normally stay private. - Field visibility applies inside a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) condition too: `when #build.debugAssertions` works because `Core` declares that field `pub`. - `rux doc` documents only effectively public declarations unless asked for private ones. ## See also - [Packages and modules](https://rux-lang.dev/docs/lang/modules/overview) — the namespaces `pub` applies to - [Imports](https://rux-lang.dev/docs/lang/modules/imports) — how a dependent package names public items - [Structs](https://rux-lang.dev/docs/lang/structs/overview) and [Constructors](https://rux-lang.dev/docs/lang/structs/constructors) — fields and the functions that build them - Learn: [Visibility](https://rux-lang.dev/docs/learn/visibility), [Documentation](https://rux-lang.dev/docs/learn/documentation) # Compile-Time Evaluation Part of every Rux program is settled while it is being **compiled**, before any of it runs. Constants are folded to values, `sizeof` and `alignof` to numbers, and a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) keeps one branch and throws the others away unread. The compiler also describes the build itself — the target machine, the build profile, the compiler, the source location and user defines — as ordinary values a program can read. None of this costs anything at run time: by then a compile-time value is a plain constant, and a discarded branch does not exist. There is no preprocessor and no separate build language. Everything in this chapter is written in Rux and checked by the same compiler. ## What is known at compile time | Construct | Known while compiling | Page | | ------------------------------------------------------ | --------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | literals and `const` declarations | the value, folded from literals, operators, casts and other constants | [Constants](https://rux-lang.dev/docs/lang/bindings/constants) | | `sizeof(T)`, `alignof(T)` | a type's size and alignment in bytes, as `uint` | [Layout](https://rux-lang.dev/docs/lang/memory/layout) | | primitive constants such as `int32::Max`, `uint::Bits` | their values | [Primitive types](https://rux-lang.dev/docs/lang/appendix/primitives) | | `#target`, `#build`, `#compiler`, `#source` | facts about the build and the source location | [Context](https://rux-lang.dev/docs/lang/comptime/context) | | `#config` | user-defined values from `Rux.toml` or `--define` | [Config](https://rux-lang.dev/docs/lang/comptime/config) | | `when` | which branch is compiled | [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) | | `#Error`, `#Warn` | diagnostics that stop or annotate the build | [Diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics) | | `intrinsic` declarations | the values and functions the compiler itself supplies | [Intrinsics](https://rux-lang.dev/docs/lang/comptime/intrinsics) | A `const` initializer may use literals, operators, casts, struct literals, other constants, `sizeof`/`alignof`, primitive constants and the scalar fields of the `#` values. A function call is never a compile-time value, even when it could be computed: ```rux func Square(x: int) -> int { return x * x; } const Nine = Square(3); ``` ```text error: call to 'Square' is not a compile-time value help: initialize a constant from literals, operators, casts, and other constants ``` ## The `#` values come from `Core` `#target`, `#build`, `#compiler`, `#source` and `#config`, and the directives `#Error` and `#Warn`, are declared in the `Core` package and imported like any other name: ```rux import Core::{ #target, #build, #Error }; ``` They are not keywords. A package that declares them itself — see [Intrinsics](https://rux-lang.dev/docs/lang/comptime/intrinsics) — can stand in for `Core`. ## Compile time and run time Rux keeps the two kinds of decision visibly apart. A chain is `if`/`else if` throughout or `when`/`else when` throughout, never a mixture, so a reader always knows which branches the compiler has already settled: | | `if` | `when` | | ------------------- | ------------------------- | --------------------------------------------- | | Decided | while the program runs | while the program is compiled | | Condition | any `bool` | an expression the compiler can fold | | Untaken branch | compiled and type-checked | parsed, then discarded before name resolution | | Scope of a branch | its own | none: declarations and bindings stay after it | | Where it can appear | inside a function body | between declarations and inside bodies | ```mermaid flowchart LR src["source"] --> parse["parse"] parse --> fold["fold 'when'
keep one branch"] fold --> resolve["resolve names,
check types"] resolve --> code["generate code"] fold -. "untaken branches
never reach here" .-> resolve ``` A `#` value read in ordinary code is just as free: `#build.debugAssertions` in an expression compiles to the constant `true` or `false`. ## See also - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — `when` in full - [Context](https://rux-lang.dev/docs/lang/comptime/context) — every field of `#target`, `#build`, `#compiler` and `#source` - [Config](https://rux-lang.dev/docs/lang/comptime/config) — values a build passes in - [Constants](https://rux-lang.dev/docs/lang/bindings/constants) — `const` declarations - Learn: [Compile time](https://rux-lang.dev/docs/learn/compile-time), [When](https://rux-lang.dev/docs/learn/when) # Conditional Compilation `when` is the compile-time counterpart of [`if`](https://rux-lang.dev/docs/lang/statements/if). Its condition is folded while compiling, the branch it selects is kept, and every other branch is discarded **before names are resolved** — so an untaken branch may call functions, name types or link libraries that do not exist in this build. That is what makes one source tree serve several targets and configurations. ```text when-chain = "when" condition "{" items "}" { "else" "when" condition "{" items "}" } [ "else" "{" items "}" ] when-match = "when" subject "{" arm { [ "," ] arm } [ "," ] "}" arm = ( pattern { "," pattern } | "else" ) "=>" arm-body ``` `items` are declarations or statements, depending on where the `when` stands. ## Chains A chain is tested from top to bottom; the first condition that holds selects its branch, and a final bare `else` is taken when none does. Without an `else`, a chain whose conditions all fail compiles to nothing. ```rux import Core::{ #compiler, SemanticVersion }; const Rux040 = SemanticVersion { major: 0, minor: 4, patch: 0 }; const Rux100 = SemanticVersion { major: 1, minor: 0, patch: 0 }; when #compiler.version >= Rux100 { func Channel() -> char8[..] { return "stable"; } } else when #compiler.version >= Rux040 { func Channel() -> char8[..] { return "preview"; } } else { // Never resolved by this compiler, so a function nobody wrote is not an error. func Channel() -> char8[..] { return LegacyChannel(); } } ``` A chain keeps the keyword it opened with. `else if` inside a `when` chain — or `else when` inside an `if` chain — is an error: ```text error: expected 'when' after 'else' in a compile-time 'when' chain; 'if' is the run-time conditional ``` ## Match form When the condition picks between several values of one subject, the match form is shorter than a chain of `==`. Each arm lists one or more patterns before `=>`; the first arm with a matching pattern is kept. Patterns are compile-time values compared for equality — enum members (the `.Linux` shorthand takes its enum from the subject), integers and strings: ```rux import Core::{ #target, #Error }; when #target.os { .Linux, .FreeBSD => const Family: char8[..] = "unix"; .macOS => const Family: char8[..] = "darwin"; .Windows => const Family: char8[..] = "windows"; else => #Error("unsupported operating system") } ``` The `else` arm must be last (`error: the 'else' arm must be last in a 'when' match`). Without one, a build that no arm matches stops: ```text error: no arm of this 'when' matches .FreeBSD ``` That is the right outcome when the code truly cannot work elsewhere; when it can, add an `else` arm. Commas between arms are optional, except after an arm whose body is a bare expression: there the comma is what ends the expression, so `.Windows => Setup(), .Linux => …` needs it. ### Arm bodies | Position | An arm body may be | | -------------------- | ----------------------------------------------------------------------------------------------- | | between declarations | one declaration, a `{ … }` block of declarations, an `import`, or an `#Error`/`#Warn` directive | | inside a body | one expression — usually a call — or a `{ … }` block of statements | A statement written bare as an arm body is refused, because it is not an expression: ```text error: a 'when' arm body that is a statement must be written as a block, as in '.Windows => { return 1; }' ``` ## Where `when` can appear - **Between declarations** — at the package root, inside a `module`, and inside another `when` branch. The taken branch's declarations are spliced into the enclosing list, as if written there. - **Inside an `extend` block** — selecting methods and associated constants. - **Inside a function body** — selecting statements. `when` cannot stand inside a struct's field list, an `extern` block or an expression. ### No new scope A taken branch opens no scope. A `let` in it, or a declaration, is still there after the `when`: ```rux import Core::{ #target }; import Io::PrintLine; func Main() -> int { when #target.os { .Windows => { let separator = "\\"; }, else => { let separator = "/"; } } PrintLine("{}", separator); return 0; } ``` The same code with `if` would fail twice: each `separator` would end with its branch, and both branches would have to compile. ## Untaken branches An untaken branch is **parsed** — it must be well-formed Rux — but never resolved or type-checked. Calls to missing functions, unknown types, a library absent on this system, assembly for another architecture: none of them is an error there. The flip side is that a mistake in an untaken branch stays hidden until a build takes it. Build for each target that matters (`rux build --target linux-x86_64`, for instance) to check every branch. ## Conditions A condition — or a match subject — must be something the compiler can fold before name resolution: | Operand | Example | | -------------------------------------------------------------------------------------- | ------------------------------------ | | `bool`, integer and string literals | `true`, `64`, `"windows-x86_64"` | | constants with foldable initializers | `when Chosen == 20` | | primitive constants (with the type imported from `Core`) | `int32::Bits == 32` | | fields of `#target`, `#build`, `#compiler`, `#source` | `#target.pointerBits`, `#build.mode` | | the queries `#target.HasFeature`, `#compiler.HasFeature`, `#config.Has`, `#config.Get` | `#config.Has("Verbose")` | | `#compiler.version` and `SemanticVersion` constants | `#compiler.version >= Rux040` | They combine with `==` `!=` on any of these, `<` `<=` `>` `>=` on integers and versions, the `SemanticVersion` methods (`IsAtLeast`, `IsLessThan`, …), `&&` `||` `!`, and integer arithmetic. Floating-point values, `sizeof`, `alignof` and function calls are not available: ```text error: 'when' condition is not a valid compile-time expression ``` A `let` binding is never a compile-time value (`error: 'minor' is not a compile-time constant`). Enum-valued fields compare only with `==` and `!=`; ordering `#target.os < .Windows` is the same "not a valid compile-time expression" error. A misspelt member lists the real ones: ```text error: '.MacOS' is not a variant of 'OperatingSystem'; the variants are: .FreeBSD, .Linux, .macOS, .Windows ``` ## See also - [Context](https://rux-lang.dev/docs/lang/comptime/context) — the fields and queries a condition reads - [Config](https://rux-lang.dev/docs/lang/comptime/config) — conditions on user defines - [Diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics) — `#Error` and `#Warn` in an untaken-by-design branch - [`if`](https://rux-lang.dev/docs/lang/statements/if) — the run-time conditional - Learn: [When](https://rux-lang.dev/docs/learn/when), [Target](https://rux-lang.dev/docs/learn/target) # Compile-Time Context Four values describe the build a program is part of. The compiler fills them in before compiling starts, and `Core` declares them, so each is imported by its `#` name: ```rux import Core::{ #target, #build, #compiler, #source }; ``` | Value | Type | Describes | | ----------- | ---------- | ------------------------------------------------- | | `#target` | `Target` | the machine the program is built **for** | | `#build` | `Build` | the build profile, its options and the output | | `#compiler` | `Compiler` | the compiler doing the build | | `#source` | `Source` | the place in the source where the read is written | A fifth, `#config`, carries values the build is given — see [Config](https://rux-lang.dev/docs/lang/comptime/config). The struct and enum types (`Target`, `OperatingSystem`, `BuildMode`, …) are ordinary `Core` declarations; import them too when code names them, as in `OperatingSystem::Linux` or `BuildMode::Debug`. Fields are read with `.`, are lowerCamelCase, and are fixed for the whole build. A read can drive a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional), initialize a scalar [constant](https://rux-lang.dev/docs/lang/bindings/constants), or appear in any expression, where it compiles to a constant. Text fields (`char8[..]`) work in expressions and conditions; a `const` of slice type, though, must be initialized with a literal, so `const Date: char8[..] = #build.date;` is refused with `a constant sequence must be initialized with an array literal or a string literal` — use a `let` instead. ## `#target` The machine the program will run on — not the machine compiling it. `rux build --target linux-x86_64` on Windows answers every field for Linux. | Field / query | Type | Notes | | --------------------- | ---------------------------- | ------------------------------------------------- | | `os` | `OperatingSystem` | the operating system | | `arch` | `Architecture` | the processor architecture | | `abi` | `ApplicationBinaryInterface` | the calling and layout conventions | | `endian` | `Endianness` | byte order; `.Little` on every supported target | | `pointerBits` | `uint` | `64` on every supported target | | `dataModel` | `DataModel` | the C widths of `long` and pointers | | `objectFormat` | `ObjectFormat` | the object format the linker emits | | `triple` | `char8[..]` | the canonical `-` name `--target` takes | | `HasFeature(feature)` | `bool` | whether the build enables a CPU feature | The eight supported targets: | `triple` | `os` | `arch` | `abi` | `dataModel` | `objectFormat` | | ----------------- | ---------- | ---------- | ------------- | ----------- | -------------- | | `windows-x86_64` | `.Windows` | `.X86_64` | `.WindowsX64` | `.LLP64` | `.COFF` | | `windows-aarch64` | `.Windows` | `.AArch64` | `.AAPCS64` | `.LLP64` | `.COFF` | | `linux-x86_64` | `.Linux` | `.X86_64` | `.SystemV` | `.LP64` | `.ELF` | | `linux-aarch64` | `.Linux` | `.AArch64` | `.AAPCS64` | `.LP64` | `.ELF` | | `macos-x86_64` | `.macOS` | `.X86_64` | `.SystemV` | `.LP64` | `.MachO` | | `macos-aarch64` | `.macOS` | `.AArch64` | `.AAPCS64` | `.LP64` | `.MachO` | | `freebsd-x86_64` | `.FreeBSD` | `.X86_64` | `.SystemV` | `.LP64` | `.ELF` | | `freebsd-aarch64` | `.FreeBSD` | `.AArch64` | `.AAPCS64` | `.LP64` | `.ELF` | `HasFeature` takes a `TargetFeature`. A build for the machine running the compiler reports the features that compiler was itself built to use; a build for any other target reports none. Inside a `when` condition the shorthand `.AVX2` works; in an ordinary expression write `TargetFeature::AVX2`. An unknown name is an error (`unknown target feature '.MMX'`). ```rux import Core::{ #target }; when #target.arch == .X86_64 && #target.HasFeature(.AVX2) { func Kernel() -> char8[..] { return "avx2"; } } else { func Kernel() -> char8[..] { return "scalar"; } } ``` ## `#build` The build profile and what it produces. Every field is the same everywhere in one build. | Field | Type | Debug profile | Release profile | | ----------------- | ------------------ | ------------- | --------------- | | `profile` | `char8[..]` | `"Debug"` | `"Release"` | | `mode` | `BuildMode` | `.Debug` | `.Release` | | `optimization` | `OptimizationMode` | `.None` | `.Speed` | | `debugAssertions` | `bool` | `true` | `false` | | `debugInfo` | `bool` | `true` | `false` | | Field | Type | Value | | ------------ | ------------ | ----------------------------------------------------------------------------------- | | `isTest` | `bool` | `true` in a `rux test` build | | `outputKind` | `OutputKind` | the artifact: `.Executable`, `.SharedLibrary`, `.StaticLibrary` or `.SourceLibrary` | | `timestamp` | `uint64` | when the build started, in seconds since the Unix epoch | | `date` | `char8[..]` | `timestamp` as `YYYY-MM-DD`, in UTC | | `time` | `char8[..]` | `timestamp` as `HH:MM:SS`, in UTC | `profile` is a name, and a project may define profiles of its own; branch on `mode`, which every profile has. `debugAssertions` decides whether [`DebugAssert`](https://rux-lang.dev/docs/lang/errors/panics) is kept — when it is `false` the call is removed and its arguments are not evaluated. The timestamp is taken once per build, and honours the `SOURCE_DATE_EPOCH` environment variable for reproducible builds. ```rux import Core::{ #build, BuildMode }; import Io::PrintLine; func Main() -> int { when #build.mode == BuildMode::Debug { PrintLine("debug build of {} at {}", #build.date, #build.time); } return 0; } ``` ## `#compiler` | Field / query | Type | Notes | | ------------------ | ----------------- | --------------------------------------------------------------- | | `version` | `SemanticVersion` | the compiler's version, with `major`, `minor`, `patch` (`uint`) | | `HasFeature(name)` | `bool` | whether the compiler reports a named feature | `SemanticVersion` is an ordinary struct with the comparison operators `==` `!=` `<` `<=` `>` `>=` and the methods `Compare`, `IsEqualTo`, `IsLessThan`, `IsAtMost`, `IsGreaterThan` and `IsAtLeast`, all of which work in a `when`. A version to compare against is a **struct literal** constant — a constructor call is a run-time call and is refused (`call to 'SemanticVersion' is not a compile-time value`): ```rux import Core::{ #compiler, SemanticVersion }; const Rux040 = SemanticVersion { major: 0, minor: 4, patch: 0 }; when #compiler.version.IsAtLeast(Rux040) { const HasDefines: bool = true; } else { const HasDefines: bool = false; } ``` `HasFeature` is the better test when code needs one capability rather than a release: it names what is needed and stays right however releases are numbered. Its argument must be a string literal; an unknown name is simply `false`. Rux 0.4.0 reports: | Feature name | Feature name | Feature name | | --------------------------- | ------------------------------ | ---------------------------- | | `"conditional-compilation"` | `"namespaced-intrinsics"` | `"target-intrinsics"` | | `"build-intrinsics"` | `"compiler-feature-detection"` | `"source-location-defaults"` | | `"extern-symbol-names"` | `"link-attribute"` | `"no-return-attribute"` | ## `#source` Each read of `#source` describes the place where that read is written — the expression itself, not the function around it or its caller. | Field | Type | Value | | ---------- | ----------- | ---------------------------------------------------------------------------------------------------------- | | `line` | `uint` | the one-based line of the read | | `column` | `uint` | the one-based column of the read | | `file` | `char8[..]` | the file name as the build names it, such as `"Main.rux"` | | `fileName` | `char8[..]` | the file's last path component, such as `"Main.rux"` | | `filePath` | `char8[..]` | the path inside the package, such as `"Src/Main.rux"` | | `function` | `char8[..]` | the enclosing function, qualified for a method (`"Point::Length"`) or a module function (`"Tools::Parse"`) | | `module` | `char8[..]` | the enclosing module path | ::note **`#source.module` includes the file name.**:br rux 0.4.0 reports the module path prefixed with the source file's name — `"Main::Tools"` for a read inside `module Tools` in `Main.rux`, and `"Main"` at the package root. Files are not modules, so do not build logic on that prefix. :: A helper that reads `#source` in its own body always reports its own body. To report where it was called from, the caller reads `#source` and passes the value: ```rux import Core::{ #source }; import Io::PrintLine; func LogAt(line: uint, message: char8[..]) { PrintLine("line {}: {}", line, message); } func Main() -> int { LogAt(#source.line, "first call"); // the line of this call LogAt(#source.line, "second call"); // and of this one return 0; } ``` The same rule covers a default argument. `func LogAt(message: char8[..], line: uint = #source.line)` reports the line where the default is written — the parameter list — for every call, so a default does not capture the caller's location. Pass the location explicitly, as above. ## Enum reference Every enum here is a `Core` declaration with a `uint8` representation. | Enum | Variants | | ---------------------------- | --------------------------------------------------------------------------------- | | `OperatingSystem` | `Unknown`, `FreeBSD`, `Linux`, `macOS`, `Windows` | | `Architecture` | `Unknown`, `AArch64`, `X86_64` | | `ApplicationBinaryInterface` | `Unknown`, `AAPCS64`, `SystemV`, `WindowsX64` | | `Endianness` | `Big`, `Little` | | `DataModel` | `Unknown`, `LLP64`, `LP64` | | `ObjectFormat` | `Unknown`, `COFF`, `ELF`, `MachO` | | `TargetFeature` | `AVX`, `AVX2`, `AVX512`, `NEON`, `SSE2`, `SSE3`, `SSE41`, `SSE42`, `SSSE3`, `SVE` | | `BuildMode` | `Debug`, `Release` | | `OptimizationMode` | `None`, `Size`, `Speed` | | `OutputKind` | `Executable`, `SharedLibrary`, `StaticLibrary`, `SourceLibrary` | No supported build produces an `Unknown` variant; it exists so a provider can describe a target the compiler has no name for. The variant is `.macOS`, with a lower-case `m`. ## See also - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — branching on these values - [Config](https://rux-lang.dev/docs/lang/comptime/config) — `#config`, the build's own values - [Intrinsics](https://rux-lang.dev/docs/lang/comptime/intrinsics) — how `Core` declares them - API: [`Target`](https://rux-lang.dev/docs/api/core/target), [`Build`](https://rux-lang.dev/docs/api/core/build), [`Compiler`](https://rux-lang.dev/docs/api/core/compiler), [`Source`](https://rux-lang.dev/docs/api/core/source) - Learn: [Target](https://rux-lang.dev/docs/learn/target), [Build mode](https://rux-lang.dev/docs/learn/build-mode), [Source location](https://rux-lang.dev/docs/learn/source-location) # Build Configuration `#config` reads **defines**: named values handed to a build, such as a feature switch, a vendor name or a limit. They come from the manifest and the command line, are fixed for the whole build, and are read while compiling, so a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) can keep only the code a configuration needs. ```rux import Core::{ #config }; ``` | Query | Type | Result | | --------------------- | ----------- | ------------------------------------------------------------- | | `#config.Has("Name")` | `bool` | whether the build defines `Name`, whatever its value | | `#config.Get("Name")` | `char8[..]` | the value of `Name`, or an empty slice when it is not defined | `Get` alone cannot tell "not defined" from "defined as empty"; ask `Has` when the difference matters. Names are case-sensitive. ## Supplying defines **In the manifest.** A `[Build.Defines]` table in `Rux.toml` gives the package's defaults. A value may be a TOML string, boolean or integer, and is always read as text: ```toml [Build.Defines] Name = "Grace" Retries = 3 Fast = true ``` Here `#config.Get("Retries")` is `"3"` and `#config.Get("Fast")` is `"true"`. The table holds at most 128 defines; see the [manifest reference](https://rux-lang.dev/docs/packaging/manifest). **On the command line.** `--define NAME=VALUE` sets or overrides one define for one build of `rux build`, `rux run`, `rux check` or `rux test`, and may be repeated. `--define NAME` without a value defines it as `"true"`; `--define NAME=` defines it as empty. ```sh rux run --define Name=Ada --define Verbose ``` ```mermaid flowchart LR m["Rux.toml
[Build.Defines]"] --> d{"the build's defines"} c["--define NAME[=VALUE]"] -- "overrides" --> d d --> has["#config.Has"] d --> get["#config.Get"] ``` ## Reading defines The argument to `Has` and `Get` must be a **string literal** written at the call, because the lookup happens while compiling. Even a constant holding the name is refused: ```text error: the argument to 'Has' must be a string literal written in the source help: the value is looked up while compiling, so it cannot come from a variable ``` Both queries work in a `when` condition — `Get` compares with `==` and `!=` against a string — and in ordinary expressions, where they compile to constants: ```rux import Core::{ #config }; import Io::PrintLine; when #config.Has("Verbose") { const Verbose: bool = true; } else { const Verbose: bool = false; } func Main() -> int { when #config.Has("Name") { let name = #config.Get("Name"); } else { let name = "World"; } PrintLine("Hello, {}!", name); when #config.Get("Name") == "Ada" { PrintLine("(a special greeting)"); } PrintLine("verbose: {}", Verbose); return 0; } ``` | Started with | Output | | -------------------------------------------- | -------------------------------------------------------- | | `rux run` | `Hello, World!` · `verbose: false` | | `rux run --define Name=Ada --define Verbose` | `Hello, Ada!` · `(a special greeting)` · `verbose: true` | A define is folded in while compiling: changing one means rebuilding. An executable built earlier keeps the defines it was built with, and the code for a configuration it was not built with is not in it at all. ## See also - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — `when` and its conditions - [Context](https://rux-lang.dev/docs/lang/comptime/context) — `#target`, `#build`, `#compiler` and `#source` - [Manifest](https://rux-lang.dev/docs/packaging/manifest) — the `[Build]` and `[Build.Defines]` tables - [`rux build`](https://rux-lang.dev/docs/cli/build) and [`rux run`](https://rux-lang.dev/docs/cli/run) — the `--define` option - API: [`Config`](https://rux-lang.dev/docs/api/core/config) · Learn: [Define](https://rux-lang.dev/docs/learn/define) # Intrinsics An **intrinsic** is something the compiler implements itself: the arithmetic of `int8`, the value of `float64::Infinity`, the fields of `#target`, the code an `Assert` turns into. A program reaches it only through an `intrinsic` **declaration**, which names it and gives its type, with no body. The `Core` package holds the standard declarations, and every program imports them from there — `import Core::{ #target, Assert, int8 };` — but `Core` has no special status: any package may declare the same intrinsics and be imported instead. ```text intrinsic-decl = [ "pub" ] "intrinsic" ( "type" identifier ";" | "const" identifier ":" type ";" | "#" identifier ":" type ";" | function-signature ";" ) ``` `intrinsic const` is written inside an `extend` block, and `intrinsic func` may be too, where it declares a method. ## Four kinds | Declaration | The compiler supplies | The provider writes | | ------------------------------------ | ------------------------------ | ------------------------------------------ | | `intrinsic type int8;` | the type and its operations | its constants and methods, as plain source | | `intrinsic const Infinity: float64;` | the value | the name and type, in an `extend` block | | `intrinsic #target: Target;` | each field the struct declares | the struct, with the fields it exposes | | `intrinsic func Assert(…);` | the code at every call | the exact signature the compiler expects | A provider package, declaring a little of each: ```rux // A type only the compiler can implement. Its constants are ordinary source. pub intrinsic type int8; extend int8 { pub const Min: int8 = -128i8; pub const Max: int8 = 127i8; } // A constant no literal can spell. pub intrinsic type float64; extend float64 { pub intrinsic const Infinity: float64; } // A compiler-supplied value, exposing only the fields this provider chooses. pub struct Target { pub pointerBits: uint; } pub intrinsic #target: Target; // Functions the compiler emits at each call. pub intrinsic func Assert(condition: bool, message: char8[..]); pub intrinsic func #Error(message: char8[..]); ``` A program that depends on it imports the same names it would otherwise take from `Core`, and works the same way: ```rux import Basis::{ #target, #Error, Assert, float64, int8 }; func Main() -> int { when #target.pointerBits < 64 { #Error("needs a 64-bit target"); } Assert(int8::Max == 127i8, "the provider's constant"); Assert(float64::Infinity > 1.0e308, "a compiler-supplied constant"); return 0; } ``` Only what the provider declares is available: with `Basis` alone, `int16` still exists as a type, but `int16::Max` does not. ## Types `intrinsic type` names an **implemented primitive scalar** in its canonical spelling — `int8` … `int512`, `uint8` … `uint512`, `int`, `uint`, `bool8` … `bool64`, `char8` … `char64`, `float32`, `float64`. Aliases such as `bool` and `byte` are ordinary `type` declarations in `Core`. Anything else is refused: ```text error: 'int7' is not a supported intrinsic scalar type ``` A primitive's representation exists whether or not it is declared; the declaration supplies its *API*. That is why `int8::Max` needs `import Core::int8;` while the type `int8` does not — see [Imports](https://rux-lang.dev/docs/lang/modules/imports#compile-time-values-and-primitive-constants). ## Constants Associated constants are normally plain source (`pub const Max: int8 = 127i8;`). The only compiler-supplied ones are `Infinity` and `NaN` of `float32` and `float64`, declared in an `extend` of their own type: ```text error: intrinsic associated constants support only floating-point Infinity and NaN ``` ## Compile-time values `intrinsic #name: Root;` declares a value the compiler fills in. The type names which value it is — one of the structs `Target`, `Build`, `Source`, `Compiler` or `Config` — and the `#` name is how code refers to it. `Core` uses `#target`, `#build`, `#source`, `#compiler` and `#config`. The struct may declare any subset of the fields the compiler supplies for its root, and nothing else, since nothing would fill a field it does not know: ```text error: field 'wordSize' of 'Target' is not one the compiler supplies for '#target' help: remove 'wordSize'; the compiler supplies 'os', 'arch', 'abi', 'endian', 'pointerBits', 'dataModel', 'objectFormat', 'triple' error: 'Widget' is not a value the compiler supplies for '#thing' help: declare '#thing' with one of 'Target', 'Build', 'Source', 'Compiler', 'Config' ``` The fields themselves are listed in [Context](https://rux-lang.dev/docs/lang/comptime/context). Field visibility is ordinary: a field the provider leaves private cannot be read by another package, in an expression or in a `when` condition. ## Functions `intrinsic func` names a function the compiler implements, so only those it knows are accepted, each with exactly one signature: | Intrinsic | Signature | Purpose | | ---------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `Assert` | `(condition: bool, message: char8[..])` | stop the program unless `condition` holds | | `DebugAssert` | `(condition: bool, message: char8[..])` | the same, removed without debug assertions | | `Panic` | `(message: char8[..])` | stop the program | | `#Error` | `(message: char8[..])` | stop the build — see [Diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics) | | `#Warn` | `(message: char8[..])` | warn during the build | | `CheckedAdd`, `CheckedSub`, `CheckedMul` | `(left: uint64, right: uint64, result: *var uint64) -> bool` | arithmetic that reports overflow (`true` on overflow) | | `Zeroize` | `(memory: *var uint8, length: uint64)` | a clearing write the optimizer may not remove | | `Target.HasFeature` | method `(self: &Target, feature: TargetFeature) -> bool` | [`#target.HasFeature`](https://rux-lang.dev/docs/lang/comptime/context#target) | | `Compiler.HasFeature` | method `(self: &Compiler, feature: char8[..]) -> bool` | [`#compiler.HasFeature`](https://rux-lang.dev/docs/lang/comptime/context#compiler) | | `Config.Get`, `Config.Has` | methods `(self: &Config, name: char8[..]) -> char8[..]` / `-> bool` | [`#config`](https://rux-lang.dev/docs/lang/comptime/config) | A method intrinsic is declared inside an `extend` of its struct, which is what gives it its `Type.Name` key: ```rux extend Target { pub intrinsic func HasFeature(self: &Target, feature: TargetFeature) -> bool; } ``` The diagnostic intrinsics are checked against their signatures, because the compiler emits the call in its own shape: ```text error: 'Square' is not a supported intrinsic function help: give the function a body, or declare a foreign function in an 'extern' block error: intrinsic 'Assert' must be declared as 'func Assert(condition: bool, message: char8[..])' note: parameter 'condition' has type 'int' error: 'intrinsic' function cannot have a body ``` ## Not intrinsics Slices, ranges, optionals, fallibles and sums are native types with no declaration at all. `intrinsic struct` does not exist (`error: intrinsic aggregate declarations have been removed; use native slice or range types`), and an ordinary struct never gains compiler-owned behaviour from its name. An `intrinsic` declaration can only bind what the compiler already implements; it cannot add a feature. ## See also - [Context](https://rux-lang.dev/docs/lang/comptime/context) — the fields of the compile-time values - [Imports](https://rux-lang.dev/docs/lang/modules/imports) — importing `#` names and primitive constants - [Primitive types](https://rux-lang.dev/docs/lang/appendix/primitives) — the constants `Core` declares for each primitive - API: [`Assert`](https://rux-lang.dev/docs/api/core/assert), [`Panic`](https://rux-lang.dev/docs/api/core/panic) · Learn: [Intrinsic](https://rux-lang.dev/docs/learn/intrinsic), [Checked arithmetic](https://rux-lang.dev/docs/learn/checked-arithmetic), [Zeroize](https://rux-lang.dev/docs/learn/zeroize) # Build Diagnostics `#Error("…")` and `#Warn("…")` are messages from the source to whoever builds it. Written as a **directive**, `#Error` stops the build where the compiler reaches it and `#Warn` prints a warning and lets the build go on. Neither produces any code. ```text directive = ( "#Error" | "#Warn" ) "(" string-literal ")" ``` ```rux import Core::{ #target, #Error }; func Main() -> int { when #target.pointerBits < 64 { #Error("this program needs a 64-bit target"); } return 0; } ``` On a build that reaches it, the error points at the directive itself: ```text Src/Main.rux:5:9: error: this program needs a 64-bit target ``` The same names written *above a declaration* are attributes, which fire at every call of that declaration instead — see [`#Error`](https://rux-lang.dev/docs/lang/attributes/error) and [`#Warn`](https://rux-lang.dev/docs/lang/attributes/warn). ## They run after `when` Directives are evaluated after [conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) has chosen its branches, and a branch that is not taken is never looked at — so a directive in it never fires. That is how they are meant to be used: `#Error` in the branch a build should never take, `#Warn` in the branch that deserves a remark. A directive outside any `when` is always reached, so a bare `#Error("…");` in a body stops every build on every target. ## Where they may be written | Position | Form | Import from `Core` | | ------------------------------------------------- | ------------------------- | ------------------ | | a statement in a function body | `#Error("…");` | yes | | a bare-expression arm of a `when` match in a body | `.AArch64 => #Warn("…"),` | yes | | an arm of a `when` match between declarations | `else => #Error("…")` | no | Between declarations, the match arm is the **only** place a directive can stand. Inside the braces of a declaration-level `when` chain, `#Error("…")` is read as the start of an attribute, and the compiler then wants a declaration after it: ```text error: expected a declaration after the attributes before '}' ``` Write the declaration-level check as a match with an `else` arm: ```rux import Core::{ #target }; when #target.os { .FreeBSD, .Linux, .macOS => const Separator: char8[..] = "/"; .Windows => const Separator: char8[..] = "\\"; else => #Error("this package does not know the path separator of this system") } ``` As a statement, a directive is a call to the intrinsic functions `Core` declares (`pub intrinsic func #Error(message: char8[..]);`), so it must be imported; without the import the build fails with `error: name '#Warn' is not defined in this scope`. ## The message The message must be a **string literal** written in place — it is printed while compiling, before any constant or variable has a value at run time. A constant is refused: ```text error: '#Warn' message must be a string literal ``` ::note **A non-literal message in a declaration-level arm.**:br rux 0.4.0 does not report a non-literal message in a declaration-level `when` arm such as `.Windows => #Error(Message)`; the compiler stops responding instead. Keep the message a literal. :: Escapes in the literal work as in any string (`\n`, `\"`, …). Write the message for the person building: say what is unsupported and, where it helps, what to do instead. ## See also - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — choosing the branch a directive sits in - [`#Error`](https://rux-lang.dev/docs/lang/attributes/error) and [`#Warn`](https://rux-lang.dev/docs/lang/attributes/warn) — the attribute forms, reported at each call - [Panics](https://rux-lang.dev/docs/lang/errors/panics) — `Panic` and `Assert`, which stop the running program instead - API: [`#Error`](https://rux-lang.dev/docs/api/core/error), [`#Warn`](https://rux-lang.dev/docs/api/core/warn) · Learn: [Compile error](https://rux-lang.dev/docs/learn/compile-error) # Attributes An **attribute** is a `#Name(...)` call written before what it describes. It instructs the compiler — link a foreign library, fix a calling convention, flag a function at every call — without changing the code of the declaration it annotates. Rux has a fixed set of attributes; a program cannot define new ones. ```text attribute = "#" identifier "(" [ argument { "," argument } ] ")" attributes = { attribute } declaration-with-attributes = attributes [ "pub" ] declaration parameter-with-attributes = attributes parameter ``` Attributes come before `pub`, one after another. The parentheses are always written, even when empty: ```rux import Core::Panic; #NoReturn() #Warn("Abort skips the cleanup that Exit runs") pub func Abort() { Panic("aborted"); } ``` ## The attributes | Attribute | Arguments | Applies to | | ----------------------------------------------------------------- | ------------------------- | --------------------------------------------------------- | | [`#Link`](https://rux-lang.dev/docs/lang/attributes/link) | library, optional symbol | `extern func`, `extern { }` blocks | | [`#Abi`](https://rux-lang.dev/docs/lang/attributes/abi) | `.C`, `.SysV` or `.Win64` | functions, `asm func`, `extern func`, `extern { }` blocks | | [`#NoReturn`](https://rux-lang.dev/docs/lang/attributes/noreturn) | none | functions and `extern func` without a result | | [`#Warn`](https://rux-lang.dev/docs/lang/attributes/warn) | a message string | functions, methods, `extern func` | | [`#Error`](https://rux-lang.dev/docs/lang/attributes/error) | a message string | functions, methods, `extern func` | | [`#Allow`](https://rux-lang.dev/docs/lang/attributes/allow) | a lint rule name | declarations (rule-dependent) | | [`#Format`](https://rux-lang.dev/docs/lang/attributes/format) | none | a `char8[..]` parameter | `#Format` is the one **parameter attribute**; it is written before a parameter inside the parameter list. All the others are declaration attributes, written before a declaration at the package root, inside a `module`, or before a member of an `extend` block. Members of an `extern { }` block take no attributes of their own — the attributes on the block apply to all of them: ```text error: expected 'func' or a variable declaration in the external block before '#' ``` `#Error` and `#Warn` also have a second form, as directives inside a function body or a `when` arm — see [Build diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics). ## Rules Each attribute checks where it stands and what it is given, and the errors name the rule: | Mistake | Error | | ------------------------------------- | ----------------------------------------------------------- | | a name Rux does not have | `unknown attribute call '#Foo'` | | an attribute on the wrong declaration | `'#Abi' can only be applied to a function or extern block` | | the same attribute twice | `duplicate '#Abi' attribute`, `duplicate '#Link' attribute` | | arguments where none are taken | `'#NoReturn' does not accept arguments` | | an unknown parameter attribute | `unknown parameter attribute '#Trim'` | Restricting code to one platform is not an attribute. Use [conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional): `when #target.os == .Windows { … }` removes the other platforms' code before it is ever resolved. ## See also - [Foreign function interface](https://rux-lang.dev/docs/lang/ffi/overview) — where `#Link` and `#Abi` are used - [Build diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics) — `#Error` and `#Warn` as directives - [Functions](https://rux-lang.dev/docs/lang/functions/declaration) — the declarations most attributes annotate - Learn: [Compile error](https://rux-lang.dev/docs/learn/compile-error), [ABI](https://rux-lang.dev/docs/learn/abi) # `#Link` `#Link` names the shared library an [`extern`](https://rux-lang.dev/docs/lang/ffi/overview) declaration is imported from, and optionally the symbol the library exports under a different name. Every extern function needs one. ```text link-attribute = "#Link" "(" library [ "," symbol ] ")" library = string-literal | constant-name symbol = string-literal | constant-name ``` ## Library The first argument is the library's file name as the system loader finds it — `"Kernel32.dll"`, `"libc.so.6"`, `"libSystem.B.dylib"` — not a path. It may be a string literal or the name of a string constant declared in the **same package**; a constant imported from another package is refused: ```text error: '#Link' library name 'CRuntime' is not a compile-time constant ``` A constant lets one [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) choose the library for every declaration that uses it: ```rux import Core::{ #target, #Error }; when #target.os { .FreeBSD => const CRuntime = "libc.so.7"; .Linux => const CRuntime = "libc.so.6"; .macOS => const CRuntime = "libSystem.B.dylib"; .Windows => const CRuntime = "ucrtbase.dll"; else => #Error("unknown C runtime") } #Link(CRuntime) extern func llabs(n: int64) -> int64; ``` ## Symbol By default the imported symbol is the declaration's own name. A second argument binds a different exported name, so the Rux name can follow Rux conventions: ```rux #Link(CRuntime, "abs") extern func AbsoluteValue(n: int32) -> int32; // calls C's abs ``` The symbol may also be a string constant. One symbol cannot stand for every function in a block, so the two-argument form applies only to a single `extern func`: ```text error: an imported symbol name cannot be applied to an extern block; use the one-argument '#Link("library")' form ``` ## On a block The one-argument form on an `extern { }` block applies the library to every member: ```rux #Link(CRuntime) extern { func abs(n: int32) -> int32; func strlen(text: *char8) -> uint; } ``` ## Rules | Mistake | Error | | ----------------------------------------- | --------------------------------------------------------------------------------- | | an `extern func` with no `#Link` | `extern function 'GetTickCount64' must specify a source DLL via #Link("dll.dll")` | | `#Link` on anything but an extern | `'#Link' can only be applied to an extern function or extern block` | | three arguments | `'#Link' accepts at most two arguments` | | a name that is not a string constant here | `'#Link' library name 'Missing' is not a compile-time constant` | | two `#Link`s on one declaration | `duplicate '#Link' attribute` | The compiler cannot see inside the library. On Windows the linker checks that the DLL exports the symbol, and a misspelt name stops the build at linking: ```text error: cannot link PE/COFF executable 'Extern': import function 'GetCurrentProcessID' was not found in DLL 'Kernel32.dll' ``` On the other systems the name is resolved by the dynamic loader when the program is loaded. ## See also - [Foreign function interface](https://rux-lang.dev/docs/lang/ffi/overview) — `extern` declarations - [Linking](https://rux-lang.dev/docs/lang/ffi/linking) — choosing libraries per platform - [`#Abi`](https://rux-lang.dev/docs/lang/attributes/abi) — the calling convention of an extern - Learn: [Extern](https://rux-lang.dev/docs/learn/extern), [ABI](https://rux-lang.dev/docs/learn/abi) # `#Abi` `#Abi` fixes the **calling convention** of a function: which registers and stack slots carry its arguments and result. Between two Rux functions the compiler handles the convention itself, so most code never names one. `#Abi` matters at a border with code the compiler did not generate — a C library calling back into Rux, or an [`asm func`](https://rux-lang.dev/docs/lang/ffi/assembly) whose body reads its arguments from particular registers. ```text abi-attribute = "#Abi" "(" "." ( "C" | "SysV" | "Win64" ) ")" ``` | Argument | Convention | | -------- | -------------------------------------------------------------------------------------------- | | `.C` | the C convention of the target being built | | `.SysV` | System V AMD64: integer arguments in `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`; result in `rax` | | `.Win64` | Microsoft x64: integer arguments in `rcx`, `rdx`, `r8`, `r9`; result in `rax` | `#Abi` applies to a function, an `asm func`, an `extern func` and an `extern { }` block, whose members all take it. Anything else is refused: ```text error: '#Abi' can only be applied to a function or extern block error: unknown ABI '.Fast'; valid ABIs are: .C, .SysV, .Win64 ``` ## Defaults Without `#Abi`, a function, an `asm func` and an `extern` all use the target's C convention — exactly what `#Abi(.C)` names: | Target | Default (`.C`) | | ---------------------------------- | ------------------------------------------------ | | Windows on x86-64 | Win64 | | Linux, macOS and FreeBSD on x86-64 | System V AMD64 | | every system on AArch64 | AAPCS64 (arguments in `x0`–`x7`, result in `x0`) | `.SysV` and `.Win64` name x86-64 conventions. Pinning one makes a function use it on every x86-64 target: the compiler adapts each call, so a Rux caller on Windows passes the arguments of a `#Abi(.SysV)` function in `rdi` and `rsi`. ::note **`.SysV` and `.Win64` on AArch64.**:br AArch64 has one convention, and an x86-64 convention means nothing there. rux 0.4.0 does not yet reject `#Abi(.SysV)` or `#Abi(.Win64)` in an AArch64 build; do not write one on AArch64 code. :: ## A function C calls A function handed to C as a callback must use the convention C uses. Rux functions already do by default, but `#Abi(.C)` writes the promise down — so it stays true, and so the next reader knows the function is called from outside Rux: ```rux import Core::{ #target, #Error }; import Io::PrintLine; when #target.os { .FreeBSD => const CRuntime = "libc.so.7"; .Linux => const CRuntime = "libc.so.6"; .macOS => const CRuntime = "libSystem.B.dylib"; .Windows => const CRuntime = "ucrtbase.dll"; else => #Error("unknown C runtime") } #Link(CRuntime) extern func qsort(base: *var opaque, count: uint, size: uint, compare: func(*opaque, *opaque) -> int32); // Called by C, so it promises C's convention. #Abi(.C) func Descending(left: *opaque, right: *opaque) -> int32 { let a = *(left as *int32); let b = *(right as *int32); return a > b ? -1i32 : a < b ? 1i32 : 0i32; } func Main() -> int { var values: int32[5] = [3, 9, 1, 7, 5]; qsort(@values[0] as *var opaque, values.length, sizeof(int32), Descending); PrintLine("{} {} {} {} {}", values[0], values[1], values[2], values[3], values[4]); // 9 7 5 3 1 return 0; } ``` ## A wrong convention is not an error The compiler cannot see what the other side expects. Pin `Descending` to `#Abi(.SysV)` and build for Windows: `qsort` passes the two addresses in `rcx` and `rdx`, the function reads `rdi` and `rsi`, and it compares whatever was there. Nothing reports it. The same holds for an `asm func` body written for one convention and pinned to another. Use `.C` for anything C calls, and pin an `asm func` to the convention its body was written for. ## See also - [Assembly](https://rux-lang.dev/docs/lang/ffi/assembly) — bodies written against a convention - [Foreign function interface](https://rux-lang.dev/docs/lang/ffi/overview) — extern declarations and function pointers - [Compile-time context](https://rux-lang.dev/docs/lang/comptime/context#target) — `#target.abi`, the target's own convention - Learn: [ABI](https://rux-lang.dev/docs/learn/abi), [Assembly](https://rux-lang.dev/docs/learn/asm) # `#NoReturn` `#NoReturn()` declares that a function never returns to its caller: it ends the program, loops forever, or transfers control somewhere else for good. The compiler uses that to check control flow — code after a call is unreachable, and a call can end a path that would otherwise need a `return`. ```text noreturn-attribute = "#NoReturn" "(" ")" ``` It applies to a function and to an `extern func`, and takes no arguments. A function that never returns has no result to declare: ```text error: '#NoReturn' function cannot declare a return type error: '#NoReturn' can only be applied to a function error: '#NoReturn' does not accept arguments ``` ## Ending a path A function with a result must return a value on every path. A call to a `#NoReturn` function counts as the end of its path: ```rux import Core::Panic; import Io::PrintLine; #NoReturn() func Fail(message: char8[..]) { PrintLine("fatal: {}", message); Panic(message); } func Digit(c: char8) -> int { if c >= c8'0' && c <= c8'9' { return (c - c8'0') as int; } Fail("not a digit"); // no return needed after this } ``` Without the attribute on `Fail`, `Digit` fails with `function 'Digit' must return a value of type 'int' on every control-flow path`. `Panic` itself is declared in `Core` as an intrinsic that never returns, and an `extern` binding of a C function such as `abort` or `exit` takes the attribute the same way: ```rux #NoReturn() #Link(CRuntime) extern func abort(); ``` An `extern { }` block cannot carry `#NoReturn` for its members; declare such a function on its own. ## The body must not return A `return` in a `#NoReturn` function is an error: ```text error: return is not allowed in a '#NoReturn' function ``` The body has to end by calling something that does not return either — `Panic`, another `#NoReturn` function, or an endless `loop`. ::note **Falling off the end.**:br A `#NoReturn` body whose last statement can complete — one that just stops after a `PrintLine` — must not reach its closing brace. rux 0.4.0 does not yet check this, and a call that does come back continues into code the compiler marked unreachable. :: ## See also - [Panics](https://rux-lang.dev/docs/lang/errors/panics) — `Panic` and `Assert` - [Functions](https://rux-lang.dev/docs/lang/functions/declaration) — results and the every-path rule - [Foreign function interface](https://rux-lang.dev/docs/lang/ffi/overview) — extern declarations - API: [`Panic`](https://rux-lang.dev/docs/api/core/panic) # `#Warn` `#Warn("message")` before a declaration makes the compiler report a **warning at every call** of it. The declaration stays usable and the build carries on; the warning points at the call, not at the declaration. It is the way to deprecate a function: callers see the message, with the replacement named, every time they build. ```text warn-attribute = "#Warn" "(" string-literal ")" ``` ```rux import Io::PrintLine; #Warn("Average rounds toward zero; call AverageRounded instead") func Average(total: int, count: int) -> int { return total / count; } func Main() -> int { PrintLine("{}", Average(7, 2)); return 0; } ``` ```text Src/Main.rux:9:21: warning: Average rounds toward zero; call AverageRounded instead ``` The message must be a string literal (`expected a message string in '#Warn'` otherwise). As an attribute, `#Warn` needs no import. ## What it applies to `#Warn` fires at calls of a function, a method in an `extend` block, and an `extern func`. ::note **Other declarations.**:br A `#Warn` on a struct, a constant, a type alias or an enum, or on a constructor, is accepted but rux 0.4.0 does not yet report it at their uses; neither does it report a function used as a value rather than called. Put the attribute on the functions callers actually call. :: ## The directive form Written as a statement in a body, or as a `when` arm, `#Warn("…")` is a directive that warns where it stands instead — see [Build diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics). ## See also - [`#Error`](https://rux-lang.dev/docs/lang/attributes/error) — refuse every call instead of warning - [Build diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics) — the directive form - [Attributes](https://rux-lang.dev/docs/lang/attributes/overview) — the full set - Learn: [Compile error](https://rux-lang.dev/docs/learn/compile-error) # `#Error` `#Error("message")` before a declaration makes **every call** of it a compile error with that message. The declaration itself compiles; only its use is refused, and the error points at the call. It lets a function that was removed, or that cannot work on this build, explain what to call instead rather than simply vanishing. ```text error-attribute = "#Error" "(" string-literal ")" ``` ```rux #Error("Connect was removed; call Open instead") func Connect() -> int { return 0; } func Main() -> int { let k = Connect(); return 0; } ``` ```text Src/Main.rux:7:13: error: Connect was removed; call Open instead ``` A declaration nobody calls produces nothing. The message must be a string literal, and as an attribute `#Error` needs no import. ## Per-platform stubs Combined with [`when`](https://rux-lang.dev/docs/lang/comptime/conditional), the attribute turns a missing implementation into a clear message at the call, instead of a missing name or a link failure: ```rux import Core::{ #target }; when #target.os == .Windows { func OpenConsole() -> bool { return true; // the real Windows implementation } } else { #Error("OpenConsole is available on Windows only") func OpenConsole() -> bool { return false; } } ``` Every other target still compiles the package; only a program that calls `OpenConsole` there is refused. ## What it applies to Like [`#Warn`](https://rux-lang.dev/docs/lang/attributes/warn), `#Error` fires at calls of a function, a method, or an `extern func`. ::note **Other declarations.**:br An `#Error` on a struct, a constant, a type alias, an enum or a constructor is accepted, but rux 0.4.0 does not yet report it at their uses, nor at a function used as a value rather than called. :: ## The directive form Written as a statement in a body or as a `when` arm, `#Error("…")` is a directive that stops the build wherever it is reached — see [Build diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics). ## See also - [`#Warn`](https://rux-lang.dev/docs/lang/attributes/warn) — report every call without refusing it - [Build diagnostics](https://rux-lang.dev/docs/lang/comptime/diagnostics) — the directive form - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — per-platform declarations - Learn: [Compile error](https://rux-lang.dev/docs/learn/compile-error) # `#Allow` `#Allow("rule")` switches off one [`rux lint`](https://rux-lang.dev/docs/cli/lint) rule for the declaration below it, and nowhere else. It is for a declaration with a deliberate reason to break a convention — most often a name that must match an outside contract, such as a C struct or a C macro. ```text allow-attribute = "#Allow" "(" string-literal ")" ``` ## Rules | Rule | `rux lint` reports | `#Allow` applies to | | ---------------- | ------------------------------------------------------------------------ | --------------------- | | `"naming.type"` | a struct, enum, variant, union or type alias name that is not PascalCase | type declarations | | `"naming.const"` | a constant name that is not PascalCase | constant declarations | | `"docs.missing"` | a public declaration without a `///` or `/** … */` comment | any declaration | ```rux // Mirrors the C struct, so it keeps C's spelling. #Allow("naming.type") struct timespec { seconds: int64; nanoseconds: int64; } // A C macro's own name, which a reader porting code will look for. #Allow("naming.const") const SEEK_SET: int32 = 0; ``` Without the attributes, `rux lint` reports: ```text warning: struct name 'timespec' should be PascalCase help: rename it to 'Timespec' warning: constant name 'SEEK_SET' should be PascalCase help: rename it to 'SeekSet' ``` One `#Allow` names one rule; stack several for several rules. The rule names are checked when the source is compiled, not only when it is linted, so a typo cannot silence nothing: | Mistake | Error | | --------------------------- | -------------------------------------------------------------------------------------------- | | an unknown rule | `unknown lint rule 'naming.konst'; valid rules are: naming.type, naming.const, docs.missing` | | `naming.type` on a function | `'#Allow("naming.type")' can only be applied to a type declaration` | | `naming.const` on a struct | `'#Allow("naming.const")' can only be applied to a constant declaration` | | the same rule twice | `duplicate '#Allow("naming.const")' attribute` | | two rules in one attribute | `'#Allow' accepts exactly one argument` | ## See also - [`rux lint`](https://rux-lang.dev/docs/cli/lint) — the checks these rules belong to - [Identifiers](https://rux-lang.dev/docs/lang/lexical/identifiers) — the naming conventions - Learn: [Compile error](https://rux-lang.dev/docs/learn/compile-error), [Tooling](https://rux-lang.dev/docs/learn/tooling) # `#Format` `#Format()` is the one attribute written before a **parameter**. It marks a `char8[..]` parameter as a format string, followed by a [variadic](https://rux-lang.dev/docs/lang/functions/parameters) parameter whose arguments fill its placeholders. When a call passes a string literal for it, the compiler counts the placeholders against the arguments and refuses a mismatch while compiling, rather than leaving it to fail at run time. ```text format-parameter = "#Format" "(" ")" identifier ":" "char8[..]" ``` The standard printing functions are declared this way: ```rux pub func PrintLine(#Format() format: char8[..], args: Display...) -> IoError? ``` A function of your own takes it just as well; the compiler recognizes no package or function by name, only the attribute: ```rux import Io::PrintLine; import Format::Display; func Log(level: int, #Format() format: char8[..], args: Display...) { PrintLine(format, args...); } func Main() -> int { Log(1, "{} of {}", 3, 4); // 3 of 4 Log(1, "{{}} {:x}", 255); // {} ff return 0; } ``` ## Placeholders A placeholder is `{}` or `{:spec}`; `{{` and `}}` are literal braces and count for nothing. A call whose literal has a different number of placeholders than arguments is an error: ```text error: format string has 2 placeholders, but 1 argument was provided note: format parameter 'format' of 'Log' declared at 'Src/Main.rux':4:32 help: pass one argument for each '{}' placeholder ``` Only a literal can be counted. A format held in a variable, a spread argument such as `args...`, and a spec the formatter itself would reject are left to the formatter's check at run time. ## Rules | Mistake | Error | | -------------------------------- | ----------------------------------------------------------------------- | | no variadic parameter after it | `'#Format' parameter 'format' must be followed by a variadic parameter` | | on the variadic parameter itself | `'#Format' cannot be applied to variadic parameter 'args'` | | on two parameters | `'#Format' is already applied to parameter 'a'` | | on the receiver | `'#Format' cannot be applied to the receiver 'self'` | | with an argument | `'#Format' does not accept arguments` | | another name before a parameter | `unknown parameter attribute '#Trim'` | A function has at most one format string, and the variadic parameter it counts against must be the last parameter. ## See also - [Parameters](https://rux-lang.dev/docs/lang/functions/parameters) — variadic parameters and spreads - [Attributes](https://rux-lang.dev/docs/lang/attributes/overview) — the declaration attributes - Learn: [Format](https://rux-lang.dev/docs/learn/format), [Variadic](https://rux-lang.dev/docs/learn/variadic) # Foreign Function Interface An `extern` declaration names a function compiled outside Rux — in the operating system or a C library — and gives its signature, with no body. Once declared, it is called like any other function. The compiler takes the declaration on trust: it cannot look inside the library, so a declaration that does not match the real function is not an error but a program that misbehaves. ```text extern-decl = attributes [ "pub" ] "extern" ( extern-func | extern-var | extern-block ) extern-func = "func" identifier "(" [ parameters [ "," "..." ] ] ")" [ "->" type ] ";" extern-var = identifier ":" type ";" extern-block = "{" { [ "pub" ] ( extern-func | extern-var ) } "}" ``` Every extern function needs a [`#Link`](https://rux-lang.dev/docs/lang/attributes/link) naming its library; [Linking](https://rux-lang.dev/docs/lang/ffi/linking) covers choosing it per platform. Its calling convention is the target's C convention unless [`#Abi`](https://rux-lang.dev/docs/lang/attributes/abi) says otherwise. ## Declarations A single function: ```rux #Link("Kernel32.dll") extern func GetCurrentProcessId() -> uint32; ``` A block groups declarations from one library, with the attributes written once. Inside a block, members are written without `extern`, may each be `pub`, and take no attributes of their own: ```rux #Link(CRuntime) extern { func strlen(text: *char8) -> uint; func malloc(size: uint) -> *var opaque; func free(memory: *var opaque); } ``` An `extern` variable, `extern Name: Type;` or `Name: Type;` inside a block, declares data defined outside the program. ::note **Extern variables.**:br rux 0.4.0 accepts extern variable declarations but cannot yet read or write one: a use fails with `cannot determine the type of this expression`. Reach foreign data through a function the library provides instead. :: ## Example The C runtime, called directly — a formatted write, a struct returned by value, and memory from `malloc`: ```rux import Core::{ #target, #Error }; import Io::PrintLine; when #target.os { .FreeBSD => const CRuntime = "libc.so.7"; .Linux => const CRuntime = "libc.so.6"; .macOS => const CRuntime = "libSystem.B.dylib"; .Windows => const CRuntime = "msvcrt.dll"; else => #Error("unknown C runtime") } // div_t: two C ints, in C's field order. struct Div { quot: int32; rem: int32; } #Link(CRuntime) extern { func sprintf(buffer: *var char8, format: *char8, ...) -> int32; func div(numerator: int32, denominator: int32) -> Div; func malloc(size: uint) -> *var opaque; func free(memory: *var opaque); } func Main() -> int { var buffer: char8[64]; let written = sprintf(@buffer[0], "%s has %d legs".data, "spider".data, 8i32); PrintLine("{}", buffer[..written as uint]); // spider has 8 legs let d = div(17i32, 5i32); PrintLine("{} r {}", d.quot, d.rem); // 3 r 2 let memory = malloc(16) as *var char8; if memory == null { return 1; } free(memory as *var opaque); return 0; } ``` ## Type mapping A C declaration translates type by type. Rux's `int` is 64 bits, so C's `int` is `int32`, never `int`: | C | Rux | | ------------------------------------------------------------ | ---------------------------------------------------------------------------- | | `int`, `unsigned int` | `int32`, `uint32` | | `short`, `long long`, `int64_t`, … | the integer of the same width | | `long` | `int32` on Windows, `int64` elsewhere — choose with `when #target.dataModel` | | `size_t`, `uintptr_t` | `uint` | | `double`, `float` | `float64`, `float32` | | `char` (as a byte) | `char8` or `int8`/`uint8` | | `const T *` | `*T` | | `T *` written through | `*var T` | | `void *` | `*opaque` or `*var opaque` | | a pointer to a struct the code never looks inside (`FILE *`) | a pointer to an empty struct, or `*opaque` | | `struct S` by value | a Rux `struct` with the same fields in the same order | | a function pointer | a function type: `func(*opaque, *opaque) -> int32` | | `void` result | no `->` | The `C` package declares aliases named after C's types — `c_int`, `c_long`, `size_t`, `time_t` — that resolve to the right width on each target, and binds much of the C runtime; prefer it to hand-written declarations. Struct layout follows the C rules, so a struct declared field for field matches C — see [Layout](https://rux-lang.dev/docs/lang/memory/layout). ## Strings C finds the end of a string by its zero byte. A Rux string literal keeps a NUL after its last unit — not counted by `.length` — so a literal's `.data` is a valid C string: ```rux let greeting = "hello"; let n = strlen(greeting.data); // 5, the same as greeting.length ``` That holds for **literals**. A slice cut from the middle of a string, or text built at run time, has no NUL after it unless the program writes one; handing its `.data` to C makes C read past the end. Passing a slice where C wants an address is a type error: ```text error: argument 1 to 'strlen' has type 'char8[..]', but parameter 'text' requires '*char8' ``` ## Pointers Arguments that C reads or writes through are [pointers](https://rux-lang.dev/docs/lang/pointers/overview): `@value` for one variable, `@array[0]` for the first element of a buffer, `.data` for a slice. A result C returns as `void *` is converted with `as` to the type it really points to, and checked for `null` — C reports failure with a sentinel, never with a panic or a fallible: | Function | Success | Failure | | --------- | ----------------- | ---------------- | | `malloc` | an address | `null` | | `sprintf` | the bytes written | a negative count | ## Variadic functions A trailing `...` declares a C variadic function. Any number of arguments of any type may follow the fixed ones, and **nothing** checks them: pass each at exactly the type the callee will read — `8i32` for `%d`, a `float64` for `%f`, `.data` for `%s`. A wrong one builds and prints garbage. Rux can call a C variadic function but not define one; a Rux variadic parameter (`args: T...`) is a different, type-checked feature — see [Parameters](https://rux-lang.dev/docs/lang/functions/parameters). ## Callbacks A Rux function can be passed where C expects a function pointer, by naming it. C then calls it with C's convention, which a Rux function uses by default; mark it [`#Abi(.C)`](https://rux-lang.dev/docs/lang/attributes/abi) to keep that promise explicit. The `qsort` example on that page shows the whole round trip. ## See also - [Linking](https://rux-lang.dev/docs/lang/ffi/linking) — `#Link` and per-platform libraries - [Assembly](https://rux-lang.dev/docs/lang/ffi/assembly) — writing the machine code yourself - [Pointers](https://rux-lang.dev/docs/lang/pointers/overview) — `*T`, `*var T` and `*opaque` - API: [C package](https://rux-lang.dev/docs/api/c) · Learn: [Extern](https://rux-lang.dev/docs/learn/extern), [C interop](https://rux-lang.dev/docs/learn/c-interop), [ABI](https://rux-lang.dev/docs/learn/abi) # Linking Libraries An [`extern`](https://rux-lang.dev/docs/lang/ffi/overview) declaration says *what* a foreign function looks like; [`#Link`](https://rux-lang.dev/docs/lang/attributes/link) says *where* it lives. The Rux linker records the shared library (`.dll`, `.so`, `.dylib`) and the symbol in the executable's import tables, and the system loader finds the library when the program starts. ```rux #Link("Kernel32.dll") extern func GetCurrentProcessId() -> uint32; // the library alone #Link("Kernel32.dll", "GetTickCount64") extern func Milliseconds() -> uint64; // and the exported symbol #Link("Kernel32.dll") extern { // every member of a block func GetCurrentThreadId() -> uint32; } ``` | Form | Library | Symbol | On | | ------------------------ | ------- | -------------------------- | --------------------------- | | `#Link("lib")` | `lib` | the declaration's own name | `extern func`, `extern { }` | | `#Link("lib", "symbol")` | `lib` | `symbol` | `extern func` only | Either argument may be a string literal or the name of a string constant declared in the same package. An `extern func` without `#Link` is an error on every target (`extern function 'GetTickCount64' must specify a source DLL via #Link("dll.dll")`). ## One library per platform A library's name differs from system to system — the C runtime alone has four. Declare its name once, as a constant chosen by [`when`](https://rux-lang.dev/docs/lang/comptime/conditional), and link against the constant: ```rux import Core::{ #target, #Error }; when #target.os { .FreeBSD => const CRuntime = "libc.so.7"; .Linux => const CRuntime = "libc.so.6"; .macOS => const CRuntime = "libSystem.B.dylib"; .Windows => const CRuntime = "ucrtbase.dll"; else => #Error("unknown C runtime") } #Link(CRuntime) extern { func abs(n: int32) -> int32; func strlen(text: *char8) -> uint; } ``` | System | C runtime | | ------- | ------------------- | | Windows | `ucrtbase.dll` | | Linux | `libc.so.6` | | macOS | `libSystem.B.dylib` | | FreeBSD | `libc.so.7` | On Windows the formatted-output family (`printf`, `sprintf`, …) is not exported by `ucrtbase.dll`, which implements it as header inlines; bind those from `msvcrt.dll`. The constant must be declared in the package that links against it; a constant imported from another package — even the `C` package's own `CRuntime` — is refused with `'#Link' library name 'CRuntime' is not a compile-time constant`. ## Functions that exist on one system only When a function exists on one system only, declare it only there, and give the other targets either another implementation or an explanation. A missing branch is better than a program that builds and then cannot find its library: ```rux import Core::{ #target }; when #target.os { .Windows => { #Link("Kernel32.dll") extern func GetTickCount64() -> uint64; func Uptime() -> uint64 { return GetTickCount64() / 1000; } }, else => { #Error("Uptime is implemented for Windows only") func Uptime() -> uint64 { return 0; } } } ``` The declarations in an untaken branch are never resolved, so a library that does not exist on the target never reaches the linker. The [`#Error`](https://rux-lang.dev/docs/lang/attributes/error) attribute makes a call to `Uptime` on any other system a compile error that says why. ## Renaming a symbol The second argument binds a Rux name to an export with a different name — a terse C name to a readable one, or one entry point to two Rux signatures: ```rux #Link(CRuntime, "abs") extern func AbsoluteValue(n: int32) -> int32; ``` `AbsoluteValue(-7)` calls the library's `abs`. One symbol cannot stand for every member of a block, so this form applies to a single `extern func`. ## What is checked The compiler cannot open the library. Building for Windows, the linker does check that the named DLL exports each symbol, and a misspelt name stops the build: ```text error: cannot link PE/COFF executable 'Extern': import function 'GetCurrentProcessID' was not found in DLL 'Kernel32.dll' ``` For Linux, macOS and FreeBSD the name is resolved by the dynamic loader when the program is loaded. Neither check covers the signature: parameter and result types are taken on trust everywhere. `rux build --target ` links for any of the eight targets from any host, so every branch of a per-platform `when` can be linked without the other systems at hand. ## See also - [Foreign function interface](https://rux-lang.dev/docs/lang/ffi/overview) — extern declarations and C types - [`#Link`](https://rux-lang.dev/docs/lang/attributes/link) — the attribute reference - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — per-platform declarations - Learn: [Extern](https://rux-lang.dev/docs/learn/extern), [ABI](https://rux-lang.dev/docs/learn/abi) # Assembly Functions An `asm func` is a function whose body is processor instructions instead of statements. Its signature is ordinary Rux, and callers call it like any function; its body is assembled by the compiler's own assembler and emitted exactly as written — no prologue, no epilogue, no checks. Use one for an instruction the language cannot express, a system-call stub, or exact control over a hot loop, and expect to manage registers, the stack and the calling convention yourself. ```text asm-func = attributes [ "pub" ] "asm" "func" identifier "(" [ parameters ] ")" [ "->" type ] "{" { asm-item } "}" asm-item = label ":" | mnemonic [ operand { "," operand } ] ``` `asm` is not a keyword; it means this only directly before `func`. Instructions are separated by nothing but whitespace — one per line by convention — and `//` and `/* … */` comments are allowed. Mnemonics and register names are case-insensitive. ## The body works on registers The parameters fix the signature and the convention callers use, but they are **not names inside the body**. The arguments are in whatever registers the convention puts them in, and the result goes where the convention says: ```rux // Win64: the first two integers arrive in rcx and rdx; the result goes back in rax. #Abi(.Win64) asm func Add(a: int64, b: int64) -> int64 { mov rax, rcx add rax, rdx ret } ``` Naming a parameter is an operand error (`add rax, b` fails with `unsupported operands for 'add'`). Nothing is added around the body, not even the `ret`: leave it out and execution runs on into whatever bytes follow. Preserving callee-saved registers and keeping the stack aligned across a `call` are the body's job. ## Choosing the convention Without [`#Abi`](https://rux-lang.dev/docs/lang/attributes/abi) an `asm func` uses the target's C convention, so a body that reads `rcx` is right on Windows and wrong on Linux. Pin every x86-64 body to the convention it was written for — `#Abi(.Win64)` or `#Abi(.SysV)` — and it works on every x86-64 system, because the compiler adapts each call to it: | Convention | Integer arguments | Float arguments | Result | | ---------- | -------------------------------------- | --------------- | -------------- | | Win64 | `rcx`, `rdx`, `r8`, `r9` | `xmm0`–`xmm3` | `rax` / `xmm0` | | System V | `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9` | `xmm0`–`xmm7` | `rax` / `xmm0` | | AAPCS64 | `x0`–`x7` | `d0`–`d7` | `x0` / `d0` | AArch64 has the one convention on every system, so its bodies carry no `#Abi`. ## Labels A label is a name followed by `:`, and jumps and branches name it. Labels belong to their function, so two functions may both use `next:`: ```rux // The sum 1 + 2 + … + n, as a label and a conditional jump. #Abi(.Win64) asm func SumTo(n: int64) -> int64 { xor rax, rax next: test rcx, rcx jle done add rax, rcx dec rcx jmp next done: ret } ``` A name that is neither a register nor a label is a **symbol**: another function, which `call` and `jmp` reach through a relocation the linker resolves. ::note **Undefined symbols.**:br rux 0.4.0 does not yet report a symbol that names nothing — a misspelt label, or a `const`, which has no storage — and the executable it produces fails as soon as it starts. Check every symbol an asm body names. :: ## One architecture per body An asm body belongs to one architecture, and the compiler checks every mnemonic against the target's: ```text error: 'csel' is an AArch64 instruction, but asm func 'A' is compiled for x86-64 ``` A package that supports both architectures writes one body per architecture and lets [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) keep the right one. The untaken body is parsed but never assembled: ```rux import Core::{ #target, #Error }; when #target.arch { .X86_64 => { #Abi(.SysV) asm func Add(a: int64, b: int64) -> int64 { mov rax, rdi add rax, rsi ret } }, .AArch64 => { asm func Add(a: int64, b: int64) -> int64 { add x0, x0, x1 ret } }, else => #Error("no assembly for this architecture") } ``` Mnemonic and operand checks run when the body is **assembled**, which `rux build` does and `rux check` does not. Build for each architecture — `rux build --target linux-aarch64` works on any host — to have every body assembled. ## x86-64 Intel syntax: the destination is the first operand. | Operand | Written as | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | register | `rax`…`r15`, `eax`…`r15d`, `ax`…`r15w`, `al`…`r15b` (with `spl`, `bpl`, `sil`, `dil`), `ah`, `bh`, `ch`, `dh`, `xmm0`…`xmm15` | | immediate | `42`, `-1`, `0x10`, `0o7`, `0b1010`, `1_000` | | memory | `[base + index*scale ± disp]`, any part optional, `scale` 1, 2, 4 or 8; a size prefix `byte`, `word`, `dword` or `qword` where the width is otherwise unclear | | symbol | a function name, as in `call Helper` | ```rux #Abi(.Win64) asm func Index(values: *int64, i: int64) -> int64 { mov rax, qword [rcx + rdx*8] ret } ``` The assembler encodes a chosen subset of x86-64: | Family | Instructions | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | data movement | `mov`, `movzx`, `movsx`, `movsxd`, `lea`, `push`, `pop` | | integer arithmetic | `add`, `adc`, `sub`, `sbb`, `inc`, `dec`, `neg`, `mul`, `imul`, `div`, `idiv`, `cmp` | | logic, shifts, rotates | `and`, `or`, `xor`, `not`, `test`, `shl`, `sal`, `shr`, `sar`, `rol`, `ror` (count an immediate or `cl`) | | sign extension | `cdq`, `cqo`, `cdqe` | | control flow | `jmp`, `jcc` (`je`, `jne`, `jl`, `jge`, `jb`, `ja`, …), `setcc` (`sete`, `setl`, …), `call`, `ret`, `leave`, `nop` | | system | `syscall`, `int`, `int3` | | SSE moves | `movd`, `movq`, `movss`, `movsd`, `movaps`, `movapd`, `movups`, `movupd` | | SSE arithmetic | `add`, `sub`, `mul`, `div`, `min`, `max`, `sqrt` in `ss`, `sd`, `ps` and `pd` forms (`addsd`, `sqrtps`, …) | | SSE compare and convert | `comiss`, `comisd`, `ucomiss`, `ucomisd`, `cvtsi2ss`, `cvtsi2sd`, `cvtss2si`, `cvtsd2si`, `cvttss2si`, `cvttsd2si`, `cvtss2sd`, `cvtsd2ss` | | SSE bitwise and integer | `andps`, `andpd`, `andnps`, `andnpd`, `orps`, `orpd`, `xorps`, `xorpd`, `pand`, `por`, `pxor`, `paddb`/`w`/`d`/`q`, `psubb`/`w`/`d`/`q`, `pmullw` | Other x86-64 instructions — `cmovcc`, `popcnt`, `bsf`, `cpuid`, `xchg`, the string instructions, the x87 stack and more — are recognized but not encoded: ```text error: instruction 'popcnt' is recognized for target 'windows-x86_64' but is not implemented by its assembler note: this is an internal compiler limitation, not malformed inline assembly ``` A misspelling is answered with the nearest real name: `unknown instruction 'movx'; did you mean 'mov'?`. ## AArch64 Most instructions take a destination and two sources (`add x0, x0, x1`), and an immediate is written with `#`. | Operand | Written as | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | register | `x0`–`x30`, `w0`–`w30`, `xzr`, `wzr`, `sp`, `wsp`, `fp` (`x29`), `lr` (`x30`); `b`, `h`, `s`, `d`, `q` `0`–`31` for the vector registers | | immediate | `#1`, `#-1`, `#0xF`; a shift or extend after a comma: `#0x1234, lsl #16`, `x1, lsl #3` | | memory | `[x0]`, `[x0, #8]`, `[x0, x1]`, `[x0, x1, lsl #3]`, `[x0, w1, sxtw #2]`; pre-index `[sp, #-16]!`; post-index `[sp], #16` | | condition | as a branch suffix, `b.lt` or `blt`; or as an operand, `csel x0, x0, x1, gt` | | symbol | a function name, as in `bl Twice` | ```rux asm func Max(a: int64, b: int64) -> int64 { cmp x0, x1 csel x0, x0, x1, gt ret } asm func Element(values: *int32, i: int32) -> int64 { ldrsw x0, [x0, w1, sxtw #2] ret } asm func Twice(x: int64) -> int64 { lsl x0, x0, #1 ret } asm func TwiceThenAddOne(x: int64) -> int64 { stp x29, x30, [sp, #-16]! mov x29, sp bl Twice add x0, x0, #1 ldp x29, x30, [sp], #16 ret } ``` A body that calls another function must save and restore `x30`, the link register, as `TwiceThenAddOne` does; AAPCS64 also gives no red zone, so a body opens its own stack space before storing below `sp`. | Family | Instructions | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | arithmetic | `add`, `adds`, `sub`, `subs`, `neg`, `negs`, `cmp`, `cmn`, `mul`, `madd`, `msub`, `mneg`, `smull`, `umull`, `smulh`, `umulh`, `smaddl`, `umaddl`, `sdiv`, `udiv` | | logic and bits | `and`, `ands`, `orr`, `orn`, `eor`, `eon`, `bic`, `bics`, `mvn`, `tst`, `lsl`, `lsr`, `asr`, `ror` (and `…v` forms), `clz`, `cls`, `rbit`, `rev`, `rev16`, `rev32`, `extr`, `bfi`, `bfxil`, `bfm`, `sbfm`, `ubfm`, `sbfx`, `ubfx`, `sxtb`, `sxth`, `sxtw`, `uxtb`, `uxth` | | moves | `mov`, `movz`, `movn`, `movk`, `adr`, `adrp`, `mrs`, `msr` | | conditional | `csel`, `csinc`, `csinv`, `csneg`, `cset`, `csetm`, `cinc`, `cinv`, `cneg` | | memory | `ldr`, `ldrb`, `ldrh`, `ldrsb`, `ldrsh`, `ldrsw`, `ldur…`, `str`, `strb`, `strh`, `stur…`, `ldp`, `stp` | | branches | `b`, `b.cond`, `bl`, `br`, `blr`, `ret`, `cbz`, `cbnz`, `tbz`, `tbnz` | | system | `svc`, `brk`, `hlt`, `hint`, `nop`, `dmb`, `dsb`, `isb`, `udf` | | floating point | `fadd`, `fsub`, `fmul`, `fdiv`, `fsqrt`, `fabs`, `fneg`, `fmin`, `fmax`, `fminnm`, `fmaxnm`, `fmadd`, `fmsub`, `fnmadd`, `fnmsub`, `fcmp`, `fcmpe`, `fccmp`, `fcsel`, `fmov`, `fcvt`, `fcvtzs`, `fcvtzu`, `scvtf`, `ucvtf`, `frinta`, `frintm`, `frintn`, `frintp`, `frintz` | `fmov` takes registers only; a floating-point immediate such as `#0.5` is not accepted. Instructions outside this set — atomics, `ccmp`, `prfm`, the cache and TLB maintenance instructions and others — are recognized but not encoded, as on x86-64. Operand mistakes name the form the instruction takes: ```text error: 'add' takes 3 operands, found 2; the form is 'ADD Rd, Rn, #imm | Rd, Rn, Rm{, shift #amount}' ``` ::note **`add` with a symbol.**:br An AArch64 `add` whose third operand is a name that is not a register is not yet rejected by rux 0.4.0 — `add x0, x0, banana` assembles. Write registers and `#` immediates only. :: ## See also - [`#Abi`](https://rux-lang.dev/docs/lang/attributes/abi) — the conventions a body is written against - [Conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — per-architecture bodies - [Compile-time context](https://rux-lang.dev/docs/lang/comptime/context#target) — `#target.arch` - [RCU](https://rux-lang.dev/docs/lang/appendix/rcu) — how an assembled body and its relocations are stored - Learn: [Assembly](https://rux-lang.dev/docs/learn/asm), [ARM assembly](https://rux-lang.dev/docs/learn/asm-arm) # Layout Every type has a **size**, the number of bytes one value occupies, and an **alignment**: a value of the type must sit at an address that is a multiple of it. The compiler fixes both for every type, and two operators report them. ```text size-query = "sizeof" "(" type ")" alignment-query = "alignof" "(" type ")" ``` ```rux let bytes = sizeof(int64) * 4; // 32 let align = alignof(float32); // 4 ``` ## `sizeof` and `alignof` `sizeof(T)` and `alignof(T)` take a type, not a value, and produce a `uint64` that is known at compile time. They can initialise a [constant](https://rux-lang.dev/docs/lang/bindings/constants) and appear in a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) condition, and they are the way to compute an allocation's size: multiply a count by `sizeof(T)`, never by a number written down. ```rux const Record: uint64 = sizeof(int64) * 4; func Main() -> int { when sizeof(int) == 8 { PrintLine("int is 64-bit on this target"); } return 0; } ``` A type whose layout is not known has no size — a [flexible tail](https://rux-lang.dev/docs/lang/arrays/overview#flexible-tails) on its own, for one: ```text error: cannot determine the size of type 'uint8[]' note: 'sizeof' needs a type whose layout is known at compile time ``` ::note **Sizes as array lengths.**:br A constant computed from `sizeof` or `alignof` is meant to be usable wherever a compile-time integer is required. rux 0.4.0 does not yet accept one as an array length or a repeat count: `uint8[sizeof(int64)]` fails with `array length must be a non-negative compile-time integer`. Write the length as a literal or a constant that does not use them. :: ## Primitive types Every supported target is 64-bit, so these values are the same on all of them. Treat them as facts about the target, nonetheless, and ask `sizeof` and `alignof` rather than writing them into a program. | Type | Size | Alignment | | --------------------------------------------------------------- | ---- | --------- | | `bool8`, `int8`, `uint8`, `char8` | 1 | 1 | | `bool16`, `int16`, `uint16`, `char16` | 2 | 2 | | `bool32`, `int32`, `uint32`, `char32`, `float32` | 4 | 4 | | `bool64`, `int64`, `uint64`, `char64`, `float64`, `int`, `uint` | 8 | 8 | | `int128`, `uint128` | 16 | 8 | | `int256`, `uint256` | 32 | 8 | | `int512`, `uint512` | 64 | 8 | | `*T`, `*var T`, `&T`, `&var T`, a function value | 8 | 8 | | `()` | 0 | 1 | The aliases have the sizes of what they name: `bool` is 1, `byte` is 1, `char` is 4 and `float` is 8. See [Primitive types](https://rux-lang.dev/docs/lang/appendix/primitives). ## Structs: natural alignment and padding A struct keeps its fields **in the order they are declared**, and each field is placed at the next offset that is a multiple of its own alignment. Unused bytes left in between are *padding*. The struct's alignment is the largest alignment of its fields, and its size is rounded up to a multiple of that alignment, so that in an array every element is aligned too. The same three fields therefore cost different amounts in different orders: ```rux struct Loose { flag: bool; total: int64; mark: uint8; } struct Tight { total: int64; flag: bool; mark: uint8; } func Main() -> int { PrintLine("Loose {} {}", sizeof(Loose), alignof(Loose)); PrintLine("Tight {} {}", sizeof(Tight), alignof(Tight)); var loose = Loose { flag: true, total: 1, mark: 2 }; let start = @loose as uint; PrintLine("flag at {}, total at {}, mark at {}", (@loose.flag as uint) - start, (@loose.total as uint) - start, (@loose.mark as uint) - start); PrintLine("Loose[4] {}, Tight[4] {}", sizeof(Loose[4]), sizeof(Tight[4])); return 0; } ``` ```text Loose 24 8 Tight 16 8 flag at 0, total at 8, mark at 16 Loose[4] 96, Tight[4] 64 ``` ```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 ``` The compiler never reorders fields. Declaring the widest fields first usually leaves the least padding. A [tuple](https://rux-lang.dev/docs/lang/tuples/overview) is laid out the same way, its elements in order: `(int8, int16, int8)` is 6 bytes, aligned to 2. ## Arrays An array `T[N]` is its `N` elements side by side, each `sizeof(T)` bytes from the last, with nothing between them: `sizeof(T[N])` is `N * sizeof(T)`, and its alignment is `T`'s. The padding inside each element is what keeps the next one aligned, so [pointer arithmetic](https://rux-lang.dev/docs/lang/pointers/arithmetic) and indexing both step by `sizeof(T)`. A **flexible tail** `T[]`, the last field of a struct, contributes its alignment and no storage. With `struct Packet { length: uint16; bytes: uint32[]; }`, `sizeof(Packet)` is 4 and `alignof(Packet)` is 4: the tail begins at offset 4, where the allocation continues. See [Arrays](https://rux-lang.dev/docs/lang/arrays/overview#flexible-tails). ## Zero-sized types `()`, an empty struct, and an aggregate made only of them occupy no bytes and have alignment 1. A zero-sized field takes no room in its struct: with `struct Marker {}`, a `struct Tagged { id: int32; marker: Marker; flag: bool; }` is 8 bytes, exactly as it would be without `marker`. ## Views, ranges and interface values | Type | Size | Alignment | Contents | | ----------------------------------- | --------------- | ------------ | ---------------------------------------- | | `T[..]`, `var T[..]` | 16 | 8 | `.data` at 0, `.length` at 8 | | `a..b`, `a..=b` of a bound type `B` | `2 * sizeof(B)` | `alignof(B)` | `.start`, then `.end` | | `a..`, `..b`, `..=b` | `sizeof(B)` | `alignof(B)` | the one bound | | `..` | 0 | 1 | nothing | | an interface value | 16 | 8 | two words: the data and its method table | A slice's elements and an interface value's data live elsewhere and are not part of these sizes. ## Optionals, fallibles and sums Each level of an [optional](https://rux-lang.dev/docs/lang/optionals/overview) `T?`, a [fallible](https://rux-lang.dev/docs/lang/errors/overview) `T ! E` and a [sum](https://rux-lang.dev/docs/lang/sums/overview) `A | B` is a tagged aggregate: an **8-byte tag at offset 0**, followed by the payload of the active case at the first offset after the tag that suits the payload's alignment. The size is that of the tag plus the largest payload, rounded up to the alignment. A zero-sized payload reserves nothing, so `()?` is the tag alone. | Type | Size | Alignment | | --------------------------------------------- | ---- | --------- | | `()?` | 8 | 8 | | `uint8?`, `int32?`, `int64?` | 16 | 8 | | `(*int)?` | 16 | 8 | | `int32??` | 24 | 8 | | `int32 ! E`, with `E` a struct of one `int32` | 16 | 8 | | `int32 | float64` | 16 | 8 | Nested levels never share a tag: `int32??` is an outer tag in front of a whole `int32?`. Absent is tag 0 and present tag 1; success is 0 and failure 1; a sum's members are numbered in their canonical order. These numbers describe what the compiler does today and are not a stable binary interface — never write a tag to a file or hand it to foreign code. A [`variant`](https://rux-lang.dev/docs/lang/variants/overview) is likewise a private tag followed by storage for its widest case, and an [`enum`](https://rux-lang.dev/docs/lang/enums/overview) is exactly its base integer type: an `enum Small: uint8` is 1 byte, an enum with no base type is 8. ## Unions A [union](https://rux-lang.dev/docs/lang/unions/overview) overlays its members: every member starts at offset 0, and the union is as large as its largest member, rounded up to the largest alignment. It stores no tag, so writing one member and reading another reinterprets the same bytes: ```rux union Bits { whole: uint32, bytes: uint8[4] } union Uneven { small: uint8, large: uint64 } func Main() -> int { PrintLine("{} {}", sizeof(Bits), sizeof(Uneven)); // 4 8 var bits = Bits { whole: 0x01020304u32 }; PrintLine("{}", bits.bytes[0]); // 4 on a little-endian target return 0; } ``` ## See also - [Arrays](https://rux-lang.dev/docs/lang/arrays/overview) — `T[N]` and flexible tails - [Structs](https://rux-lang.dev/docs/lang/structs/overview) — field declarations - [Unions](https://rux-lang.dev/docs/lang/unions/overview) — overlapping storage - [Pointer arithmetic](https://rux-lang.dev/docs/lang/pointers/arithmetic) — offsets scaled by `sizeof` - [Foreign functions](https://rux-lang.dev/docs/lang/ffi/overview) — layout at a C boundary - Learn: [Layout](https://rux-lang.dev/docs/learn/layout), [Union](https://rux-lang.dev/docs/learn/union), [Raw memory](https://rux-lang.dev/docs/learn/raw-memory) # Primitive Types Every primitive type in Rux, by family. The names are predefined type names, not [keywords](https://rux-lang.dev/docs/lang/lexical/keywords). A **Reserved** type is named by the language — its width and family are fixed — but rux 0.4.0 does not implement it, and using it is `error: primitive type 'float16' is reserved but is not implemented in this compiler version`. | Type | Family | Bits | Bytes | Holds | Literal | Status | | ---------- | ---------------- | ---- | ----- | ---------------------------------------------------- | ----------------- | ----------- | | `int8` | Signed integer | 8 | 1 | −128 to 127 | `i8` | Implemented | | `int16` | Signed integer | 16 | 2 | −32,768 to 32,767 | `i16` | Implemented | | `int32` | Signed integer | 32 | 4 | −231 to 231 − 1 | `i32` | Implemented | | `int64` | Signed integer | 64 | 8 | −263 to 263 − 1 | `i64` | Implemented | | `int128` | Signed integer | 128 | 16 | −2127 to 2127 − 1 | `i128` | Implemented | | `int256` | Signed integer | 256 | 32 | −2255 to 2255 − 1 | `i256` | Implemented | | `int512` | Signed integer | 512 | 64 | −2511 to 2511 − 1 | `i512` | Implemented | | `int` | Signed integer | 64 | 8 | Pointer-sized; as `int64` on every supported target | `i`, or none | Implemented | | `uint8` | Unsigned integer | 8 | 1 | 0 to 255 | `u8` | Implemented | | `uint16` | Unsigned integer | 16 | 2 | 0 to 65,535 | `u16` | Implemented | | `uint32` | Unsigned integer | 32 | 4 | 0 to 232 − 1 | `u32` | Implemented | | `uint64` | Unsigned integer | 64 | 8 | 0 to 264 − 1 | `u64` | Implemented | | `uint128` | Unsigned integer | 128 | 16 | 0 to 2128 − 1 | `u128` | Implemented | | `uint256` | Unsigned integer | 256 | 32 | 0 to 2256 − 1 | `u256` | Implemented | | `uint512` | Unsigned integer | 512 | 64 | 0 to 2512 − 1 | `u512` | Implemented | | `uint` | Unsigned integer | 64 | 8 | Pointer-sized; as `uint64` on every supported target | `u` | Implemented | | `float8` | Floating-point | 8 | 1 | E4M3 | `f8` | Reserved | | `float16` | Floating-point | 16 | 2 | IEEE 754 binary16 | `f16` | Reserved | | `float32` | Floating-point | 32 | 4 | IEEE 754 binary32 | `f32` | Implemented | | `float64` | Floating-point | 64 | 8 | IEEE 754 binary64 | `f64`, or none | Implemented | | `float80` | Floating-point | 80 | 16 | x87 extended precision | `f80` | Reserved | | `float128` | Floating-point | 128 | 16 | IEEE 754 binary128 | `f128` | Reserved | | `float256` | Floating-point | 256 | 32 | IEEE 754 binary256 | `f256` | Reserved | | `float512` | Floating-point | 512 | 64 | IEEE 754 binary512 | `f512` | Reserved | | `bool8` | Boolean | 8 | 1 | `false`, `true` | `true`, `false` | Implemented | | `bool16` | Boolean | 16 | 2 | `false`, `true` | — | Implemented | | `bool32` | Boolean | 32 | 4 | `false`, `true` | — | Implemented | | `bool64` | Boolean | 64 | 8 | `false`, `true` | — | Implemented | | `bool128` | Boolean | 128 | 16 | `false`, `true` | — | Reserved | | `bool256` | Boolean | 256 | 32 | `false`, `true` | — | Reserved | | `bool512` | Boolean | 512 | 64 | `false`, `true` | — | Reserved | | `char8` | Character | 8 | 1 | A UTF-8 code unit, 0 to 255 | `c8'…'` | Implemented | | `char16` | Character | 16 | 2 | A UTF-16 code unit, 0 to 65,535 | `c16'…'` | Implemented | | `char32` | Character | 32 | 4 | A Unicode scalar value | `c32'…'`, or none | Implemented | | `char64` | Character | 64 | 8 | A Unicode scalar value | `c64'…'` | Implemented | | `char128` | Character | 128 | 16 | — | — | Reserved | | `char256` | Character | 256 | 32 | — | — | Reserved | | `char512` | Character | 512 | 64 | — | — | Reserved | ## Built-in aliases | Alias | Same type as | | ------- | ------------ | | `bool` | `bool8` | | `byte` | `uint8` | | `char` | `char32` | | `float` | `float64` | An alias is another name for its target, not a separate type; see [Type Aliases](https://rux-lang.dev/docs/lang/types/aliases#built-in-aliases). ## Defaults - An unsuffixed integer literal is an `int`, unless its context requires another integer type. - An unsuffixed floating-point literal is a `float64`. - An unprefixed character literal is a `char32`, unless it initialises a `char8` or `char16`. - An unprefixed string literal is a `char8[..]`. ## Associated constants Each implemented primitive has constants declared in `Core`, available once the type is imported, as in `import Core::int32;`: | Family | Constants | | -------------- | ----------------------------------------------------------------------------- | | Integers | `Bits`, `Bytes`, `Min`, `Max` | | Floating-point | `Bits`, `Bytes`, `Lowest`, `Max`, `MinPositive`, `Epsilon`, `Infinity`, `NaN` | | Booleans | `Bits`, `Bytes` | | Characters | `Bits`, `Bytes`, `Min`, `Max` | ## See also - [Types](https://rux-lang.dev/docs/lang/types/overview) — conversions and type expressions - [Integers](https://rux-lang.dev/docs/lang/types/integers), [Floating-Point](https://rux-lang.dev/docs/lang/types/floating-point), [Booleans](https://rux-lang.dev/docs/lang/types/booleans), [Characters](https://rux-lang.dev/docs/lang/types/characters) - [Literals](https://rux-lang.dev/docs/lang/lexical/literals) — suffixes and prefixes # Token Reference The lexer turns a source file into a sequence of tokens, reading each one by the [longest-match rule](https://rux-lang.dev/docs/lang/lexical/source-files#tokens). This page lists every token kind under the name the compiler gives it. ## Literals | Token | Examples | See | | --------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | `IntLiteral` | `42`, `0xFF`, `0o77`, `0b1010`, `200u8` | [Integer literals](https://rux-lang.dev/docs/lang/lexical/literals#integer-literals) | | `FloatLiteral` | `3.14`, `1.0e-9`, `0.5f32` | [Floating-point literals](https://rux-lang.dev/docs/lang/lexical/literals#floating-point-literals) | | `StringLiteral` | `"hello"`, `c8"hello"`, `c16"hello"`, `c32"hello"` | [String literals](https://rux-lang.dev/docs/lang/lexical/literals#string-literals) | | `CharLiteral` | `'A'`, `'\n'`, `c8'A'`, `c16'A'`, `c32'A'`, `c64'A'` | [Character literals](https://rux-lang.dev/docs/lang/lexical/literals#character-literals) | | `BoolLiteral` | `true`, `false` | [Boolean literals](https://rux-lang.dev/docs/lang/lexical/literals#boolean-literals) | A numeric suffix and a character or string prefix are part of the literal's token. ## Identifiers | Token | Examples | | ------------ | ------------------------ | | `Identifier` | `count`, `Point`, `_tmp` | Contextual words such as `asm`, `sizeof` and `Self` are identifiers to the lexer; see [Keywords](https://rux-lang.dev/docs/lang/lexical/keywords#contextual-words). ## Keywords | Token | Spelling | Token | Spelling | | ----------------- | ---------- | ------------------ | ----------- | | `AsKeyword` | `as` | `InterfaceKeyword` | `interface` | | `BreakKeyword` | `break` | `IntrinsicKeyword` | `intrinsic` | | `CatchKeyword` | `catch` | `IsKeyword` | `is` | | `ConstKeyword` | `const` | `LetKeyword` | `let` | | `ContinueKeyword` | `continue` | `LoopKeyword` | `loop` | | `DeferKeyword` | `defer` | `MatchKeyword` | `match` | | `DoKeyword` | `do` | `ModuleKeyword` | `module` | | `ElseKeyword` | `else` | `NoneKeyword` | `none` | | `EnumKeyword` | `enum` | `NullKeyword` | `null` | | `ExtendKeyword` | `extend` | `PubKeyword` | `pub` | | `ExternKeyword` | `extern` | `ReturnKeyword` | `return` | | `FailKeyword` | `fail` | `SelfKeyword` | `self` | | `ForKeyword` | `for` | `StructKeyword` | `struct` | | `FuncKeyword` | `func` | `TypeKeyword` | `type` | | `IfKeyword` | `if` | `UnionKeyword` | `union` | | `ImportKeyword` | `import` | `VarKeyword` | `var` | | `InKeyword` | `in` | `VariantKeyword` | `variant` | | | | `WhenKeyword` | `when` | | | | `WhileKeyword` | `while` | Together with `true` and `false`, which are `BoolLiteral` tokens, these are the 38 reserved words. ## Punctuation | Token | Spelling | Token | Spelling | | -------------- | -------- | ------------------ | -------- | | `LeftParen` | `(` | `Dot` | `.` | | `RightParen` | `)` | `DotDot` | `..` | | `LeftBrace` | `{` | `DotDotDot` | `...` | | `RightBrace` | `}` | `DotDotEqual` | `..=` | | `LeftBracket` | `[` | `Arrow` | `->` | | `RightBracket` | `]` | `FatArrow` | `=>` | | `Comma` | `,` | `At` | `@` | | `Semicolon` | `;` | `Hash` | `#` | | `Colon` | `:` | `Question` | `?` | | `ColonColon` | `::` | `QuestionQuestion` | `??` | ## Operators | Token | Spelling | Group | Token | Spelling | Group | | ----------------------- | -------- | ---------- | ----------------------------- | -------- | ---------- | | `Plus` | `+` | Arithmetic | `Equal` | `==` | Comparison | | `Minus` | `-` | Arithmetic | `BangEqual` | `!=` | Comparison | | `Star` | `*` | Arithmetic | `Less` | `<` | Comparison | | `Slash` | `/` | Arithmetic | `LessEqual` | `<=` | Comparison | | `Percent` | `%` | Arithmetic | `Greater` | `>` | Comparison | | `PlusPlus` | `++` | Arithmetic | `GreaterEqual` | `>=` | Comparison | | `MinusMinus` | `--` | Arithmetic | `MoveArrow` | `<-` | Assignment | | `Amp` | `&` | Bitwise | `Assign` | `=` | Assignment | | `Pipe` | `|` | Bitwise | `PlusAssign` | `+=` | Assignment | | `Caret` | `^` | Bitwise | `MinusAssign` | `-=` | Assignment | | `Tilde` | `~` | Bitwise | `StarAssign` | `*=` | Assignment | | `LessLess` | `<<` | Bitwise | `SlashAssign` | `/=` | Assignment | | `GreaterGreater` | `>>` | Bitwise | `PercentAssign` | `%=` | Assignment | | `GreaterGreaterGreater` | `>>>` | Bitwise | `AmpAssign` | `&=` | Assignment | | `AmpAmp` | `&&` | Logical | `PipeAssign` | `|=` | Assignment | | `PipePipe` | `||` | Logical | `CaretAssign` | `^=` | Assignment | | `Bang` | `!` | Logical | `LessLessAssign` | `<<=` | Assignment | | | | | `GreaterGreaterAssign` | `>>=` | Assignment | | | | | `GreaterGreaterGreaterAssign` | `>>>=` | Assignment | [Operators and Punctuation](https://rux-lang.dev/docs/lang/lexical/operators) says what each one means. ## Special tokens | Token | Meaning | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | | `DocComment` | A documentation comment, `/// …` or `/** … */`, kept so the parser can attach it to the next declaration. Ordinary comments produce no token. | | `EndOfFile` | Appended after the last token of every file | | `Unknown` | A character that starts no token, kept so the error can quote it | | `NewLine` | Defined but never produced: line breaks are whitespace | ## See also - [Source Files](https://rux-lang.dev/docs/lang/lexical/source-files) — how the lexer reads a file - [Keywords](https://rux-lang.dev/docs/lang/lexical/keywords) and [Literals](https://rux-lang.dev/docs/lang/lexical/literals) - [Operators and Punctuation](https://rux-lang.dev/docs/lang/lexical/operators) # Rux Compiled Unit A **Rux Compiled Unit** (RCU, `.rcu`) is the object format of the Rux compiler. Each back end — x86-64 and AArch64 — encodes its instructions itself and stores them, with symbols and relocations, in one RCU per compiled source file. The Rux linker reads the units of a build and writes the target's container: a PE image on Windows, ELF on Linux and FreeBSD, Mach-O on macOS, or a relocatable object or static archive in the target's own format. No external assembler, compiler or linker takes part. A normal build keeps its units in memory. `rux build --emit rcu` also writes them out, as `Temp/Obj/.rcu`, with a readable dump of each in `Temp/Rcu/.rcu.txt` (see [`rux build`](https://rux-lang.dev/docs/cli/build)). ```mermaid flowchart LR src["Src/*.rux"] --> fe["front end
HIR, LIR"] fe --> x64["x86-64 back end"] fe --> a64["AArch64 back end"] x64 --> rcu["RCU units"] a64 --> rcu rcu --> link["Rux linker"] link --> out["PE, ELF or Mach-O"] ``` ## File layout All multi-byte integers are **little-endian**, and every field sits at the exact offset listed — there is no implicit padding. The regions follow one another in this order: ```text ┌──────────────────────────────────────────┐ offset 0 │ File header 32 bytes │ ├──────────────────────────────────────────┤ offset 32 │ Section table section_count × 40 │ ├──────────────────────────────────────────┤ 32 + section_count × 40 │ Symbol table symbol_count × 20 │ ├──────────────────────────────────────────┤ │ For each section, in table order: │ │ raw data (aligned to section) │ │ relocations (aligned to 4) │ ├──────────────────────────────────────────┤ │ String table string_table_size │ ├──────────────────────────────────────────┤ │ Rux metadata 64 bytes (aligned to 8) │ when present └──────────────────────────────────────────┘ ``` Every gap introduced by alignment is filled with `0x00`. A reader needs only the header to find everything else. ## File header | Offset | Size | Type | Field | Meaning | | ------ | ---- | ------- | ------------------- | ---------------------------------------------------------------- | | 0 | 4 | `u8[4]` | `magic` | `52 43 55 00` — `"RCU\0"` | | 4 | 2 | `u16` | `version` | `(major << 8) | minor`; this format is 1.0, `0x0100` | | 6 | 1 | `u8` | `arch` | target architecture — see below | | 7 | 1 | `u8` | `flags` | bit 0 `F_HAS_METADATA`; bits 1–7 are `0` | | 8 | 2 | `u16` | `section_count` | entries in the section table | | 10 | 2 | `u16` | `_reserved` | `0` | | 12 | 4 | `u32` | `symbol_count` | entries in the symbol table | | 16 | 4 | `u32` | `string_table_off` | file offset of the string table | | 20 | 4 | `u32` | `string_table_size` | its size in bytes | | 24 | 4 | `u32` | `metadata_offset` | file offset of the metadata block, or `0` when there is none | | 28 | 4 | `u32` | `checksum` | CRC-32C of the whole file, computed with these four bytes as `0` | | `arch` | Architecture | | ------ | ---------------------- | | `0x00` | unknown | | `0x01` | x86-64, little-endian | | `0x02` | AArch64, little-endian | The architecture byte names the processor only. Operating system and calling convention are not recorded: the instruction bytes were generated for one target, and the linker is told which one it is linking for. A unit is only meaningful to a link for the same architecture, and the linker rejects any other. A reader must reject a file whose magic differs, and should reject one whose major version is newer than it understands. ## Section table `section_count` entries of 40 bytes, starting at offset 32. | Offset | Size | Type | Field | Meaning | | ------ | ---- | --------- | -------------- | ---------------------------------------------------------- | | 0 | 8 | `char[8]` | `name` | ASCII name, at most 7 characters, padded with `\0` | | 8 | 4 | `u32` | `type` | section type | | 12 | 4 | `u32` | `flags` | section flags | | 16 | 4 | `u32` | `raw_offset` | file offset of the section's bytes, aligned to `alignment` | | 20 | 4 | `u32` | `raw_size` | number of bytes stored | | 24 | 4 | `u32` | `virtual_size` | size in memory: `raw_size`, or `1` for an empty section | | 28 | 2 | `u16` | `alignment` | required alignment, a power of two | | 30 | 2 | `u16` | `reloc_count` | relocation entries for this section | | 32 | 4 | `u32` | `reloc_offset` | file offset of those entries, or `0` when there are none | | 36 | 4 | `u32` | `_reserved` | `0` | An empty section still has a `raw_offset` — the aligned position its bytes would start at. | `type` | Constant | Contents | | ------ | ------------ | ------------------------------------------- | | `0` | `SEC_NULL` | an unused entry | | `1` | `SEC_TEXT` | machine code | | `2` | `SEC_DATA` | initialized writable data | | `3` | `SEC_RODATA` | read-only data: literals, constant tables | | `4` | `SEC_BSS` | zero-initialized data, with no bytes stored | | `5` | `SEC_META` | Rux-specific metadata | | Bit | Constant | Meaning | | --- | ------------ | ------------------------------- | | 0 | `SF_ALLOC` | occupies memory at run time | | 1 | `SF_EXEC` | executable | | 2 | `SF_READ` | readable | | 3 | `SF_WRITE` | writable | | 4 | `SF_MERGE` | identical entries may be merged | | 5 | `SF_STRINGS` | holds NUL-terminated strings | Both back ends emit the same three sections, always in this order: | Index | Name | Type | Flags | Alignment | Contents | | ----- | --------- | ------------ | ------------------------------- | --------- | -------------------------------------- | | 0 | `.text` | `SEC_TEXT` | `SF_ALLOC | SF_EXEC | SF_READ` | 16 | functions, including `asm func` bodies | | 1 | `.rodata` | `SEC_RODATA` | `SF_ALLOC | SF_READ` | 8 | string literals and constant data | | 2 | `.data` | `SEC_DATA` | `SF_ALLOC | SF_READ | SF_WRITE` | 8 | writable data | ## Symbol table `symbol_count` entries of 20 bytes, starting right after the section table. | Offset | Size | Type | Field | Meaning | | ------ | ---- | ----- | --------------- | ------------------------------------------------------------------------------------------------------------------------- | | 0 | 4 | `u32` | `name_off` | string-table offset of the name | | 4 | 4 | `u32` | `value` | offset within the section; the value itself for an absolute symbol | | 8 | 4 | `u32` | `size` | size in bytes, `0` when unknown | | 12 | 2 | `u16` | `section_idx` | owning section; `0xFFFF` for an external symbol, `0xFFFE` for an absolute one | | 14 | 1 | `u8` | `kind` | symbol kind | | 15 | 1 | `u8` | `visibility` | symbol binding | | 16 | 4 | `u32` | `type_name_off` | string-table offset of a type spelling — a function's result type, such as `"int32"`, or a constant's type; `0` when none | | `kind` | Constant | Meaning | | ------ | ----------------- | ---------------------------------------------- | | `0` | `SYM_UNKNOWN` | unclassified | | `1` | `SYM_FUNC` | a function defined in this unit | | `2` | `SYM_DATA` | writable data defined in this unit | | `3` | `SYM_CONST` | read-only data defined in this unit | | `4` | `SYM_SECTION` | a section | | `5` | `SYM_FILE` | a source file name | | `6` | `SYM_EXTERN_FUNC` | a function this unit calls but does not define | | `7` | `SYM_EXTERN_DATA` | data this unit uses but does not define | An external symbol has `section_idx = 0xFFFF` and `value = 0`; it is a reference to a definition in another unit of the link or, for an `extern` declaration, to a shared library. | `visibility` | Constant | Meaning | | ------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `0` | `VIS_LOCAL` | a package-private definition | | `1` | `VIS_GLOBAL` | a public definition of a dependency, linkable across units but not part of the artifact's interface; also every external reference | | `2` | `VIS_WEAK` | a definition another unit's global definition overrides | | `3` | `VIS_EXPORTED` | a public definition of the package being built — what a library artifact publishes | The linker binds each reference to exactly one definition and reports a duplicate or a missing one. An executable's root is `Main`; a library's roots are its exported definitions. ## Relocations Each section's relocations are an array of 16-byte entries at `reloc_offset`. | Offset | Size | Type | Field | Meaning | | ------ | ---- | ----- | ---------------- | ----------------------------------------------- | | 0 | 4 | `u32` | `section_offset` | where in the section the field to patch begins | | 4 | 4 | `u32` | `symbol_index` | the symbol whose address is used | | 8 | 2 | `u16` | `type` | relocation type | | 10 | 2 | `u16` | `_reserved` | `0` | | 12 | 4 | `i32` | `addend` | a signed constant added to the symbol's address | In the formulas, `S` is the symbol's address, `A` the addend and `P` the address of the patched field. Types below 16 patch a whole little-endian field and are architecture-neutral; types from 16 patch bit fields of one AArch64 instruction word and keep the names of their ELF counterparts. | Type | Name | Patches | Value | | --------- | --------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- | | `0` | `NONE` | nothing | — | | `1` | `ABS_64` | 64-bit field | `S + A` | | `2` | `ABS_32` | 32-bit field | `S + A`, which must fit in 32 bits | | `3` | `REL_32` | 32-bit field | `S + A − (P + 4)`, a signed 32-bit displacement — x86-64 `call`, `jmp`, `jcc`, `[rip + …]` | | `16` | `AARCH64_CALL26` | `bl` imm26 | `(S + A − P) >> 2` | | `17` | `AARCH64_JUMP26` | `b` imm26 | `(S + A − P) >> 2` | | `18` | `AARCH64_CONDBR19` | `b.cond`, `cbz`, `cbnz` imm19 | `(S + A − P) >> 2` | | `19` | `AARCH64_TSTBR14` | `tbz`, `tbnz` imm14 | `(S + A − P) >> 2` | | `20` | `AARCH64_ADR_PREL_PG_HI21` | `adrp` immhi\:immlo | `(page(S + A) − page(P)) >> 12`, `page(x) = x & ~0xFFF` | | `21` | `AARCH64_ADD_ABS_LO12_NC` | `add` imm12 | `(S + A) & 0xFFF` | | `22` | `AARCH64_LDST_ABS_LO12_NC` | load/store imm12 | `((S + A) & 0xFFF) >> scale`, where `scale` is the access size; the address must be aligned to it | | `23`–`26` | `AARCH64_MOVW_UABS_G0`…`G3` | `movz`/`movk` imm16 | bits 0–15, 16–31, 32–47 or 48–63 of `S + A` | | `27` | `AARCH64_PREL32` | 32-bit field | `S + A − P`, signed | | `28` | `AARCH64_PREL64` | 64-bit field | `S + A − P` | A displacement that does not fit its field, or a misaligned load/store target, is a link error that names the symbol. ## String table A flat block of NUL-terminated UTF-8 strings. Offset `0` is always the empty string, so a `0` offset anywhere means "no name". Each distinct string is stored once. The writer interns, in this order: the source path, the package name, each symbol's name and type spelling, and the section names. Symbol and section names are printable ASCII; the source path may be any UTF-8. ## Metadata When `F_HAS_METADATA` is set, a 64-byte block sits at `metadata_offset`, aligned to 8. The compiler writes it into every unit. | Offset | Size | Type | Field | Meaning | | ------ | ---- | -------- | ------------------ | --------------------------------------------------------------------------- | | 0 | 4 | `u8[4]` | `magic` | `4D 45 54 41` — `"META"` | | 4 | 4 | `u32` | `block_size` | `64` | | 8 | 4 | `u32` | `source_path_off` | string-table offset of the source file's path | | 12 | 4 | `u32` | `package_name_off` | string-table offset of the package name | | 16 | 8 | `u64` | `build_timestamp` | the build's start, in Unix seconds — the same instant as `#build.timestamp` | | 24 | 4 | `u32` | `rux_version` | the compiler version, `(major << 16) | (minor << 8) | patch` | | 28 | 4 | `u32` | `compiler_flags` | reserved; written as `0` | | 32 | 32 | `u8[32]` | `source_hash` | reserved for a SHA-256 of the source; written as zeros | ## Checksum `checksum` is a CRC-32C — the Castagnoli polynomial, reflected form `0x82F63B78`, initial value and final XOR `0xFFFFFFFF` — over every byte of the file, with the checksum field itself counted as four zero bytes. It is computed last, after every other field is written. It is deliberately not the IEEE CRC-32 of ZIP, so the two are not interchangeable. ## Example `Src/Main.rux` of a package called `Math`: ```rux func Add(a: int32, b: int32) -> int32 { return a + b; } func Main() -> int { return Add(3, 4) as int; } ``` `rux build --emit rcu --target linux-x86_64` writes `Temp/Obj/Main.rcu`, whose dump begins: ```text ; RCU Rux Compiled Unit v1.0 ; Architecture: x86-64 ; Package: Math ; Rux version: 0.4.0 Sections: 3 [ 0] .text flags:AER align:16 data:239B relocs:1 [ 1] .rodata flags:AR align:8 data:0B relocs:0 [ 2] .data flags:ARW align:8 data:0B relocs:0 Symbols: 2 [ 0] Add .text+0x0000 size=150 FUNC LOCAL "int32" [ 1] Main .text+0x0096 size=89 FUNC LOCAL "int" Relocations (.text): [ 0] off=0x00CE sym[0]=Add REL_32 addend=0 ``` The one relocation is the `call Add` inside `Main`: four placeholder bytes at `.text + 0xCE` that the linker replaces with `Add − (P + 4)`. Built for `linux-aarch64` instead, the same call is a `bl` patched by an `AARCH64_CALL26` relocation. With a source path of 120 bytes, the file is laid out as follows: | Offset | Region | Size | | ------- | ---------------------------------- | --------------------------------------------------------------------------------------- | | `0x000` | header | 32 | | `0x020` | section table, 3 entries | 120 | | `0x098` | symbol table, 2 entries | 40 | | `0x0C0` | `.text` bytes (aligned to 16) | 239 | | `0x1B0` | `.text` relocations (aligned to 4) | 16 | | `0x1C0` | `.rodata`, `.data` (empty) | 0 — both `raw_offset`s are `0x1C0` | | `0x1C0` | string table | 166: `\0`, the path, `Math`, `Add`, `int32`, `Main`, `int`, `.text`, `.rodata`, `.data` | | `0x268` | metadata (aligned to 8) | 64 | | `0x2A8` | end of file | | and its header reads: ```text 52 43 55 00 magic "RCU\0" 00 01 version 0x0100 01 arch x86-64 01 flags F_HAS_METADATA 03 00 section_count 3 00 00 _reserved 02 00 00 00 symbol_count 2 C0 01 00 00 string_table_off 0x1C0 A6 00 00 00 string_table_size 166 68 02 00 00 metadata_offset 0x268 xx xx xx xx checksum CRC-32C, computed last ``` ## See also - [Assembly](https://rux-lang.dev/docs/lang/ffi/assembly) — `asm func` bodies, whose symbols become relocations - [`rux build`](https://rux-lang.dev/docs/cli/build) — `--emit rcu` and the other inspection outputs - [Linking](https://rux-lang.dev/docs/lang/ffi/linking) — external symbols bound to shared libraries # Rux Language Reference This reference describes the Rux language exactly as **rux 0.4.0** implements it: every type, expression, statement and declaration, the rules the compiler enforces and the messages it prints when a rule is broken. Every example on these pages compiles with rux 0.4.0. It is written for looking things up. To learn the language from the beginning, follow [Learn Rux](https://rux-lang.dev/docs/learn) — a course of short, runnable lessons — and come back here for the full rules. The [Introduction](https://rux-lang.dev/docs/lang/introduction) explains how the chapters are organised and the grammar notation they use. ## The basics ::u-page-grid :::u-page-card --- description: Design goals, a first program, and how to read this reference. icon: i-lucide-book-open title: Introduction to: https://rux-lang.dev/docs/lang/introduction variant: subtle --- ::: :::u-page-card --- description: Source files, comments, identifiers, keywords, literals and operator tokens. icon: i-lucide-text title: Lexical structure to: https://rux-lang.dev/docs/lang/lexical/source-files variant: subtle --- ::: :::u-page-card --- description: Integers, floating point, booleans, characters, text and type aliases. icon: i-lucide-shapes title: Types to: https://rux-lang.dev/docs/lang/types/overview variant: subtle --- ::: :::u-page-card --- description: let, var and const, initialisation and destructuring. icon: i-lucide-tag title: Bindings to: https://rux-lang.dev/docs/lang/bindings/overview variant: subtle --- ::: :::u-page-card --- description: Every operator, its precedence, and casts with as. icon: i-lucide-plus title: Expressions to: https://rux-lang.dev/docs/lang/expressions/overview variant: subtle --- ::: :::u-page-card --- description: if, the four loops, labelled break and continue, and return. icon: i-lucide-git-branch title: Statements to: https://rux-lang.dev/docs/lang/statements/overview variant: subtle --- ::: :::u-page-card --- description: match, exhaustiveness, and every pattern form. icon: i-lucide-scan-search title: Patterns to: https://rux-lang.dev/docs/lang/patterns/match variant: subtle --- ::: :: ## Functions and data ::u-page-grid :::u-page-card --- description: Declarations, parameters, overloading, function types and Main. icon: i-lucide-square-function title: Functions to: https://rux-lang.dev/docs/lang/functions/declaration variant: subtle --- ::: :::u-page-card --- description: Structs, methods with typed receivers, constructors and extensions. icon: i-lucide-box title: Structures to: https://rux-lang.dev/docs/lang/structs/overview variant: subtle --- ::: :::u-page-card --- description: Scalar enums with a backing type and explicit values. icon: i-lucide-list-ordered title: Enumerations to: https://rux-lang.dev/docs/lang/enums/overview variant: subtle --- ::: :::u-page-card --- description: Tagged unions whose cases carry their own data. icon: i-lucide-split title: Variants to: https://rux-lang.dev/docs/lang/variants/overview variant: subtle --- ::: :::u-page-card --- description: Untagged overlays of several types in one place. icon: i-lucide-layers title: Unions to: https://rux-lang.dev/docs/lang/unions/overview variant: subtle --- ::: :::u-page-card --- description: Anonymous products, the unit type and structural equality. icon: i-lucide-parentheses title: Tuples to: https://rux-lang.dev/docs/lang/tuples/overview variant: subtle --- ::: :::u-page-card --- description: Fixed-length T[N], repeat literals and bounds checks. icon: i-lucide-brackets title: Arrays to: https://rux-lang.dev/docs/lang/arrays/overview variant: subtle --- ::: :::u-page-card --- description: Read-only T[..] and writable var T[..] views. icon: i-lucide-scissors title: Slices to: https://rux-lang.dev/docs/lang/slices/overview variant: subtle --- ::: :::u-page-card --- description: The six range forms and where they are used. icon: i-lucide-move-horizontal title: Ranges to: https://rux-lang.dev/docs/lang/ranges/overview variant: subtle --- ::: :: ## Absence and failure ::u-page-grid :::u-page-card --- description: T? and none, ?? fallbacks and ? propagation. icon: i-lucide-circle-dashed title: Optionals to: https://rux-lang.dev/docs/lang/optionals/overview variant: subtle --- ::: :::u-page-card --- description: Fallibles T ! E, fail, catch, propagation and panics. icon: i-lucide-triangle-alert title: Errors to: https://rux-lang.dev/docs/lang/errors/overview variant: subtle --- ::: :::u-page-card --- description: A | B values, typed patterns and the is test. icon: i-lucide-combine title: Sum types to: https://rux-lang.dev/docs/lang/sums/overview variant: subtle --- ::: :: ## Memory and ownership ::u-page-grid :::u-page-card --- description: Borrowing with &T and &var T, and the exclusivity rule. icon: i-lucide-link title: References to: https://rux-lang.dev/docs/lang/references/overview variant: subtle --- ::: :::u-page-card --- description: "*T and *var T, @ and *, null, arithmetic and slicing." icon: i-lucide-mouse-pointer-2 title: Pointers to: https://rux-lang.dev/docs/lang/pointers/overview variant: subtle --- ::: :::u-page-card --- description: Copies and moves, destructors and defer. icon: i-lucide-key-round title: Ownership to: https://rux-lang.dev/docs/lang/ownership/overview variant: subtle --- ::: :::u-page-card --- description: sizeof, alignof, padding and the layout of native forms. icon: i-lucide-ruler title: Memory layout to: https://rux-lang.dev/docs/lang/memory/layout variant: subtle --- ::: :: ## Abstraction ::u-page-grid :::u-page-card --- description: Declarations, interface values, core interfaces, operators, indexers and iteration. icon: i-lucide-plug title: Interfaces to: https://rux-lang.dev/docs/lang/interfaces/overview variant: subtle --- ::: :::u-page-card --- description: Generic functions, types and methods, and bounds. icon: i-lucide-variable title: Generics to: https://rux-lang.dev/docs/lang/generics/overview variant: subtle --- ::: :::u-page-card --- description: Packages and modules, imports and pub visibility. icon: i-lucide-package title: Modules to: https://rux-lang.dev/docs/lang/modules/overview variant: subtle --- ::: :: ## Compile time and platform ::u-page-grid :::u-page-card --- description: "when, the #target, #build, #compiler, #source and #config context, intrinsics and diagnostics." icon: i-lucide-cpu title: Compile time to: https://rux-lang.dev/docs/lang/comptime/overview variant: subtle --- ::: :::u-page-card --- description: "#Link, #Abi, #NoReturn, #Warn, #Error, #Allow and #Format." icon: i-lucide-at-sign title: Attributes to: https://rux-lang.dev/docs/lang/attributes/overview variant: subtle --- ::: :::u-page-card --- description: extern declarations, linking libraries and inline assembly. icon: i-lucide-cable title: Foreign Function Interface to: https://rux-lang.dev/docs/lang/ffi/overview variant: subtle --- ::: :: ## Appendix - [Primitive types](https://rux-lang.dev/docs/lang/appendix/primitives) — every primitive type and alias in one table, implemented and reserved. - [Tokens](https://rux-lang.dev/docs/lang/appendix/tokens) — every token the lexer produces. - [Rux Compiled Unit](https://rux-lang.dev/docs/lang/appendix/rcu) — the compiler's native object format. Package manifests, dependencies and publishing are covered in [Packaging](https://rux-lang.dev/docs/packaging), and the standard packages in the [API Reference](https://rux-lang.dev/docs/api). # Global options ## Purpose Global options configure command-independent CLI behavior and may appear before or after the command name. ## Synopsis ```sh rux [global-options] [command-options] [operands] ``` Both `--option value` and `--option=value` are accepted for options that take a value. Only `rux run` accepts `--`; every following token is passed unchanged to the child program. ## Behavior `-q`/`--quiet` and `-v`/`--verbose` conflict. Help and version exit 0. Unknown options, missing values, invalid color values, conflicts, and incorrect operand counts exit 2 and are written to stderr. Help is available as `rux help`, `rux help `, or command-level `-h`/`--help`. Close command and option spellings receive a suggestion. ## Options | Option | Description | | ----------------------------- | --------------------------------------------------- | | `--color ` | Control colored console output | | `-h, --help` | Show help information | | `--manifest ` | Use the specified manifest file instead of Rux.toml | | `-q, --quiet` | Do not show log messages | | `-v, --verbose` | Use verbose output | | `-V, --version` | Show version information | ### Color `--color` accepts exactly `auto`, `always`, or `never`. An explicit CLI value overrides the environment. In auto mode, stdout and stderr terminal attachment are detected independently. ### Manifest `--manifest ` selects a specific manifest instead of searching upward for `Rux.toml`. ## Output and exit status Primary output and structured JSON use stdout. Progress, warnings, and errors use stderr. Success, help, and version exit 0; invalid CLI usage exits 2; operational and compiler failures exit 1. ## Examples ```sh rux --manifest examples/Rux.toml check rux build --color=never rux --quiet test rux help build rux help --json ``` ## Environment variables - A non-empty `NO_COLOR` disables color in auto mode. - `TERM=dumb` disables color in auto mode. - An explicit `--color always` or `--color never` overrides both variables. ## Related commands - [`rux help`](https://rux-lang.dev/docs/cli/help) - [`rux version`](https://rux-lang.dev/docs/cli/version) # rux add ## Purpose Add a brand new external dependency link directly into the package configuration file. ## Synopsis ```sh rux add [namespace]/[package] rux add [namespace]/[package]@[requirement] rux add [package] --path [path] ``` ## Behavior Registry dependencies require a namespaced identity such as `Rux/Io`; an optional `@requirement` constrains the resolved version. `--path` adds a local dependency instead. The command updates `Rux.toml` only after registry validation succeeds. ## Arguments | Argument | Required | Description | | --------- | -------- | ------------------------------------------------------ | | `package` | Yes | A namespaced package identity and optional requirement | ## Options | Option | Description | | ------------------ | -------------------------------------------------- | | `--path ` | Add a local path-based dependency | | `--registry ` | Registry API base URL to check the package against | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux add Rux/Io rux add Rux/Io@^0.1.0 rux add Json --path ../Json ``` ## Environment variables `RUX_REGISTRY_URL` sets the default registry when `--registry` is absent. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux install`](https://rux-lang.dev/docs/cli/install) - [`rux remove`](https://rux-lang.dev/docs/cli/remove) - [`rux update`](https://rux-lang.dev/docs/cli/update) # rux build ## Purpose Build the current package ## Synopsis ```sh rux build [options] ``` ## Behavior Builds the normal package artifact and may additionally write inspection output selected by repeatable or comma-separated `--emit`. `--debug` and `--release` are mutually exclusive. Frontend target selection is supported, but linking a foreign target is rejected until that backend is available end to end. Executable, SharedLibrary, and StaticLibrary packages build their conventional native artifact. A Windows SharedLibrary additionally writes its `.lib` import library. SourceLibrary packages can be checked but cannot be built directly. ### `--emit` values | Kind | Additional output | | -------- | -------------------------------------------------------- | | `tokens` | Token streams in `Temp/Tokens/` | | `ast` | Parsed syntax trees in `Temp/Ast/` | | `sema` | Semantic-analysis dump in `Temp/Sema/sema.txt` | | `hir` | High-level IR in `Temp/Hir/hir.txt` | | `lir` | Low-level IR in `Temp/Lir/lir.txt` | | `asm` | Target assembly in `Temp/Asm/` | | `rcu` | RCU objects in `Temp/Obj/` and text dumps in `Temp/Rcu/` | The option is repeatable and accepts comma-separated values, so `--emit ast --emit sema` and `--emit ast,sema` are equivalent. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------------- | --------------------------------------------------------------------------------------------- | | `--all` | Build all 16 target/profile cells; incompatible with --target, --debug, --release, and --emit | | `--debug` | Build with debug symbols (unoptimized output) | | `--define ` | Set or override a config compile-time value | | `--emit ` | Additionally emit tokens, ast, sema, hir, lir, asm, or rcu inspection output | | `--release` | Build with release profile (optimized, no debug info) | | `--stats` | Print build timing, source, performance, and output statistics | | `--target ` | Build for the specified target platform (e.g. linux-x86\_64, linux-aarch64) | | `-q, --quiet` | Suppress non-essential output (only errors are shown) | | `-v, --verbose` | Enable verbose output for detailed build information | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status The artifact is written below configured `[Build].Output` using the selected profile. Inspection output is written below `Temp/` and does not replace the normal artifact. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux build rux build --all --stats rux build --debug rux build --release --target linux-x86_64 rux build --stats rux build --verbose --release rux build --emit ast,sema ``` ## Environment variables `SOURCE_DATE_EPOCH` fixes compiler timestamps for reproducible builds. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux check`](https://rux-lang.dev/docs/cli/check) - [`rux run`](https://rux-lang.dev/docs/cli/run) - [`rux clean`](https://rux-lang.dev/docs/cli/clean) # rux check ## Purpose Parse and analyze the source workspace files for syntactic or semantic safety flaws without emitting compilation binaries. ## Synopsis ```sh rux check [options] ``` ## Behavior Runs lexing, parsing, dependency loading, conditional-compilation folding, and semantic analysis without linking. Any supported target may be checked, including a foreign target. `--define` values override `[Build.Defines]`. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------------- | ------------------------------------------------ | | `--define ` | Set or override a config compile-time value | | `--json` | Output diagnostics in JSON format | | `--target ` | Check code health for a specific target platform | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Text diagnostics go to stderr. With `--json`, stdout receives one complete JSON document with a success flag and an escaped diagnostics array, including on failure. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux check rux check --json rux check --target windows-x86_64 ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux build`](https://rux-lang.dev/docs/cli/build) - [`rux lint`](https://rux-lang.dev/docs/cli/lint) - [`rux doc`](https://rux-lang.dev/docs/cli/doc) # rux clean ## Purpose Purge compiled code modules, system logs, and cached local build tracking states from the environment workspace. ## Synopsis ```sh rux clean [options] ``` ## Behavior Removes the directory named by `[Build].Output` and the package `Temp/` directory. `--temp` preserves configured build artifacts and removes only `Temp/`. ## Arguments This command accepts no operands. ## Options | Option | Description | | -------- | ------------------------------------------------- | | `--temp` | Remove only the temporary build directory (Temp/) | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux clean rux clean --temp ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux build`](https://rux-lang.dev/docs/cli/build) # rux doc ## Purpose Generate package documentation ## Synopsis ```sh rux doc [options] ``` ## Behavior Runs the same folded, semantically checked frontend as `rux check`, then documents public modules, types, functions, constants, fields, variants, interface members, externs, and public extension methods. `--document-private-items` includes private items. Consecutive outer `///` comments attach when no blank line intervenes and support safe Markdown; raw HTML is escaped and unsafe link schemes are omitted. Foreign supported targets and repeated `--define` values select the API that is generated. ## Arguments This command accepts no operands. ## Options | Option | Description | | -------------------------- | --------------------------------------------------- | | `--define ` | Set or override a config compile-time value | | `--document-private-items` | Include private declarations and members | | `-o, --output ` | Write the generated site to this directory | | `--open` | Open the generated documentation index in a browser | | `--target ` | Document APIs enabled for a supported target | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status The default is `[Build].Output/Docs` (normally `Bin/Docs`). A workspace gets a landing page and one member section; dependencies are excluded. Generation is deterministic and self-contained. Rux writes through a temporary directory, marks managed output, and refuses to replace a non-empty unmarked directory. `--open` launches the generated index only after success. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux doc rux doc --open ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux check`](https://rux-lang.dev/docs/cli/check) - [`rux build`](https://rux-lang.dev/docs/cli/build) # rux fmt ## Purpose Format all \*.rux source files and \*.toml manifests ## Synopsis ```sh rux fmt [options] ``` ## Behavior Formats both `Src/**/*.rux` and `Rux.toml` by default. `--source-only` and `--manifest-only` are mutually exclusive. `--check` reports drift without writing files. ## Arguments This command accepts no operands. ## Options | Option | Description | | ----------------- | ----------------------------------------------------------- | | `--check` | Check file formatting status without modifying source files | | `--manifest-only` | Format only the manifest configuration file (Rux.toml) | | `--source-only` | Format only Rux source files | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux fmt rux fmt --check rux fmt --manifest-only ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux check`](https://rux-lang.dev/docs/cli/check) - [`rux lint`](https://rux-lang.dev/docs/cli/lint) # rux help ## Purpose Show help information ## Synopsis ```sh rux help ``` ## Behavior Without an operand, lists all commands and global options. With a command operand, prints command help. `--json` emits schema version 1 with stable program, command, usage, argument, option, example, and documentation URL data; build timestamps are intentionally absent. ## Arguments | Argument | Required | Description | | --------- | -------- | -------------------------------------- | | `command` | No | The command whose help should be shown | ## Options | Option | Description | | -------- | -------------------------------------------- | | `--json` | Print the versioned command contract as JSON | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Help and JSON are written to stdout. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux help ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux version`](https://rux-lang.dev/docs/cli/version) # rux info ## Purpose Show information about the current or an installed Rux package ## Synopsis ```sh rux info rux info [namespace]/[package] rux info [namespace]/[package]@[requirement] ``` ## Behavior Without an operand, reads the current manifest. With a namespaced identity, selects the highest installed version matching the requirement and consults the registry only to explain a cache miss. Package identities remain namespace-qualified. ## Arguments | Argument | Required | Description | | --------- | -------- | ------------------------------------------------------ | | `package` | No | A namespaced package identity and optional requirement | ## Options | Option | Description | | ------------------ | ---------------------------------------------------------- | | `--json` | Output package metadata in JSON format | | `--registry ` | Registry API base URL to look an uninstalled package up on | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Human-readable metadata goes to stdout. `--json` writes one complete, fully escaped JSON document on success or failure. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux info rux info Rux/Io rux info Rux/Io@0.1.0 rux info --json ``` ## Environment variables `RUX_REGISTRY_URL` sets the lookup registry when `--registry` is absent. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux list`](https://rux-lang.dev/docs/cli/list) - [`rux install`](https://rux-lang.dev/docs/cli/install) # rux init ## Purpose Initialize a Rux package in the current directory ## Synopsis ```sh rux init [options] ``` ## Behavior Creates a manifest and starter source in the current directory. `--executable`, `--shared`, `--static`, and `--source` are mutually exclusive; Executable is the default. `--namespace` prepares a registry identity. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------ | ----------------------------------------------------------- | | `--executable` | Create an Executable package with an entry point (default) | | `--shared` | Create a SharedLibrary package | | `--static` | Create a StaticLibrary package | | `--source` | Create a SourceLibrary package compiled into its dependents | | `--namespace ` | Set the registry namespace required to publish | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux init rux init --executable rux init --shared --namespace Rux rux init --static rux init --source ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux new`](https://rux-lang.dev/docs/cli/new) # rux install ## Purpose Install dependencies ## Synopsis ```sh rux install rux install [namespace]/[package] rux install [namespace]/[package]@[requirement] ``` ## Behavior Without an operand, installs registry dependencies from the current package or every workspace member. With a namespaced identity, installs that package without adding it to the manifest. Resolution honors requirements, target conditions, transitive dependencies, yank state, `MinRux`, published SHA-256 checksums, and exact-version cache directories. ## Arguments | Argument | Required | Description | | --------- | -------- | ------------------------------------------------------ | | `package` | No | A namespaced package identity and optional requirement | ## Options | Option | Description | | ------------------- | ---------------------------------------------------- | | `--registry ` | Registry API base URL to resolve and download from | | `--target ` | Install dependencies applicable to a target platform | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux install rux install Rux/Io rux install Rux/Io@^0.1.0 rux install --target windows-x86_64 rux install --registry http://localhost:8080 ``` ## Environment variables `RUX_REGISTRY_URL` sets the registry when `--registry` is absent. `LOCALAPPDATA` on Windows or `HOME` elsewhere determines the user package cache. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux add`](https://rux-lang.dev/docs/cli/add) - [`rux update`](https://rux-lang.dev/docs/cli/update) - [`rux uninstall`](https://rux-lang.dev/docs/cli/uninstall) # rux lint ## Purpose Run source-level diagnostics without emitting build artifacts ## Synopsis ```sh rux lint [options] ``` ## Behavior Runs fast source-level lint rules for the package or every workspace member without building an artifact. Use `rux check` when full semantic validation is required. ## Arguments This command accepts no operands. ## Options This command has no command-specific options. Global options still apply. Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux lint rux lint --verbose ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux check`](https://rux-lang.dev/docs/cli/check) - [`rux fmt`](https://rux-lang.dev/docs/cli/fmt) # rux list ## Purpose List packages in the manifest file ## Synopsis ```sh rux list [options] ``` ## Behavior Lists manifest dependencies and the installed versions that satisfy them. `--global` lists every exact version in the user cache. ## Arguments This command accepts no operands. ## Options | Option | Description | | ---------- | --------------------------------------------------------------------------- | | `--global` | List packages in the global environment cache instead of the local manifest | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux list rux list --global ``` ## Environment variables `LOCALAPPDATA` on Windows or `HOME` elsewhere determines the user package cache. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux info`](https://rux-lang.dev/docs/cli/info) - [`rux install`](https://rux-lang.dev/docs/cli/install) - [`rux uninstall`](https://rux-lang.dev/docs/cli/uninstall) # rux login ## Purpose Authenticate to a Rux package registry ## Synopsis ```sh rux login [options] ``` ## Behavior Reads a registry token from stdin so it does not enter shell history or the process list, verifies it when the registry supports verification, and stores it per registry with owner-only permissions. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------ | ---------------------------------------- | | `--registry ` | Registry API base URL to authenticate to | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux login rux login --registry http://localhost:8080 ``` ## Environment variables `RUX_REGISTRY_URL` selects the registry when `--registry` is absent. `RUX_TOKEN` overrides a stored token for commands that authenticate. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux logout`](https://rux-lang.dev/docs/cli/logout) - [`rux publish`](https://rux-lang.dev/docs/cli/publish) # rux logout ## Purpose Forget the token stored for a Rux package registry ## Synopsis ```sh rux logout [options] ``` ## Behavior Removes only the stored token associated with the selected registry. It does not change the `RUX_TOKEN` environment variable or credentials for other registries. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------ | --------------------------------------------- | | `--registry ` | Registry API base URL to forget the token for | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux logout rux logout --registry http://localhost:8080 ``` ## Environment variables `RUX_REGISTRY_URL` selects the registry when `--registry` is absent. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux login`](https://rux-lang.dev/docs/cli/login) # rux new ## Purpose Create a new Rux package in a new directory ## Synopsis ```sh rux new [name] [options] ``` ## Behavior Creates a new directory containing a manifest and starter source. `--executable`, `--shared`, `--static`, and `--source` are mutually exclusive; Executable is the default. `--path` selects the parent directory and `--namespace` sets the registry namespace. ## Arguments | Argument | Required | Description | | -------- | -------- | ------------------------------ | | `name` | Yes | The package or dependency name | ## Options | Option | Description | | ------------------ | ----------------------------------------------------------- | | `--executable` | Create an Executable package with an entry point (default) | | `--shared` | Create a SharedLibrary package | | `--static` | Create a StaticLibrary package | | `--source` | Create a SourceLibrary package compiled into its dependents | | `--namespace ` | Set the registry namespace required to publish | | `--path ` | Create the workspace in a specific directory | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux new App rux new App --executable rux new Io --shared --namespace Rux rux new Math --static rux new Json --source ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux init`](https://rux-lang.dev/docs/cli/init) # rux pack ## Purpose Build the .ruxpkg archive that 'rux publish' uploads ## Synopsis ```sh rux pack [options] ``` ## Behavior Validates the publication profile and creates a deterministic `.ruxpkg` containing `Rux.toml`, `Src/`, and declared readme and license files. Workspaces and path dependencies cannot be packed. ## Arguments This command accepts no operands. ## Options | Option | Description | | --------------------- | ------------------------------------ | | `-o, --output ` | Write the archive to a specific path | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status The default archive is `-.ruxpkg` below configured build output. `-o` and `--output` select an explicit path. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux pack rux pack --output Dist/Math.ruxpkg ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux publish`](https://rux-lang.dev/docs/cli/publish) # rux publish ## Purpose Upload the package archive to a Rux package registry ## Synopsis ```sh rux publish [options] ``` ## Behavior Applies the same validation and packing rules as `rux pack`, then uploads an immutable version. `--dry-run` stops after validation and archive creation. Authentication tokens are never accepted as command-line options. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------ | ------------------------------------------------ | | `--registry ` | Registry API base URL to publish to | | `--dry-run` | Validate and build the archive without uploading | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux publish rux publish --dry-run rux publish --registry http://localhost:8080 ``` ## Environment variables `RUX_REGISTRY_URL` sets the registry when `--registry` is absent. `RUX_TOKEN` takes precedence over a token stored by `rux login`. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux pack`](https://rux-lang.dev/docs/cli/pack) - [`rux login`](https://rux-lang.dev/docs/cli/login) # rux remove ## Purpose Remove a dependency from the manifest ## Synopsis ```sh rux remove [name] ``` ## Behavior Removes the dependency with the given import name from `Rux.toml`. It does not remove cached package versions; use `rux uninstall` for cache cleanup. ## Arguments | Argument | Required | Description | | -------- | -------- | ------------------------------ | | `name` | Yes | The package or dependency name | ## Options This command has no command-specific options. Global options still apply. Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux remove Json rux remove Random ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux add`](https://rux-lang.dev/docs/cli/add) - [`rux uninstall`](https://rux-lang.dev/docs/cli/uninstall) # rux run ## Purpose Build and execute a runnable target ## Synopsis ```sh rux run [options] [-- args...] ``` ## Behavior Builds an Executable package, then runs it. SharedLibrary, StaticLibrary, and SourceLibrary packages cannot run. `--define` overrides compile-time configuration and `--release` selects the release profile. Only arguments after `--` are passed to the child program. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------------- | ------------------------------------------- | | `--define ` | Set or override a config compile-time value | | `--release` | Build with release profile | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Compiler progress and diagnostics use the usual streams. After a successful build, the command returns the child program's exit code. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux run rux run --release rux run -- --port 8080 ``` ## Environment variables `SOURCE_DATE_EPOCH` fixes compiler timestamps for reproducible builds. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux build`](https://rux-lang.dev/docs/cli/build) - [`rux test`](https://rux-lang.dev/docs/cli/test) # rux test ## Purpose Run package unit tests ## Synopsis ```sh rux test [options] ``` ## Behavior Discovers test Program packages, builds them with test mode enabled, and runs every executable. Workspaces include root and member test trees. `--define` and `--release` apply consistently to every test target. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------------- | --------------------------------------------------------- | | `--define ` | Set or override a config compile-time value | | `--release` | Build with release profile | | `--target ` | Build and run the tests for the specified target platform | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status A summary is written to stdout; compiler diagnostics and failed test details identify unsuccessful targets. The command returns 1 when any build or test fails. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux test rux test --release ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux check`](https://rux-lang.dev/docs/cli/check) - [`rux run`](https://rux-lang.dev/docs/cli/run) # rux uninstall ## Purpose Uninstall dependencies from the local cache ## Synopsis ```sh rux uninstall rux uninstall [namespace]/[package] rux uninstall [namespace]/[package]@[requirement] rux uninstall [options] ``` ## Behavior Without an operand, removes cached versions of dependencies declared by the current manifest. A namespaced identity removes all matching versions, or only versions matching an `@requirement`. `--global` is mutually exclusive with an operand and clears the package cache. ## Arguments | Argument | Required | Description | | --------- | -------- | ------------------------------------------------------ | | `package` | No | A namespaced package identity and optional requirement | ## Options | Option | Description | | ---------- | ------------------------------------------------------- | | `--global` | Uninstall every package in the global environment cache | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux uninstall rux uninstall Rux/Json rux uninstall Rux/Json@0.1.0 rux uninstall --global ``` ## Environment variables `LOCALAPPDATA` on Windows or `HOME` elsewhere determines the user package cache. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux install`](https://rux-lang.dev/docs/cli/install) - [`rux list`](https://rux-lang.dev/docs/cli/list) # rux update ## Purpose Update dependencies ## Synopsis ```sh rux update [options] ``` ## Behavior Re-resolves manifest dependencies and installs the newest versions allowed by their requirements and target conditions. `--global` updates every cached identity. Older exact versions remain cached until `rux uninstall` removes them. ## Arguments This command accepts no operands. ## Options | Option | Description | | ------------------- | --------------------------------------------------- | | `--global` | Update all entries in the global environment cache | | `--registry ` | Registry API base URL to resolve and download from | | `--target ` | Update dependencies applicable to a target platform | Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Primary results are written to stdout. Progress, warnings, and errors are written to stderr. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux update rux update --global rux update --target linux-aarch64 rux update --registry http://localhost:8080 ``` ## Environment variables `RUX_REGISTRY_URL` sets the registry when `--registry` is absent. `LOCALAPPDATA` on Windows or `HOME` elsewhere determines the user package cache. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux install`](https://rux-lang.dev/docs/cli/install) - [`rux uninstall`](https://rux-lang.dev/docs/cli/uninstall) # rux version ## Purpose Show information about the Rux toolchain version ## Synopsis ```sh rux version ``` ## Behavior Prints the compiler's source version. Human-readable version output may include build date and time; the stable help JSON contract contains only the semantic version. ## Arguments This command accepts no operands. ## Options This command has no command-specific options. Global options still apply. Global options may appear before or after the command. See [Global options](https://rux-lang.dev/docs/cli/global). ## Output and exit status Version information is written to stdout and the command exits 0. Success exits 0, invalid command-line usage exits 2, and compiler, validation, filesystem, network, or generation failures exit 1 unless stated otherwise above. ## Examples ```sh rux version rux -V rux --version ``` ## Environment variables This command defines no command-specific environment variables. The global color environment behavior is described under [Global options](https://rux-lang.dev/docs/cli/global#environment-variables). ## Related commands - [`rux help`](https://rux-lang.dev/docs/cli/help) # CLI Reference The Rux compiler is the source of truth for this reference. The checked-in contract was generated by `rux 0.4.0` using help schema version 1. ## Command syntax ```sh rux [global-options] [command-options] [operands] ``` Global options may appear before or after the command. Value options accept separated and equals forms. Only [`rux run`](https://rux-lang.dev/docs/cli/run) treats tokens after `--` as child-program arguments. ## Commands | Command | Purpose | | ---------------------------------------------------------- | ----------------------------------------------------- | | [`rux add`](https://rux-lang.dev/docs/cli/add) | Add a dependency to the manifest | | [`rux build`](https://rux-lang.dev/docs/cli/build) | Build the current package | | [`rux check`](https://rux-lang.dev/docs/cli/check) | Check package source code for errors without building | | [`rux clean`](https://rux-lang.dev/docs/cli/clean) | Remove build artifacts | | [`rux doc`](https://rux-lang.dev/docs/cli/doc) | Generate package documentation | | [`rux fmt`](https://rux-lang.dev/docs/cli/fmt) | Format source files and manifests | | [`rux help`](https://rux-lang.dev/docs/cli/help) | Show help information | | [`rux info`](https://rux-lang.dev/docs/cli/info) | Show package metadata and manifest information | | [`rux init`](https://rux-lang.dev/docs/cli/init) | Initialize a Rux package in the current directory | | [`rux install`](https://rux-lang.dev/docs/cli/install) | Install dependencies | | [`rux lint`](https://rux-lang.dev/docs/cli/lint) | Lint package source files | | [`rux list`](https://rux-lang.dev/docs/cli/list) | List dependencies | | [`rux login`](https://rux-lang.dev/docs/cli/login) | Store a registry token for publishing | | [`rux logout`](https://rux-lang.dev/docs/cli/logout) | Remove a stored registry token | | [`rux new`](https://rux-lang.dev/docs/cli/new) | Create a new Rux package | | [`rux pack`](https://rux-lang.dev/docs/cli/pack) | Build the publishable package archive | | [`rux publish`](https://rux-lang.dev/docs/cli/publish) | Publish the package to the registry | | [`rux remove`](https://rux-lang.dev/docs/cli/remove) | Remove a dependency from the manifest | | [`rux run`](https://rux-lang.dev/docs/cli/run) | Build and run the main executable | | [`rux test`](https://rux-lang.dev/docs/cli/test) | Run all test targets | | [`rux uninstall`](https://rux-lang.dev/docs/cli/uninstall) | Uninstall dependencies | | [`rux update`](https://rux-lang.dev/docs/cli/update) | Update dependencies | | [`rux version`](https://rux-lang.dev/docs/cli/version) | Show version information | ## Streams and exit status Primary output and JSON use stdout. Progress, warnings, and errors use stderr. Success, help, and version exit 0; invalid CLI usage exits 2; compiler or operational failures exit 1. [`rux run`](https://rux-lang.dev/docs/cli/run) returns the child program's exit code after a successful build. ## Structured help `rux help --json` returns the full stable contract. `rux help --json` returns one command. Both use `schemaVersion: 1` and omit build timestamps. ## Start here - [Global options](https://rux-lang.dev/docs/cli/global) - [Build a package](https://rux-lang.dev/docs/cli/build) - [Check without linking](https://rux-lang.dev/docs/cli/check) - [Generate API documentation](https://rux-lang.dev/docs/cli/doc) - [Package and publish](https://rux-lang.dev/docs/packaging) # Package Types Manifest Version 1 accepts exactly four case-sensitive package types: | Type | Manifest value | `rux build` | `rux run` | Native artifact | | -------------- | ----------------- | ----------- | --------- | --------------- | | Executable | `"Executable"` | Yes | Yes | See below | | Shared library | `"SharedLibrary"` | Yes | No | See below | | Static library | `"StaticLibrary"` | Yes | No | See below | | Source library | `"SourceLibrary"` | No | No | None | `Program`, `Library`, and `Source` are retired spellings and are invalid; there are no compatibility aliases. An **Executable** must define `Main()`. Its artifact is `Name.exe` on Windows and `Name` on ELF targets and macOS. A **SharedLibrary** exports its public symbols. It produces `Name.dll` and the `Name.lib` import library on Windows, `libName.so` on ELF targets, or `libName.dylib` on macOS. A **StaticLibrary** preserves unresolved external references for the final native link. It produces `Name.lib` on Windows or `libName.a` on ELF targets and macOS. A **SourceLibrary** is checked on its own and compiled directly into whichever package depends on it. It has no standalone build or run artifact. ::note Rux 0.4.0 still consumes every Rux dependency from source. Declaring a local dependency as `SharedLibrary` or `StaticLibrary` does not make the consuming package link its binary artifact yet. :: The compile-time `#build.outputKind` value is `OutputKind::Executable`, `SharedLibrary`, `StaticLibrary`, or `SourceLibrary` when a package is checked on its own. Source compiled as a dependency observes the consuming package's output kind. For Rux 0.4.0, only **SourceLibrary** packages can be packed or published to a registry. Shared and static libraries are local native artifacts in this release. # Yanking Yanking withdraws a published version from **new** dependency resolution while leaving it available to builds that already depend on it. It is the way to retire a release that turned out to be broken, insecure, or published by mistake. ## What yanking does and does not do | Yanking **does** | Yanking **does not** | | -------------------------------------------------------- | ---------------------------------------- | | Stop resolvers choosing the version for a new dependency | Delete the release or its files | | Mark the version on the package page | Break builds that already resolved to it | | Take effect immediately | Remove the version from the index | A yanked version stays downloadable, and the registry keeps serving its metadata and archive. That is deliberate: a lockfile or a CI pipeline pinned to it keeps working instead of failing the moment you withdraw the release. Yanked versions stay in the resolver index for the same reason — an existing build must still be able to identify what it pinned. ::warning Yanking is not deletion and not a security control. The bytes remain public. If you published a credential, treat it as compromised and rotate it — yanking does not take it back. :: ## Yanking a version Open the package from your [dashboard](https://rux-lang.dev/packages/-/dashboard) and yank the version there. You need `owner` or `maintainer` [membership](https://rux-lang.dev/docs/packaging/namespaces#sharing-a-namespace) of the package's namespace, and — for the API — a token with the [`yank` scope](https://rux-lang.dev/docs/packaging/tokens#scopes). Unyanking restores the version to normal resolution. Both directions are idempotent: yanking an already-yanked version changes nothing and is not recorded twice. ::note There is no `rux yank` command yet. Yanking is done from the registry, or through the `PATCH /v1/packages/{namespace}/{package}/{version}` API with a `yank`-scoped token. :: ## When to yank Yank when a release actively harms the people who install it — a security problem, corrupted contents, a build that cannot possibly work, or an accidental publish. Do not yank simply because a newer version exists. Ordinary supersession is what [version requirements](https://rux-lang.dev/docs/packaging/dependencies#version-requirements) are for, and yanking healthy releases makes a package's history harder to depend on. ## After yanking Publish a fixed version. Because published versions are [immutable](https://rux-lang.dev/docs/packaging/versioning#immutability), the fix is always a new release rather than a correction of the old one — usually a patch bump, or a minor bump if the fix changed the API. Say what happened in your changelog and, for anything security-related, in the repository's advisories. A yank marks a version in the registry; it does not explain itself to the people who were using it. # Directory Layout Every package follows a predictable directory layout. The exact contents depend on the [package type](https://rux-lang.dev/docs/packaging/types). ## Executables An Executable package builds from `Src/Main.rux`, which defines `Main()`: ```text App/ ├── Rux.toml # Package manifest ├── Src/ # Source files │ ├── Main.rux # Contains the Main entry point │ └── ... # Other folders or source files ├── Bin/ # Created by the compiler │ ├── Debug/ # Debug build output │ └── Release/ # Release build output ├── Temp/ # Intermediate build files ├── README.md # Brief Markdown description ├── LICENSE.md # License of the package └── .gitignore # Excludes /Bin and /Temp from the repository ``` `Bin/` and `Temp/` are generated by the toolchain and should be listed in `.gitignore`. The root may also hold other directories such as `Art`, `Docs`, `Icons`, or `Images`. `README.md` and `LICENSE.md` are the conventional targets of the [manifest](https://rux-lang.dev/docs/packaging/manifest)'s `ReadmeFile` and `LicenseFile` fields, which is what makes them show up on the package's registry page. ## Native libraries SharedLibrary and StaticLibrary packages use the same layout without an entry point. Scaffolding creates `Src/Lib.rux`: ```text Math/ ├── Rux.toml # Package manifest ├── Src/ # Source files │ ├── Lib.rux # Library declarations and public symbols │ └── ... # Other folders or source files ├── Bin/ # Created by the compiler │ ├── Debug/ # Debug build output │ └── Release/ # Release build output ├── Temp/ # Intermediate build files ├── README.md # Brief Markdown description ├── LICENSE.md # License of the package └── .gitignore # Excludes /Bin and /Temp from the repository ``` The output filename follows the platform conventions listed under [Package Types](https://rux-lang.dev/docs/packaging/types). ## Source libraries SourceLibrary packages cannot be built directly. They are compiled into other projects as [dependencies](https://rux-lang.dev/docs/packaging/dependencies), so they have no `Bin/` output: ```text Core/ ├── Rux.toml # Package manifest ├── Src/ # Source files │ ├── Lib.rux # Source-library declarations │ └── ... # Other folders or source files ├── README.md # Brief Markdown description ├── LICENSE.md # License of the package └── .gitignore # Excludes some items if necessary ``` # Package Manifest Every Rux package is described by a `Rux.toml` file at its root. It carries the package's identity, metadata, build settings, and dependencies, and it is required for anything that takes part in the build system or a workspace. ```toml [Manifest] Version = 1 MinRux = "0.4.0" [Package] Namespace = "Acme" Name = "Widget" Version = "1.2.3" Type = "SourceLibrary" Description = "A widget for every occasion" Authors = ["Your Name "] License = "MIT" [Dependencies] Io = { Namespace = "Rux", Version = "^1.0.0" } ``` The file is UTF-8 and uses a deliberately small subset of [TOML](https://toml.io){rel=""nofollow""}: basic quoted strings, integers, booleans, arrays of quoted strings, the dependency inline table, comments, and the tables documented on this page. Section names, field names, and enum values are **PascalCase**, and parsing is case-sensitive. ::warning Manifest Version 1 is strict. Unknown sections and unknown fields are **errors**, not warnings — a typo fails the build instead of silently changing it. Duplicate keys, wrong value types, missing required fields, and invalid identities are rejected the same way, each with the file, line, and column of the offending token. :: ## `[Manifest]` The schema header. Every manifest starts with one. | Field | Presence | Contract | | --------- | ------------------------------------- | ------------------------------------------------------ | | `Version` | Required | Integer schema version; `1` is the only accepted value | | `MinRux` | Optional locally, required to publish | Semantic version, at least `0.4.0` | `MinRux` is the oldest compiler release that can build the package. A compiler older than the declared minimum refuses to build it. Leaving it out keeps `rux new` and `rux init` free of a field only publication needs. ::note `[Manifest].Version` is the schema version of the file. `[Package].Version` is your package's own release number. They are unrelated. :: ## `[Package]` Identity and metadata. ```toml [Package] Namespace = "Acme" Name = "Widget" Version = "1.2.3" Type = "SharedLibrary" Description = "A widget for every occasion" Authors = ["Your Name "] Keywords = ["Widget", "Ui"] License = "MIT" LicenseFile = "LICENSE.md" Repository = "https://github.com/acme/widget" Homepage = "https://acme.dev" ReadmeFile = "README.md" ``` | Field | Presence | Contract | | ------------- | ------------------------------------- | ----------------------------------------------------------------------------------------- | | `Namespace` | Optional locally, required to publish | One [identity segment](https://rux-lang.dev/docs/packaging/namespaces) | | `Name` | Required | One identity segment | | `Version` | Required | Strict [Semantic Version](https://rux-lang.dev/docs/packaging/versioning), no leading `v` | | `Type` | Required | Exactly `Executable`, `SharedLibrary`, `StaticLibrary`, or `SourceLibrary` | | `Description` | Optional | Short summary | | `Authors` | Optional | Array of strings | | `Keywords` | Optional | Array of identity segments, unique after normalization | | `License` | Optional | SPDX expression | | `LicenseFile` | Optional | Package-relative path | | `Repository` | Optional | Absolute `http`/`https` URL with a host and no credentials | | `Homepage` | Optional | Absolute `http`/`https` URL with a host and no credentials | | `ReadmeFile` | Optional | Package-relative path | [`Type`](https://rux-lang.dev/docs/packaging/types) decides what the package produces and which commands accept it. `Authors` must be an array — the older scalar spelling is invalid. The two licence fields are independent, and setting both is the norm. `License` is what machines read — it is the field a dependency-tree licence audit and the registry's filters work from. `LicenseFile` is what people read: it points at the licence text shipped inside the package, so it carries the copyright holder and year that an SPDX identifier cannot express, it is covered by the release checksum, and it stays readable offline. The conventional target is the `LICENSE.md` in the standard [package layout](https://rux-lang.dev/docs/packaging/layout). A licence with no SPDX identifier uses SPDX's own `LicenseRef-` form alongside the file: ```toml [Package] License = "LicenseRef-Acme-Commercial" LicenseFile = "LICENSE.md" ``` ## `[Dependencies]` Each key is the name you import the dependency under; each value is an inline table. ```toml [Dependencies] Io = { Namespace = "Rux", Version = "^1.0.0" } Json = { Namespace = "Acme", Package = "FastJson", Version = ">=2.0.0, <3.0.0", TargetOS = ["Linux", "MacOS"] } Util = { Path = "../Util", TargetOS = ["Windows"] } ``` | Form | Requires | Notes | | -------- | ---------------------- | -------------------------------------------------------------------------- | | Registry | `Namespace`, `Version` | Resolved from the [registry](https://rux-lang.dev/docs/packaging/registry) | | Path | `Path` | A local directory; cannot carry `Namespace`/`Version` | Either form may set `Package` when the dependency's own name differs from the import name; it defaults to the import name. Two dependencies cannot produce the same import name after normalization. Either form may also set a non-empty, duplicate-free `TargetOS` allow-list. The dependency applies only when the selected target operating system appears in the list. Omitting `TargetOS` makes it unconditional. Values are case-sensitive and exactly `Windows`, `Linux`, `MacOS`, `FreeBSD`, `OpenBSD`, `NetBSD`, `DragonFlyBSD`, or `Illumos`. ::warning A path dependency makes a manifest **unpublishable** — the directory it names exists only on your machine. See [Dependencies](https://rux-lang.dev/docs/packaging/dependencies) for the full requirement syntax. :: ## `[Build]` Optional build settings. ```toml [Build] Output = "Dist" [Build.Defines] Channel = "Nightly" CheckedArithmetic = true Retries = 3 ``` `Output` is a package-relative path and defaults to `Bin`. `[Build.Defines]` is an optional table of string, boolean, and integer values exposed to [compile-time configuration](https://rux-lang.dev/docs/lang/comptime/context) through `#config`, and overridable per build with `--define NAME[=VALUE]`. ## Workspaces A workspace manifest groups member packages instead of describing one: ```toml [Manifest] Version = 1 [Workspace] Packages = [ "Packages/Math", "Packages/Memory", ] ``` `Packages` is a non-empty, duplicate-free array of explicit relative paths — no globs, no parent traversal. `[Workspace]` and `[Package]` are mutually exclusive; a manifest has exactly one of them. A workspace declares no dependencies or build settings and cannot be published. ## Paths Manifest paths are UTF-8, relative, and `/`-separated. Backslashes, absolute roots, empty components, and `.` components are invalid. A field whose name ends in `File` names a path **inside** the package — it must exist in the published archive, and like workspace paths it rejects `..`. Dependency and output paths may begin with `..` components, but parent traversal cannot follow a normal component. ## Validation profiles The rules applied depend on the operation, not on anything stored in the file. | Profile | Applies to | Accepts | | --------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | | **Local** | `build`, `check`, `run`, `test`, … | Package and workspace manifests; `Namespace` and `MinRux` optional; path dependencies allowed | | **Publication** | [`pack`](https://rux-lang.dev/docs/cli/pack), [`publish`](https://rux-lang.dev/docs/cli/publish) | Package manifests only; `Namespace` and `MinRux` required; path dependencies rejected | Both `rux pack` and `rux publish` apply the publication profile before doing any other work, so a manifest that cannot be published is reported locally rather than by the registry. ## Canonical form `rux fmt --manifest-only`, `rux add`, `rux remove`, `rux new`, and `rux init` all write the same order: `[Manifest]`, then `[Package]` or `[Workspace]`, then `[Dependencies]`, `[Build]`, and `[Build.Defines]`. Only recognized fields are written. Metadata arrays and `TargetOS` values keep their order; dependency and define keys use a stable deterministic order. In a dependency inline table, `TargetOS` follows `Version` or `Path`. Changing a dependency with `rux add` preserves its existing target condition. ## Limits All limits count UTF-8 bytes. | Resource | Limit | | --------------------------------- | -------: | | Manifest source | 65,536 | | Dependencies / workspace packages | 256 each | | Defines per table | 128 | | Authors / keywords | 32 each | | Description | 2,048 | | Author | 256 | | URL or path | 2,048 | | SPDX expression / version range | 512 | | Semantic version | 256 | | Identity segment | 64 | # Dependencies Dependencies are other packages your project builds against. They are declared in the [`[Dependencies]`](https://rux-lang.dev/docs/packaging/manifest#dependencies) table of `Rux.toml` and managed with the `rux` command-line tool. ## Declaring a dependency Each key is the name you import the dependency under, and each value is an inline table: ```toml [Dependencies] Io = { Namespace = "Rux", Version = "^1.0.0" } Json = { Namespace = "Acme", Package = "FastJson", Version = ">=2.0.0, <3.0.0" } Windows = { Namespace = "Rux", Version = "0.1.0", TargetOS = ["Windows"] } Bsd = { Path = "../Bsd", TargetOS = ["DragonFlyBSD", "FreeBSD", "NetBSD", "OpenBSD"] } ``` A **registry** dependency needs a `Namespace` and a `Version`. A **path** dependency needs a `Path` and cannot carry either. Both may set `Package` when the dependency's own name differs from the import name. Both forms may set `TargetOS` to a non-empty allow-list. The dependency participates in builds and resolution only when the selected target OS appears in the list. Omitting it means every target. The exact supported names are `Windows`, `Linux`, `MacOS`, `FreeBSD`, `OpenBSD`, `NetBSD`, `DragonFlyBSD`, and `Illumos`; duplicates and other spellings are invalid. An active import of a dependency that excludes the selected build target is an error. Imports removed by `when #target.os` are inactive and therefore allowed, so platform packages should guard their imports with the same target condition as their dependency entry. ::warning A path dependency is fine for local development but makes the manifest **unpublishable**. Replace it with a registry dependency before you [publish](https://rux-lang.dev/docs/packaging/publishing). :: ## Adding and removing `rux add` writes the entry for you: ```sh # Registry dependency, any stable version rux add Rux/Io # Registry dependency with a requirement rux add Rux/Math@^0.1.0 # Local path dependency rux add Util --path ../Util ``` Omitting the requirement writes `*`. A registry dependency needs a namespace — `rux add Io` alone is rejected, because the bare name does not identify a package in the registry. Remove one with [`rux remove`](https://rux-lang.dev/docs/cli/remove): ```sh rux remove Json ``` ## Version requirements A requirement is a comma-separated **intersection** of comparators; whitespace around them is insignificant. | Requirement | Matches | | ----------------- | -------------------------------------------- | | `*`, `x`, `X` | Any stable version; cannot be combined | | `=1.2.3` | Exactly that version | | `>=1.2.0, <2.0.0` | Both bounds at once | | `^1.2.3` | `>=1.2.3, <2.0.0` | | `^0.2.3` | `>=0.2.3, <0.3.0` | | `^0.0.3` | `>=0.0.3, <0.0.4` | | `~1.2.3` | `>=1.2.3, <1.3.0` | | `1.2.3` | Same as `^1.2.3` — a bare operand is a caret | Operands may be partial (`^1.0`, `2`) or use a trailing component wildcard (`1.2.*`). A numeric component cannot follow a wildcard, and a prerelease or build suffix needs a complete `major.minor.patch` operand. Requirements reject OR expressions, hyphen ranges, space-separated comparators, more than three numeric components, and more than 32 comparators. A prerelease is only eligible when some comparator names the same `major.minor.patch` and carries a prerelease operand of its own, so `^1.0.0` never selects `2.0.0-beta.1`. See [Versioning](https://rux-lang.dev/docs/packaging/versioning) for how versions are ordered. ## Installing and updating Download everything the manifest declares: ```sh rux install ``` Install for another supported target without changing hosts: ```sh rux install --target windows-x86_64 ``` Update to the latest compatible versions: ```sh # Packages listed in Rux.toml rux update # Everything in the local store rux update --global # Resolve and update for Linux rux update --target linux-x86_64 ``` Both commands default to the host target. Before resolution they discard root requirements and transitive edges whose `TargetOS` excludes the selected target, so filtered dependencies cannot create version conflicts or downloads. Naming a package explicitly with `rux install` still installs that root package; only its conditional transitive edges are filtered. ## Package storage Downloaded packages live in a shared, per-user store separate from any project: | Platform | Location | | ------------- | ----------------------------- | | Windows | `%LOCALAPPDATA%\Rux\Packages` | | Linux / macOS | `~/.rux/packages` | Each package occupies its own subdirectory named after the package. The store is managed by [`rux install`](https://rux-lang.dev/docs/cli/install) and [`rux update`](https://rux-lang.dev/docs/cli/update) and should not be edited by hand; use [`rux uninstall`](https://rux-lang.dev/docs/cli/uninstall) to remove entries. ## Workspaces Inside a [workspace](https://rux-lang.dev/docs/packaging/manifest#workspaces), a registry dependency that matches a member package by normalized identity resolves to that member instead of the registry. That is what lets the packages in one repository depend on each other in publishable registry form while still building entirely from the local tree. # The Registry The Rux registry hosts published packages. It is browsable at [rux-lang.dev/packages](https://rux-lang.dev/packages) and served programmatically from `https://api.rux-lang.dev`. Reading from it — browsing, searching, downloading, installing — needs no account. ## Finding a package The [registry home](https://rux-lang.dev/packages) is the catalog. Search matches package names, namespaces, descriptions and keywords; an empty query browses everything. Narrow the results by namespace, keyword or package type, and order them by relevance, name, total or recent downloads, most recently updated, or most recently added. Search is literal rather than fuzzy. Punctuation and wildcard characters are treated as text, so `http-client` searches for that phrase instead of being read as a pattern. Packages also carry [keywords](https://rux-lang.dev/packages/-/keywords), which are a good way into a subject area when you do not yet know what a package is called. ## The package page Each package has a page at `/packages//` showing: - every published **version**, newest first, with [yanked](https://rux-lang.dev/docs/packaging/yanking) releases marked; - the **manifest metadata** of the selected release — description, authors, keywords, license, repository and homepage links, and the minimum Rux version it needs; - its **dependencies** and the packages that **depend on it**; - the **README** as published, and the **license file** when the manifest names one; - the **SHA-256 checksum** and byte size of the release archive; and - **download statistics** over the last thirty days and all time. Everything shown comes from the release that was uploaded. The registry does not edit or re-render what you publish. ## Installing from the registry Add a dependency by its qualified identity and install it: ```sh rux add Rux/Io@^1.0.0 rux install ``` `rux add` records the entry in `Rux.toml`; [`rux install`](https://rux-lang.dev/docs/cli/install) downloads it and everything it depends on into the shared [package store](https://rux-lang.dev/docs/packaging/dependencies#package-storage). See [Dependencies](https://rux-lang.dev/docs/packaging/dependencies) for requirement syntax and updating. ## Identity is case-insensitive Registry lookup lowercases ASCII letters and folds `_` to `-`, so `Rux/My_Pkg` and `rux/my-pkg` are the same package. Tools and pages preserve the spelling an author chose for display. This is described in full under [Namespaces](https://rux-lang.dev/docs/packaging/namespaces). ## Availability The registry is a live service, so registry-backed commands need network access. A package already in your local store builds offline; workspace members resolve locally and never consult the registry at all. # Namespaces A published package is identified by two parts: a **namespace** and a **name**. The namespace is the account or organisation that owns the package, so `Rux/Io` and `Acme/Io` are different packages that happen to share a name. ```toml [Package] Namespace = "Acme" Name = "Widget" ``` Together they form the identity used everywhere — `Acme/Widget` on the command line, in a dependency entry, and in the registry URL. ::note `Namespace` is optional locally. A package without one is **local-only**: it builds, runs, and can be depended on by path, but it cannot be published. `rux new` and `rux init` take an optional `--namespace ` when you already know where a package will live. :: ## Identity segments Namespaces, package names, keywords, and dependency import names all use the same grammar. A segment is 1 to 64 bytes of ASCII alphanumeric characters separated by single `-` or `_` characters. It cannot start or end with a separator, and it cannot contain two separators in a row. | Valid | Invalid | Why | | -------- | --------- | ---------------------- | | `Rux` | `-Pkg` | Leading separator | | `My_Pkg` | `Pkg-` | Trailing separator | | `my-pkg` | `My__Pkg` | Adjacent separators | | `7zip` | `My.Pkg` | `.` is not a separator | ## Normalization Lookup and uniqueness lowercase ASCII letters and fold `_` to `-`. `Rux/My_Pkg` and `rux/my-pkg` therefore identify the same package, and only one of them can be claimed. Tools preserve the spelling you wrote for display, so a package published as `Acme/FastJson` keeps its capitals on its page and in `Rux.toml` while resolving as `acme/fastjson`. Registry URLs use the normalized form. Keywords follow the same rule and must stay unique after normalization, so `["Http", "HTTP"]` is rejected as a duplicate. ## Claiming a namespace Sign in to the registry with GitHub and claim one from your [dashboard](https://rux-lang.dev/packages/-/dashboard). Claiming makes you its first **owner**. Because normalized spellings collide, `Foo_Bar` and `foo-bar` cannot be claimed separately. A namespace you own is the only place you can publish. Attempting to publish into a namespace that does not exist, or one you are not a member of, is rejected — see [Publishing](https://rux-lang.dev/docs/packaging/publishing#when-publishing-fails). ## Sharing a namespace A namespace has two roles: | Role | Can | | ------------ | -------------------------------------------------------------------------------- | | `owner` | Publish, yank, invite and remove members, change roles, and manage the namespace | | `maintainer` | Publish and yank | Owners invite an existing registry user by GitHub login from the namespace page in the dashboard. Invitations expire after seven days, and only one unresolved invitation can exist for a user and namespace at a time. The invitee accepts or declines from their own dashboard; an owner can revoke an invitation before it is accepted. A namespace always keeps at least one owner. Removing the last one is refused, and so is deleting an account that is the final owner of any namespace — promote or add another owner first. # API Tokens An API token lets the `rux` command line act on your registry account without a browser. Publishing needs one; browsing and installing do not. ## Creating a token Sign in to the registry with GitHub, then open [Dashboard → Tokens](https://rux-lang.dev/packages/-/dashboard/tokens) and create one. A token has: - a **display name**, so you can tell your tokens apart later; - one to three **scopes**; and - an optional **expiry** — 30, 90, or 365 days, a date you choose, or never. ::warning The credential is shown **once**, in the response to creating it. The registry stores only a SHA-256 hash and a short display prefix, so a lost token cannot be recovered — revoke it and create another. :: ## Scopes A token carries only the permissions you give it. Choose the narrowest set that does the job. | Scope | Allows | | ----------- | ------------------------------------------------------------------------------ | | `publish` | Publishing new package versions | | `yank` | [Yanking and unyanking](https://rux-lang.dev/docs/packaging/yanking) a version | | `namespace` | Managing namespaces, members, and invitations | A token with the wrong scope is rejected with a clear error rather than partially succeeding. Scopes do not grant namespace membership: a `publish` token can only publish into namespaces its owner already belongs to. ## Using a token On your own machine, hand the token to [`rux login`](https://rux-lang.dev/docs/cli/login) once and every later [`rux publish`](https://rux-lang.dev/docs/cli/publish) picks it up: ```sh rux login ``` You are prompted for the token, and your typing is not echoed. It is stored per registry in a file only your account can read — `%LOCALAPPDATA%\Rux\Credentials.toml` on Windows, `~/.rux/credentials.toml` elsewhere — and [`rux logout`](https://rux-lang.dev/docs/cli/logout) removes it again. There is deliberately **no `--token` flag**, and `rux login` reads the token from standard input rather than an argument for the same reason: a credential on the command line ends up in shell history and in the process list, where other users on the machine can read it. ### In CI Set `RUX_TOKEN` instead. It overrides any stored token, so a job is never shadowed by a file left behind on a self-hosted runner. ::code-group ```sh [Linux / macOS] export RUX_TOKEN='rux_pat_...' rux publish ``` ```powershell [Windows] $env:RUX_TOKEN = 'rux_pat_...' rux publish ``` :: Store the token as an encrypted secret and expose it as `RUX_TOKEN` for the publishing step only. ::note Prefer `rux login` to exporting `RUX_TOKEN` from your shell profile. An exported variable is inherited by **every** process your shell starts and shows up in `env` dumps pasted into bug reports; the credentials file is read by `rux` alone, and only when it publishes. :: ## Managing tokens The tokens page lists every token you have created, newest first, including expired and revoked ones. It shows each token's display name, prefix, scopes, creation time, last use, expiry, and status — `active`, `expired`, or `revoked`. It never shows a credential again. Revoke a token the moment you suspect it has leaked. Revocation takes effect immediately, and revoked tokens stay in the list as history. Revoking is not the same as [`rux logout`](https://rux-lang.dev/docs/cli/logout), which only forgets the token on the machine you run it on — a leaked token has to be revoked here. Releases published with a revoked token are unaffected: publication is permanent and independent of the credential that made it. ## Deleting your account Deleting your registry account revokes every session and token you hold and clears your GitHub identity. Packages you published stay published — see [Yanking](https://rux-lang.dev/docs/packaging/yanking) for what can and cannot be withdrawn. # Publishing Publishing uploads a package release to the [registry](https://rux-lang.dev/docs/packaging/registry), where anyone can depend on it. A published version is permanent. ## Before you publish Publication applies stricter rules than a local build. Your manifest must: - declare a [`Namespace`](https://rux-lang.dev/docs/packaging/namespaces) you own or maintain; - declare `MinRux`, the oldest compiler release that can build the package; - describe a package, not a [workspace](https://rux-lang.dev/docs/packaging/manifest#workspaces); and - carry **no path dependencies** — every dependency must resolve from the registry. - use `Type = "SourceLibrary"`; Rux 0.4.0 does not publish native binary artifacts. ```toml [Manifest] Version = 1 MinRux = "0.4.0" [Package] Namespace = "Acme" Name = "Widget" Version = "1.0.0" Type = "SourceLibrary" ``` You also need an [API token](https://rux-lang.dev/docs/packaging/tokens) with the `publish` scope. Store it once with [`rux login`](https://rux-lang.dev/docs/cli/login): ```sh rux login ``` In CI, set `RUX_TOKEN` instead — it overrides any stored token. Nothing about publishing builds or checks your package, so run [`rux check`](https://rux-lang.dev/docs/cli/check) first. ::warning Both `rux pack` and `rux publish` accept only SourceLibrary packages in Rux 0.4.0. They reject Executable, SharedLibrary, and StaticLibrary before credential lookup, archive creation, filesystem output, or network access. Binary distribution and binary dependency consumption will be enabled only by a later coordinated compiler and registry release. :: ## The package archive A release is a single `.ruxpkg` file — a ZIP archive containing: | Entry | Included when | | ------------ | ----------------------------------------------- | | `Rux.toml` | Always, at the archive root | | `Src/**` | Always; at least one `Src/**/*.rux` is required | | `README.md` | `Package.ReadmeFile` names it | | `LICENSE.md` | `Package.LicenseFile` names it | Build one without uploading anything: ```sh rux pack ``` It is written to your build output directory as `-.ruxpkg`, or wherever `--output` says. Entries are sorted and carry a fixed timestamp, so packing the same tree twice produces byte-identical archives. Limits: 5 MiB for the archive, 10 MiB expanded, 2 MiB for any single file, 64 KiB for the manifest, and 1,024 entries. ## Publishing a release Validate first — this does everything except upload, and needs no token: ```sh rux publish --dry-run ``` Then publish: ```sh rux publish ``` ```text Packed Acme/Widget 1.0.0 (12 files, 24.1 KiB) Uploading to https://api.rux-lang.dev Published Acme/Widget 1.0.0 ``` The release appears immediately at `/packages/acme/widget`. ## Publishing a new version Bump `Package.Version` and publish again. An existing version cannot be replaced — see [Versioning](https://rux-lang.dev/docs/packaging/versioning) for how to choose the next number, and [Yanking](https://rux-lang.dev/docs/packaging/yanking) if you need to withdraw one. ## When publishing fails Problems in your manifest or archive are reported locally, before anything is uploaded. The registry reports the rest. | Message | Cause and fix | | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | `publication requires [Package].Namespace` | Add a `Namespace` to the manifest | | `publication requires [Manifest].MinRux` | Add `MinRux`, at least `0.4.0` | | `dependency '…' uses Path = "…"` | Replace the path dependency with a registry one | | `a workspace cannot be published` | Run the command from a member package | | `[Package].Type = "…" cannot be published by Rux 0.4.0; this release publishes only Type = "SourceLibrary"` | Change the package to `SourceLibrary`; native artifacts are local-only in 0.4.0 | | `no credential for …` | Run [`rux login`](https://rux-lang.dev/docs/cli/login), or set `RUX_TOKEN` | | `namespace '…' is not claimed` | [Claim it](https://rux-lang.dev/docs/packaging/namespaces#claiming-a-namespace) first | | `does not own or maintain namespace '…'` | Ask an owner to invite you | | `lacks the 'publish' scope` | Create a token with the right [scope](https://rux-lang.dev/docs/packaging/tokens#scopes) | | `version … is already published` | Bump `Package.Version` — published versions are immutable | ## Publishing to another registry `rux publish` targets the official registry by default. `--registry ` or the `RUX_REGISTRY_URL` environment variable points it somewhere else, which is how you exercise the whole flow against a local registry before releasing for real. ```sh rux login --registry http://localhost:8080 rux publish --registry http://localhost:8080 ``` Credentials are stored per registry, so logging in to a local registry never sends it the token you use for the official one. # Versioning Every release carries a version in `Package.Version`. Rux uses strict [Semantic Versioning 2.0.0](https://semver.org){rel=""nofollow""}, without a leading `v`. ```toml [Package] Version = "1.2.3" ``` ## What the numbers mean `MAJOR.MINOR.PATCH`, incremented by the kind of change you made: | Part | Increment when | | ------- | ------------------------------------------------------------- | | `MAJOR` | You made an incompatible change to your public API | | `MINOR` | You added functionality that existing code keeps working with | | `PATCH` | You fixed something without changing the API | Below `1.0.0` the leading zero absorbs the meaning: a `0.x` release may break compatibility on a minor bump, which is why `^0.2.3` only allows `<0.3.0`. See [version requirements](https://rux-lang.dev/docs/packaging/dependencies#version-requirements). ## Prereleases A prerelease suffix marks a version as not yet ready: ```toml Version = "2.0.0-alpha.1" ``` Prereleases sort **before** their release — `2.0.0-alpha.1` precedes `2.0.0` — and are never selected by an ordinary requirement. A resolver picks one only when a comparator names the same `major.minor.patch` and carries a prerelease of its own, so `^1.0.0` will never quietly upgrade you to `2.0.0-beta.1`. ## Build metadata A `+` suffix carries build metadata: ```toml Version = "1.0.0+linux" ``` Metadata is **ignored when ordering** versions but is **part of publication identity**. `1.0.0+linux` and `1.0.0+windows` are two distinct releases that compare as equal for requirement matching. Most packages never need this; reach for it only when one logical version genuinely ships as several artifacts. ## Immutability **A published version can never be replaced.** Publishing a version that already exists is rejected: ```text error: version 1.0.0 of Acme/Widget is already published; published versions are immutable, so publish a new version ``` Immutability is what makes a build reproducible: a dependency resolved today resolves to the same bytes next year. It also means a mistake ships. If you published something broken, publish a fixed patch release and [yank](https://rux-lang.dev/docs/packaging/yanking) the bad one — there is no way to overwrite or delete it. Because build metadata is part of identity, `1.0.0+fix` counts as a different version from `1.0.0` and will be accepted. Do not use that as a substitute for a patch bump: resolvers treat the two as equal precedence, so which one a build selects is not something you control. ## Choosing MinRux `Manifest.MinRux` is the oldest compiler release that can build your package, and it is required to publish. Set it to the earliest version you have actually tested against, not to whatever you happen to have installed — a compiler older than the declared minimum refuses to build the package at all. Raising `MinRux` in a new release is a compatibility change for anyone on an older toolchain, so treat it like any other breaking change when choosing the version number. # Packaging A **package** is the unit Rux builds, ships, and depends on. Every package is a directory with a [`Rux.toml` manifest](https://rux-lang.dev/docs/packaging/manifest) at its root and its sources under `Src/`. That is true whether the package never leaves your machine or ends up on the public registry. This section follows a package through its whole life: describing it, depending on other packages, finding it, and publishing it for other people to use. ## Local packaging Everything here works offline, with no account and no registry. | Page | Covers | | ---------------------------------------------------------------- | ---------------------------------------------------------- | | [Package Types](https://rux-lang.dev/docs/packaging/types) | Executable, shared, static, and source libraries | | [Directory Layout](https://rux-lang.dev/docs/packaging/layout) | Where sources, build output, and metadata live | | [Package Manifest](https://rux-lang.dev/docs/packaging/manifest) | Every field of `Rux.toml`, and the rules each one follows | | [Dependencies](https://rux-lang.dev/docs/packaging/dependencies) | Declaring, resolving, and updating what your package needs | ## The registry The public registry at [rux-lang.dev/packages](https://rux-lang.dev/packages) hosts published packages. Reading from it needs no account; publishing to it does. | Page | Covers | | ------------------------------------------------------------ | ------------------------------------------------------------- | | [The Registry](https://rux-lang.dev/docs/packaging/registry) | Browsing, searching, and installing published packages | | [Namespaces](https://rux-lang.dev/docs/packaging/namespaces) | Package identity, claiming a namespace, and sharing ownership | | [API Tokens](https://rux-lang.dev/docs/packaging/tokens) | Credentials that let the CLI act on your behalf | | [Publishing](https://rux-lang.dev/docs/packaging/publishing) | Preparing a package and uploading a release | | [Versioning](https://rux-lang.dev/docs/packaging/versioning) | Semantic Versioning, immutability, and version requirements | | [Yanking](https://rux-lang.dev/docs/packaging/yanking) | Withdrawing a release without breaking existing builds | ## From a new package to a published one The short version of the whole section: ```sh rux new Widget --source --namespace Acme # Create a publishable package rux add Io # Depend on something rux check # Make sure it compiles rux pack # Inspect the archive rux publish --dry-run # Validate without uploading rux publish # Release it ``` Each step has its own page here, and every command has a page in the [CLI reference](https://rux-lang.dev/docs/cli). ::note Publishing has two requirements a local package does not: a [`Namespace`](https://rux-lang.dev/docs/packaging/namespaces) and a `MinRux` version. Both are described in the [manifest reference](https://rux-lang.dev/docs/packaging/manifest). :: # Introduction This reference documents the packages that ship with Rux: what each function does, what it returns, and where its behavior differs from the platform underneath. It is a lookup reference rather than a tutorial — if you are new to the language, start with the [Get Started guide](https://rux-lang.dev/docs/learn) or the [Rux Language Reference](https://rux-lang.dev/docs/lang). Rux has no monolithic runtime. Everything below is a **package** you add to a project with [`rux add`](https://rux-lang.dev/docs/cli/add), and a program depends only on what it asks for. ::warning **Unstable API**:br Every package here is under active development and none of them has a **stable API** yet. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: ## Two Layers The packages come in two layers, and the one you should reach for first is the portable one. **Cross-platform packages** are portable. The same call compiles on every supported target, and the package hides the platform underneath it — [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) is one function whether it becomes a Win32 heap call or an anonymous `mmap`. Write against these unless they do not expose what you need. | Package | Description | | ------------------------------------------------ | --------------------------------------------------------------------------------------- | | [`C`](https://rux-lang.dev/docs/api/c) | Thin bindings to the platform C standard library: math, I/O, general utilities, time. | | [`Format`](https://rux-lang.dev/docs/api/format) | Values as text: a conversion for every primitive, and an interface for your own types. | | [`Io`](https://rux-lang.dev/docs/api/io) | Standard input and output: print a value or a formatted line, and read a line back. | | [`Math`](https://rux-lang.dev/docs/api/math) | Constants and floating-point functions, for both `float64` and `float32`. | | [`Memory`](https://rux-lang.dev/docs/api/memory) | Allocate, resize, and release raw blocks, and fill, copy, and compare their bytes. | | [`Core`](https://rux-lang.dev/docs/api/core) | The core language: primitive types, `Result`, `Slice`, ranges, and compiler intrinsics. | | [`Text`](https://rux-lang.dev/docs/api/text) | Strings and fundamental text manipulation: an immutable string and a builder for one. | **Platform-dependent packages** are the layer below — thin, direct declarations of one operating system's own entry points, with no portability layer and no safety net. A program that calls them is a program for that platform. Reach for them when the cross-platform packages have no answer, and guard every call with [conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional). | Package | Description | | -------------------------------------------------- | --------------------------------------------------------- | | [`BSD`](https://rux-lang.dev/docs/api/bsd) | Syscalls for the BSD family. | | [`Linux`](https://rux-lang.dev/docs/api/linux) | Linux syscalls, invoked directly, without libc. | | [`MacOS`](https://rux-lang.dev/docs/api/macos) | BSD-layer syscalls for macOS. | | [`Windows`](https://rux-lang.dev/docs/api/windows) | Win32 bindings, imported from `kernel32.dll` and friends. | ## Installation Every package is added and installed the same way. [`rux add`](https://rux-lang.dev/docs/cli/add) records the dependency in the [package manifest](https://rux-lang.dev/docs/packaging/manifest), and [`rux install`](https://rux-lang.dev/docs/cli/install) fetches it: ```sh rux add Math rux install ``` Then import what you need. A whole module, a single symbol, or a list of them: ```rux import Math; // Math::Sqrt(2.0) import Math::Sqrt; // Sqrt(2.0) import Memory::{ Alloc, Free }; // Alloc(1024) ``` The examples throughout this reference use the third form and call functions unqualified — `Alloc(1024)` rather than `Memory::Alloc(1024)` — so that the call reads the way it will in your own code. ## Platform Support FreeBSD, Linux, macOS, and Windows are supported. The cross-platform packages run on all four; the platform-specific packages each target one. | Package | FreeBSD | Linux | macOS | Windows | | -------------------------------------------------- | ------- | ----- | ----- | ------- | | [`C`](https://rux-lang.dev/docs/api/c) | ✓ | ✓ | ✓ | ✓ | | [`Format`](https://rux-lang.dev/docs/api/format) | ✓ | ✓ | ✓ | ✓ | | [`Io`](https://rux-lang.dev/docs/api/io) | ✓ | ✓ | ✓ | ✓ | | [`Math`](https://rux-lang.dev/docs/api/math) | ✓ | ✓ | ✓ | ✓ | | [`Memory`](https://rux-lang.dev/docs/api/memory) | ✓ | ✓ | ✓ | ✓ | | [`Core`](https://rux-lang.dev/docs/api/core) | ✓ | ✓ | ✓ | ✓ | | [`Text`](https://rux-lang.dev/docs/api/text) | ✓ | ✓ | ✓ | ✓ | | [`BSD`](https://rux-lang.dev/docs/api/bsd) | ✓ | — | — | — | | [`Linux`](https://rux-lang.dev/docs/api/linux) | — | ✓ | — | — | | [`MacOS`](https://rux-lang.dev/docs/api/macos) | — | — | ✓ | — | | [`Windows`](https://rux-lang.dev/docs/api/windows) | — | — | — | ✓ | The platform-dependent packages each build on exactly one platform by design — a `—` means the package does not apply there, not that support is missing. [`Math`](https://rux-lang.dev/docs/api/math) is portable everywhere for a simple reason: it is pure computation, with no platform-specific code in it at all, so it has nothing to port. ## Reading This Reference Each package has an overview page listing everything it contains, and each function gets a page of its own with the same sections in the same order: - **Signature** — the declaration, including every overload. - **Parameters** — one row per argument. - **Returns** — the result and its edge cases. Functions that return nothing have **Remarks** in this slot instead. - **Example** — a short snippet, with the interesting values in trailing comments. - **See also** — the neighboring functions worth knowing about. Edge cases are where a reference earns its keep, so they are stated on the page rather than left to the reader: which argument a NaN loses to in [`Max`](https://rux-lang.dev/docs/api/math/max), why [`Compare`](https://rux-lang.dev/docs/api/memory/compare) reports equality as the length rather than as `0`, and which block survives when [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) fails. ## Where to Go Next - **Looking for a function?** Start from a package overview — [`Format`](https://rux-lang.dev/docs/api/format), [`Io`](https://rux-lang.dev/docs/api/io), [`Math`](https://rux-lang.dev/docs/api/math), [`Memory`](https://rux-lang.dev/docs/api/memory), or [`Text`](https://rux-lang.dev/docs/api/text) — each of which lists its full contents in one table. - **Working close to the metal?** The platform-dependent packages mirror their operating systems closely: [`Linux`](https://rux-lang.dev/docs/api/linux), [`MacOS`](https://rux-lang.dev/docs/api/macos), and [`Windows`](https://rux-lang.dev/docs/api/windows). - **Writing platform-specific code?** See [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) and the [Foreign Function Interface](https://rux-lang.dev/docs/lang/ffi/overview). - **Learning the language?** The [Rux Language Reference](https://rux-lang.dev/docs/lang) covers the syntax and the type system these packages are built on. # Brk Changes the process program break. **Package:** `Bsd` ## Signature ```rux func Brk(addr: *opaque) -> int64; ``` ## Parameters | Name | Type | Description | | ------ | --------- | ---------------------------- | | `addr` | `*opaque` | Requested new program break. | ## Returns `int64` - `0` on success, or a negative errno value on failure. ::caution `Brk` changes memory traditionally managed by process allocators. Calling it independently of the runtime allocator can corrupt the heap. Prefer [`Memory`](https://rux-lang.dev/docs/api/memory) or [`Mmap`](https://rux-lang.dev/docs/api/bsd/mmap) for application allocations. :: ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview # ClockGetTime Reads the current value of a BSD clock. **Package:** `Bsd` ## Signature ```rux func ClockGetTime(clk_id: int32, tp: *Timespec) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | ----------- | ----------------------------------------- | | `clk_id` | `int32` | Clock to read, such as `ClockMonotonic`. | | `tp` | `*Timespec` | Writable destination for the clock value. | ## Returns `int64` - `0` on success, or a negative errno value on failure. The compiler-provided thunk selects the target BSD syscall. Use `ClockRealtime` for adjustable wall-clock time and `ClockMonotonic` for elapsed-time measurements. ## Example ```rux import Bsd::{ ClockGetTime, Timespec, ClockMonotonic }; func Main() -> int { var now: Timespec; if ClockGetTime(ClockMonotonic, @now) != 0i64 { return 1; } return 0; } ``` ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Types and constants`](https://rux-lang.dev/docs/api/bsd/types) - clocks and `Timespec` # Close Closes a file descriptor. **Package:** `Bsd` ## Signature ```rux func Close(fd: int32) -> int64; ``` ## Parameters | Name | Type | Description | | ---- | ------- | ------------------------- | | `fd` | `int32` | File descriptor to close. | ## Returns `int64` - `0` on success, or a negative errno value on failure. After success, the descriptor may be reused and must not be closed again. ::warning Do not blindly retry `Close` after an error. The descriptor's state may be uncertain and its number may have been reused. :: ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview # Errno Extracts the positive errno from a raw syscall result. **Package:** `Bsd` ## Signature ```rux func Errno(result: int64) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | ------- | ------------------------- | | `result` | `int64` | Raw syscall return value. | ## Returns `int64` - the positive errno (`1` through `4095`) when `result` is a negative errno in the range `-1` through `-4095`, or `0` for any other value, which the package treats as success. `Errno` does not read or modify a global or thread-local errno variable. It negates the result when [`IsError`](https://rux-lang.dev/docs/api/bsd/iserror) reports one, and returns `0` otherwise. ## Example ```rux import Bsd::{ Close, Errno }; func Main() -> int { let code = Errno(Close(3)); if code != 0i64 { // The close failed with errno `code`. } return 0; } ``` ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`IsError`](https://rux-lang.dev/docs/api/bsd/iserror) - test whether a result is an error - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/bsd/syscalls) - raw syscall entry points # Exit Terminates the process immediately. **Package:** `Bsd` ## Signature ```rux func Exit(code: int32); ``` ## Parameters | Name | Type | Description | | ------ | ------- | ---------------------------------------- | | `code` | `int32` | Status reported to the process's parent. | ## Description `Exit` invokes the low-level target BSD exit operation. It does not return, unwind the stack, flush user-space buffers, or run cleanup code. A waiting parent normally observes only the low 8 bits of the status. ::warning Prefer returning from `Main` for normal termination. Use `Exit` only when the process must stop immediately. :: ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview # GetPid Returns the calling process ID. **Package:** `Bsd` ## Signature ```rux func GetPid() -> int64; ``` ## Returns `int64` - the process ID assigned by the kernel. ## Example ```rux import Bsd::GetPid; func Main() -> int { let pid = GetPid(); return 0; } ``` ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview # Bsd Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: Direct syscall bindings for FreeBSD, OpenBSD, NetBSD, and DragonFly BSD. **Package:** `Bsd` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Bsd](https://github.com/rux-lang/Rux/tree/main/Packages/Bsd){rel=""nofollow""} The package provides raw zero-to-six-argument syscall entry points, typed wrappers for common I/O, process, memory, and time operations, and the constants and structures those wrappers require. It calls the kernel without libc and uses compiler-provided thunks where the supported BSD variants differ. ## Requirements - FreeBSD, OpenBSD, NetBSD, or DragonFly BSD on x86-64 - A Rux compiler with the BSD syscall thunks The syscall ABI is platform- and architecture-specific. Prefer the cross-platform packages — [`Io`](https://rux-lang.dev/docs/api/io), [`Memory`](https://rux-lang.dev/docs/api/memory) — when they provide the operation you need. ## Installation ```sh rux add Bsd rux install ``` Then import the symbols you need: ```rux import Bsd::{ StdOut, Write }; ``` ## Platform Compatibility The package is **x86-64 only** — the raw syscall entry points are hand-written x86-64 assembly, and there is no AArch64 path yet. On any other architecture the package does not apply. Syscall numbers and clock IDs differ between the four supported BSDs, so the package selects the right value for the active target at compile time; `Mmap`, `Munmap`, `Nanosleep`, and `ClockGetTime` route through dedicated per-target paths. `ClockMonotonic` is `4` on FreeBSD and DragonFly BSD and `3` on NetBSD and OpenBSD. The mapping and protection flags are the same on all four. ## Result Convention The wrappers return the kernel result directly: a non-negative value on success — a byte count, a process ID, a mapped address, or `0` — and a **negative errno** (`-1` through `-4095`) on failure. [`IsError`](https://rux-lang.dev/docs/api/bsd/iserror) tests for that negative range and [`Errno`](https://rux-lang.dev/docs/api/bsd/errno) turns it back into a positive errno number, so a legitimate positive result is never mistaken for an error. ## Functions ### I/O | Function | Description | | -------------------------------------------------- | ---------------------------------- | | [`Read`](https://rux-lang.dev/docs/api/bsd/read) | Read bytes from a file descriptor. | | [`Write`](https://rux-lang.dev/docs/api/bsd/write) | Write bytes to a file descriptor. | | [`Close`](https://rux-lang.dev/docs/api/bsd/close) | Close a file descriptor. | ### Memory | Function | Description | | ---------------------------------------------------- | --------------------------------- | | [`Mmap`](https://rux-lang.dev/docs/api/bsd/mmap) | Create a virtual-memory mapping. | | [`Munmap`](https://rux-lang.dev/docs/api/bsd/munmap) | Remove a virtual-memory mapping. | | [`Brk`](https://rux-lang.dev/docs/api/bsd/brk) | Change the process program break. | ### Process | Function | Description | | ---------------------------------------------------- | ---------------------------------- | | [`Exit`](https://rux-lang.dev/docs/api/bsd/exit) | Terminate the process immediately. | | [`GetPid`](https://rux-lang.dev/docs/api/bsd/getpid) | Return the calling process ID. | ### Time | Function | Description | | ---------------------------------------------------------------- | -------------------------------- | | [`ClockGetTime`](https://rux-lang.dev/docs/api/bsd/clockgettime) | Read a BSD clock. | | [`Nanosleep`](https://rux-lang.dev/docs/api/bsd/nanosleep) | Suspend for a relative interval. | ### Raw syscalls | Function | Description | | ------------------------------------------------------------------- | ------------------------------------------ | | [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/bsd/syscalls) | Invoke an arbitrary syscall by number. | | [`IsError`](https://rux-lang.dev/docs/api/bsd/iserror) | Test whether a result is a negative errno. | | [`Errno`](https://rux-lang.dev/docs/api/bsd/errno) | Extract the positive errno from a result. | ## Types and constants The standard descriptors, syscall numbers, mapping and protection flags, clock IDs, and the [`Timespec`](https://rux-lang.dev/docs/api/bsd/types) structure are listed on the [types and constants](https://rux-lang.dev/docs/api/bsd/types) page. ## Example ```rux import Bsd::{ StdOut, Write }; func Main() -> int { let message = "hello from BSD\n"; let result = Write(StdOut, message.data, message.length); return result == message.length as int64 ? 0 : 1; } ``` # IsError Tests whether a raw syscall result is an error. **Package:** `Bsd` ## Signature ```rux func IsError(result: int64) -> bool; ``` ## Parameters | Name | Type | Description | | -------- | ------- | ------------------------- | | `result` | `int64` | Raw syscall return value. | ## Returns `bool` - `true` when `result` is a negative errno in the range `-1` through `-4095`; otherwise `false`. The wrappers report failure as a small negative value, so a non-negative result — a byte count, a process ID, a mapped address, or `0` — is always a success. `IsError` therefore never misclassifies a legitimate result. ## Example ```rux import Bsd::{ IsError, Mmap, MapAnonymous, MapPrivate, ProtectionRead, ProtectionWrite }; func Main() -> int { let result = Mmap(null, 4096u, ProtectionRead | ProtectionWrite, MapPrivate | MapAnonymous, -1i32, 0u64); if IsError(result) { return 1; } return 0; } ``` ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Errno`](https://rux-lang.dev/docs/api/bsd/errno) - extract the positive errno value - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/bsd/syscalls) - raw syscall entry points # Mmap Creates a virtual-memory mapping through the target-specific BSD thunk. **Package:** `Bsd` ## Signature ```rux func Mmap( address: *opaque, length: uint, protection: int32, flags: int32, fd: int32, offset: uint64 ) -> int64; ``` ## Parameters | Name | Type | Description | | ------------ | --------- | ------------------------------------------------- | | `address` | `*opaque` | Requested address hint, or `null`. | | `length` | `uint` | Mapping length in bytes. | | `protection` | `int32` | Page protections, such as `ProtectionRead`. | | `flags` | `int32` | Mapping behavior, such as `MapPrivate`. | | `fd` | `int32` | Backing descriptor, or `-1` for anonymous memory. | | `offset` | `uint64` | Page-aligned offset in the backing object. | ## Returns `int64` - the mapped address encoded as an integer on success, or a negative errno value on failure. For private anonymous memory, combine `MapPrivate | MapAnonymous`, pass `-1i32` for `fd`, and use an offset of `0u64`. The compiler thunk handles the target-specific mmap syscall details. ::caution Check the result before casting it to a pointer. Release every successful mapping with [`Munmap`](https://rux-lang.dev/docs/api/bsd/munmap) using its correct base address and length. :: ## Example ```rux import Bsd::{ IsError, Mmap, Munmap, MapAnonymous, MapPrivate, ProtectionRead, ProtectionWrite }; func Main() -> int { let result = Mmap(null, 4096u, ProtectionRead | ProtectionWrite, MapPrivate | MapAnonymous, -1i32, 0u64); if IsError(result) { return 1; } let memory = result as *opaque; Munmap(memory, 4096u); return 0; } ``` ## See also - [`Types and constants`](https://rux-lang.dev/docs/api/bsd/types) - protection and mapping flags - [`Munmap`](https://rux-lang.dev/docs/api/bsd/munmap) - remove a mapping # Munmap Removes a virtual-memory mapping. **Package:** `Bsd` ## Signature ```rux func Munmap(addr: *opaque, length: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | ----------------------------------- | | `addr` | `*opaque` | Base address of the range to unmap. | | `length` | `uint` | Number of bytes in the range. | ## Returns `int64` - `0` on success, or a negative errno value on failure. After success, the unmapped range is invalid and must not be accessed. ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Mmap`](https://rux-lang.dev/docs/api/bsd/mmap) - create a mapping # Nanosleep Suspends execution for a relative interval. **Package:** `Bsd` ## Signature ```rux func Nanosleep(req: *Timespec, rem: *Timespec) -> int64; ``` ## Parameters | Name | Type | Description | | ----- | ----------- | -------------------------------------------------- | | `req` | `*Timespec` | Requested relative duration. | | `rem` | `*Timespec` | Receives remaining time if interrupted, or `null`. | ## Returns `int64` - `0` after the interval elapses, or a negative errno value on failure or interruption. `req.nanoseconds` must normally be from `0` through `999999999`. When interrupted, a non-null `rem` receives the unslept duration. ## Example ```rux import Bsd::{ Nanosleep, Timespec }; func Main() -> int { var delay = Timespec { seconds: 0i64, nanoseconds: 250000000i64 }; if Nanosleep(@delay, null) != 0i64 { return 1; } return 0; } ``` ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Types and constants`](https://rux-lang.dev/docs/api/bsd/types) - `Timespec` # Read Reads bytes from a file descriptor. **Package:** `Bsd` ## Signature ```rux func Read(fd: int32, buffer: *opaque, count: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | --------------------------------------------- | | `fd` | `int32` | File descriptor to read. | | `buffer` | `*opaque` | Writable destination for up to `count` bytes. | | `count` | `uint` | Maximum number of bytes to read. | ## Returns `int64` - bytes read on success, `0` at end of file, or a negative errno value on failure. A successful call may return fewer than `count` bytes. ## Example ```rux import Bsd::{ Read, StdIn }; func Main() -> int { var buffer: char8[256]; let result = Read(StdIn, buffer.data, 256u); if result == 0i64 { // End of file. } return 0; } ``` ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Write`](https://rux-lang.dev/docs/api/bsd/write) - write bytes # Raw Syscalls Invoke arbitrary BSD x86-64 syscalls and inspect raw results. **Package:** `Bsd` ## Signatures ```rux func Syscall0(number: uint64) -> int64; func Syscall1(number: uint64, arg0: uint64) -> int64; func Syscall2(number: uint64, arg0: uint64, arg1: uint64) -> int64; func Syscall3(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64) -> int64; func Syscall4(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64) -> int64; func Syscall5(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64) -> int64; func Syscall6(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64, arg5: uint64) -> int64; ``` Choose the function whose suffix matches the syscall's argument count. `number` and every argument must match the active BSD target's x86-64 ABI. Pointers and signed values must be converted to raw 64-bit representations. ## Returns `int64` - the kernel result: a non-negative value on success, or a negative errno (`-1` through `-4095`) on failure. No validation, type conversion, retry, or resource management is performed. Use [`IsError`](https://rux-lang.dev/docs/api/bsd/iserror) and [`Errno`](https://rux-lang.dev/docs/api/bsd/errno) to interpret it. ::caution The supported BSD systems do not share every syscall number or ABI detail. An incorrect target assumption, argument count, pointer, or buffer length can corrupt memory, leak resources, or terminate the process. Prefer a typed wrapper when available. :: ## Error Helpers | Function | Description | | ------------------------------------------------------ | ------------------------------------------------------ | | [`Errno`](https://rux-lang.dev/docs/api/bsd/errno) | Return the positive errno of a failing result, else 0. | | [`IsError`](https://rux-lang.dev/docs/api/bsd/iserror) | Test whether a result is a negative errno. | ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Types and constants`](https://rux-lang.dev/docs/api/bsd/types) - shared syscall numbers # Types and Constants Types and constants exported by the `Bsd` package. **Package:** `Bsd` ## Standard File Descriptors | Name | Type | Value | Description | | -------- | ------- | ----: | ---------------- | | `StdIn` | `int32` | `0` | Standard input. | | `StdOut` | `int32` | `1` | Standard output. | | `StdErr` | `int32` | `2` | Standard error. | A process may close or redirect these conventional descriptors. ## Syscall Numbers These numbers are the same on all four supported BSDs: | Name | Type | Value | | ----------- | -------- | ----: | | `SysExit` | `uint64` | `1` | | `SysRead` | `uint64` | `3` | | `SysWrite` | `uint64` | `4` | | `SysClose` | `uint64` | `6` | | `SysBrk` | `uint64` | `17` | | `SysGetPid` | `uint64` | `20` | | `SysMunmap` | `uint64` | `73` | `SysMmap`, `SysNanosleep`, and `SysClockGetTime` are also exported, but their values are selected for the active target, since they differ between FreeBSD, OpenBSD, NetBSD, and DragonFly BSD. ## Memory Protection | Name | Type | Value | Description | | ------------------- | ------- | ----: | ---------------------- | | `ProtectionNone` | `int32` | `0` | No access. | | `ProtectionRead` | `int32` | `1` | Pages may be read. | | `ProtectionWrite` | `int32` | `2` | Pages may be written. | | `ProtectionExecute` | `int32` | `4` | Pages may be executed. | ## Mapping Flags | Name | Type | Value | Description | | -------------- | ------- | -----: | --------------------------------------- | | `MapShared` | `int32` | `1` | Create a shared mapping. | | `MapPrivate` | `int32` | `2` | Create a private copy-on-write mapping. | | `MapFixed` | `int32` | `16` | Place the mapping at the exact address. | | `MapAnonymous` | `int32` | `4096` | Create a mapping not backed by a file. | These values are shared by all four supported BSDs. ## Clock IDs | Name | Type | Value | Description | | ---------------- | ------- | ------: | ---------------------------------------- | | `ClockRealtime` | `int32` | `0` | Adjustable wall-clock time. | | `ClockMonotonic` | `int32` | `4`/`3` | Monotonic time for interval measurement. | `ClockMonotonic` is `4` on FreeBSD and DragonFly BSD and `3` on NetBSD and OpenBSD; the package selects the right value for the active target. Use these constants with [`ClockGetTime`](https://rux-lang.dev/docs/api/bsd/clockgettime). ## `Timespec` ```rux struct Timespec { seconds: int64; nanoseconds: int64; } ``` | Field | Type | Description | | ------------- | ------- | ------------------------------------------------------ | | `seconds` | `int64` | Whole seconds. | | `nanoseconds` | `int64` | Nanoseconds within the second, normally 0–999,999,999. | Used by [`Nanosleep`](https://rux-lang.dev/docs/api/bsd/nanosleep) and [`ClockGetTime`](https://rux-lang.dev/docs/api/bsd/clockgettime). ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Mmap`](https://rux-lang.dev/docs/api/bsd/mmap) — uses the protection and mapping flags - [`ClockGetTime`](https://rux-lang.dev/docs/api/bsd/clockgettime) — uses the clock IDs and `Timespec` - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/bsd/syscalls) — use the syscall numbers directly # Write Writes bytes to a file descriptor. **Package:** `Bsd` ## Signature ```rux func Write(fd: int32, buffer: *opaque, count: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | ----------------------------------------- | | `fd` | `int32` | File descriptor to write. | | `buffer` | `*opaque` | Source containing at least `count` bytes. | | `count` | `uint` | Number of bytes requested. | ## Returns `int64` - bytes written on success, or a negative errno value on failure. A successful call may write fewer than `count` bytes. ## Example ```rux import Bsd::{ StdOut, Write }; func Main() -> int { let text = "hello\n"; let result = Write(StdOut, text.data, text.length); if result != text.length as int64 { return 1; } return 0; } ``` ## See also - [`Bsd`](https://rux-lang.dev/docs/api/bsd) — the package overview - [`Read`](https://rux-lang.dev/docs/api/bsd/read) - read bytes # abort Causes abnormal program termination without cleaning up. **Package:** `C` **C reference:** [`abort`](https://en.cppreference.com/c/program/abort){rel=""nofollow""} ## Signature ```rux func abort(); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # abs Computes the absolute value of an integer value. **Package:** `C` **C reference:** [`abs`](https://en.cppreference.com/c/numeric/math/abs){rel=""nofollow""} ## Signature ```rux func abs(n: int32) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # acos Computes arc cosine. **Package:** `C` **C reference:** [`acos`](https://en.cppreference.com/c/numeric/math/acos){rel=""nofollow""} ## Signature ```rux func acos(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # acosf Computes arc cosine. **Package:** `C` **C reference:** [`acosf`](https://en.cppreference.com/c/numeric/math/acos){rel=""nofollow""} ## Signature ```rux func acosf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # acosh Computes inverse hyperbolic cosine. **Package:** `C` **C reference:** [`acosh`](https://en.cppreference.com/c/numeric/math/acosh){rel=""nofollow""} ## Signature ```rux func acosh(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # acoshf Computes inverse hyperbolic cosine. **Package:** `C` **C reference:** [`acoshf`](https://en.cppreference.com/c/numeric/math/acosh){rel=""nofollow""} ## Signature ```rux func acoshf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # asctime Converts a tm object to a textual representation. **Package:** `C` **C reference:** [`asctime`](https://en.cppreference.com/c/chrono/asctime){rel=""nofollow""} ## Signature ```rux func asctime(time_ptr: *tm) -> *char8; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # asin Computes arc sine. **Package:** `C` **C reference:** [`asin`](https://en.cppreference.com/c/numeric/math/asin){rel=""nofollow""} ## Signature ```rux func asin(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # asinf Computes arc sine. **Package:** `C` **C reference:** [`asinf`](https://en.cppreference.com/c/numeric/math/asin){rel=""nofollow""} ## Signature ```rux func asinf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # asinh Computes inverse hyperbolic sine. **Package:** `C` **C reference:** [`asinh`](https://en.cppreference.com/c/numeric/math/asinh){rel=""nofollow""} ## Signature ```rux func asinh(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # asinhf Computes inverse hyperbolic sine. **Package:** `C` **C reference:** [`asinhf`](https://en.cppreference.com/c/numeric/math/asinh){rel=""nofollow""} ## Signature ```rux func asinhf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atan Computes arc tangent. **Package:** `C` **C reference:** [`atan`](https://en.cppreference.com/c/numeric/math/atan){rel=""nofollow""} ## Signature ```rux func atan(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atan2 Computes arc tangent, using signs to determine quadrants. **Package:** `C` **C reference:** [`atan2`](https://en.cppreference.com/c/numeric/math/atan2){rel=""nofollow""} ## Signature ```rux func atan2(y: float64, x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atan2f Computes arc tangent, using signs to determine quadrants. **Package:** `C` **C reference:** [`atan2f`](https://en.cppreference.com/c/numeric/math/atan2){rel=""nofollow""} ## Signature ```rux func atan2f(y: float32, x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atanf Computes arc tangent. **Package:** `C` **C reference:** [`atanf`](https://en.cppreference.com/c/numeric/math/atan){rel=""nofollow""} ## Signature ```rux func atanf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atanh Computes inverse hyperbolic tangent. **Package:** `C` **C reference:** [`atanh`](https://en.cppreference.com/c/numeric/math/atanh){rel=""nofollow""} ## Signature ```rux func atanh(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atanhf Computes inverse hyperbolic tangent. **Package:** `C` **C reference:** [`atanhf`](https://en.cppreference.com/c/numeric/math/atanh){rel=""nofollow""} ## Signature ```rux func atanhf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atof Converts a byte string to a floating point value. **Package:** `C` **C reference:** [`atof`](https://en.cppreference.com/c/string/byte/atof){rel=""nofollow""} ## Signature ```rux func atof(str: *char8) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atoi Converts a byte string to an integer value. **Package:** `C` **C reference:** [`atoi`](https://en.cppreference.com/c/string/byte/atoi){rel=""nofollow""} ## Signature ```rux func atoi(str: *char8) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atol Converts a byte string to a long integer value. **Package:** `C` **C reference:** [`atol`](https://en.cppreference.com/c/string/byte/atoi){rel=""nofollow""} ## Signature ```rux func atol(str: *char8) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # atoll Converts a byte string to a long long integer value. **Package:** `C` **C reference:** [`atoll`](https://en.cppreference.com/c/string/byte/atoi){rel=""nofollow""} ## Signature ```rux func atoll(str: *char8) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # calloc Allocates memory for an array of num objects of size and zero-initializes it. **Package:** `C` **C reference:** [`calloc`](https://en.cppreference.com/c/memory/calloc){rel=""nofollow""} ## Signature ```rux func calloc(num: uint, size: uint) -> *opaque; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # cbrt Computes cube root. **Package:** `C` **C reference:** [`cbrt`](https://en.cppreference.com/c/numeric/math/cbrt){rel=""nofollow""} ## Signature ```rux func cbrt(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # cbrtf Computes cube root. **Package:** `C` **C reference:** [`cbrtf`](https://en.cppreference.com/c/numeric/math/cbrt){rel=""nofollow""} ## Signature ```rux func cbrtf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ceil Computes smallest integer not less than the given value. **Package:** `C` **C reference:** [`ceil`](https://en.cppreference.com/c/numeric/math/ceil){rel=""nofollow""} ## Signature ```rux func ceil(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ceilf Computes smallest integer not less than the given value. **Package:** `C` **C reference:** [`ceilf`](https://en.cppreference.com/c/numeric/math/ceil){rel=""nofollow""} ## Signature ```rux func ceilf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # clearerr Clears the end-of-file and error indicators for the given stream. **Package:** `C` **C reference:** [`clearerr`](https://en.cppreference.com/c/io/clearerr){rel=""nofollow""} ## Signature ```rux func clearerr(stream: *opaque); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # clock Returns the approximate processor time used by the program. **Package:** `C` **C reference:** [`clock`](https://en.cppreference.com/c/chrono/clock){rel=""nofollow""} ## Signature ```rux func clock() -> clock_t; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # copysign Copies the sign of a floating-point value. **Package:** `C` **C reference:** [`copysign`](https://en.cppreference.com/c/numeric/math/copysign){rel=""nofollow""} ## Signature ```rux func copysign(x: float64, y: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # copysignf Copies the sign of a floating-point value. **Package:** `C` **C reference:** [`copysignf`](https://en.cppreference.com/c/numeric/math/copysign){rel=""nofollow""} ## Signature ```rux func copysignf(x: float32, y: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # cos Computes cosine. **Package:** `C` **C reference:** [`cos`](https://en.cppreference.com/c/numeric/math/cos){rel=""nofollow""} ## Signature ```rux func cos(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # cosf Computes cosine. **Package:** `C` **C reference:** [`cosf`](https://en.cppreference.com/c/numeric/math/cos){rel=""nofollow""} ## Signature ```rux func cosf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # cosh Computes hyperbolic cosine. **Package:** `C` **C reference:** [`cosh`](https://en.cppreference.com/c/numeric/math/cosh){rel=""nofollow""} ## Signature ```rux func cosh(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # coshf Computes hyperbolic cosine. **Package:** `C` **C reference:** [`coshf`](https://en.cppreference.com/c/numeric/math/cosh){rel=""nofollow""} ## Signature ```rux func coshf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ctime Converts a time\_t object to a textual representation. **Package:** `C` **C reference:** [`ctime`](https://en.cppreference.com/c/chrono/ctime){rel=""nofollow""} ## Signature ```rux func ctime(timer: *time_t) -> *char8; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # difftime Computes the difference in seconds between two calendar times. **Package:** `C` **C reference:** [`difftime`](https://en.cppreference.com/c/chrono/difftime){rel=""nofollow""} ## Signature ```rux func difftime(time_end: time_t, time_beg: time_t) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # erf Computes error function. **Package:** `C` **C reference:** [`erf`](https://en.cppreference.com/c/numeric/math/erf){rel=""nofollow""} ## Signature ```rux func erf(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # erfc Computes complementary error function. **Package:** `C` **C reference:** [`erfc`](https://en.cppreference.com/c/numeric/math/erfc){rel=""nofollow""} ## Signature ```rux func erfc(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # erfcf Computes complementary error function. **Package:** `C` **C reference:** [`erfcf`](https://en.cppreference.com/c/numeric/math/erfc){rel=""nofollow""} ## Signature ```rux func erfcf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # erff Computes error function. **Package:** `C` **C reference:** [`erff`](https://en.cppreference.com/c/numeric/math/erf){rel=""nofollow""} ## Signature ```rux func erff(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # exit Causes normal program termination with cleanup. **Package:** `C` **C reference:** [`exit`](https://en.cppreference.com/c/program/exit){rel=""nofollow""} ## Signature ```rux func exit(status: int32); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # exp Computes e raised to the given power. **Package:** `C` **C reference:** [`exp`](https://en.cppreference.com/c/numeric/math/exp){rel=""nofollow""} ## Signature ```rux func exp(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # exp2 Computes 2 raised to the given power. **Package:** `C` **C reference:** [`exp2`](https://en.cppreference.com/c/numeric/math/exp2){rel=""nofollow""} ## Signature ```rux func exp2(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # exp2f Computes 2 raised to the given power. **Package:** `C` **C reference:** [`exp2f`](https://en.cppreference.com/c/numeric/math/exp2){rel=""nofollow""} ## Signature ```rux func exp2f(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # expf Computes e raised to the given power. **Package:** `C` **C reference:** [`expf`](https://en.cppreference.com/c/numeric/math/exp){rel=""nofollow""} ## Signature ```rux func expf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # expm1 Computes e raised to the given power, minus one. **Package:** `C` **C reference:** [`expm1`](https://en.cppreference.com/c/numeric/math/expm1){rel=""nofollow""} ## Signature ```rux func expm1(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # expm1f Computes e raised to the given power, minus one. **Package:** `C` **C reference:** [`expm1f`](https://en.cppreference.com/c/numeric/math/expm1){rel=""nofollow""} ## Signature ```rux func expm1f(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fabs Computes the absolute value of a floating-point value. **Package:** `C` **C reference:** [`fabs`](https://en.cppreference.com/c/numeric/math/fabs){rel=""nofollow""} ## Signature ```rux func fabs(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fclose Closes the given file stream. **Package:** `C` **C reference:** [`fclose`](https://en.cppreference.com/c/io/fclose){rel=""nofollow""} ## Signature ```rux func fclose(stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fdim Computes positive difference of two floating-point values. **Package:** `C` **C reference:** [`fdim`](https://en.cppreference.com/c/numeric/math/fdim){rel=""nofollow""} ## Signature ```rux func fdim(x: float64, y: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fdimf Computes positive difference of two floating-point values. **Package:** `C` **C reference:** [`fdimf`](https://en.cppreference.com/c/numeric/math/fdim){rel=""nofollow""} ## Signature ```rux func fdimf(x: float32, y: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # feof Checks if the end-of-file indicator is set for the given stream. **Package:** `C` **C reference:** [`feof`](https://en.cppreference.com/c/io/feof){rel=""nofollow""} ## Signature ```rux func feof(stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ferror Checks if the error indicator is set for the given stream. **Package:** `C` **C reference:** [`ferror`](https://en.cppreference.com/c/io/ferror){rel=""nofollow""} ## Signature ```rux func ferror(stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fflush Writes any unwritten data from the stream's buffer to the file. **Package:** `C` **C reference:** [`fflush`](https://en.cppreference.com/c/io/fflush){rel=""nofollow""} ## Signature ```rux func fflush(stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fgetc Reads the next character from the given stream. **Package:** `C` **C reference:** [`fgetc`](https://en.cppreference.com/c/io/fgetc){rel=""nofollow""} ## Signature ```rux func fgetc(stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fgetpos Gets the current file position of the stream. **Package:** `C` **C reference:** [`fgetpos`](https://en.cppreference.com/c/io/fgetpos){rel=""nofollow""} ## Signature ```rux func fgetpos(stream: *opaque, pos: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fgets Reads at most count-1 characters from the stream into a string. **Package:** `C` **C reference:** [`fgets`](https://en.cppreference.com/c/io/fgets){rel=""nofollow""} ## Signature ```rux func fgets(str: *char8, count: int32, stream: *opaque) -> *char8; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # floor Computes largest integer not greater than the given value. **Package:** `C` **C reference:** [`floor`](https://en.cppreference.com/c/numeric/math/floor){rel=""nofollow""} ## Signature ```rux func floor(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # floorf Computes largest integer not greater than the given value. **Package:** `C` **C reference:** [`floorf`](https://en.cppreference.com/c/numeric/math/floor){rel=""nofollow""} ## Signature ```rux func floorf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fma Computes fused multiply-add. **Package:** `C` **C reference:** [`fma`](https://en.cppreference.com/c/numeric/math/fma){rel=""nofollow""} ## Signature ```rux func fma(x: float64, y: float64, z: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fmaf Computes fused multiply-add. **Package:** `C` **C reference:** [`fmaf`](https://en.cppreference.com/c/numeric/math/fma){rel=""nofollow""} ## Signature ```rux func fmaf(x: float32, y: float32, z: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fmax Computes larger of two floating-point values. **Package:** `C` **C reference:** [`fmax`](https://en.cppreference.com/c/numeric/math/fmax){rel=""nofollow""} ## Signature ```rux func fmax(x: float64, y: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fmaxf Computes larger of two floating-point values. **Package:** `C` **C reference:** [`fmaxf`](https://en.cppreference.com/c/numeric/math/fmax){rel=""nofollow""} ## Signature ```rux func fmaxf(x: float32, y: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fmin Computes smaller of two floating-point values. **Package:** `C` **C reference:** [`fmin`](https://en.cppreference.com/c/numeric/math/fmin){rel=""nofollow""} ## Signature ```rux func fmin(x: float64, y: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fminf Computes smaller of two floating-point values. **Package:** `C` **C reference:** [`fminf`](https://en.cppreference.com/c/numeric/math/fmin){rel=""nofollow""} ## Signature ```rux func fminf(x: float32, y: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fmod Computes remainder of the floating-point division operation. **Package:** `C` **C reference:** [`fmod`](https://en.cppreference.com/c/numeric/math/fmod){rel=""nofollow""} ## Signature ```rux func fmod(x: float64, y: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fmodf Computes remainder of the floating-point division operation. **Package:** `C` **C reference:** [`fmodf`](https://en.cppreference.com/c/numeric/math/fmod){rel=""nofollow""} ## Signature ```rux func fmodf(x: float32, y: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fopen Opens a file indicated by filename with the given mode. **Package:** `C` **C reference:** [`fopen`](https://en.cppreference.com/c/io/fopen){rel=""nofollow""} ## Signature ```rux func fopen(filename: *char8, mode: *char8) -> *opaque; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fprintf Prints formatted output to a file stream. **Package:** `C` **C reference:** [`fprintf`](https://en.cppreference.com/c/io/fprintf){rel=""nofollow""} ## Signature ```rux func fprintf(stream: *opaque, format: *char8, ...) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fputc Writes a character to the given stream. **Package:** `C` **C reference:** [`fputc`](https://en.cppreference.com/c/io/fputc){rel=""nofollow""} ## Signature ```rux func fputc(ch: int32, stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fputs Writes a string to the given stream. **Package:** `C` **C reference:** [`fputs`](https://en.cppreference.com/c/io/fputs){rel=""nofollow""} ## Signature ```rux func fputs(str: *char8, stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fread Reads up to count objects of the given size from the stream into a buffer. **Package:** `C` **C reference:** [`fread`](https://en.cppreference.com/c/io/fread){rel=""nofollow""} ## Signature ```rux func fread(buffer: *opaque, size: uint, count: uint, stream: *opaque) -> uint; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # free Deallocates the memory previously allocated by malloc, calloc or realloc. **Package:** `C` **C reference:** [`free`](https://en.cppreference.com/c/memory/free){rel=""nofollow""} ## Signature ```rux func free(ptr: *opaque); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # freopen Reopens a stream with a different file or mode. **Package:** `C` **C reference:** [`freopen`](https://en.cppreference.com/c/io/freopen){rel=""nofollow""} ## Signature ```rux func freopen(filename: *char8, mode: *char8, stream: *opaque) -> *opaque; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # frexp Decomposes a number into significand and a power of two. **Package:** `C` **C reference:** [`frexp`](https://en.cppreference.com/c/numeric/math/frexp){rel=""nofollow""} ## Signature ```rux func frexp(x: float64, exp: *int32) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fscanf Reads formatted input from a file stream. **Package:** `C` **C reference:** [`fscanf`](https://en.cppreference.com/c/io/fscanf){rel=""nofollow""} ## Signature ```rux func fscanf(stream: *opaque, format: *char8, ...) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fseek Sets the file position indicator for the stream. **Package:** `C` **C reference:** [`fseek`](https://en.cppreference.com/c/io/fseek){rel=""nofollow""} ## Signature ```rux func fseek(stream: *opaque, offset: int64, origin: int32) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fsetpos Sets the file position of the stream to the given position. **Package:** `C` **C reference:** [`fsetpos`](https://en.cppreference.com/c/io/fsetpos){rel=""nofollow""} ## Signature ```rux func fsetpos(stream: *opaque, pos: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ftell Returns the current file position of the stream. **Package:** `C` **C reference:** [`ftell`](https://en.cppreference.com/c/io/ftell){rel=""nofollow""} ## Signature ```rux func ftell(stream: *opaque) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # fwrite Writes count objects of the given size from a buffer to the stream. **Package:** `C` **C reference:** [`fwrite`](https://en.cppreference.com/c/io/fwrite){rel=""nofollow""} ## Signature ```rux func fwrite(buffer: *opaque, size: uint, count: uint, stream: *opaque) -> uint; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # getc Reads the next character from the given stream. **Package:** `C` **C reference:** [`getc`](https://en.cppreference.com/c/io/fgetc){rel=""nofollow""} ## Signature ```rux func getc(stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # getchar Reads the next character from stdin. **Package:** `C` **C reference:** [`getchar`](https://en.cppreference.com/c/io/getchar){rel=""nofollow""} ## Signature ```rux func getchar() -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # getenv Returns the value of an environment variable. **Package:** `C` **C reference:** [`getenv`](https://en.cppreference.com/c/program/getenv){rel=""nofollow""} ## Signature ```rux func getenv(name: *char8) -> *char8; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # gmtime Converts a time\_t object to calendar time expressed as UTC. **Package:** `C` **C reference:** [`gmtime`](https://en.cppreference.com/c/chrono/gmtime){rel=""nofollow""} ## Signature ```rux func gmtime(timer: *time_t) -> *tm; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # hypot Computes square root of the sum of the squares of two given numbers. **Package:** `C` **C reference:** [`hypot`](https://en.cppreference.com/c/numeric/math/hypot){rel=""nofollow""} ## Signature ```rux func hypot(x: float64, y: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ilogb Extracts exponent of the number. **Package:** `C` **C reference:** [`ilogb`](https://en.cppreference.com/c/numeric/math/ilogb){rel=""nofollow""} ## Signature ```rux func ilogb(x: float64) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ilogbf Extracts exponent of the number. **Package:** `C` **C reference:** [`ilogbf`](https://en.cppreference.com/c/numeric/math/ilogb){rel=""nofollow""} ## Signature ```rux func ilogbf(x: float32) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # C Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: Thin bindings to the platform C standard library for Rux programs. **Package:** `C` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/C](https://github.com/rux-lang/Rux/tree/main/Packages/C){rel=""nofollow""} The package links the platform C library through `#Link` and exposes its functions directly — the math library (`libm`), standard I/O, the general utilities, and the time functions. Each entry is a 1:1 binding to the C function of the same name, so the authoritative behavior is the C standard and the linked reference on every page. ## Requirements - A target with a C standard library — every supported OS (BSD, Linux, macOS, Windows) ships one, selected automatically per target. Each function follows the C calling convention and the C contract exactly: no argument checking, no errno decoding, and no memory management beyond what the C function itself does. Prefer the cross-platform packages ([`Math`](https://rux-lang.dev/docs/api/math), [`Io`](https://rux-lang.dev/docs/api/io), [`Memory`](https://rux-lang.dev/docs/api/memory)) for portable application code, and reach for these bindings when you need the C library specifically. ## Installation ```sh rux add C rux install ``` Then import the symbols you need: ```rux import C::{ printf, malloc, free }; ``` ## Types | Type | Description | | --------------------------------------------------- | --------------------------------------------- | | [`time_t`](https://rux-lang.dev/docs/api/c/types) | Arithmetic type representing a calendar time. | | [`clock_t`](https://rux-lang.dev/docs/api/c/types) | Arithmetic type representing processor time. | | [`tm`](https://rux-lang.dev/docs/api/c/types) | Calendar time broken into its components. | | [`timespec`](https://rux-lang.dev/docs/api/c/types) | A time in seconds and nanoseconds. | ## Math | Function | Description | | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | [`acosf`](https://rux-lang.dev/docs/api/c/acosf) | Computes arc cosine. | | [`acos`](https://rux-lang.dev/docs/api/c/acos) | Computes arc cosine. | | [`acoshf`](https://rux-lang.dev/docs/api/c/acoshf) | Computes inverse hyperbolic cosine. | | [`acosh`](https://rux-lang.dev/docs/api/c/acosh) | Computes inverse hyperbolic cosine. | | [`asinf`](https://rux-lang.dev/docs/api/c/asinf) | Computes arc sine. | | [`asin`](https://rux-lang.dev/docs/api/c/asin) | Computes arc sine. | | [`asinhf`](https://rux-lang.dev/docs/api/c/asinhf) | Computes inverse hyperbolic sine. | | [`asinh`](https://rux-lang.dev/docs/api/c/asinh) | Computes inverse hyperbolic sine. | | [`atanf`](https://rux-lang.dev/docs/api/c/atanf) | Computes arc tangent. | | [`atan`](https://rux-lang.dev/docs/api/c/atan) | Computes arc tangent. | | [`atan2f`](https://rux-lang.dev/docs/api/c/atan2f) | Computes arc tangent, using signs to determine quadrants. | | [`atan2`](https://rux-lang.dev/docs/api/c/atan2) | Computes arc tangent, using signs to determine quadrants. | | [`atanhf`](https://rux-lang.dev/docs/api/c/atanhf) | Computes inverse hyperbolic tangent. | | [`atanh`](https://rux-lang.dev/docs/api/c/atanh) | Computes inverse hyperbolic tangent. | | [`cbrtf`](https://rux-lang.dev/docs/api/c/cbrtf) | Computes cube root. | | [`cbrt`](https://rux-lang.dev/docs/api/c/cbrt) | Computes cube root. | | [`ceilf`](https://rux-lang.dev/docs/api/c/ceilf) | Computes smallest integer not less than the given value. | | [`ceil`](https://rux-lang.dev/docs/api/c/ceil) | Computes smallest integer not less than the given value. | | [`copysignf`](https://rux-lang.dev/docs/api/c/copysignf) | Copies the sign of a floating-point value. | | [`copysign`](https://rux-lang.dev/docs/api/c/copysign) | Copies the sign of a floating-point value. | | [`cosf`](https://rux-lang.dev/docs/api/c/cosf) | Computes cosine. | | [`cos`](https://rux-lang.dev/docs/api/c/cos) | Computes cosine. | | [`coshf`](https://rux-lang.dev/docs/api/c/coshf) | Computes hyperbolic cosine. | | [`cosh`](https://rux-lang.dev/docs/api/c/cosh) | Computes hyperbolic cosine. | | [`erff`](https://rux-lang.dev/docs/api/c/erff) | Computes error function. | | [`erf`](https://rux-lang.dev/docs/api/c/erf) | Computes error function. | | [`erfcf`](https://rux-lang.dev/docs/api/c/erfcf) | Computes complementary error function. | | [`erfc`](https://rux-lang.dev/docs/api/c/erfc) | Computes complementary error function. | | [`expf`](https://rux-lang.dev/docs/api/c/expf) | Computes e raised to the given power. | | [`exp`](https://rux-lang.dev/docs/api/c/exp) | Computes e raised to the given power. | | [`exp2f`](https://rux-lang.dev/docs/api/c/exp2f) | Computes 2 raised to the given power. | | [`exp2`](https://rux-lang.dev/docs/api/c/exp2) | Computes 2 raised to the given power. | | [`expm1f`](https://rux-lang.dev/docs/api/c/expm1f) | Computes e raised to the given power, minus one. | | [`expm1`](https://rux-lang.dev/docs/api/c/expm1) | Computes e raised to the given power, minus one. | | [`fabs`](https://rux-lang.dev/docs/api/c/fabs) | Computes the absolute value of a floating-point value. | | [`fdimf`](https://rux-lang.dev/docs/api/c/fdimf) | Computes positive difference of two floating-point values. | | [`fdim`](https://rux-lang.dev/docs/api/c/fdim) | Computes positive difference of two floating-point values. | | [`floorf`](https://rux-lang.dev/docs/api/c/floorf) | Computes largest integer not greater than the given value. | | [`floor`](https://rux-lang.dev/docs/api/c/floor) | Computes largest integer not greater than the given value. | | [`fmaf`](https://rux-lang.dev/docs/api/c/fmaf) | Computes fused multiply-add. | | [`fma`](https://rux-lang.dev/docs/api/c/fma) | Computes fused multiply-add. | | [`fmaxf`](https://rux-lang.dev/docs/api/c/fmaxf) | Computes larger of two floating-point values. | | [`fmax`](https://rux-lang.dev/docs/api/c/fmax) | Computes larger of two floating-point values. | | [`fminf`](https://rux-lang.dev/docs/api/c/fminf) | Computes smaller of two floating-point values. | | [`fmin`](https://rux-lang.dev/docs/api/c/fmin) | Computes smaller of two floating-point values. | | [`fmodf`](https://rux-lang.dev/docs/api/c/fmodf) | Computes remainder of the floating-point division operation. | | [`fmod`](https://rux-lang.dev/docs/api/c/fmod) | Computes remainder of the floating-point division operation. | | [`frexp`](https://rux-lang.dev/docs/api/c/frexp) | Decomposes a number into significand and a power of two. | | [`hypot`](https://rux-lang.dev/docs/api/c/hypot) | Computes square root of the sum of the squares of two given numbers. | | [`ilogbf`](https://rux-lang.dev/docs/api/c/ilogbf) | Extracts exponent of the number. | | [`ilogb`](https://rux-lang.dev/docs/api/c/ilogb) | Extracts exponent of the number. | | [`ldexp`](https://rux-lang.dev/docs/api/c/ldexp) | Multiplies a number by 2 raised to an integer power. | | [`lgammaf`](https://rux-lang.dev/docs/api/c/lgammaf) | Computes natural logarithm of the absolute value of the gamma function. | | [`lgamma`](https://rux-lang.dev/docs/api/c/lgamma) | Computes natural logarithm of the absolute value of the gamma function. | | [`llrintf`](https://rux-lang.dev/docs/api/c/llrintf) | Rounds to nearest integer using current rounding mode. | | [`llrint`](https://rux-lang.dev/docs/api/c/llrint) | Rounds to nearest integer using current rounding mode. | | [`llroundf`](https://rux-lang.dev/docs/api/c/llroundf) | Rounds to nearest integer, rounding away from zero in halfway cases. | | [`llround`](https://rux-lang.dev/docs/api/c/llround) | Rounds to nearest integer, rounding away from zero in halfway cases. | | [`logf`](https://rux-lang.dev/docs/api/c/logf) | Computes natural (base e) logarithm. | | [`log`](https://rux-lang.dev/docs/api/c/log) | Computes natural (base e) logarithm. | | [`log10f`](https://rux-lang.dev/docs/api/c/log10f) | Computes common (base 10) logarithm. | | [`log10`](https://rux-lang.dev/docs/api/c/log10) | Computes common (base 10) logarithm. | | [`log1pf`](https://rux-lang.dev/docs/api/c/log1pf) | Computes natural logarithm of 1 plus the given number. | | [`log1p`](https://rux-lang.dev/docs/api/c/log1p) | Computes natural logarithm of 1 plus the given number. | | [`log2f`](https://rux-lang.dev/docs/api/c/log2f) | Computes base 2 logarithm. | | [`log2`](https://rux-lang.dev/docs/api/c/log2) | Computes base 2 logarithm. | | [`logbf`](https://rux-lang.dev/docs/api/c/logbf) | Extracts exponent of the number. | | [`logb`](https://rux-lang.dev/docs/api/c/logb) | Extracts exponent of the number. | | [`lrintf`](https://rux-lang.dev/docs/api/c/lrintf) | Rounds to nearest integer using current rounding mode. | | [`lrint`](https://rux-lang.dev/docs/api/c/lrint) | Rounds to nearest integer using current rounding mode. | | [`lroundf`](https://rux-lang.dev/docs/api/c/lroundf) | Rounds to nearest integer, rounding away from zero in halfway cases. | | [`lround`](https://rux-lang.dev/docs/api/c/lround) | Rounds to nearest integer, rounding away from zero in halfway cases. | | [`modff`](https://rux-lang.dev/docs/api/c/modff) | Decomposes a number into integer and fractional parts. | | [`modf`](https://rux-lang.dev/docs/api/c/modf) | Decomposes a number into integer and fractional parts. | | [`nanf`](https://rux-lang.dev/docs/api/c/nanf) | Generates a quiet NaN. | | [`nan`](https://rux-lang.dev/docs/api/c/nan) | Generates a quiet NaN. | | [`nearbyintf`](https://rux-lang.dev/docs/api/c/nearbyintf) | Rounds to nearest integer using current rounding mode without raising the inexact exception. | | [`nearbyint`](https://rux-lang.dev/docs/api/c/nearbyint) | Rounds to nearest integer using current rounding mode without raising the inexact exception. | | [`nextafterf`](https://rux-lang.dev/docs/api/c/nextafterf) | Determines next representable floating-point value toward the given value. | | [`nextafter`](https://rux-lang.dev/docs/api/c/nextafter) | Determines next representable floating-point value toward the given value. | | [`powf`](https://rux-lang.dev/docs/api/c/powf) | Computes a number raised to the given power. | | [`pow`](https://rux-lang.dev/docs/api/c/pow) | Computes a number raised to the given power. | | [`remainderf`](https://rux-lang.dev/docs/api/c/remainderf) | Computes signed remainder of the floating-point division operation. | | [`remainder`](https://rux-lang.dev/docs/api/c/remainder) | Computes signed remainder of the floating-point division operation. | | [`remquof`](https://rux-lang.dev/docs/api/c/remquof) | Computes signed remainder as well as the three last bits of the division operation. | | [`remquo`](https://rux-lang.dev/docs/api/c/remquo) | Computes signed remainder as well as the three last bits of the division operation. | | [`rintf`](https://rux-lang.dev/docs/api/c/rintf) | Rounds to nearest integer using current rounding mode. | | [`rint`](https://rux-lang.dev/docs/api/c/rint) | Rounds to nearest integer using current rounding mode. | | [`roundf`](https://rux-lang.dev/docs/api/c/roundf) | Rounds to nearest integer, rounding away from zero in halfway cases. | | [`round`](https://rux-lang.dev/docs/api/c/round) | Rounds to nearest integer, rounding away from zero in halfway cases. | | [`scalbnf`](https://rux-lang.dev/docs/api/c/scalbnf) | Multiplies a number by FLT\_RADIX raised to an integer power. | | [`scalbn`](https://rux-lang.dev/docs/api/c/scalbn) | Multiplies a number by FLT\_RADIX raised to an integer power. | | [`sinf`](https://rux-lang.dev/docs/api/c/sinf) | Computes sine. | | [`sin`](https://rux-lang.dev/docs/api/c/sin) | Computes sine. | | [`sinhf`](https://rux-lang.dev/docs/api/c/sinhf) | Computes hyperbolic sine. | | [`sinh`](https://rux-lang.dev/docs/api/c/sinh) | Computes hyperbolic sine. | | [`sqrtf`](https://rux-lang.dev/docs/api/c/sqrtf) | Computes square root. | | [`sqrt`](https://rux-lang.dev/docs/api/c/sqrt) | Computes square root. | | [`tanf`](https://rux-lang.dev/docs/api/c/tanf) | Computes tangent. | | [`tan`](https://rux-lang.dev/docs/api/c/tan) | Computes tangent. | | [`tanhf`](https://rux-lang.dev/docs/api/c/tanhf) | Computes hyperbolic tangent. | | [`tanh`](https://rux-lang.dev/docs/api/c/tanh) | Computes hyperbolic tangent. | | [`tgammaf`](https://rux-lang.dev/docs/api/c/tgammaf) | Computes gamma function. | | [`tgamma`](https://rux-lang.dev/docs/api/c/tgamma) | Computes gamma function. | | [`truncf`](https://rux-lang.dev/docs/api/c/truncf) | Rounds to nearest integer not greater in magnitude than the given value. | | [`trunc`](https://rux-lang.dev/docs/api/c/trunc) | Rounds to nearest integer not greater in magnitude than the given value. | ## Standard I/O | Function | Description | | ------------------------------------------------------ | -------------------------------------------------------------------------- | | [`clearerr`](https://rux-lang.dev/docs/api/c/clearerr) | Clears the end-of-file and error indicators for the given stream. | | [`fclose`](https://rux-lang.dev/docs/api/c/fclose) | Closes the given file stream. | | [`feof`](https://rux-lang.dev/docs/api/c/feof) | Checks if the end-of-file indicator is set for the given stream. | | [`ferror`](https://rux-lang.dev/docs/api/c/ferror) | Checks if the error indicator is set for the given stream. | | [`fflush`](https://rux-lang.dev/docs/api/c/fflush) | Writes any unwritten data from the stream's buffer to the file. | | [`fgetc`](https://rux-lang.dev/docs/api/c/fgetc) | Reads the next character from the given stream. | | [`fgetpos`](https://rux-lang.dev/docs/api/c/fgetpos) | Gets the current file position of the stream. | | [`fgets`](https://rux-lang.dev/docs/api/c/fgets) | Reads at most count-1 characters from the stream into a string. | | [`fopen`](https://rux-lang.dev/docs/api/c/fopen) | Opens a file indicated by filename with the given mode. | | [`fprintf`](https://rux-lang.dev/docs/api/c/fprintf) | Prints formatted output to a file stream. | | [`fputc`](https://rux-lang.dev/docs/api/c/fputc) | Writes a character to the given stream. | | [`fputs`](https://rux-lang.dev/docs/api/c/fputs) | Writes a string to the given stream. | | [`fread`](https://rux-lang.dev/docs/api/c/fread) | Reads up to count objects of the given size from the stream into a buffer. | | [`freopen`](https://rux-lang.dev/docs/api/c/freopen) | Reopens a stream with a different file or mode. | | [`fscanf`](https://rux-lang.dev/docs/api/c/fscanf) | Reads formatted input from a file stream. | | [`fseek`](https://rux-lang.dev/docs/api/c/fseek) | Sets the file position indicator for the stream. | | [`fsetpos`](https://rux-lang.dev/docs/api/c/fsetpos) | Sets the file position of the stream to the given position. | | [`ftell`](https://rux-lang.dev/docs/api/c/ftell) | Returns the current file position of the stream. | | [`fwrite`](https://rux-lang.dev/docs/api/c/fwrite) | Writes count objects of the given size from a buffer to the stream. | | [`getc`](https://rux-lang.dev/docs/api/c/getc) | Reads the next character from the given stream. | | [`getchar`](https://rux-lang.dev/docs/api/c/getchar) | Reads the next character from stdin. | | [`perror`](https://rux-lang.dev/docs/api/c/perror) | Prints an error message describing the last error to stderr. | | [`printf`](https://rux-lang.dev/docs/api/c/printf) | Prints formatted output to stdout. | | [`putc`](https://rux-lang.dev/docs/api/c/putc) | Writes a character to the given stream. | | [`putchar`](https://rux-lang.dev/docs/api/c/putchar) | Writes a character to stdout. | | [`puts`](https://rux-lang.dev/docs/api/c/puts) | Writes a string followed by a newline to stdout. | | [`remove`](https://rux-lang.dev/docs/api/c/remove) | Deletes the file identified by the given path. | | [`rename`](https://rux-lang.dev/docs/api/c/rename) | Renames a file, moving it if necessary. | | [`rewind`](https://rux-lang.dev/docs/api/c/rewind) | Moves the file position indicator to the beginning of the stream. | | [`scanf`](https://rux-lang.dev/docs/api/c/scanf) | Reads formatted input from stdin. | | [`setbuf`](https://rux-lang.dev/docs/api/c/setbuf) | Sets the buffer to be used by the given stream. | | [`setvbuf`](https://rux-lang.dev/docs/api/c/setvbuf) | Sets the buffering mode and buffer to be used by the given stream. | | [`sprintf`](https://rux-lang.dev/docs/api/c/sprintf) | Prints formatted output to a string. | | [`sscanf`](https://rux-lang.dev/docs/api/c/sscanf) | Reads formatted input from a string. | | [`tmpfile`](https://rux-lang.dev/docs/api/c/tmpfile) | Creates and opens a temporary file with a unique name. | | [`tmpnam`](https://rux-lang.dev/docs/api/c/tmpnam) | Generates a unique filename that does not name an existing file. | | [`ungetc`](https://rux-lang.dev/docs/api/c/ungetc) | Puts a character back into the given stream. | ## Standard library | Function | Description | | ---------------------------------------------------- | ----------------------------------------------------------------------------- | | [`abort`](https://rux-lang.dev/docs/api/c/abort) | Causes abnormal program termination without cleaning up. | | [`abs`](https://rux-lang.dev/docs/api/c/abs) | Computes the absolute value of an integer value. | | [`atof`](https://rux-lang.dev/docs/api/c/atof) | Converts a byte string to a floating point value. | | [`atoi`](https://rux-lang.dev/docs/api/c/atoi) | Converts a byte string to an integer value. | | [`atol`](https://rux-lang.dev/docs/api/c/atol) | Converts a byte string to a long integer value. | | [`atoll`](https://rux-lang.dev/docs/api/c/atoll) | Converts a byte string to a long long integer value. | | [`calloc`](https://rux-lang.dev/docs/api/c/calloc) | Allocates memory for an array of num objects of size and zero-initializes it. | | [`exit`](https://rux-lang.dev/docs/api/c/exit) | Causes normal program termination with cleanup. | | [`free`](https://rux-lang.dev/docs/api/c/free) | Deallocates the memory previously allocated by malloc, calloc or realloc. | | [`getenv`](https://rux-lang.dev/docs/api/c/getenv) | Returns the value of an environment variable. | | [`labs`](https://rux-lang.dev/docs/api/c/labs) | Computes the absolute value of a long integer value. | | [`llabs`](https://rux-lang.dev/docs/api/c/llabs) | Computes the absolute value of a long long integer value. | | [`malloc`](https://rux-lang.dev/docs/api/c/malloc) | Allocates size bytes of uninitialized memory. | | [`rand`](https://rux-lang.dev/docs/api/c/rand) | Returns a pseudo-random integer value between 0 and RAND\_MAX. | | [`realloc`](https://rux-lang.dev/docs/api/c/realloc) | Changes the size of the memory block pointed to by ptr to size bytes. | | [`srand`](https://rux-lang.dev/docs/api/c/srand) | Seeds the pseudo-random number generator used by rand. | | [`system`](https://rux-lang.dev/docs/api/c/system) | Calls the host environment's command processor. | ## Time | Function | Description | | -------------------------------------------------------- | ------------------------------------------------------------------------- | | [`asctime`](https://rux-lang.dev/docs/api/c/asctime) | Converts a tm object to a textual representation. | | [`clock`](https://rux-lang.dev/docs/api/c/clock) | Returns the approximate processor time used by the program. | | [`ctime`](https://rux-lang.dev/docs/api/c/ctime) | Converts a time\_t object to a textual representation. | | [`difftime`](https://rux-lang.dev/docs/api/c/difftime) | Computes the difference in seconds between two calendar times. | | [`gmtime`](https://rux-lang.dev/docs/api/c/gmtime) | Converts a time\_t object to calendar time expressed as UTC. | | [`localtime`](https://rux-lang.dev/docs/api/c/localtime) | Converts a time\_t object to calendar time expressed as local time. | | [`mktime`](https://rux-lang.dev/docs/api/c/mktime) | Converts calendar time to a time\_t object, normalizing the tm structure. | | [`strftime`](https://rux-lang.dev/docs/api/c/strftime) | Converts a tm object to a custom textual representation. | | [`time`](https://rux-lang.dev/docs/api/c/time) | Returns the current calendar time since the epoch. | # labs Computes the absolute value of a long integer value. **Package:** `C` **C reference:** [`labs`](https://en.cppreference.com/c/numeric/math/abs){rel=""nofollow""} ## Signature ```rux func labs(n: int64) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ldexp Multiplies a number by 2 raised to an integer power. **Package:** `C` **C reference:** [`ldexp`](https://en.cppreference.com/c/numeric/math/ldexp){rel=""nofollow""} ## Signature ```rux func ldexp(x: float64, exp: int32) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # lgamma Computes natural logarithm of the absolute value of the gamma function. **Package:** `C` **C reference:** [`lgamma`](https://en.cppreference.com/c/numeric/math/lgamma){rel=""nofollow""} ## Signature ```rux func lgamma(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # lgammaf Computes natural logarithm of the absolute value of the gamma function. **Package:** `C` **C reference:** [`lgammaf`](https://en.cppreference.com/c/numeric/math/lgamma){rel=""nofollow""} ## Signature ```rux func lgammaf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # llabs Computes the absolute value of a long long integer value. **Package:** `C` **C reference:** [`llabs`](https://en.cppreference.com/c/numeric/math/abs){rel=""nofollow""} ## Signature ```rux func llabs(n: int64) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # llrint Rounds to nearest integer using current rounding mode. **Package:** `C` **C reference:** [`llrint`](https://en.cppreference.com/c/numeric/math/rint){rel=""nofollow""} ## Signature ```rux func llrint(x: float64) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # llrintf Rounds to nearest integer using current rounding mode. **Package:** `C` **C reference:** [`llrintf`](https://en.cppreference.com/c/numeric/math/rint){rel=""nofollow""} ## Signature ```rux func llrintf(x: float32) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # llround Rounds to nearest integer, rounding away from zero in halfway cases. **Package:** `C` **C reference:** [`llround`](https://en.cppreference.com/c/numeric/math/round){rel=""nofollow""} ## Signature ```rux func llround(x: float64) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # llroundf Rounds to nearest integer, rounding away from zero in halfway cases. **Package:** `C` **C reference:** [`llroundf`](https://en.cppreference.com/c/numeric/math/round){rel=""nofollow""} ## Signature ```rux func llroundf(x: float32) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # localtime Converts a time\_t object to calendar time expressed as local time. **Package:** `C` **C reference:** [`localtime`](https://en.cppreference.com/c/chrono/localtime){rel=""nofollow""} ## Signature ```rux func localtime(timer: *time_t) -> *tm; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # log Computes natural (base e) logarithm. **Package:** `C` **C reference:** [`log`](https://en.cppreference.com/c/numeric/math/log){rel=""nofollow""} ## Signature ```rux func log(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # log10 Computes common (base 10) logarithm. **Package:** `C` **C reference:** [`log10`](https://en.cppreference.com/c/numeric/math/log10){rel=""nofollow""} ## Signature ```rux func log10(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # log10f Computes common (base 10) logarithm. **Package:** `C` **C reference:** [`log10f`](https://en.cppreference.com/c/numeric/math/log10){rel=""nofollow""} ## Signature ```rux func log10f(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # log1p Computes natural logarithm of 1 plus the given number. **Package:** `C` **C reference:** [`log1p`](https://en.cppreference.com/c/numeric/math/log1p){rel=""nofollow""} ## Signature ```rux func log1p(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # log1pf Computes natural logarithm of 1 plus the given number. **Package:** `C` **C reference:** [`log1pf`](https://en.cppreference.com/c/numeric/math/log1p){rel=""nofollow""} ## Signature ```rux func log1pf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # log2 Computes base 2 logarithm. **Package:** `C` **C reference:** [`log2`](https://en.cppreference.com/c/numeric/math/log2){rel=""nofollow""} ## Signature ```rux func log2(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # log2f Computes base 2 logarithm. **Package:** `C` **C reference:** [`log2f`](https://en.cppreference.com/c/numeric/math/log2){rel=""nofollow""} ## Signature ```rux func log2f(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # logb Extracts exponent of the number. **Package:** `C` **C reference:** [`logb`](https://en.cppreference.com/c/numeric/math/logb){rel=""nofollow""} ## Signature ```rux func logb(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # logbf Extracts exponent of the number. **Package:** `C` **C reference:** [`logbf`](https://en.cppreference.com/c/numeric/math/logb){rel=""nofollow""} ## Signature ```rux func logbf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # logf Computes natural (base e) logarithm. **Package:** `C` **C reference:** [`logf`](https://en.cppreference.com/c/numeric/math/log){rel=""nofollow""} ## Signature ```rux func logf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # lrint Rounds to nearest integer using current rounding mode. **Package:** `C` **C reference:** [`lrint`](https://en.cppreference.com/c/numeric/math/rint){rel=""nofollow""} ## Signature ```rux func lrint(x: float64) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # lrintf Rounds to nearest integer using current rounding mode. **Package:** `C` **C reference:** [`lrintf`](https://en.cppreference.com/c/numeric/math/rint){rel=""nofollow""} ## Signature ```rux func lrintf(x: float32) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # lround Rounds to nearest integer, rounding away from zero in halfway cases. **Package:** `C` **C reference:** [`lround`](https://en.cppreference.com/c/numeric/math/round){rel=""nofollow""} ## Signature ```rux func lround(x: float64) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # lroundf Rounds to nearest integer, rounding away from zero in halfway cases. **Package:** `C` **C reference:** [`lroundf`](https://en.cppreference.com/c/numeric/math/round){rel=""nofollow""} ## Signature ```rux func lroundf(x: float32) -> int64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # malloc Allocates size bytes of uninitialized memory. **Package:** `C` **C reference:** [`malloc`](https://en.cppreference.com/c/memory/malloc){rel=""nofollow""} ## Signature ```rux func malloc(size: uint) -> *opaque; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # mktime Converts calendar time to a time\_t object, normalizing the tm structure. **Package:** `C` **C reference:** [`mktime`](https://en.cppreference.com/c/chrono/mktime){rel=""nofollow""} ## Signature ```rux func mktime(time_ptr: *tm) -> time_t; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # modf Decomposes a number into integer and fractional parts. **Package:** `C` **C reference:** [`modf`](https://en.cppreference.com/c/numeric/math/modf){rel=""nofollow""} ## Signature ```rux func modf(x: float64, iptr: *float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # modff Decomposes a number into integer and fractional parts. **Package:** `C` **C reference:** [`modff`](https://en.cppreference.com/c/numeric/math/modf){rel=""nofollow""} ## Signature ```rux func modff(x: float32, iptr: *float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # nan Generates a quiet NaN. **Package:** `C` **C reference:** [`nan`](https://en.cppreference.com/c/numeric/math/nan){rel=""nofollow""} ## Signature ```rux func nan(arg: *char8) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # nanf Generates a quiet NaN. **Package:** `C` **C reference:** [`nanf`](https://en.cppreference.com/c/numeric/math/nan){rel=""nofollow""} ## Signature ```rux func nanf(arg: *char8) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # nearbyint Rounds to nearest integer using current rounding mode without raising the inexact exception. **Package:** `C` **C reference:** [`nearbyint`](https://en.cppreference.com/c/numeric/math/nearbyint){rel=""nofollow""} ## Signature ```rux func nearbyint(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # nearbyintf Rounds to nearest integer using current rounding mode without raising the inexact exception. **Package:** `C` **C reference:** [`nearbyintf`](https://en.cppreference.com/c/numeric/math/nearbyint){rel=""nofollow""} ## Signature ```rux func nearbyintf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # nextafter Determines next representable floating-point value toward the given value. **Package:** `C` **C reference:** [`nextafter`](https://en.cppreference.com/c/numeric/math/nextafter){rel=""nofollow""} ## Signature ```rux func nextafter(from: float64, to: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # nextafterf Determines next representable floating-point value toward the given value. **Package:** `C` **C reference:** [`nextafterf`](https://en.cppreference.com/c/numeric/math/nextafter){rel=""nofollow""} ## Signature ```rux func nextafterf(from: float32, to: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # perror Prints an error message describing the last error to stderr. **Package:** `C` **C reference:** [`perror`](https://en.cppreference.com/c/io/perror){rel=""nofollow""} ## Signature ```rux func perror(str: *char8); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # pow Computes a number raised to the given power. **Package:** `C` **C reference:** [`pow`](https://en.cppreference.com/c/numeric/math/pow){rel=""nofollow""} ## Signature ```rux func pow(base: float64, exponent: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # powf Computes a number raised to the given power. **Package:** `C` **C reference:** [`powf`](https://en.cppreference.com/c/numeric/math/pow){rel=""nofollow""} ## Signature ```rux func powf(base: float32, exponent: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # printf Prints formatted output to stdout. **Package:** `C` **C reference:** [`printf`](https://en.cppreference.com/c/io/printf){rel=""nofollow""} ## Signature ```rux func printf(format: *char8, ...) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # putc Writes a character to the given stream. **Package:** `C` **C reference:** [`putc`](https://en.cppreference.com/c/io/fputc){rel=""nofollow""} ## Signature ```rux func putc(ch: int32, stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # putchar Writes a character to stdout. **Package:** `C` **C reference:** [`putchar`](https://en.cppreference.com/c/io/putchar){rel=""nofollow""} ## Signature ```rux func putchar(ch: int32) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # puts Writes a string followed by a newline to stdout. **Package:** `C` **C reference:** [`puts`](https://en.cppreference.com/c/io/puts){rel=""nofollow""} ## Signature ```rux func puts(str: *char8) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # rand Returns a pseudo-random integer value between 0 and RAND\_MAX. **Package:** `C` **C reference:** [`rand`](https://en.cppreference.com/c/numeric/random/rand){rel=""nofollow""} ## Signature ```rux func rand() -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # realloc Changes the size of the memory block pointed to by ptr to size bytes. **Package:** `C` **C reference:** [`realloc`](https://en.cppreference.com/c/memory/realloc){rel=""nofollow""} ## Signature ```rux func realloc(ptr: *opaque, size: uint) -> *opaque; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # remainder Computes signed remainder of the floating-point division operation. **Package:** `C` **C reference:** [`remainder`](https://en.cppreference.com/c/numeric/math/remainder){rel=""nofollow""} ## Signature ```rux func remainder(x: float64, y: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # remainderf Computes signed remainder of the floating-point division operation. **Package:** `C` **C reference:** [`remainderf`](https://en.cppreference.com/c/numeric/math/remainder){rel=""nofollow""} ## Signature ```rux func remainderf(x: float32, y: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # remove Deletes the file identified by the given path. **Package:** `C` **C reference:** [`remove`](https://en.cppreference.com/c/io/remove){rel=""nofollow""} ## Signature ```rux func remove(pathname: *char8) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # remquo Computes signed remainder as well as the three last bits of the division operation. **Package:** `C` **C reference:** [`remquo`](https://en.cppreference.com/c/numeric/math/remquo){rel=""nofollow""} ## Signature ```rux func remquo(x: float64, y: float64, quo: *int32) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # remquof Computes signed remainder as well as the three last bits of the division operation. **Package:** `C` **C reference:** [`remquof`](https://en.cppreference.com/c/numeric/math/remquo){rel=""nofollow""} ## Signature ```rux func remquof(x: float32, y: float32, quo: *int32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # rename Renames a file, moving it if necessary. **Package:** `C` **C reference:** [`rename`](https://en.cppreference.com/c/io/rename){rel=""nofollow""} ## Signature ```rux func rename(old_filename: *char8, new_filename: *char8) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # rewind Moves the file position indicator to the beginning of the stream. **Package:** `C` **C reference:** [`rewind`](https://en.cppreference.com/c/io/rewind){rel=""nofollow""} ## Signature ```rux func rewind(stream: *opaque); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # rint Rounds to nearest integer using current rounding mode. **Package:** `C` **C reference:** [`rint`](https://en.cppreference.com/c/numeric/math/rint){rel=""nofollow""} ## Signature ```rux func rint(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # rintf Rounds to nearest integer using current rounding mode. **Package:** `C` **C reference:** [`rintf`](https://en.cppreference.com/c/numeric/math/rint){rel=""nofollow""} ## Signature ```rux func rintf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # round Rounds to nearest integer, rounding away from zero in halfway cases. **Package:** `C` **C reference:** [`round`](https://en.cppreference.com/c/numeric/math/round){rel=""nofollow""} ## Signature ```rux func round(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # roundf Rounds to nearest integer, rounding away from zero in halfway cases. **Package:** `C` **C reference:** [`roundf`](https://en.cppreference.com/c/numeric/math/round){rel=""nofollow""} ## Signature ```rux func roundf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # scalbn Multiplies a number by FLT\_RADIX raised to an integer power. **Package:** `C` **C reference:** [`scalbn`](https://en.cppreference.com/c/numeric/math/scalbn){rel=""nofollow""} ## Signature ```rux func scalbn(x: float64, n: int32) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # scalbnf Multiplies a number by FLT\_RADIX raised to an integer power. **Package:** `C` **C reference:** [`scalbnf`](https://en.cppreference.com/c/numeric/math/scalbn){rel=""nofollow""} ## Signature ```rux func scalbnf(x: float32, n: int32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # scanf Reads formatted input from stdin. **Package:** `C` **C reference:** [`scanf`](https://en.cppreference.com/c/io/scanf){rel=""nofollow""} ## Signature ```rux func scanf(format: *char8, ...) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # setbuf Sets the buffer to be used by the given stream. **Package:** `C` **C reference:** [`setbuf`](https://en.cppreference.com/c/io/setbuf){rel=""nofollow""} ## Signature ```rux func setbuf(stream: *opaque, buffer: *char8); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # setvbuf Sets the buffering mode and buffer to be used by the given stream. **Package:** `C` **C reference:** [`setvbuf`](https://en.cppreference.com/c/io/setvbuf){rel=""nofollow""} ## Signature ```rux func setvbuf(stream: *opaque, buffer: *char8, mode: int32, size: uint) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sin Computes sine. **Package:** `C` **C reference:** [`sin`](https://en.cppreference.com/c/numeric/math/sin){rel=""nofollow""} ## Signature ```rux func sin(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sinf Computes sine. **Package:** `C` **C reference:** [`sinf`](https://en.cppreference.com/c/numeric/math/sin){rel=""nofollow""} ## Signature ```rux func sinf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sinh Computes hyperbolic sine. **Package:** `C` **C reference:** [`sinh`](https://en.cppreference.com/c/numeric/math/sinh){rel=""nofollow""} ## Signature ```rux func sinh(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sinhf Computes hyperbolic sine. **Package:** `C` **C reference:** [`sinhf`](https://en.cppreference.com/c/numeric/math/sinh){rel=""nofollow""} ## Signature ```rux func sinhf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sprintf Prints formatted output to a string. **Package:** `C` **C reference:** [`sprintf`](https://en.cppreference.com/c/io/sprintf){rel=""nofollow""} ## Signature ```rux func sprintf(buffer: *char8, format: *char8, ...) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sqrt Computes square root. **Package:** `C` **C reference:** [`sqrt`](https://en.cppreference.com/c/numeric/math/sqrt){rel=""nofollow""} ## Signature ```rux func sqrt(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sqrtf Computes square root. **Package:** `C` **C reference:** [`sqrtf`](https://en.cppreference.com/c/numeric/math/sqrt){rel=""nofollow""} ## Signature ```rux func sqrtf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # srand Seeds the pseudo-random number generator used by rand. **Package:** `C` **C reference:** [`srand`](https://en.cppreference.com/c/numeric/random/srand){rel=""nofollow""} ## Signature ```rux func srand(seed: uint32); ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # sscanf Reads formatted input from a string. **Package:** `C` **C reference:** [`sscanf`](https://en.cppreference.com/c/io/sscanf){rel=""nofollow""} ## Signature ```rux func sscanf(buffer: *char8, format: *char8, ...) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # strftime Converts a tm object to a custom textual representation. **Package:** `C` **C reference:** [`strftime`](https://en.cppreference.com/c/chrono/strftime){rel=""nofollow""} ## Signature ```rux func strftime(str: *char8, count: uint, format: *char8, time_ptr: *tm) -> uint; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # system Calls the host environment's command processor. **Package:** `C` **C reference:** [`system`](https://en.cppreference.com/c/program/system){rel=""nofollow""} ## Signature ```rux func system(command: *char8) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tan Computes tangent. **Package:** `C` **C reference:** [`tan`](https://en.cppreference.com/c/numeric/math/tan){rel=""nofollow""} ## Signature ```rux func tan(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tanf Computes tangent. **Package:** `C` **C reference:** [`tanf`](https://en.cppreference.com/c/numeric/math/tan){rel=""nofollow""} ## Signature ```rux func tanf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tanh Computes hyperbolic tangent. **Package:** `C` **C reference:** [`tanh`](https://en.cppreference.com/c/numeric/math/tanh){rel=""nofollow""} ## Signature ```rux func tanh(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tanhf Computes hyperbolic tangent. **Package:** `C` **C reference:** [`tanhf`](https://en.cppreference.com/c/numeric/math/tanh){rel=""nofollow""} ## Signature ```rux func tanhf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tgamma Computes gamma function. **Package:** `C` **C reference:** [`tgamma`](https://en.cppreference.com/c/numeric/math/tgamma){rel=""nofollow""} ## Signature ```rux func tgamma(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tgammaf Computes gamma function. **Package:** `C` **C reference:** [`tgammaf`](https://en.cppreference.com/c/numeric/math/tgamma){rel=""nofollow""} ## Signature ```rux func tgammaf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # time Returns the current calendar time since the epoch. **Package:** `C` **C reference:** [`time`](https://en.cppreference.com/c/chrono/time){rel=""nofollow""} ## Signature ```rux func time(timer: *time_t) -> time_t; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tmpfile Creates and opens a temporary file with a unique name. **Package:** `C` **C reference:** [`tmpfile`](https://en.cppreference.com/c/io/tmpfile){rel=""nofollow""} ## Signature ```rux func tmpfile() -> *opaque; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # tmpnam Generates a unique filename that does not name an existing file. **Package:** `C` **C reference:** [`tmpnam`](https://en.cppreference.com/c/io/tmpnam){rel=""nofollow""} ## Signature ```rux func tmpnam(filename: *char8) -> *char8; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # trunc Rounds to nearest integer not greater in magnitude than the given value. **Package:** `C` **C reference:** [`trunc`](https://en.cppreference.com/c/numeric/math/trunc){rel=""nofollow""} ## Signature ```rux func trunc(x: float64) -> float64; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # truncf Rounds to nearest integer not greater in magnitude than the given value. **Package:** `C` **C reference:** [`truncf`](https://en.cppreference.com/c/numeric/math/trunc){rel=""nofollow""} ## Signature ```rux func truncf(x: float32) -> float32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # Types Types exported by the `C` package, mirroring the C standard library. **Package:** `C` ## `time_t` **C reference:** [`time_t`](https://en.cppreference.com/c/chrono/time_t){rel=""nofollow""} ```rux type time_t = int64; ``` An arithmetic type capable of representing a calendar time, in seconds since the epoch. Used by [`time`](https://rux-lang.dev/docs/api/c/time), [`ctime`](https://rux-lang.dev/docs/api/c/ctime), [`gmtime`](https://rux-lang.dev/docs/api/c/gmtime), [`localtime`](https://rux-lang.dev/docs/api/c/localtime), [`mktime`](https://rux-lang.dev/docs/api/c/mktime), and [`difftime`](https://rux-lang.dev/docs/api/c/difftime). ## `clock_t` **C reference:** [`clock_t`](https://en.cppreference.com/c/chrono/clock_t){rel=""nofollow""} ```rux type clock_t = int64; ``` An arithmetic type capable of representing processor time. Returned by [`clock`](https://rux-lang.dev/docs/api/c/clock). ## `tm` **C reference:** [`tm`](https://en.cppreference.com/c/chrono/tm){rel=""nofollow""} ```rux struct tm { tm_sec: int32; // seconds after the minute [0, 60] tm_min: int32; // minutes after the hour [0, 59] tm_hour: int32; // hours since midnight [0, 23] tm_mday: int32; // day of the month [1, 31] tm_mon: int32; // months since January [0, 11] tm_year: int32; // years since 1900 tm_wday: int32; // days since Sunday [0, 6] tm_yday: int32; // days since January 1 [0, 365] tm_isdst: int32; // daylight saving time flag tm_gmtoff: int64; // seconds east of UTC tm_zone: *char8; // timezone abbreviation } ``` Calendar time broken into its components. On Windows the layout stops at `tm_isdst`; the `tm_gmtoff` and `tm_zone` fields exist only on the Unix targets. Filled by [`gmtime`](https://rux-lang.dev/docs/api/c/gmtime) and [`localtime`](https://rux-lang.dev/docs/api/c/localtime), and consumed by [`asctime`](https://rux-lang.dev/docs/api/c/asctime), [`mktime`](https://rux-lang.dev/docs/api/c/mktime), and [`strftime`](https://rux-lang.dev/docs/api/c/strftime). ## `timespec` **C reference:** [`timespec`](https://en.cppreference.com/c/chrono/timespec){rel=""nofollow""} ```rux struct timespec { tv_sec: time_t; // whole seconds tv_nsec: int64; // nanoseconds [0, 999999999] } ``` A time expressed in whole seconds plus a nanosecond remainder. ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # ungetc Puts a character back into the given stream. **Package:** `C` **C reference:** [`ungetc`](https://en.cppreference.com/c/io/ungetc){rel=""nofollow""} ## Signature ```rux func ungetc(ch: int32, stream: *opaque) -> int32; ``` ## See also - [`C`](https://rux-lang.dev/docs/api/c) — the package overview # Format Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: The package converts between values and text — a [`ToString`](https://rux-lang.dev/docs/api/format/tostring) for every primitive type, a [`Stringable`](https://rux-lang.dev/docs/api/format/stringable) interface for the types you write yourself, a family of [`Write`](https://rux-lang.dev/docs/api/format/writeint) functions that append into a builder instead of allocating, and a set of [`Parse`](https://rux-lang.dev/docs/api/format/parseint64) functions that read a value back out of text. **Package:** `Format` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Format](https://github.com/rux-lang/Rux/tree/main/Packages/Format){rel=""nofollow""} A conversion to text produces a [`Text::String`](https://rux-lang.dev/docs/api/text/string), which is what this package is built on. The [`Parse`](https://rux-lang.dev/docs/api/format/parseint64) functions go the other way, reading a value out of a `Slice` or a `String` and allocating nothing. ```rux import Format::ToString; import Io::PrintLine; func Main() -> int { var count = ToString(42); // "42" var ratio = ToString(0.1); // "0.1" var flag = ToString(true); // "true" PrintLine(count); ratio.Free(); count.Free(); flag.Free(); return 0; } ``` ## Installation ```sh rux add Format rux install ``` ## Platform support The package is pure computation over strings, so it runs wherever [`Text`](https://rux-lang.dev/docs/api/text) does: FreeBSD, Linux, macOS, and Windows. ## Ownership Every `ToString` returns a `String` the caller owns and passes to [`String::Free`](https://rux-lang.dev/docs/api/text/string/free) exactly once, on the terms [`Text`](https://rux-lang.dev/docs/api/text#ownership) sets out. That is one allocation per conversion, so converting several values only to join them allocates a `String` that is thrown away again for each one. The `Write` functions are the way around that. They allocate nothing of their own — they append into a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) the caller supplies, and the caller frees the builder, or takes its contents with [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring): ```rux import Format::{ WriteInt, WriteFloat }; import Text::StringBuilder; var builder = StringBuilder::New(); builder.Append("point "); WriteInt(@builder, 3); // no String allocated builder.Append(", "); WriteFloat(@builder, 4.5); var line = builder.IntoString(); // "point 3, 4.5" line.Free(); ``` ## Widths and aliases `bool` is an alias for `bool8`, `char` for `char32`, and `float` for `float64`, so a call on an alias reaches the overload for the sized type it names. The `Write` functions take the widest type of their family — an `int64` for the signed integers, a `uint64` for the unsigned ones — and a narrower value widens on the way in, which loses nothing. That is why there is one [`WriteInt`](https://rux-lang.dev/docs/api/format/writeint) rather than one per width, while [`ToString`](https://rux-lang.dev/docs/api/format/tostring) has an overload for each. ## Floating-point text A float is rendered to the digits its type actually carries — fifteen for a `float64`, seven for a `float32` — with the trailing zeros dropped, so `0.1` prints as `0.1` rather than as the `0.1000000000000000055` it really is. The cost is that the text is **not a round trip**: two values that differ only past the last digit print the same. Notation follows the printf `%g` rule. Fixed while the decimal exponent stays in `[-4, digits)`, scientific outside it, where fixed notation would spend its width on zeros. A NaN prints as `NaN`, the infinities as `Inf` and `-Inf`, and a negative zero keeps its sign. ## Interfaces | Interface | Description | | --------------------------------------------------------------- | ------------------------------------------------------- | | [`Stringable`](https://rux-lang.dev/docs/api/format/stringable) | What a type implements to convert itself to a `String`. | ## Errors | Type | Description | | --------------------------------------------------------------- | -------------------------------------------------------------- | | [`ParseError`](https://rux-lang.dev/docs/api/format/parseerror) | Why a parse failed: empty input, an invalid byte, or overflow. | ## Functions ### Conversion | Function | Description | | ----------------------------------------------------------- | --------------------------------------------------- | | [`ToString`](https://rux-lang.dev/docs/api/format/tostring) | The text of a value, in a `String` the caller owns. | ### Appending Each appends into a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) and allocates no `String` of its own. | Function | Description | | --------------------------------------------------------------- | ------------------------------------------- | | [`WriteInt`](https://rux-lang.dev/docs/api/format/writeint) | The decimal digits of a signed integer. | | [`WriteUint`](https://rux-lang.dev/docs/api/format/writeuint) | The decimal digits of an unsigned integer. | | [`WriteFloat`](https://rux-lang.dev/docs/api/format/writefloat) | The decimal text of a floating-point value. | | [`WriteBool`](https://rux-lang.dev/docs/api/format/writebool) | `true` or `false`. | | [`WriteChar`](https://rux-lang.dev/docs/api/format/writechar) | The UTF-8 bytes of a character. | ### Classification | Function | Description | | --------------------------------------------------------------- | --------------------------------------------- | | [`IsNan`](https://rux-lang.dev/docs/api/format/isnan) | Whether a value is a NaN. | | [`IsInfinite`](https://rux-lang.dev/docs/api/format/isinfinite) | Whether a value is one of the two infinities. | | [`IsFinite`](https://rux-lang.dev/docs/api/format/isfinite) | Whether a value is an ordinary number. | ### Parsing Each reads a value out of a `Slice` or a `String`, allocating nothing. The `Parse` forms return a [`Result`](https://rux-lang.dev/docs/lang/errors/overview); the `TryParse` forms write through a pointer and return whether it worked. | Function | Description | | ------------------------------------------------------------------------- | --------------------------------------------------- | | [`ParseInt64`](https://rux-lang.dev/docs/api/format/parseint64) | Parse a signed integer, as a `Result`. | | [`ParseFloat64`](https://rux-lang.dev/docs/api/format/parsefloat64) | Parse a floating-point value, as a `Result`. | | [`TryParseInt64`](https://rux-lang.dev/docs/api/format/tryparseint64) | Parse a signed integer into an out-parameter. | | [`TryParseFloat64`](https://rux-lang.dev/docs/api/format/tryparsefloat64) | Parse a floating-point value into an out-parameter. | ## See also - [`Text`](https://rux-lang.dev/docs/api/text) — the `String` and `StringBuilder` every conversion here produces # IsFinite Reports whether a value is an ordinary number. **Package:** `Format` ## Signature ```rux func IsFinite(value: float64) -> bool; func IsFinite(value: float32) -> bool; ``` ## Parameters | Name | Type | Description | | ------- | --------------------- | ------------------ | | `value` | `float64` / `float32` | The value to test. | ## Returns `true` when the value is neither a NaN nor an infinity: every real number the type can hold, from the largest finite value down through the subnormals to zero, and the negative of each. This is exactly the negation of [`IsNan`](https://rux-lang.dev/docs/api/format/isnan) or [`IsInfinite`](https://rux-lang.dev/docs/api/format/isinfinite) — the three partition the values, and every value answers `true` to precisely one of them. A NaN is not finite: the test is a pair of comparisons against the range of the type, and every comparison against a NaN is false. As with [`IsInfinite`](https://rux-lang.dev/docs/api/format/isinfinite), the width matters, and the overload is chosen by the argument. A `float32` is measured against the `float32` range. ## Example ```rux import Format::IsFinite; func Main() -> int { IsFinite(0.0); // true IsFinite(-1.5e300); // true IsFinite(5.0e-324); // true -- the smallest subnormal is still a number IsFinite(1.0 / 0.0); // false IsFinite(0.0 / 0.0); // false IsFinite(3.4028235e38f32); // true -- the largest finite float32 IsFinite(1.0e300); // true -- as a float64 return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`IsNan`](https://rux-lang.dev/docs/api/format/isnan) — whether the value is a NaN - [`IsInfinite`](https://rux-lang.dev/docs/api/format/isinfinite) — whether the value is one of the two infinities - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — what each of the three classes prints as # IsInfinite Reports whether a value is one of the two infinities. **Package:** `Format` ## Signature ```rux func IsInfinite(value: float64) -> bool; func IsInfinite(value: float32) -> bool; ``` ## Parameters | Name | Type | Description | | ------- | --------------------- | ------------------ | | `value` | `float64` / `float32` | The value to test. | ## Returns `true` for the positive infinity and for the negative one, and `false` for every finite value. The sign is not reported — compare against zero for that. The test is a comparison against the largest finite value of the type, so **the width matters**: the overload is chosen by the argument, and a `float32` infinity is measured against the `float32` range rather than being widened into the far larger `float64` one and found finite there. A NaN answers `false`. Every comparison against a NaN is false, so it fails this test rather than being mistaken for an infinity. ## Example ```rux import Format::IsInfinite; func Main() -> int { let inf = 1.0 / 0.0; IsInfinite(inf); // true IsInfinite(-inf); // true IsInfinite(0.0 / 0.0); // false -- a NaN is not an infinity IsInfinite(1.7976931348623157e308); // false -- the largest finite float64 if IsInfinite(inf) && inf < 0.0 { // the negative one } return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`IsNan`](https://rux-lang.dev/docs/api/format/isnan) — whether the value is a NaN - [`IsFinite`](https://rux-lang.dev/docs/api/format/isfinite) — whether the value is an ordinary number - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the infinities convert to the text `Inf` and `-Inf` # IsNan Reports whether a value is a NaN. **Package:** `Format` ## Signature ```rux func IsNan(value: float64) -> bool; func IsNan(value: float32) -> bool; ``` ## Parameters | Name | Type | Description | | ------- | --------------------- | ------------------ | | `value` | `float64` / `float32` | The value to test. | ## Returns `true` when the value is a NaN — the result of `0.0 / 0.0`, of an infinity minus an infinity, and of every arithmetic operation that has no answer at all. A NaN is the only value that is not equal to itself, which is the whole test: no bit pattern is read, and every NaN answers `true` regardless of its payload or its sign. Because every comparison against a NaN is false, a NaN is not infinite and not finite either — [`IsInfinite`](https://rux-lang.dev/docs/api/format/isinfinite) and [`IsFinite`](https://rux-lang.dev/docs/api/format/isfinite) both answer `false` for one, so the three functions partition the values rather than overlapping. ## Example ```rux import Format::IsNan; func Main() -> int { let nan = 0.0 / 0.0; IsNan(nan); // true IsNan(1.0); // false IsNan(1.0 / 0.0); // false -- an infinity is a value, just not a finite one nan == nan; // false -- a NaN is the only value not equal to itself return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`IsInfinite`](https://rux-lang.dev/docs/api/format/isinfinite) — whether the value is one of the two infinities - [`IsFinite`](https://rux-lang.dev/docs/api/format/isfinite) — whether the value is an ordinary number - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — a NaN converts to the text `NaN` # ParseError Why a string could not be parsed into a value. **Package:** `Format` ## Definition ```rux enum ParseError { Empty, InvalidCharacter(uint), Overflow } ``` The error half of the [`Result`](https://rux-lang.dev/docs/lang/errors/overview) that [`ParseInt64`](https://rux-lang.dev/docs/api/format/parseint64) and [`ParseFloat64`](https://rux-lang.dev/docs/api/format/parsefloat64) return. The [`TryParse`](https://rux-lang.dev/docs/api/format/tryparseint64) functions collapse it to a `bool`, so it only surfaces from the `Parse` functions. ## Variants | Variant | Meaning | | ------------------------ | --------------------------------------------------------------- | | `Empty` | The input had no bytes. | | `InvalidCharacter(uint)` | A byte that does not fit the grammar; the payload is its index. | | `Overflow` | The value is outside the range the target type can hold. | The index carried by `InvalidCharacter` points at the offending byte — the first non-digit, a lone sign, or an exponent marker with no digits after it — which is enough to report where the input went wrong. ## Example ```rux import Format::{ ParseInt64, ParseError }; import Io::PrintLine; func Main() -> int { match ParseInt64("12x4") { .Success(n) => PrintLine(n), .Error(e) => match e { .Empty => PrintLine("empty"), .InvalidCharacter(i) => PrintLine(i), // 2 -- the 'x' .Overflow => PrintLine("overflow") } } return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`ParseInt64`](https://rux-lang.dev/docs/api/format/parseint64) / [`ParseFloat64`](https://rux-lang.dev/docs/api/format/parsefloat64) — the functions that return it - [`Result`](https://rux-lang.dev/docs/lang/errors/overview) — the success-or-error type it is the error half of # ParseFloat64 Parses a floating-point value from decimal text. **Package:** `Format` ## Signature ```rux func ParseFloat64( str: Slice ) -> Result; func ParseFloat64( str: String ) -> Result; ``` ## Parameters | Name | Type | Description | | ----- | ------------------------- | ------------------ | | `str` | `Slice` / `String` | The text to parse. | ## Returns A [`Result`](https://rux-lang.dev/docs/lang/errors/overview): `Success` holds the parsed `float64`, and `Error` holds a [`ParseError`](https://rux-lang.dev/docs/api/format/parseerror) saying why the text was rejected. The accepted grammar is a decimal number with an optional sign, an optional fraction, and an optional exponent: ```text [+-]? ( digits [ . digits? ]? | . digits ) ( [eE] [+-]? digits )? ``` So `1`, `1.5`, `.5`, `1.`, `+3.14`, `1e10`, and `1.5E-3` all parse, and the whole string has to fit the grammar — a trailing byte is rejected. Up to nineteen significant digits are kept, which is more than a `float64` can hold, so the result is correctly rounded from every digit that matters. The three failures are: - **`Empty`** — the input has no bytes. - **`InvalidCharacter(index)`** — a byte outside the grammar, a sign with no number after it, an `e`/`E` with no exponent digits, or trailing text after a valid number; `index` is where it sits. - **`Overflow`** — the magnitude is too large for a `float64` and would parse to an infinity. A magnitude too small to represent underflows to `0.0` instead, which is not an error. ## Example ```rux import Format::ParseFloat64; import Io::PrintLine; func Main() -> int { match ParseFloat64("3.14159") { .Success(x) => PrintLine(x), // 3.14159 .Error(_) => PrintLine("not a number") } match ParseFloat64("1.5e-3") { .Success(x) => PrintLine(x), // 0.0015 .Error(_) => {} } return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`TryParseFloat64`](https://rux-lang.dev/docs/api/format/tryparsefloat64) — the same parse, into an out-parameter, without a `Result` - [`ParseInt64`](https://rux-lang.dev/docs/api/format/parseint64) — parse a signed integer instead - [`ParseError`](https://rux-lang.dev/docs/api/format/parseerror) — the reason a parse failed - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the inverse, a value to its text # ParseInt64 Parses a signed integer from decimal text. **Package:** `Format` ## Signature ```rux func ParseInt64( str: Slice ) -> Result; func ParseInt64( str: String ) -> Result; ``` ## Parameters | Name | Type | Description | | ----- | ------------------------- | ------------------ | | `str` | `Slice` / `String` | The text to parse. | ## Returns A [`Result`](https://rux-lang.dev/docs/lang/errors/overview): `Success` holds the parsed `int64`, and `Error` holds a [`ParseError`](https://rux-lang.dev/docs/api/format/parseerror) saying why the text was rejected. The accepted form is an optional `+` or `-` followed by one or more decimal digits, and nothing else — the whole string has to be a number. There is no whitespace to trim, no other base, and no digit separators. The three failures are: - **`Empty`** — the input has no bytes. - **`InvalidCharacter(index)`** — a byte that is not a digit, or a sign with no digits after it; `index` is where it sits. - **`Overflow`** — the value is outside the `int64` range. The negative limit is one larger than the positive one, so the most negative `int64`, `-9223372036854775808`, parses rather than overflowing. ## Example ```rux import Format::ParseInt64; import Io::PrintLine; func Main() -> int { match ParseInt64("42") { .Success(n) => PrintLine(n), // 42 .Error(_) => PrintLine("not an integer") } match ParseInt64("-9223372036854775808") { .Success(n) => PrintLine(n), // int64::Min, parsed exactly .Error(_) => {} } match ParseInt64("12x") { .Success(_) => {}, .Error(e) => {} // InvalidCharacter(2) } return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`TryParseInt64`](https://rux-lang.dev/docs/api/format/tryparseint64) — the same parse, into an out-parameter, without a `Result` - [`ParseFloat64`](https://rux-lang.dev/docs/api/format/parsefloat64) — parse a floating-point value instead - [`ParseError`](https://rux-lang.dev/docs/api/format/parseerror) — the reason a parse failed - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the inverse, a value to its text # Stringable The interface a type implements to convert itself into a `String`. **Package:** `Format` ## Interface ```rux interface Stringable { func ToString() -> String; } ``` A type is `Stringable` when it can hand back its own text. Every primitive already is — the package extends each of them — so the interface is there for the types you write yourself, and for code that wants to take *anything* convertible rather than one type in particular. The `String` a `ToString` returns is the caller's, and the caller frees it exactly once. An implementation that breaks that rule — handing back a `String` it still holds, or one it frees itself — hands the caller a double free. ## Implementing Implement it with an `extend ... : Stringable` block. The primitives do the same, which is what puts a `ToString` method on `42` as well as behind `ToString(42)`: ```rux import Format::{ Stringable, WriteInt }; import Text::{ String, StringBuilder }; struct Point { x: int; y: int; } extend Point : Stringable { func ToString(self) -> String { var builder = StringBuilder::New(); builder.Append(c8'('); WriteInt(@builder, self.x as int64); builder.Append(", "); WriteInt(@builder, self.y as int64); builder.Append(c8')'); return builder.IntoString(); } } ``` Building the text with a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) and the [`Write`](https://rux-lang.dev/docs/api/format/writeint) functions is what keeps the conversion to one allocation. Reaching for [`ToString`](https://rux-lang.dev/docs/api/format/tostring) on each field instead would allocate a `String` per field and throw each one away again. ## Example ```rux import Format::{ Stringable, WriteInt }; import Text::{ String, StringBuilder }; import Io::PrintLine; struct Point { x: int; y: int; } extend Point : Stringable { func ToString(self) -> String { var builder = StringBuilder::New(); builder.Append(c8'('); WriteInt(@builder, self.x as int64); builder.Append(", "); WriteInt(@builder, self.y as int64); builder.Append(c8')'); return builder.IntoString(); } } func Main() -> int { var origin = Point{ x: 0, y: 0 }; var text = origin.ToString(); // "(0, 0)" PrintLine(text); text.Free(); return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the conversion the primitives already have - [`WriteInt`](https://rux-lang.dev/docs/api/format/writeint) — append into a builder, without a `String` of its own - [`Text::StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — accumulate the text an implementation returns # ToString Returns the text of a value, in a `String` the caller owns. **Package:** `Format` ## Signature ```rux func ToString(value: int8) -> String; func ToString(value: int16) -> String; func ToString(value: int32) -> String; func ToString(value: int64) -> String; func ToString(value: int) -> String; func ToString(value: uint8) -> String; func ToString(value: uint16) -> String; func ToString(value: uint32) -> String; func ToString(value: uint64) -> String; func ToString(value: uint) -> String; func ToString(value: float32) -> String; func ToString(value: float64) -> String; func ToString(value: bool8) -> String; func ToString(value: bool16) -> String; func ToString(value: bool32) -> String; func ToString(value: char8) -> String; func ToString(value: char16) -> String; func ToString(value: char32) -> String; ``` Each type is also extended with [`Stringable`](https://rux-lang.dev/docs/api/format/stringable), so the same conversion is reachable as a method: `42.ToString()` and `ToString(42)` are the same call. ## Parameters | Name | Type | Description | | ------- | ------------------- | --------------------- | | `value` | Any primitive above | The value to convert. | `bool` is an alias for `bool8`, `char` for `char32`, and `float` for `float64`, so a call on an alias reaches the overload for the sized type it names. `int` and `uint` are types of their own rather than names for `int64` and `uint64`, and have overloads of their own. ## Returns A `String` holding the text of the value, in a block the caller owns and passes to [`String::Free`](https://rux-lang.dev/docs/api/text/string/free) exactly once. **Integers** come out as decimal digits, with a minus sign on a negative value and no plus sign on a positive one. The most negative value of a type has no positive counterpart, and converts correctly anyway: `-9223372036854775808` prints in full. **Floats** are rendered to the digits the type actually carries — fifteen for a `float64`, seven for a `float32` — with the trailing zeros dropped, and a fractional part always written, so `1.0` prints as `1.0` rather than `1`. Notation is fixed while the decimal exponent stays in `[-4, digits)` and scientific outside it, which is the printf `%g` rule. A NaN is `NaN`, the infinities are `Inf` and `-Inf`, and a negative zero keeps its sign as `-0.0`. The digit cutoff is what keeps the noise of the binary representation out of the text: `0.1` is really `0.1000000000000000055`, and printing every digit would say so. The cost is that the text is **not a round trip** — two values that differ only past the last digit print the same. **Bools** print as `true` or `false`, never as `0` or `1`. The wider bools say the same two words: what a `bool32` has over a `bool8` is room, not vocabulary. **Characters** come out as UTF-8. A `char32` is one code point and becomes one to four bytes; a `char16` is one UTF-16 code unit and becomes one to three. A value that is not a code point at all — one past U+10FFFF, or a lone half of a surrogate pair, which only means something inside UTF-16 — comes out as the replacement character U+FFFD rather than as an ill-formed sequence. A `char8` is the exception: it is **one byte of UTF-8, not one character**, and it is passed through as it stands. Nothing is encoded and nothing is validated, so a byte taken out of the middle of a multi-byte sequence yields a one-byte `String` holding a fragment rather than a letter. ## Example ```rux import Format::ToString; func Main() -> int { var a = ToString(-42); // "-42" var b = ToString(3.5); // "3.5" var c = ToString(1.0e20); // "1.0e+20" var d = ToString(false); // "false" var e = ToString(c32'€'); // "€", three bytes var f = 7.ToString(); // "7", through Stringable f.Free(); e.Free(); d.Free(); c.Free(); b.Free(); a.Free(); return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`Stringable`](https://rux-lang.dev/docs/api/format/stringable) — put a `ToString` on a type of your own - [`WriteInt`](https://rux-lang.dev/docs/api/format/writeint) / [`WriteFloat`](https://rux-lang.dev/docs/api/format/writefloat) — the same text, appended to a builder, with no `String` allocated - [`IsNan`](https://rux-lang.dev/docs/api/format/isnan) — ask about a value rather than reading `NaN` back out of its text # TryParseFloat64 Parses a floating-point value into an out-parameter, reporting only success or failure. **Package:** `Format` ## Signature ```rux func TryParseFloat64( str: Slice, value: *var float64 ) -> bool; func TryParseFloat64( str: String, value: *var float64 ) -> bool; ``` ## Parameters | Name | Type | Description | | ------- | ------------------------- | ---------------------------------- | | `str` | `Slice` / `String` | The text to parse. | | `value` | `*var float64` | Where the parsed value is written. | ## Returns `true` when `str` parsed, with the value written through `value`; `false` otherwise, leaving `value` untouched. A `null` `value` pointer also returns `false`. This is the same parse as [`ParseFloat64`](https://rux-lang.dev/docs/api/format/parsefloat64) — the same grammar and the same rounding — with the outcome flattened to a `bool` for a caller that only wants the value and a yes-or-no, and does not need to know which [`ParseError`](https://rux-lang.dev/docs/api/format/parseerror) a failure was. ## Example ```rux import Format::TryParseFloat64; import Io::PrintLine; func Main() -> int { var x: float64 = 0.0; if TryParseFloat64("2.5", @x) { PrintLine(x); // 2.5 } return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`ParseFloat64`](https://rux-lang.dev/docs/api/format/parsefloat64) — the same parse, returning a `Result` with the reason for a failure - [`TryParseInt64`](https://rux-lang.dev/docs/api/format/tryparseint64) — the integer counterpart # TryParseInt64 Parses a signed integer into an out-parameter, reporting only success or failure. **Package:** `Format` ## Signature ```rux func TryParseInt64( str: Slice, value: *var int64 ) -> bool; func TryParseInt64( str: String, value: *var int64 ) -> bool; ``` ## Parameters | Name | Type | Description | | ------- | ------------------------- | ---------------------------------- | | `str` | `Slice` / `String` | The text to parse. | | `value` | `*var int64` | Where the parsed value is written. | ## Returns `true` when `str` parsed, with the value written through `value`; `false` otherwise, leaving `value` untouched. A `null` `value` pointer also returns `false`. This is the same parse as [`ParseInt64`](https://rux-lang.dev/docs/api/format/parseint64) — the same accepted form and the same limits — with the outcome flattened to a `bool` for a caller that only wants the value and a yes-or-no, and does not need to know which [`ParseError`](https://rux-lang.dev/docs/api/format/parseerror) a failure was. ## Example ```rux import Format::TryParseInt64; import Io::PrintLine; func Main() -> int { var n: int64 = 0; if TryParseInt64("100", @n) { PrintLine(n); // 100 } if !TryParseInt64("12x", @n) { PrintLine(n); // still 100 -- left unchanged on failure } return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`ParseInt64`](https://rux-lang.dev/docs/api/format/parseint64) — the same parse, returning a `Result` with the reason for a failure - [`TryParseFloat64`](https://rux-lang.dev/docs/api/format/tryparsefloat64) — the floating-point counterpart # WriteBool Appends `true` or `false` to a builder. **Package:** `Format` ## Signature ```rux func WriteBool( builder: *StringBuilder, value: bool8 ); ``` ## Parameters | Name | Type | Description | | --------- | ---------------- | ------------------------- | | `builder` | `*StringBuilder` | The builder to append to. | | `value` | `bool8` | The value to write. | `bool` is an alias for `bool8`, so an ordinary `bool` reaches this directly. A `bool16` or a `bool32` has to be narrowed to `bool8` on the way in — the wider bools say the same two words, and there is only this one function to say them. ## Remarks Writes the word `true` or the word `false`, never `1` or `0`. Nothing else is appended around it, and nothing is allocated beyond whatever growth the builder needs. ## Example ```rux import Format::WriteBool; import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Append("cached: "); WriteBool(@builder, true); var line = builder.IntoString(); // "cached: true" line.Free(); return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the word in a `String` of its own - [`Text::StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder being appended to # WriteChar Appends the UTF-8 bytes of a character to a builder. **Package:** `Format` ## Signature ```rux func WriteChar( builder: *StringBuilder, value: char32 ); func WriteChar( builder: *StringBuilder, value: char16 ); ``` ## Parameters | Name | Type | Description | | --------- | ------------------- | ------------------------- | | `builder` | `*StringBuilder` | The builder to append to. | | `value` | `char32` / `char16` | The character to write. | `char` is an alias for `char32`, so an ordinary `char` reaches that overload. There is no `char8` overload, and none is needed: a `char8` is already one byte of UTF-8, so [`StringBuilder::Append`](https://rux-lang.dev/docs/api/text/stringbuilder/append) takes it as it stands, with nothing to encode. ## Remarks Encodes the character as UTF-8 and appends the bytes. A `char32` holds one code point and becomes one to four bytes; a `char16` holds one UTF-16 code unit and becomes one to three. A `String` holds bytes and its contents are UTF-8, which is why a character has to be encoded on the way in rather than copied. A value that is not a code point comes out as the **replacement character** U+FFFD (three bytes, `EF BF BD`) rather than as an ill-formed sequence. Two things fail that test: a value past U+10FFFF, which is beyond the last code point there is, and a lone half of a surrogate pair, which only means something inside UTF-16 — the character it belongs to is spread across two code units, and this conversion sees one. The compiler rejects a surrogate as a constant, so one can only arrive through a cast at run time. ## Example ```rux import Format::WriteChar; import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); WriteChar(@builder, c32'R'); // one byte WriteChar(@builder, c32'é'); // two WriteChar(@builder, c32'€'); // three builder.Append(c8'!'); // a char8 needs no encoding var text = builder.IntoString(); // "Ré€!" text.Length(); // 7 bytes, 4 characters text.Free(); return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the character in a `String` of its own, and what a `char8` does instead - [`Text::StringBuilder::Append`](https://rux-lang.dev/docs/api/text/stringbuilder/append) — append bytes that are already UTF-8 - [`Text::String::Length`](https://rux-lang.dev/docs/api/text/string/length) — bytes, not characters # WriteFloat Appends the decimal text of a floating-point value to a builder. **Package:** `Format` ## Signature ```rux func WriteFloat( builder: *StringBuilder, value: float64 ); func WriteFloat( builder: *StringBuilder, value: float32 ); ``` ## Parameters | Name | Type | Description | | --------- | --------------------- | ------------------------- | | `builder` | `*StringBuilder` | The builder to append to. | | `value` | `float64` / `float32` | The value to write. | The two widths keep separate overloads, unlike the integers, because the width is not something the value can be widened out of: a `float32` reaches a `float64` exactly, but what it reaches is its **full binary value**, and printing that to the digits a `float64` earns would print noise the `float32` never carried. The overload is what says how many digits the value is worth. ## Remarks Writes the same text [`ToString`](https://rux-lang.dev/docs/api/format/tostring) would produce, into the builder rather than into a `String` of its own, so a caller who is already accumulating pays no allocation for it. The value is rendered to fifteen significant digits from a `float64` and seven from a `float32`, with the trailing zeros dropped and a fractional part always written — `1.0` is `1.0`, not `1`. Notation is fixed while the decimal exponent stays in `[-4, digits)` and scientific outside it, following the printf `%g` rule: below `-4` the text would be mostly leading zeros, and at the digit count and above it would be mostly trailing ones. A NaN is `NaN` and the infinities are `Inf` and `-Inf`. A negative zero keeps its sign as `-0.0`, which is the one thing arithmetic cannot see about it, since it compares equal to a positive zero. ## Example ```rux import Format::WriteFloat; import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); WriteFloat(@builder, 0.1); // "0.1" -- not 0.1000000000000000055 builder.Append(c8' '); WriteFloat(@builder, 0.1f32); // "0.1" builder.Append(c8' '); WriteFloat(@builder, 2.5e-7); // "2.5e-07" builder.Append(c8' '); WriteFloat(@builder, 1.0 / 0.0); // "Inf" var line = builder.IntoString(); // "0.1 0.1 2.5e-07 Inf" line.Free(); return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview, and what the digit cutoff costs - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the same text in a `String` of its own - [`IsNan`](https://rux-lang.dev/docs/api/format/isnan) / [`IsInfinite`](https://rux-lang.dev/docs/api/format/isinfinite) — classify a value before writing it - [`Text::StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder being appended to # WriteInt Appends the decimal digits of a signed integer to a builder. **Package:** `Format` ## Signature ```rux func WriteInt( builder: *StringBuilder, value: int64 ); ``` ## Parameters | Name | Type | Description | | --------- | ---------------- | ------------------------- | | `builder` | `*StringBuilder` | The builder to append to. | | `value` | `int64` | The value to write. | There is one overload rather than one per width: a narrower signed value widens to `int64` on the way in, which loses nothing, so `WriteInt(@builder, x as int64)` covers every signed type. ## Remarks Writes a minus sign for a negative value and then the digits, with no plus sign on a positive one. Zero is the single digit `0`. Nothing is allocated here beyond whatever growth the builder needs, which is the point: [`ToString`](https://rux-lang.dev/docs/api/format/tostring) allocates a `String` per value, and a caller who is already accumulating would only free it again. The digits go straight into the block the builder already has. The most negative `int64` has no positive counterpart in its own type — negating `-9223372036854775808` overflows straight back to itself — so the magnitude is taken as a `uint64`, and that value prints in full like any other. ## Example ```rux import Format::WriteInt; import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Append("offset "); WriteInt(@builder, -42i64); builder.Append(" of "); WriteInt(@builder, 100i64); var line = builder.IntoString(); // "offset -42 of 100" line.Free(); return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`WriteUint`](https://rux-lang.dev/docs/api/format/writeuint) — the same, without a sign to write - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the digits in a `String` of their own - [`Text::StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder being appended to # WriteUint Appends the decimal digits of an unsigned integer to a builder. **Package:** `Format` ## Signature ```rux func WriteUint( builder: *StringBuilder, value: uint64 ); ``` ## Parameters | Name | Type | Description | | --------- | ---------------- | ------------------------- | | `builder` | `*StringBuilder` | The builder to append to. | | `value` | `uint64` | The value to write. | As with [`WriteInt`](https://rux-lang.dev/docs/api/format/writeint), there is one overload rather than one per width: a narrower unsigned value widens to `uint64` on the way in, which loses nothing. ## Remarks Writes the digits and nothing else — there is no sign on an unsigned value. Zero is the single digit `0`, and the largest `uint64`, `18446744073709551615`, is the longest output there is at twenty digits. Nothing is allocated beyond whatever growth the builder needs. This is the function to reach for while accumulating; [`ToString`](https://rux-lang.dev/docs/api/format/tostring) is the one that hands back a `String` of its own. ## Example ```rux import Format::WriteUint; import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Append("read "); WriteUint(@builder, 4096u64); builder.Append(" bytes"); var line = builder.IntoString(); // "read 4096 bytes" line.Free(); return 0; } ``` ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the package overview - [`WriteInt`](https://rux-lang.dev/docs/api/format/writeint) — the same, with a sign for a negative value - [`ToString`](https://rux-lang.dev/docs/api/format/tostring) — the digits in a `String` of their own - [`Text::StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder being appended to # Io Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: The package provides standard input and output — a [`Print`](https://rux-lang.dev/docs/api/io/print) and a [`PrintLine`](https://rux-lang.dev/docs/api/io/printline) for every primitive type and for text, and a [`ReadLine`](https://rux-lang.dev/docs/api/io/readline) that reads a line back. **Package:** `Io` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Io](https://github.com/rux-lang/Rux/tree/main/Packages/Io){rel=""nofollow""} Every function talks to the process's own standard streams: the standard handles on Windows and macOS, and the standard file descriptors on Linux. Nothing is buffered on the way — a `Print` is a write. ```rux import Io::{ Print, PrintLine, ReadLine }; func Main() -> int { Print("What is your name? "); var name = ReadLine(); PrintLine("Hello, {}!", name); name.Free(); return 0; } ``` ## Installation ```sh rux add Io rux install ``` ## Platform support Implemented on BSD, Linux, macOS, and Windows. ## Values as text A value is turned into text by [`Format`](https://rux-lang.dev/docs/api/format), which this package is built on, and it is the same text you would get from [`ToString`](https://rux-lang.dev/docs/api/format/tostring): a `bool` prints as `true` or `false` rather than as `0` or `1`, a `char` prints as its UTF-8 bytes, and a float is rendered to the digits its type carries with the trailing zeros dropped, so `0.1` prints as `0.1`. Both `Print` and `PrintLine` also take a format string, where each `{}` takes the next argument in order and `{{` and `}}` write a literal brace: ```rux PrintLine("{} of {} done", 3, 10); // 3 of 10 done PrintLine("{{{}}}", "braced"); // {braced} ``` The arguments are [`Stringable`](https://rux-lang.dev/docs/api/format/stringable), so a type of your own goes into a `{}` the moment it implements that interface. Any `String` a placeholder allocates on the way is freed for you. ## Widths and aliases `bool` is an alias for `bool8`, `char` for `char32`, and `float` for `float64`, so a call on an alias reaches the overload for the sized type it names. `int` and `uint` are types of their own rather than names for `int64` and `uint64`, and have overloads of their own. ## Ownership `Print` and `PrintLine` own nothing: a [`String`](https://rux-lang.dev/docs/api/text/string) passed to either is only read, and it is still yours to [`Free`](https://rux-lang.dev/docs/api/text/string/free) afterwards. [`ReadLine`](https://rux-lang.dev/docs/api/io/readline) is the other way around. The `String` it returns is a fresh allocation the caller owns and passes to [`String::Free`](https://rux-lang.dev/docs/api/text/string/free) exactly once, on the terms [`Text`](https://rux-lang.dev/docs/api/text#ownership) sets out — including the line that came back empty. ## Functions ### Output | Function | Description | | --------------------------------------------------------- | --------------------------------- | | [`Print`](https://rux-lang.dev/docs/api/io/print) | Write a value to standard output. | | [`PrintLine`](https://rux-lang.dev/docs/api/io/printline) | The same, followed by a newline. | ### Input | Function | Description | | ------------------------------------------------------- | ------------------------------------------------------------ | | [`ReadLine`](https://rux-lang.dev/docs/api/io/readline) | Read standard input through the next newline, as a `String`. | ## See also - [`Format`](https://rux-lang.dev/docs/api/format) — the conversions and the `{}` substitution behind every call here - [`Text`](https://rux-lang.dev/docs/api/text) — the `String` these functions print and return # Print Writes a value to standard output. **Package:** `Io` ## Signature ```rux // Text func Print(value: String); func Print(value: Slice); // Formatted func Print(format: Slice, args: Stringable...); // Signed integers func Print(value: int8); func Print(value: int16); func Print(value: int32); func Print(value: int64); func Print(value: int); // Unsigned integers func Print(value: uint8); func Print(value: uint16); func Print(value: uint32); func Print(value: uint64); func Print(value: uint); // Floating-point func Print(value: float32); func Print(value: float64); // Booleans func Print(value: bool8); func Print(value: bool16); func Print(value: bool32); // Characters func Print(value: char8); func Print(value: char16); func Print(value: char32); ``` ## Parameters | Name | Type | Description | | -------- | --------------- | ------------------------------------------------- | | `value` | Any type above | The value to write. | | `format` | `Slice` | Format string; each `{}` takes the next argument. | | `args` | `Stringable...` | The values substituted into `format`, in order. | `bool` is an alias for `bool8`, `char` for `char32`, and `float` for `float64`, so a call on an alias reaches the overload for the sized type it names. ## Remarks Nothing is appended — the next `Print` continues on the same line. Use [`PrintLine`](https://rux-lang.dev/docs/api/io/printline) to end one. A value that is not already text is rendered by [`Format::ToString`](https://rux-lang.dev/docs/api/format/tostring), so it prints exactly as that function describes: a bool as `true` or `false`, a character as its UTF-8 bytes, a float to the digits its type carries with the trailing zeros dropped and `NaN`, `Inf`, and `-Inf` for the values that have no digits. The `String` this costs is allocated and freed inside the call. The format overload substitutes each `{}` with the next argument, and writes a literal brace for `{{` and `}}`. The substitution is [`Format`](https://rux-lang.dev/docs/api/format)'s, and the arguments are [`Stringable`](https://rux-lang.dev/docs/api/format/stringable), so a type of your own can go into a `{}` as soon as it implements that interface. A `String` argument is only read. It stays yours, and you still [`Free`](https://rux-lang.dev/docs/api/text/string/free) it. Output is unbuffered and goes straight to the stream, and a write that the platform cuts short is retried until the whole value is out. Nothing is reported back: if the stream is closed or fails outright, the call gives up quietly rather than returning an error. ## Example ```rux import Io::Print; import Text::String; func Main() -> int { Print("Loading"); Print(c8'.'); Print(3); // Loading.3 var name = String::From("Rux"); Print(" {} {}\n", name, 1.5); // " Rux 1.5" name.Free(); return 0; } ``` ## See also - [`Io`](https://rux-lang.dev/docs/api/io) — the package overview - [`PrintLine`](https://rux-lang.dev/docs/api/io/printline) — the same overloads, with a newline after the value - [`ReadLine`](https://rux-lang.dev/docs/api/io/readline) — read a line back from standard input - [`Format::ToString`](https://rux-lang.dev/docs/api/format/tostring) — the text every non-text overload prints # PrintLine Writes a value to standard output, followed by a newline. **Package:** `Io` ## Signature `PrintLine` mirrors every [`Print`](https://rux-lang.dev/docs/api/io/print) overload and adds a form that takes nothing and writes only the newline: ```rux // Newline only func PrintLine(); // Text func PrintLine(value: String); func PrintLine(value: Slice); // Formatted func PrintLine(format: Slice, args: Stringable...); // Signed integers func PrintLine(value: int8); func PrintLine(value: int16); func PrintLine(value: int32); func PrintLine(value: int64); func PrintLine(value: int); // Unsigned integers func PrintLine(value: uint8); func PrintLine(value: uint16); func PrintLine(value: uint32); func PrintLine(value: uint64); func PrintLine(value: uint); // Floating-point func PrintLine(value: float32); func PrintLine(value: float64); // Booleans func PrintLine(value: bool8); func PrintLine(value: bool16); func PrintLine(value: bool32); // Characters func PrintLine(value: char8); func PrintLine(value: char16); func PrintLine(value: char32); ``` ## Parameters | Name | Type | Description | | -------- | --------------- | ------------------------------------------------- | | `value` | Any type above | The value to write. | | `format` | `Slice` | Format string; each `{}` takes the next argument. | | `args` | `Stringable...` | The values substituted into `format`, in order. | `bool` is an alias for `bool8`, `char` for `char32`, and `float` for `float64`, so a call on an alias reaches the overload for the sized type it names. ## Remarks Each overload is its [`Print`](https://rux-lang.dev/docs/api/io/print) counterpart followed by a newline, and everything that page says about the text of a value, about `{}` substitution, and about a `String` argument staying yours to [`Free`](https://rux-lang.dev/docs/api/text/string/free) holds here unchanged. The newline written is a single LF (`\n`), on every platform, including Windows. Nothing translates it to CRLF on the way out. `PrintLine()` with no argument writes that newline by itself, which is how you leave a blank line. ## Example ```rux import Io::PrintLine; func Main() -> int { PrintLine("Report"); PrintLine(); // blank line PrintLine("{} items, {} failed", 12, 0); PrintLine(true); // true PrintLine(1.0 / 3.0); // 0.333333333333333 return 0; } ``` ## See also - [`Io`](https://rux-lang.dev/docs/api/io) — the package overview - [`Print`](https://rux-lang.dev/docs/api/io/print) — the same overloads, with no newline after the value - [`ReadLine`](https://rux-lang.dev/docs/api/io/readline) — read a line back from standard input - [`Format::ToString`](https://rux-lang.dev/docs/api/format/tostring) — the text every non-text overload prints # ReadLine Reads standard input through the next newline, and returns it as a `String`. **Package:** `Io` ## Signature ```rux func ReadLine() -> String; ``` ## Returns A `String` holding the bytes read, without the line ending, in a block the caller owns and passes to [`String::Free`](https://rux-lang.dev/docs/api/text/string/free) exactly once — an empty line included, since an empty `String` is still a value to free. The line ending is removed: the LF that terminated the line, and a CR immediately before it, so a CRLF-terminated line comes back the same as an LF-terminated one. A CR anywhere else is data and is kept, which is what makes a lone `\r` in the middle of a line survive the round trip. There is no length limit — the line is accumulated in a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) that grows as it goes. An empty `String` comes back from two different things: an empty line, and standard input that was already at EOF. The call does not distinguish them. If EOF arrives partway through a line, whatever had been read is returned as if the line had ended there. The bytes are returned as they were read. Nothing is validated as UTF-8, so input that is not UTF-8 comes back intact rather than being replaced or rejected. ## Remarks Input is read a byte at a time, straight from the stream, with nothing buffered between calls. The read is blocking: the call does not return until a newline arrives or the stream ends. ## Example ```rux import Io::{ Print, PrintLine, ReadLine }; func Main() -> int { Print("Name: "); var name = ReadLine(); if name.IsEmpty() { PrintLine("Nothing to greet."); } else { PrintLine("Hello, {}!", name); } name.Free(); return 0; } ``` Read until the input runs out, one line per iteration: ```rux import Io::{ PrintLine, ReadLine }; func Main() -> int { var count = 0; while true { var line = ReadLine(); let empty = line.IsEmpty(); line.Free(); if empty { break; } // an empty line, or EOF count += 1; } PrintLine("{} lines", count); return 0; } ``` ## See also - [`Io`](https://rux-lang.dev/docs/api/io) — the package overview - [`Print`](https://rux-lang.dev/docs/api/io/print) — write a prompt before reading - [`Text::String`](https://rux-lang.dev/docs/api/text/string) — the type returned, and how to free it - [`Text::StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — what the line is accumulated in # Brk Invokes the kernel's raw program-break operation. **Package:** `Linux` ## Signature ```rux func Brk(addr: *opaque) -> int64; ``` ## Parameters | Name | Type | Description | | ------ | --------- | --------------------------------------------------- | | `addr` | `*opaque` | Requested new program break, or `null` to query it. | ## Returns `int64` — the resulting program-break address. ## Description This is the raw Linux syscall contract, which differs from libc's `brk` wrapper. The kernel returns the current break when it cannot set the requested value; that result is not represented as negative errno. To detect failure, compare the returned address with the requested address. ::caution Changing the program break can conflict with the runtime allocator and corrupt process memory. Application code should normally use [`Memory`](https://rux-lang.dev/docs/api/memory) or [`Mmap`](https://rux-lang.dev/docs/api/linux/mmap) instead. :: ## Example ```rux import Linux::Brk; func Main() -> int { let currentBreak = Brk(null) as *opaque; return 0; } ``` # ClockGetTime Reads the current value of a Linux clock. **Package:** `Linux` ## Signature ```rux func ClockGetTime(clockId: int32, tp: *Timespec) -> int64; ``` ## Parameters | Name | Type | Description | | --------- | ----------- | ----------------------------------------- | | `clockId` | `int32` | Clock to read, such as `ClockMonotonic`. | | `tp` | `*Timespec` | Writable destination for the clock value. | ## Returns `int64` — `0` on success, or a negative errno result on failure. ## Description Use `ClockRealtime` for calendar time that can jump when the system clock is adjusted. Use `ClockMonotonic` for elapsed-time measurements; it is not affected by discontinuous wall-clock changes. ## Example ```rux import Linux::{ ClockGetTime, IsError, Timespec, ClockMonotonic }; func Main() -> int { var now: Timespec; if IsError(ClockGetTime(ClockMonotonic, @now)) { return 1; } return 0; } ``` ## See also - [`Constants`](https://rux-lang.dev/docs/api/linux/types) — exported clock IDs - [`Timespec`](https://rux-lang.dev/docs/api/linux/types) — result structure - [`Nanosleep`](https://rux-lang.dev/docs/api/linux/nanosleep) — relative sleep # Close Closes a file descriptor. **Package:** `Linux` ## Signature ```rux func Close(fd: int32) -> int64; ``` ## Parameters | Name | Type | Description | | ---- | ------- | ------------------------- | | `fd` | `int32` | File descriptor to close. | ## Returns `int64` — `0` on success, or a negative errno result on failure. ## Description After a successful close, `fd` no longer refers to the resource and may be reused by a later operation. Do not close a descriptor more than once. ::warning Retrying `Close` after an error is unsafe on Linux because the descriptor may already have been released and reused. Handle the reported error without blindly repeating the call. :: ## Example ```rux import Linux::{ Close, IsError }; func Main() -> int { if IsError(Close(fd)) { return 1; } return 0; } ``` # Errno Decodes the errno value from a raw syscall result. **Package:** `Linux` ## Signature ```rux func Errno(result: int64) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | ------- | ------------------------- | | `result` | `int64` | Raw syscall return value. | ## Returns `int64` — the positive errno value when `result` is between `-4095` and `-1`, or `0` when the value does not encode an error. ## Description `Errno` does not read or modify a global or thread-local errno variable. It only decodes the supplied kernel return value. ::tip Call [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) when you need to distinguish success from failure. `Errno(result) == 0` means the input was not an encoded syscall error. :: ## Example ```rux import Linux::{ Errno, IsError, Write }; func Main() -> int { let result = Write(fd, data, length); if IsError(result) { let errorNumber = Errno(result); } return 0; } ``` ## See also - [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) — test a raw result # Exit Terminates the calling thread immediately. **Package:** `Linux` ## Signature ```rux func Exit(code: int32); ``` ## Parameters | Name | Type | Description | | ------ | ------- | -------------------------------------------- | | `code` | `int32` | Status made available to the waiting parent. | ## Description `Exit` directly invokes Linux `exit(2)`. It does not return, unwind the stack, flush user-space buffers, or run cleanup code. Only the low 8 bits of the status are normally visible to a parent process. ::warning The underlying syscall terminates only the calling thread. In a multithreaded process, it is not equivalent to `exit_group(2)`. Prefer returning from `Main` or using the standard library for normal process termination. :: ## Example ```rux import Linux::Exit; func Main() -> int { if unrecoverable { Exit(1i32); } return 0; } ``` # GetPid Returns the calling process ID. **Package:** `Linux` ## Signature ```rux func GetPid() -> int64; ``` ## Returns `int64` — the process ID assigned by the kernel. This syscall succeeds for a running process. ## Example ```rux import Linux::GetPid; func Main() -> int { let pid = GetPid(); return 0; } ``` # Linux Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: Direct Linux system-call bindings for Rux programs. **Package:** `Linux` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Linux](https://github.com/rux-lang/Rux/tree/main/Packages/Linux){rel=""nofollow""} The package calls the kernel without going through libc. It provides raw zero-to-six-argument syscall entry points, typed wrappers for a focused set of common operations, and the constants and structures those wrappers need. ## Requirements - Linux on x86-64 or AArch64 - A Rux compiler with the Linux syscall support On x86-64 the wrappers issue the `syscall` instruction directly; on AArch64 the native backend lowers them through libc's `syscall` function. The syscall numbers are architecture-specific and the package selects the right set for the active target at compile time. ## Installation ```sh rux add Linux rux install ``` Then import the symbols you need: ```rux import Linux::{ StdOut, Write }; ``` ## Result Convention The wrappers return the kernel result directly: a non-negative value on success — a byte count, a process ID, a mapped address, or `0` — and a **negative errno** (`-1` through `-4095`) on failure. [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) tests for that negative range and [`Errno`](https://rux-lang.dev/docs/api/linux/errno) turns it back into a positive errno number. The package does not set a thread-local `errno`, retry interrupted calls, or convert failures into exceptions. ## Functions ### I/O | Function | Description | | ---------------------------------------------------- | ---------------------------------- | | [`Read`](https://rux-lang.dev/docs/api/linux/read) | Read bytes from a file descriptor. | | [`Write`](https://rux-lang.dev/docs/api/linux/write) | Write bytes to a file descriptor. | | [`Close`](https://rux-lang.dev/docs/api/linux/close) | Close a file descriptor. | ### Memory | Function | Description | | ------------------------------------------------------ | ------------------------------------------------ | | [`Mmap`](https://rux-lang.dev/docs/api/linux/mmap) | Create a virtual-memory mapping. | | [`Munmap`](https://rux-lang.dev/docs/api/linux/munmap) | Remove a virtual-memory mapping. | | [`Brk`](https://rux-lang.dev/docs/api/linux/brk) | Invoke the kernel's raw program-break operation. | ### Process | Function | Description | | ------------------------------------------------------ | ---------------------------------- | | [`Exit`](https://rux-lang.dev/docs/api/linux/exit) | Terminate the process immediately. | | [`GetPid`](https://rux-lang.dev/docs/api/linux/getpid) | Return the calling process ID. | ### Time | Function | Description | | ------------------------------------------------------------------ | -------------------------------- | | [`ClockGetTime`](https://rux-lang.dev/docs/api/linux/clockgettime) | Read a Linux clock. | | [`Nanosleep`](https://rux-lang.dev/docs/api/linux/nanosleep) | Suspend for a relative interval. | ### Raw syscalls | Function | Description | | --------------------------------------------------------------------- | ------------------------------------------ | | [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/linux/syscalls) | Invoke an arbitrary syscall by number. | | [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) | Test whether a result is a negative errno. | | [`Errno`](https://rux-lang.dev/docs/api/linux/errno) | Extract the positive errno from a result. | ## Types and constants The standard descriptors, syscall numbers, mapping and protection flags, clock IDs, and the [`Timespec`](https://rux-lang.dev/docs/api/linux/types) structure are listed on the [types and constants](https://rux-lang.dev/docs/api/linux/types) page. ## Example ```rux import Linux::{ IsError, StdOut, Write }; func Main() -> int { let message = "hello from Linux\n"; let result = Write(StdOut, message.data, message.length); if IsError(result) { return 1; } return result == message.length as int64 ? 0 : 2; } ``` # IsError Tests whether a raw syscall result encodes a Linux error. **Package:** `Linux` ## Signature ```rux func IsError(result: int64) -> bool; ``` ## Parameters | Name | Type | Description | | -------- | ------- | ------------------------- | | `result` | `int64` | Raw syscall return value. | ## Returns `bool` — `true` when `result` is in the Linux error range `-4095` through `-1`; otherwise `false`. ## Example ```rux import Linux::{ Errno, IsError, Read, StdIn }; func Main() -> int { let result = Read(StdIn, buffer, capacity); if IsError(result) { let errorNumber = Errno(result); } return 0; } ``` ## See also - [`Errno`](https://rux-lang.dev/docs/api/linux/errno) — convert an error result to positive errno - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/linux/syscalls) — raw syscall entry points # Mmap Creates a virtual-memory mapping. **Package:** `Linux` ## Signature ```rux func Mmap( address: *opaque, length: uint, protection: int32, flags: int32, fd: int32, offset: uint64 ) -> int64; ``` ## Parameters | Name | Type | Description | | ------------ | --------- | ---------------------------------------------------------- | | `address` | `*opaque` | Requested address hint, or `null`. | | `length` | `uint` | Mapping length in bytes; must be greater than 0. | | `protection` | `int32` | Page protections, such as `ProtectionRead`. | | `flags` | `int32` | Mapping behavior, such as `MapPrivate`. | | `fd` | `int32` | Backing file descriptor, or `-1` for an anonymous mapping. | | `offset` | `uint64` | Page-aligned offset in the backing file. | ## Returns `int64` — the mapped address encoded as an integer on success, or a negative errno result on failure. ## Description Combine protection and mapping flags with bitwise OR. For an anonymous private mapping, pass `MapPrivate | MapAnonymous`, `-1i32` for `fd`, and `0u64` for `offset`. Release a successful mapping with [`Munmap`](https://rux-lang.dev/docs/api/linux/munmap). ::caution Call [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) before casting the result to a pointer. Casting a negative errno result produces an invalid address. :: ## Example ```rux import Linux::{ IsError, Mmap, Munmap, MapAnonymous, MapPrivate, ProtectionRead, ProtectionWrite }; func Main() -> int { let result = Mmap(null, 4096u, ProtectionRead | ProtectionWrite, MapPrivate | MapAnonymous, -1i32, 0u64); if IsError(result) { return 1; } let memory = result as *opaque; // Use memory within the mapped 4096-byte range. Munmap(memory, 4096u); return 0; } ``` ## See also - [`Constants`](https://rux-lang.dev/docs/api/linux/types) — available protection and mapping flags - [`Munmap`](https://rux-lang.dev/docs/api/linux/munmap) — remove a mapping # Munmap Removes a virtual-memory mapping. **Package:** `Linux` ## Signature ```rux func Munmap(addr: *opaque, length: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | ----------------------------------------- | | `addr` | `*opaque` | Page-aligned start of the range to unmap. | | `length` | `uint` | Number of bytes in the range. | ## Returns `int64` — `0` on success, or a negative errno result on failure. ## Description After a successful call, accessing the unmapped range is invalid. The address must be page-aligned; Linux rounds `length` up to a page boundary. ## Example ```rux import Linux::{ IsError, Munmap }; func Main() -> int { if IsError(Munmap(memory, 4096u)) { return 1; } return 0; } ``` ## See also - [`Mmap`](https://rux-lang.dev/docs/api/linux/mmap) — create a mapping # Nanosleep Suspends execution for a relative time interval. **Package:** `Linux` ## Signature ```rux func Nanosleep(req: *const Timespec, rem: *Timespec) -> int64; ``` ## Parameters | Name | Type | Description | | ----- | ----------------- | -------------------------------------------------- | | `req` | `*const Timespec` | Requested relative duration. | | `rem` | `*Timespec` | Receives remaining time if interrupted, or `null`. | ## Returns `int64` — `0` after the requested interval elapses, or a negative errno result on failure or interruption. ## Description `req.nanoseconds` must be between `0` and `999999999`. When a signal interrupts the sleep, Linux returns a negative `EINTR` result and, when `rem` is non-null, writes the unslept duration to `rem`. ## Example ```rux import Linux::{ IsError, Nanosleep, Timespec }; func Main() -> int { var delay = Timespec { seconds: 1i64, nanoseconds: 0i64 }; if IsError(Nanosleep(@delay, null)) { return 1; } return 0; } ``` ## See also - [`Timespec`](https://rux-lang.dev/docs/api/linux/types) — time structure - [`ClockGetTime`](https://rux-lang.dev/docs/api/linux/clockgettime) — read a clock # Read Reads bytes from a file descriptor. **Package:** `Linux` ## Signature ```rux func Read(fd: int32, buffer: *opaque, count: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | --------------------------------------------- | | `fd` | `int32` | File descriptor to read. | | `buffer` | `*opaque` | Writable destination for up to `count` bytes. | | `count` | `uint` | Maximum number of bytes to read. | ## Returns `int64` — bytes read on success, `0` at end of file, or a negative errno result on failure. ## Description `Read` is a thin wrapper around Linux `read(2)`. A successful call may return fewer than `count` bytes. The caller owns `buffer` and must ensure it remains writable for the requested range. ::warning Do not use the return value as a length until [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) has ruled out a negative result. :: ## Example ```rux import Linux::{ IsError, Read, StdIn }; func Main() -> int { var buffer: char8[256]; let result = Read(StdIn, buffer.data, 256u); if IsError(result) { return 1; } let bytesRead = result as uint; return 0; } ``` ## See also - [`Write`](https://rux-lang.dev/docs/api/linux/write) — write bytes to a descriptor - [`Close`](https://rux-lang.dev/docs/api/linux/close) — close a descriptor # `Syscall0`–`Syscall6` Invoke an arbitrary Linux x86-64 syscall with zero to six arguments. **Package:** `Linux` ## Signatures ```rux func Syscall0(number: uint64) -> int64; func Syscall1(number: uint64, arg0: uint64) -> int64; func Syscall2(number: uint64, arg0: uint64, arg1: uint64) -> int64; func Syscall3(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64) -> int64; func Syscall4(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64) -> int64; func Syscall5(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64) -> int64; func Syscall6(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64, arg5: uint64) -> int64; ``` ## Parameters | Name | Type | Description | | ------------- | -------- | --------------------------------------------------- | | `number` | `uint64` | Linux x86-64 syscall number. | | `arg0`…`arg5` | `uint64` | Arguments in the order required by the syscall ABI. | Pointers and signed values must be converted to their raw 64-bit representations before being passed. ## Returns `int64` — the raw kernel result. Use [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) before interpreting the value as a successful result. ## Description Choose the function whose suffix matches the syscall's argument count. These entry points perform no validation, type conversion, retry, or resource management. ::caution An incorrect syscall number, argument count, pointer, or buffer length can corrupt memory, leak resources, or terminate the process. Prefer a typed wrapper when the package provides one. :: ## Example ```rux import Linux::{ IsError, SYS_GETPID, Syscall0 }; func Main() -> int { let result = Syscall0(SYS_GETPID); if !IsError(result) { let pid = result; } return 0; } ``` ## See also - [`Constants`](https://rux-lang.dev/docs/api/linux/types) — exported syscall numbers - [`IsError`](https://rux-lang.dev/docs/api/linux/iserror) — test the raw result - [`Errno`](https://rux-lang.dev/docs/api/linux/errno) — decode an error result # Types and Constants Types and constants exported by the `Linux` package. **Package:** `Linux` ## Standard File Descriptors | Name | Type | Value | Description | | -------- | ------- | ----: | ---------------- | | `StdIn` | `int32` | `0` | Standard input. | | `StdOut` | `int32` | `1` | Standard output. | | `StdErr` | `int32` | `2` | Standard error. | A process may close or redirect these conventional descriptors. ## Syscall Numbers Syscall numbers differ by architecture; the package selects the right set for the active target: | Name | x86-64 | AArch64 | | ----------------- | -----: | ------: | | `SysRead` | `0` | `63` | | `SysWrite` | `1` | `64` | | `SysClose` | `3` | `57` | | `SysMmap` | `9` | `222` | | `SysMunmap` | `11` | `215` | | `SysBrk` | `12` | `214` | | `SysNanosleep` | `35` | `101` | | `SysGetPid` | `39` | `172` | | `SysExit` | `60` | `93` | | `SysClockGetTime` | `228` | `113` | ## Memory Protection | Name | Type | Value | Description | | ------------------- | ------- | ----: | ---------------------- | | `ProtectionNone` | `int32` | `0` | No access. | | `ProtectionRead` | `int32` | `1` | Pages may be read. | | `ProtectionWrite` | `int32` | `2` | Pages may be written. | | `ProtectionExecute` | `int32` | `4` | Pages may be executed. | Combine protection values with bitwise OR. ## Mapping Flags | Name | Type | Value | Description | | -------------- | ------- | ----: | --------------------------------------- | | `MapShared` | `int32` | `1` | Create a shared mapping. | | `MapPrivate` | `int32` | `2` | Create a private copy-on-write mapping. | | `MapFixed` | `int32` | `16` | Place the mapping at the exact address. | | `MapAnonymous` | `int32` | `32` | Create a mapping not backed by a file. | ## Clock IDs | Name | Type | Value | Description | | ---------------- | ------- | ----: | ------------------------------------------ | | `ClockRealtime` | `int32` | `0` | Settable wall-clock time. | | `ClockMonotonic` | `int32` | `1` | Monotonic time since an unspecified point. | ## `Timespec` ```rux struct Timespec { seconds: int64; nanoseconds: int64; } ``` | Field | Type | Description | | ------------- | ------- | ------------------------------------------------------ | | `seconds` | `int64` | Whole seconds. | | `nanoseconds` | `int64` | Nanoseconds within the second, normally 0–999,999,999. | [`Nanosleep`](https://rux-lang.dev/docs/api/linux/nanosleep) reads it as a relative duration; [`ClockGetTime`](https://rux-lang.dev/docs/api/linux/clockgettime) writes a timestamp for the selected clock. ## See also - [`Linux`](https://rux-lang.dev/docs/api/linux) — the package overview - [`Mmap`](https://rux-lang.dev/docs/api/linux/mmap) — uses the protection and mapping flags - [`ClockGetTime`](https://rux-lang.dev/docs/api/linux/clockgettime) — uses the clock IDs and `Timespec` - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/linux/syscalls) — use the syscall numbers directly # Write Writes bytes to a file descriptor. **Package:** `Linux` ## Signature ```rux func Write(fd: int32, buffer: *const opaque, count: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------------- | ----------------------------------------- | | `fd` | `int32` | File descriptor to write. | | `buffer` | `*const opaque` | Source containing at least `count` bytes. | | `count` | `uint` | Number of bytes requested. | ## Returns `int64` — bytes written on success, or a negative errno result on failure. ## Description `Write` is a thin wrapper around Linux `write(2)`. A successful call may write fewer than `count` bytes. Code that must emit the complete buffer should advance the pointer and repeat until all bytes are written or an error occurs. ## Example ```rux import Linux::{ IsError, StdOut, Write }; func Main() -> int { let message = "hello\n"; let result = Write(StdOut, message.data, message.length); if IsError(result) || result != message.length as int64 { return 1; } return 0; } ``` ## See also - [`Read`](https://rux-lang.dev/docs/api/linux/read) — read bytes from a descriptor - [`Close`](https://rux-lang.dev/docs/api/linux/close) — close a descriptor # Close Closes a file descriptor. **Package:** `MacOS` ## Signature ```rux func Close(fd: int32) -> int64; ``` ## Parameters | Name | Type | Description | | ---- | ------- | ------------------------- | | `fd` | `int32` | File descriptor to close. | ## Returns `int64` - `0` on success, or a negative errno value on failure. After success, the descriptor may be reused and must not be closed again. ::warning Do not blindly retry `Close` after an error. The descriptor's state may be uncertain and its number may have been reused. :: ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview # Errno Extracts the positive errno from a raw syscall result. **Package:** `MacOS` ## Signature ```rux func Errno(result: int64) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | ------- | ------------------------- | | `result` | `int64` | Raw syscall return value. | ## Returns `int64` - the positive errno (`1` through `4095`) when `result` is a negative errno in the range `-1` through `-4095`, or `0` for any other value, which the package treats as success. `Errno` does not read or modify a global or thread-local errno variable. It negates the result when [`IsError`](https://rux-lang.dev/docs/api/macos/iserror) reports one, and returns `0` otherwise. ## Example ```rux import MacOS::{ Close, Errno }; func Main() -> int { let code = Errno(Close(3)); if code != 0i64 { // The close failed with errno `code`. } return 0; } ``` ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`IsError`](https://rux-lang.dev/docs/api/macos/iserror) - test whether a result is an error - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/macos/syscalls) - raw syscall entry points # Exit Terminates the process immediately. **Package:** `MacOS` ## Signature ```rux func Exit(code: int32); ``` ## Parameters | Name | Type | Description | | ------ | ------- | ---------------------------------------- | | `code` | `int32` | Status reported to the process's parent. | ## Description `Exit` invokes the low-level target macOS exit operation. It does not return, unwind the stack, flush user-space buffers, or run cleanup code. A waiting parent normally observes only the low 8 bits of the status. ::warning Prefer returning from `Main` for normal termination. Use `Exit` only when the process must stop immediately. :: ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview # GetPid Returns the calling process ID. **Package:** `MacOS` ## Signature ```rux func GetPid() -> int64; ``` ## Returns `int64` - the process ID assigned by the kernel. ## Example ```rux import MacOS::GetPid; func Main() -> int { let pid = GetPid(); return 0; } ``` ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview # GetTimeOfDay Reads the current wall-clock time. **Package:** `MacOS` ## Signature ```rux func GetTimeOfDay(time: *Timeval) -> int64; ``` ## Parameters | Name | Type | Description | | ------ | ---------- | -------------------------------------- | | `time` | `*Timeval` | Destination for the current wall time. | ## Returns `int64` - `0` on success, or a negative errno value on failure. The raw Darwin entry point has an optional third `mach_absolute_time` output; this wrapper leaves it null and fills only the [`Timeval`](https://rux-lang.dev/docs/api/macos/types) with seconds and microseconds since the Unix epoch. As wall-clock time, the value can jump backward or forward when the system clock is adjusted. ## Example ```rux import MacOS::{ GetTimeOfDay, Timeval }; func Main() -> int { var now: Timeval; if GetTimeOfDay(@now) != 0i64 { return 1; } return 0; } ``` ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Types and constants`](https://rux-lang.dev/docs/api/macos/types) - the `Timeval` structure # MacOS Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: Direct macOS (Darwin) system-call bindings for Rux programs. **Package:** `MacOS` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/MacOS](https://github.com/rux-lang/Rux/tree/main/Packages/MacOS){rel=""nofollow""} The package calls the Darwin kernel without going through libc. It provides raw zero-to-six-argument syscall entry points, typed wrappers for a focused set of common operations, and the constants and structures those wrappers need. ## Requirements - macOS on x86-64 - A Rux compiler with the macOS syscall support The package is **x86-64 only** — the syscall entry points are hand-written x86-64 assembly, and there is no Apple Silicon (AArch64) path yet. On any other architecture the package does not apply. ## Installation ```sh rux add MacOS rux install ``` Then import the symbols you need: ```rux import MacOS::{ StdOut, Write }; ``` ## Result Convention The wrappers return the kernel result directly: a non-negative value on success — a byte count, a process ID, a mapped address, or `0` — and a **negative errno** (`-1` through `-4095`) on failure. Darwin itself reports errors as a positive errno with the carry flag set; the wrappers normalize that to `-errno` for parity with the Linux and BSD packages. [`IsError`](https://rux-lang.dev/docs/api/macos/iserror) tests for the negative range and [`Errno`](https://rux-lang.dev/docs/api/macos/errno) turns it back into a positive errno number. ## Functions ### I/O | Function | Description | | ---------------------------------------------------- | ---------------------------------- | | [`Read`](https://rux-lang.dev/docs/api/macos/read) | Read bytes from a file descriptor. | | [`Write`](https://rux-lang.dev/docs/api/macos/write) | Write bytes to a file descriptor. | | [`Close`](https://rux-lang.dev/docs/api/macos/close) | Close a file descriptor. | ### Memory | Function | Description | | ------------------------------------------------------ | -------------------------------- | | [`Mmap`](https://rux-lang.dev/docs/api/macos/mmap) | Create a virtual-memory mapping. | | [`Munmap`](https://rux-lang.dev/docs/api/macos/munmap) | Remove a virtual-memory mapping. | ### Process | Function | Description | | ------------------------------------------------------ | ---------------------------------- | | [`Exit`](https://rux-lang.dev/docs/api/macos/exit) | Terminate the process immediately. | | [`GetPid`](https://rux-lang.dev/docs/api/macos/getpid) | Return the calling process ID. | ### Time | Function | Description | | ------------------------------------------------------------------ | --------------------------------- | | [`GetTimeOfDay`](https://rux-lang.dev/docs/api/macos/gettimeofday) | Read the current wall-clock time. | ### Raw syscalls | Function | Description | | --------------------------------------------------------------------- | ------------------------------------------ | | [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/macos/syscalls) | Invoke an arbitrary syscall by number. | | [`IsError`](https://rux-lang.dev/docs/api/macos/iserror) | Test whether a result is a negative errno. | | [`Errno`](https://rux-lang.dev/docs/api/macos/errno) | Extract the positive errno from a result. | ## Types and constants The standard descriptors, class-qualified syscall numbers, mapping and protection flags, and the [`Timeval`](https://rux-lang.dev/docs/api/macos/types) structure are listed on the [types and constants](https://rux-lang.dev/docs/api/macos/types) page. ## Example ```rux import MacOS::{ StdOut, Write }; func Main() -> int { let message = "hello from macOS\n"; let result = Write(StdOut, message.data, message.length); return result == message.length as int64 ? 0 : 1; } ``` # IsError Tests whether a raw syscall result is an error. **Package:** `MacOS` ## Signature ```rux func IsError(result: int64) -> bool; ``` ## Parameters | Name | Type | Description | | -------- | ------- | ------------------------- | | `result` | `int64` | Raw syscall return value. | ## Returns `bool` - `true` when `result` is a negative errno in the range `-1` through `-4095`; otherwise `false`. The wrappers report failure as a small negative value, so a non-negative result — a byte count, a process ID, a mapped address, or `0` — is always a success. `IsError` therefore never misclassifies a legitimate result. ## Example ```rux import MacOS::{ IsError, Mmap, MapAnonymous, MapPrivate, ProtectionRead, ProtectionWrite }; func Main() -> int { let result = Mmap(null, 4096u, ProtectionRead | ProtectionWrite, MapPrivate | MapAnonymous, -1i32, 0u64); if IsError(result) { return 1; } return 0; } ``` ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Errno`](https://rux-lang.dev/docs/api/macos/errno) - extract the positive errno value - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/macos/syscalls) - raw syscall entry points # Mmap Creates a virtual-memory mapping. **Package:** `MacOS` ## Signature ```rux func Mmap( address: *opaque, length: uint, protection: int32, flags: int32, fd: int32, offset: uint64 ) -> int64; ``` ## Parameters | Name | Type | Description | | ------------ | --------- | ------------------------------------------------- | | `address` | `*opaque` | Requested address hint, or `null`. | | `length` | `uint` | Mapping length in bytes. | | `protection` | `int32` | Page protections, such as `ProtectionRead`. | | `flags` | `int32` | Mapping behavior, such as `MapPrivate`. | | `fd` | `int32` | Backing descriptor, or `-1` for anonymous memory. | | `offset` | `uint64` | Page-aligned offset in the backing object. | ## Returns `int64` - the mapped address encoded as an integer on success, or a negative errno value on failure. For private anonymous memory, combine `MapPrivate | MapAnonymous`, pass `-1i32` for `fd`, and use an offset of `0u64`. ::caution Check the result with [`IsError`](https://rux-lang.dev/docs/api/macos/iserror) before casting it to a pointer. Release every successful mapping with [`Munmap`](https://rux-lang.dev/docs/api/macos/munmap) using its correct base address and length. :: ## Example ```rux import MacOS::{ IsError, Mmap, Munmap, MapAnonymous, MapPrivate, ProtectionRead, ProtectionWrite }; func Main() -> int { let result = Mmap(null, 4096u, ProtectionRead | ProtectionWrite, MapPrivate | MapAnonymous, -1i32, 0u64); if IsError(result) { return 1; } let memory = result as *opaque; Munmap(memory, 4096u); return 0; } ``` ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Munmap`](https://rux-lang.dev/docs/api/macos/munmap) - remove a mapping - [`Types and constants`](https://rux-lang.dev/docs/api/macos/types) - protection and mapping flags # Munmap Removes a virtual-memory mapping. **Package:** `MacOS` ## Signature ```rux func Munmap(addr: *opaque, length: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | ----------------------------------- | | `addr` | `*opaque` | Base address of the range to unmap. | | `length` | `uint` | Number of bytes in the range. | ## Returns `int64` - `0` on success, or a negative errno value on failure. After success, the unmapped range is invalid and must not be accessed. ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Mmap`](https://rux-lang.dev/docs/api/macos/mmap) - create a mapping # Read Reads bytes from a file descriptor. **Package:** `MacOS` ## Signature ```rux func Read(fd: int32, buffer: *opaque, count: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | --------------------------------------------- | | `fd` | `int32` | File descriptor to read. | | `buffer` | `*opaque` | Writable destination for up to `count` bytes. | | `count` | `uint` | Maximum number of bytes to read. | ## Returns `int64` - bytes read on success, `0` at end of file, or a negative errno value on failure. A successful call may return fewer than `count` bytes. ## Example ```rux import MacOS::{ Read, StdIn }; func Main() -> int { var buffer: char8[256]; let result = Read(StdIn, buffer.data, 256u); if result == 0i64 { // End of file. } return 0; } ``` ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Write`](https://rux-lang.dev/docs/api/macos/write) - write bytes # Syscalls Invoke arbitrary Darwin (macOS) x86-64 syscalls and inspect raw results. **Package:** `MacOS` ## Signatures ```rux func Syscall0(number: uint64) -> int64; func Syscall1(number: uint64, arg0: uint64) -> int64; func Syscall2(number: uint64, arg0: uint64, arg1: uint64) -> int64; func Syscall3(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64) -> int64; func Syscall4(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64) -> int64; func Syscall5(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64) -> int64; func Syscall6(number: uint64, arg0: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64, arg5: uint64) -> int64; ``` Choose the function whose suffix matches the syscall's argument count. On Darwin the number must be **class-qualified**: Unix/BSD calls carry `UnixSyscallClass` (`0x02000000`) in bits 24-31, so pass a complete value such as `SysRead`, not the bare ordinal. Every argument must match the x86-64 System V calling convention, with pointers and signed values converted to raw 64-bit representations. ## Returns `int64` - the kernel result: a non-negative value on success, or a negative errno (`-1` through `-4095`) on failure. No validation, type conversion, retry, or resource management is performed. Use [`IsError`](https://rux-lang.dev/docs/api/macos/iserror) and [`Errno`](https://rux-lang.dev/docs/api/macos/errno) to interpret it. ::caution An incorrect syscall number, argument count, pointer, or buffer length can corrupt memory, leak resources, or terminate the process. Prefer a typed wrapper when one exists. :: ## Error Helpers | Function | Description | | -------------------------------------------------------- | ------------------------------------------------------ | | [`Errno`](https://rux-lang.dev/docs/api/macos/errno) | Return the positive errno of a failing result, else 0. | | [`IsError`](https://rux-lang.dev/docs/api/macos/iserror) | Test whether a result is a negative errno. | ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Types and constants`](https://rux-lang.dev/docs/api/macos/types) - class-qualified syscall numbers # Types and Constants Types and constants exported by the `MacOS` package. **Package:** `MacOS` ## Standard File Descriptors | Name | Type | Value | Description | | -------- | ------- | ----: | ---------------- | | `StdIn` | `int32` | `0` | Standard input. | | `StdOut` | `int32` | `1` | Standard output. | | `StdErr` | `int32` | `2` | Standard error. | A process may close or redirect these conventional descriptors. ## Syscall Numbers Darwin partitions syscall numbers by class: Unix/BSD calls carry the class bits `UnixSyscallClass` (`0x02000000`), so each exported number is the class OR'd with the bare ordinal. | Name | Value | | ------------------ | ------------------------ | | `UnixSyscallClass` | `0x02000000` | | `SysExit` | `UnixSyscallClass | 1` | | `SysRead` | `UnixSyscallClass | 3` | | `SysWrite` | `UnixSyscallClass | 4` | | `SysClose` | `UnixSyscallClass | 6` | | `SysGetPid` | `UnixSyscallClass | 20` | | `SysMunmap` | `UnixSyscallClass | 73` | | `SysGetTimeOfDay` | `UnixSyscallClass | 116` | | `SysMmap` | `UnixSyscallClass | 197` | ## Memory Protection | Name | Type | Value | Description | | ------------------- | ------- | ----: | ---------------------- | | `ProtectionNone` | `int32` | `0` | No access. | | `ProtectionRead` | `int32` | `1` | Pages may be read. | | `ProtectionWrite` | `int32` | `2` | Pages may be written. | | `ProtectionExecute` | `int32` | `4` | Pages may be executed. | ## Mapping Flags | Name | Type | Value | Description | | -------------- | ------- | -----: | --------------------------------------- | | `MapShared` | `int32` | `1` | Create a shared mapping. | | `MapPrivate` | `int32` | `2` | Create a private copy-on-write mapping. | | `MapFixed` | `int32` | `16` | Place the mapping at the exact address. | | `MapAnonymous` | `int32` | `4096` | Create a mapping not backed by a file. | ## `Timeval` ```rux struct Timeval { seconds: int64; microseconds: int32; } ``` | Field | Type | Description | | -------------- | ------- | ------------------------------------------ | | `seconds` | `int64` | Whole seconds since the Unix epoch. | | `microseconds` | `int32` | Microseconds within the second, 0–999,999. | Filled by [`GetTimeOfDay`](https://rux-lang.dev/docs/api/macos/gettimeofday). ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Mmap`](https://rux-lang.dev/docs/api/macos/mmap) — uses the protection and mapping flags - [`Syscall0`–`Syscall6`](https://rux-lang.dev/docs/api/macos/syscalls) — use the class-qualified syscall numbers # Write Writes bytes to a file descriptor. **Package:** `MacOS` ## Signature ```rux func Write(fd: int32, buffer: *opaque, count: uint) -> int64; ``` ## Parameters | Name | Type | Description | | -------- | --------- | ----------------------------------------- | | `fd` | `int32` | File descriptor to write. | | `buffer` | `*opaque` | Source containing at least `count` bytes. | | `count` | `uint` | Number of bytes requested. | ## Returns `int64` - bytes written on success, or a negative errno value on failure. A successful call may write fewer than `count` bytes. ## Example ```rux import MacOS::{ StdOut, Write }; func Main() -> int { let text = "hello\n"; let result = Write(StdOut, text.data, text.length); if result != text.length as int64 { return 1; } return 0; } ``` ## See also - [`MacOS`](https://rux-lang.dev/docs/api/macos) — the package overview - [`Read`](https://rux-lang.dev/docs/api/macos/read) - read bytes # Abs Returns the absolute value. **Package:** `Math` ## Signature ```rux func Abs(x: float64) -> float64; func Abs(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | The value to measure. | ## Returns The magnitude of `x`, in the same precision as the argument. The sign bit is cleared directly, so `Abs(-0.0)` is `0.0` and `Abs(NaN)` is a NaN with the same payload but a cleared sign. ## Example ```rux import Math::Abs; func Main() -> int { let a = Abs(-3.5); // 3.5 let b = Abs(-0.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Min`](https://rux-lang.dev/docs/api/math/min) / [`Max`](https://rux-lang.dev/docs/api/math/max) — pick the smaller or larger of two values # ArcCos Returns the inverse cosine, in radians. **Package:** `Math` ## Signature ```rux func ArcCos(x: float64) -> float64; func ArcCos(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | A value in `[-1, 1]`. | ## Returns The angle in `[0, Pi]` whose cosine is `x`. `ArcCos(1.0)` is `0.0` and `ArcCos(-1.0)` is `Pi`. For `|x| > 1.0` the result is a NaN, since no real angle has that cosine; a NaN argument also propagates. ## Example ```rux import Math::ArcCos; func Main() -> int { let a = ArcCos(1.0); // 0.0 let b = ArcCos(-1.0); // 3.141592653589793 (Pi) return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Cos`](https://rux-lang.dev/docs/api/math/cos) — the function `ArcCos` inverts - [`ArcSin`](https://rux-lang.dev/docs/api/math/arcsin) / [`ArcTan`](https://rux-lang.dev/docs/api/math/arctan) — the other inverse trigonometric functions # ArcCot Returns the inverse cotangent, in radians. **Package:** `Math` ## Signature ```rux func ArcCot(x: float64) -> float64; func ArcCot(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------- | | `x` | `float64` / `float32` | Any value. | ## Returns `HalfPi - ArcTan(x)`, the angle in `(0, Pi)` whose cotangent is `x`, defined for every real `x` — including `0.0`, where `ArcCot(0.0)` is `HalfPi` rather than a pole. A NaN argument propagates. ## Example ```rux import Math::ArcCot; func Main() -> int { let a = ArcCot(0.0); // 1.5707963267948966 (HalfPi) let b = ArcCot(1.0); // 0.7853981633974483 (QuarterPi) return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Cotan`](https://rux-lang.dev/docs/api/math/cotan) — the function `ArcCot` inverts - [`ArcTan`](https://rux-lang.dev/docs/api/math/arctan) — `HalfPi - ArcCot(x)` # ArcSin Returns the inverse sine, in radians. **Package:** `Math` ## Signature ```rux func ArcSin(x: float64) -> float64; func ArcSin(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | A value in `[-1, 1]`. | ## Returns The angle in `[-HalfPi, HalfPi]` whose sine is `x`. `ArcSin(±1.0)` is `±HalfPi`. For `|x| > 1.0` the result is a NaN, since no real angle has that sine; a NaN argument also propagates. ## Example ```rux import Math::ArcSin; func Main() -> int { let a = ArcSin(1.0); // 1.5707963267948966 (HalfPi) let b = ArcSin(0.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sin`](https://rux-lang.dev/docs/api/math/sin) — the function `ArcSin` inverts - [`ArcCos`](https://rux-lang.dev/docs/api/math/arccos) / [`ArcTan`](https://rux-lang.dev/docs/api/math/arctan) — the other inverse trigonometric functions # ArcTan Returns the inverse tangent, in radians. **Package:** `Math` ## Signature ```rux func ArcTan(x: float64) -> float64; func ArcTan(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------- | | `x` | `float64` / `float32` | Any value. | ## Returns The angle in `(-HalfPi, HalfPi)` whose tangent is `x`, defined for every real `x`. `ArcTan(±Inf)` is `±HalfPi`. A NaN argument propagates. ## Example ```rux import Math::ArcTan; func Main() -> int { let a = ArcTan(1.0); // 0.7853981633974483 (QuarterPi) let b = ArcTan(0.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Tan`](https://rux-lang.dev/docs/api/math/tan) — the function `ArcTan` inverts - [`ArcSin`](https://rux-lang.dev/docs/api/math/arcsin) / [`ArcCos`](https://rux-lang.dev/docs/api/math/arccos) — the other inverse trigonometric functions - [`ArcCot`](https://rux-lang.dev/docs/api/math/arccot) — `HalfPi - ArcTan(x)` # Cbrt Returns the cube root. **Package:** `Math` ## Signature ```rux func Cbrt(x: float64) -> float64; func Cbrt(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------- | | `x` | `float64` / `float32` | Any value. | ## Returns The real cube root of `x`. Unlike [`Sqrt`](https://rux-lang.dev/docs/api/math/sqrt), the result is defined for a negative `x`: `Cbrt(-8.0)` is `-2.0`. `Cbrt(NaN)` and `Cbrt(±Inf)` are themselves, and `Cbrt(±0.0)` preserves the sign of the zero. ## Example ```rux import Math::Cbrt; func Main() -> int { let a = Cbrt(27.0); // 3.0 let b = Cbrt(-8.0); // -2.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sqrt`](https://rux-lang.dev/docs/api/math/sqrt) — square root - [`Pow`](https://rux-lang.dev/docs/api/math/pow) — general exponentiation (`Pow(x, 1.0 / 3.0)` is not equivalent for negative `x`) # Ceil Rounds up to the nearest integer value. **Package:** `Math` ## Signature ```rux func Ceil(x: float64) -> float64; func Ceil(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------------- | | `x` | `float64` / `float32` | The value to round. | ## Returns The smallest integer value at or above `x`, still represented as a floating-point number. `Ceil(-0.5)` is `-0.0` rather than `0.0` — truncation carries the sign of `x` across, and IEEE-754 asks for that sign to survive. A NaN passes through unchanged. ## Example ```rux import Math::Ceil; func Main() -> int { let a = Ceil(3.2); // 4.0 let b = Ceil(-3.7); // -3.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Floor`](https://rux-lang.dev/docs/api/math/floor) — round down - [`Round`](https://rux-lang.dev/docs/api/math/round) — round to the nearest integer - [`Trunc`](https://rux-lang.dev/docs/api/math/trunc) — round toward zero # Cos Returns the cosine of an angle in radians. **Package:** `Math` ## Signature ```rux func Cos(x: float64) -> float64; func Cos(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | An angle, in radians. | ## Returns The cosine of `x`, in the range `[-1, 1]`. Cosine has no limit at infinity, so `Cos(±Inf)` is a NaN; a NaN argument also propagates. ## Example ```rux import Math::{ Cos, Pi }; func Main() -> int { let a = Cos(0.0); // 1.0 let b = Cos(Pi); // -1.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sin`](https://rux-lang.dev/docs/api/math/sin) — sine - [`Tan`](https://rux-lang.dev/docs/api/math/tan) — tangent - [`ArcCos`](https://rux-lang.dev/docs/api/math/arccos) — the inverse of `Cos` - [`DegToRad`](https://rux-lang.dev/docs/api/math/degtorad) — convert a degree value to radians first # Cosh Returns the hyperbolic cosine. **Package:** `Math` ## Signature ```rux func Cosh(x: float64) -> float64; func Cosh(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------- | | `x` | `float64` / `float32` | Any value. | ## Returns The hyperbolic cosine of `x`, always at least `1.0`. `Cosh(±Inf)` is `+Inf`. `Cosh` overflows to `+Inf` for large `|x|`. A NaN argument propagates. ## Example ```rux import Math::Cosh; func Main() -> int { let a = Cosh(0.0); // 1.0 let b = Cosh(1.0); // 1.5430806348152437 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sinh`](https://rux-lang.dev/docs/api/math/sinh) — hyperbolic sine - [`Tanh`](https://rux-lang.dev/docs/api/math/tanh) — hyperbolic tangent - [`Exp`](https://rux-lang.dev/docs/api/math/exp) — the exponential `Cosh` is built on # Cotan Returns the cotangent of an angle in radians. **Package:** `Math` ## Signature ```rux func Cotan(x: float64) -> float64; func Cotan(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | An angle, in radians. | ## Returns The cotangent of `x`, computed directly rather than as `1 / Tan(x)` so the result near a pole keeps the bits a second rounding would lose. `Cotan(0.0)` is `+Inf` and `Cotan(-0.0)` is `-Inf`, carrying the pole at zero with the right sign. The cotangent has no limit at infinity, so `Cotan(±Inf)` is a NaN; a NaN argument also propagates. ## Example ```rux import Math::{ Cotan, QuarterPi }; func Main() -> int { let a = Cotan(QuarterPi); // 1.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Tan`](https://rux-lang.dev/docs/api/math/tan) — tangent - [`ArcCot`](https://rux-lang.dev/docs/api/math/arccot) — the inverse of `Cotan` # Cotanh Returns the hyperbolic cotangent. **Package:** `Math` ## Signature ```rux func Cotanh(x: float64) -> float64; func Cotanh(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------- | | `x` | `float64` / `float32` | Any value. | ## Returns The hyperbolic cotangent of `x`. `Cotanh(0.0)` is `+Inf` and `Cotanh(-0.0)` is `-Inf`, carrying the pole at zero with the right sign. `Cotanh` saturates to `±1.0` once `|x|` is large enough that the distinction is no longer representable. A NaN argument propagates. ## Example ```rux import Math::Cotanh; func Main() -> int { let a = Cotanh(1.0); // 1.3130352854993312 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Tanh`](https://rux-lang.dev/docs/api/math/tanh) — hyperbolic tangent - [`Sinh`](https://rux-lang.dev/docs/api/math/sinh) / [`Cosh`](https://rux-lang.dev/docs/api/math/cosh) — hyperbolic sine and cosine # DegToRad Converts an angle from degrees to radians. **Package:** `Math` ## Signature ```rux func DegToRad(x: float64) -> float64; func DegToRad(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | An angle, in degrees. | ## Returns `x` scaled by `Pi/180`. The conversion is faithful enough that round numbers come out exact: `DegToRad(180.0)` is `Pi`. Infinities, NaN, and the sign of a zero all fall out of the multiplication unchanged. ## Example ```rux import Math::DegToRad; func Main() -> int { let radians = DegToRad(180.0); // 3.141592653589793 (Pi) let quarter = DegToRad(90.0); // 1.5707963267948966 (HalfPi) return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`RadToDeg`](https://rux-lang.dev/docs/api/math/radtodeg) — the inverse conversion - [`Sin`](https://rux-lang.dev/docs/api/math/sin) / [`Cos`](https://rux-lang.dev/docs/api/math/cos) / [`Tan`](https://rux-lang.dev/docs/api/math/tan) — trigonometric functions that expect radians - `RadPerDeg` — the constant this function multiplies by # Exp Returns e raised to a power. **Package:** `Math` ## Signature ```rux func Exp(x: float64) -> float64; func Exp(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------- | | `x` | `float64` / `float32` | The exponent. | ## Returns `e^x`, in the same precision as the argument. `Exp` overflows to `+Inf` for large positive `x` and underflows to `+0.0` for large negative `x`. `Exp(+Inf)` is `+Inf`, `Exp(-Inf)` is `+0.0`, and a NaN propagates. ## Example ```rux import Math::Exp; func Main() -> int { let a = Exp(1.0); // 2.718281828459045 (e) let b = Exp(0.0); // 1.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Exp2`](https://rux-lang.dev/docs/api/math/exp2) — 2 raised to a power - [`Log`](https://rux-lang.dev/docs/api/math/log) — the inverse of `Exp` - [`Pow`](https://rux-lang.dev/docs/api/math/pow) — an arbitrary base raised to a power # Exp2 Returns 2 raised to a power. **Package:** `Math` ## Signature ```rux func Exp2(x: float64) -> float64; func Exp2(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------- | | `x` | `float64` / `float32` | The exponent. | ## Returns `2^x`, in the same precision as the argument. `Exp2` overflows to `+Inf` for `x >= 1024.0` and underflows to `+0.0` for `x < -1075.0`. A NaN propagates. ## Example ```rux import Math::Exp2; func Main() -> int { let a = Exp2(10.0); // 1024.0 let b = Exp2(-1.0); // 0.5 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Exp`](https://rux-lang.dev/docs/api/math/exp) — e raised to a power - [`Log2`](https://rux-lang.dev/docs/api/math/log2) — the inverse of `Exp2` - [`Pow`](https://rux-lang.dev/docs/api/math/pow) — an arbitrary base raised to a power # Floor Rounds down to the nearest integer value. **Package:** `Math` ## Signature ```rux func Floor(x: float64) -> float64; func Floor(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------------- | | `x` | `float64` / `float32` | The value to round. | ## Returns The largest integer value at or below `x`, still represented as a floating-point number. `Floor(-0.0)` is `-0.0` and `Floor(-Inf)` is `-Inf`; a NaN passes through unchanged. ## Example ```rux import Math::Floor; func Main() -> int { let a = Floor(3.7); // 3.0 let b = Floor(-3.2); // -4.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Ceil`](https://rux-lang.dev/docs/api/math/ceil) — round up - [`Round`](https://rux-lang.dev/docs/api/math/round) — round to the nearest integer - [`Trunc`](https://rux-lang.dev/docs/api/math/trunc) — round toward zero # Hypot Returns the length of the hypotenuse of a right triangle with legs `x` and `y`. **Package:** `Math` ## Signature ```rux func Hypot(x: float64, y: float64) -> float64; func Hypot(x: float32, y: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------------------ | | `x` | `float64` / `float32` | One leg of the triangle. | | `y` | `float64` / `float32` | The other leg. | ## Returns `√(x² + y²)`, computed without the overflow that squaring a large argument would cause, without the underflow that squaring a small one would, and without the extra rounding of forming `x*x + y*y` directly. `Hypot` is infinite if either argument is infinite, even when the other is a NaN; otherwise a NaN in either argument produces a NaN. ## Example ```rux import Math::Hypot; func Main() -> int { let a = Hypot(3.0, 4.0); // 5.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sqrt`](https://rux-lang.dev/docs/api/math/sqrt) — the square root `Hypot` is built on - [`Abs`](https://rux-lang.dev/docs/api/math/abs) — magnitude of a single value # Math Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: The package provides mathematical constants and floating-point functions. **Package:** `Math` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Math](https://github.com/rux-lang/Rux/tree/main/Packages/Math){rel=""nofollow""} Every function is provided for both `float64` and `float32`. Pass `float64` arguments to get a `float64` result, or `float32` for a `float32` result — each overload rounds only once, so the `float32` result is as accurate as the `float64` computation allows. ```rux import Math::{ Pi, Pow, Sin, Sqrt }; import Io::PrintLine; func Main() -> int { PrintLine(Sqrt(2.0)); // 1.4142135623730951 PrintLine(Pow(2.0, 10.0)); // 1024 PrintLine(Sin(Pi / 2.0)); // 1 return 0; } ``` ## Installation ```sh rux add Math rux install ``` ## Constants | Name | Type | Value | Description | | ----------- | --------- | --------- | -------------------------------------------------- | | `Pi` | `float64` | 3.14159… | Ratio of a circle's circumference to its diameter. | | `Tau` | `float64` | 6.28318… | 2 × `Pi`, a full turn in radians. | | `HalfPi` | `float64` | 1.57079… | `Pi` / 2. | | `QuarterPi` | `float64` | 0.78539… | `Pi` / 4. | | `InvPi` | `float64` | 0.31830… | 1 / `Pi`. | | `InvTau` | `float64` | 0.15915… | 1 / `Tau`. | | `E` | `float64` | 2.71828… | Euler's number, the base of the natural logarithm. | | `Log2E` | `float64` | 1.44269… | log₂(e). | | `Log10E` | `float64` | 0.43429… | log₁₀(e). | | `Ln2` | `float64` | 0.69314… | ln(2). | | `Ln10` | `float64` | 2.30258… | ln(10). | | `Sqrt2` | `float64` | 1.41421… | √2. | | `InvSqrt2` | `float64` | 0.70710… | 1 / √2. | | `RadPerDeg` | `float64` | 0.01745… | `Pi` / 180, radians per degree. | | `DegPerRad` | `float64` | 57.29577… | 180 / `Pi`, degrees per radian. | ```rux import Math::Pi; let circumference = 2.0 * Pi * radius; ``` ## Functions ### Elementary operations | Function | Description | | --------------------------------------------------- | --------------------------------------------------- | | [`Abs`](https://rux-lang.dev/docs/api/math/abs) | Absolute value. | | [`Min`](https://rux-lang.dev/docs/api/math/min) | The smaller of two values. | | [`Max`](https://rux-lang.dev/docs/api/math/max) | The larger of two values. | | [`Mod`](https://rux-lang.dev/docs/api/math/mod) | Floating-point remainder, signed like the dividend. | | [`Pow`](https://rux-lang.dev/docs/api/math/pow) | Raise a base to an exponent. | | [`Sqrt`](https://rux-lang.dev/docs/api/math/sqrt) | Square root. | | [`Cbrt`](https://rux-lang.dev/docs/api/math/cbrt) | Cube root, defined for negative arguments too. | | [`Hypot`](https://rux-lang.dev/docs/api/math/hypot) | √(x² + y²), without spurious overflow or underflow. | ### Rounding | Function | Description | | --------------------------------------------------- | -------------------------------------------------- | | [`Floor`](https://rux-lang.dev/docs/api/math/floor) | Round down to the nearest integer value. | | [`Ceil`](https://rux-lang.dev/docs/api/math/ceil) | Round up to the nearest integer value. | | [`Round`](https://rux-lang.dev/docs/api/math/round) | Round to the nearest integer, ties away from zero. | | [`Trunc`](https://rux-lang.dev/docs/api/math/trunc) | Round toward zero. | ### Exponential and logarithmic | Function | Description | | --------------------------------------------------- | -------------------- | | [`Exp`](https://rux-lang.dev/docs/api/math/exp) | e raised to a power. | | [`Exp2`](https://rux-lang.dev/docs/api/math/exp2) | 2 raised to a power. | | [`Log`](https://rux-lang.dev/docs/api/math/log) | Natural logarithm. | | [`Log2`](https://rux-lang.dev/docs/api/math/log2) | Base-2 logarithm. | | [`Log10`](https://rux-lang.dev/docs/api/math/log10) | Base-10 logarithm. | ### Trigonometry | Function | Description | | ----------------------------------------------------- | --------------------------------- | | [`Sin`](https://rux-lang.dev/docs/api/math/sin) | Sine of an angle in radians. | | [`Cos`](https://rux-lang.dev/docs/api/math/cos) | Cosine of an angle in radians. | | [`Tan`](https://rux-lang.dev/docs/api/math/tan) | Tangent of an angle in radians. | | [`Cotan`](https://rux-lang.dev/docs/api/math/cotan) | Cotangent of an angle in radians. | | [`ArcSin`](https://rux-lang.dev/docs/api/math/arcsin) | Inverse sine, in radians. | | [`ArcCos`](https://rux-lang.dev/docs/api/math/arccos) | Inverse cosine, in radians. | | [`ArcTan`](https://rux-lang.dev/docs/api/math/arctan) | Inverse tangent, in radians. | | [`ArcCot`](https://rux-lang.dev/docs/api/math/arccot) | Inverse cotangent, in radians. | ### Hyperbolic | Function | Description | | ----------------------------------------------------- | --------------------- | | [`Sinh`](https://rux-lang.dev/docs/api/math/sinh) | Hyperbolic sine. | | [`Cosh`](https://rux-lang.dev/docs/api/math/cosh) | Hyperbolic cosine. | | [`Tanh`](https://rux-lang.dev/docs/api/math/tanh) | Hyperbolic tangent. | | [`Cotanh`](https://rux-lang.dev/docs/api/math/cotanh) | Hyperbolic cotangent. | ### Angle conversion | Function | Description | | --------------------------------------------------------- | --------------------------- | | [`DegToRad`](https://rux-lang.dev/docs/api/math/degtorad) | Convert degrees to radians. | | [`RadToDeg`](https://rux-lang.dev/docs/api/math/radtodeg) | Convert radians to degrees. | # Log Returns the natural logarithm. **Package:** `Math` ## Signature ```rux func Log(x: float64) -> float64; func Log(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | A non-negative value. | ## Returns `ln(x)`, in the same precision as the argument. `Log(±0.0)` is `-Inf`; `Log` of a negative value is a NaN; `Log(+Inf)` is `+Inf`; a NaN propagates. ## Example ```rux import Math::{ E, Log }; func Main() -> int { let a = Log(E); // 1.0 let b = Log(1.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Log2`](https://rux-lang.dev/docs/api/math/log2) — base-2 logarithm - [`Log10`](https://rux-lang.dev/docs/api/math/log10) — base-10 logarithm - [`Exp`](https://rux-lang.dev/docs/api/math/exp) — the inverse of `Log` # Log10 Returns the base-10 logarithm. **Package:** `Math` ## Signature ```rux func Log10(x: float64) -> float64; func Log10(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | A non-negative value. | ## Returns `log₁₀(x)`, in the same precision as the argument. `Log10(±0.0)` is `-Inf`; `Log10` of a negative value is a NaN; `Log10(+Inf)` is `+Inf`; a NaN propagates. ## Example ```rux import Math::Log10; func Main() -> int { let a = Log10(1000.0); // 3.0 let b = Log10(1.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Log`](https://rux-lang.dev/docs/api/math/log) — natural logarithm - [`Log2`](https://rux-lang.dev/docs/api/math/log2) — base-2 logarithm - [`Exp`](https://rux-lang.dev/docs/api/math/exp) — exponentiation with base e # Log2 Returns the base-2 logarithm. **Package:** `Math` ## Signature ```rux func Log2(x: float64) -> float64; func Log2(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | A non-negative value. | ## Returns `log₂(x)`, in the same precision as the argument. `Log2(±0.0)` is `-Inf`; `Log2` of a negative value is a NaN; `Log2(+Inf)` is `+Inf`; a NaN propagates. ## Example ```rux import Math::Log2; func Main() -> int { let a = Log2(8.0); // 3.0 let b = Log2(1.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Log`](https://rux-lang.dev/docs/api/math/log) — natural logarithm - [`Log10`](https://rux-lang.dev/docs/api/math/log10) — base-10 logarithm - [`Exp2`](https://rux-lang.dev/docs/api/math/exp2) — the inverse of `Log2` # Max Returns the larger of two values. **Package:** `Math` ## Signature ```rux func Max(a: float64, b: float64) -> float64; func Max(a: float32, b: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------------- | | `a` | `float64` / `float32` | The first value. | | `b` | `float64` / `float32` | The second value. | ## Returns The larger of `a` and `b`. A NaN loses to anything else — `Max(NaN, y)` is `y` and `Max(x, NaN)` is `x` — and only two NaNs produce a NaN. When `a` and `b` compare equal, the sign of a zero still decides: `Max(0.0, -0.0)` is `0.0`. ## Example ```rux import Math::Max; func Main() -> int { let a = Max(3.0, 7.0); // 7.0 let b = Max(0.0, -0.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Min`](https://rux-lang.dev/docs/api/math/min) — the larger value's counterpart - [`Abs`](https://rux-lang.dev/docs/api/math/abs) — magnitude of a single value # Min Returns the smaller of two values. **Package:** `Math` ## Signature ```rux func Min(a: float64, b: float64) -> float64; func Min(a: float32, b: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------------- | | `a` | `float64` / `float32` | The first value. | | `b` | `float64` / `float32` | The second value. | ## Returns The smaller of `a` and `b`. A NaN loses to anything else — `Min(NaN, y)` is `y` and `Min(x, NaN)` is `x` — and only two NaNs produce a NaN. When `a` and `b` compare equal, the sign of a zero still decides: `Min(0.0, -0.0)` is `-0.0`. ## Example ```rux import Math::Min; func Main() -> int { let a = Min(3.0, 7.0); // 3.0 let b = Min(0.0, -0.0); // -0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Max`](https://rux-lang.dev/docs/api/math/max) — the smaller value's counterpart - [`Abs`](https://rux-lang.dev/docs/api/math/abs) — magnitude of a single value # Mod Returns the floating-point remainder of `x / y`. **Package:** `Math` ## Signature ```rux func Mod(x: float64, y: float64) -> float64; func Mod(x: float32, y: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------- | | `x` | `float64` / `float32` | The dividend. | | `y` | `float64` / `float32` | The divisor. | ## Returns The exact remainder `x - n * y`, for the integer `n` that truncates `x / y`, matching C's `fmod`. The result carries the sign of `x`; it is `x` unchanged when `|x| < |y|`, and a zero signed like `x` when `|x| == |y|`. The result is a NaN when `y` is zero, when `y` is a NaN, or when `x` is infinite or a NaN. ## Example ```rux import Math::Mod; func Main() -> int { let a = Mod(5.5, 2.0); // 1.5 let b = Mod(-5.5, 2.0); // -1.5 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Trunc`](https://rux-lang.dev/docs/api/math/trunc) — the truncation `Mod` reduces against # Pow Raises a base to an exponent. **Package:** `Math` ## Signature ```rux func Pow(x: float64, y: float64) -> float64; func Pow(x: float32, y: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------- | | `x` | `float64` / `float32` | The base. | | `y` | `float64` / `float32` | The exponent. | ## Returns `x` raised to `y`, in the same precision as the arguments. `Pow` follows IEEE-754's special cases, of which the ones most likely to be surprising are: - `Pow(x, 0.0)` is `1.0` for every `x`, a NaN included. - `Pow(1.0, y)` is `1.0` for every `y`, a NaN included. - `Pow(x, y)` is a NaN when `x` is negative and `y` is not an integer — there is no real result. - When `x` is negative and `y` is an odd integer, the result is negative; when `y` is an even integer, it is positive. - `Pow(x, 2.0)` and `Pow(x, 0.5)` (for non-negative `x`) are recognized and computed directly, as `x * x` and [`Sqrt(x)`](https://rux-lang.dev/docs/api/math/sqrt). ## Example ```rux import Math::Pow; func Main() -> int { let a = Pow(2.0, 10.0); // 1024.0 let b = Pow(9.0, 0.5); // 3.0 let c = Pow(-8.0, 1.0 / 3.0); // NaN -- exponent is not an integer return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sqrt`](https://rux-lang.dev/docs/api/math/sqrt) — the common square-root case - [`Cbrt`](https://rux-lang.dev/docs/api/math/cbrt) — real cube roots, including negative arguments # RadToDeg Converts an angle from radians to degrees. **Package:** `Math` ## Signature ```rux func RadToDeg(x: float64) -> float64; func RadToDeg(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | An angle, in radians. | ## Returns `x` scaled by `180/Pi`. The conversion is faithful enough that round numbers come out exact: `RadToDeg(Pi)` is `180.0`. Infinities, NaN, and the sign of a zero all fall out of the multiplication unchanged. ## Example ```rux import Math::{ HalfPi, Pi, RadToDeg }; func Main() -> int { let degrees = RadToDeg(Pi); // 180.0 let ninety = RadToDeg(HalfPi); // 90.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`DegToRad`](https://rux-lang.dev/docs/api/math/degtorad) — the inverse conversion - [`Sin`](https://rux-lang.dev/docs/api/math/sin) / [`Cos`](https://rux-lang.dev/docs/api/math/cos) / [`Tan`](https://rux-lang.dev/docs/api/math/tan) — trigonometric functions that return radians - `DegPerRad` — the constant this function multiplies by # Round Rounds to the nearest integer value. **Package:** `Math` ## Signature ```rux func Round(x: float64) -> float64; func Round(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------------- | | `x` | `float64` / `float32` | The value to round. | ## Returns The nearest integer value to `x`, still represented as a floating-point number. A tie rounds away from zero: `Round(2.5)` is `3.0` and `Round(-2.5)` is `-3.0`. Infinities and NaNs pass through unchanged. ## Example ```rux import Math::Round; func Main() -> int { let a = Round(2.5); // 3.0 let b = Round(-2.5); // -3.0 let c = Round(2.4); // 2.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Floor`](https://rux-lang.dev/docs/api/math/floor) — round down - [`Ceil`](https://rux-lang.dev/docs/api/math/ceil) — round up - [`Trunc`](https://rux-lang.dev/docs/api/math/trunc) — round toward zero # Sin Returns the sine of an angle in radians. **Package:** `Math` ## Signature ```rux func Sin(x: float64) -> float64; func Sin(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | An angle, in radians. | ## Returns The sine of `x`, in the range `[-1, 1]`. `Sin(±0.0)` is `±0.0`, preserving the sign. Sine has no limit at infinity, so `Sin(±Inf)` is a NaN; a NaN argument also propagates. ## Example ```rux import Math::{ HalfPi, Sin }; func Main() -> int { let a = Sin(HalfPi); // 1.0 let b = Sin(0.0); // 0.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Cos`](https://rux-lang.dev/docs/api/math/cos) — cosine - [`Tan`](https://rux-lang.dev/docs/api/math/tan) — tangent - [`ArcSin`](https://rux-lang.dev/docs/api/math/arcsin) — the inverse of `Sin` - [`DegToRad`](https://rux-lang.dev/docs/api/math/degtorad) — convert a degree value to radians first # Sinh Returns the hyperbolic sine. **Package:** `Math` ## Signature ```rux func Sinh(x: float64) -> float64; func Sinh(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------- | | `x` | `float64` / `float32` | Any value. | ## Returns The hyperbolic sine of `x`. `Sinh(±0.0)` is `±0.0`, preserving the sign, and `Sinh(±Inf)` is `±Inf`. `Sinh` overflows to `±Inf` for large `|x|`. A NaN argument propagates. ## Example ```rux import Math::Sinh; func Main() -> int { let a = Sinh(0.0); // 0.0 let b = Sinh(1.0); // 1.1752011936438014 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Cosh`](https://rux-lang.dev/docs/api/math/cosh) — hyperbolic cosine - [`Tanh`](https://rux-lang.dev/docs/api/math/tanh) — hyperbolic tangent - [`Exp`](https://rux-lang.dev/docs/api/math/exp) — the exponential `Sinh` is built on # Sqrt Returns the square root. **Package:** `Math` ## Signature ```rux func Sqrt(x: float64) -> float64; func Sqrt(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | A non-negative value. | ## Returns The non-negative square root of `x`, computed directly by the hardware square root instruction and correctly rounded. `Sqrt(-0.0)` is `-0.0`; `Sqrt(x)` for any other negative `x` is a NaN. ## Example ```rux import Math::Sqrt; func Main() -> int { let a = Sqrt(144.0); // 12.0 let b = Sqrt(-1.0); // NaN return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Cbrt`](https://rux-lang.dev/docs/api/math/cbrt) — cube root, defined for negative arguments - [`Pow`](https://rux-lang.dev/docs/api/math/pow) — general exponentiation - [`Hypot`](https://rux-lang.dev/docs/api/math/hypot) — √(x² + y²) without overflow # Tan Returns the tangent of an angle in radians. **Package:** `Math` ## Signature ```rux func Tan(x: float64) -> float64; func Tan(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | --------------------- | | `x` | `float64` / `float32` | An angle, in radians. | ## Returns The tangent of `x`. `Tan(±0.0)` is `±0.0`, preserving the sign. The tangent has no limit at infinity, so `Tan(±Inf)` is a NaN; a NaN argument also propagates. Near an odd multiple of `Pi/2` the result grows without bound, as expected of a pole. ## Example ```rux import Math::{ QuarterPi, Tan }; func Main() -> int { let a = Tan(0.0); // 0.0 let b = Tan(QuarterPi); // 1.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sin`](https://rux-lang.dev/docs/api/math/sin) / [`Cos`](https://rux-lang.dev/docs/api/math/cos) — sine and cosine - [`Cotan`](https://rux-lang.dev/docs/api/math/cotan) — the reciprocal function, computed directly rather than as `1 / Tan(x)` - [`ArcTan`](https://rux-lang.dev/docs/api/math/arctan) — the inverse of `Tan` # Tanh Returns the hyperbolic tangent. **Package:** `Math` ## Signature ```rux func Tanh(x: float64) -> float64; func Tanh(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ----------- | | `x` | `float64` / `float32` | Any value. | ## Returns The hyperbolic tangent of `x`, in the range `(-1, 1)`. `Tanh(±0.0)` is `±0.0`, preserving the sign, and `Tanh` saturates to `±1.0` once `|x|` is large enough that the distinction is no longer representable. A NaN argument propagates. ## Example ```rux import Math::Tanh; func Main() -> int { let a = Tanh(0.0); // 0.0 let b = Tanh(1.0); // 0.7615941559557649 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Sinh`](https://rux-lang.dev/docs/api/math/sinh) / [`Cosh`](https://rux-lang.dev/docs/api/math/cosh) — hyperbolic sine and cosine - [`Cotanh`](https://rux-lang.dev/docs/api/math/cotanh) — the reciprocal function, with a pole at zero # Trunc Rounds toward zero. **Package:** `Math` ## Signature ```rux func Trunc(x: float64) -> float64; func Trunc(x: float32) -> float32; ``` ## Parameters | Name | Type | Description | | ---- | --------------------- | ------------------- | | `x` | `float64` / `float32` | The value to round. | ## Returns The integer part of `x`, discarding any fractional part, still represented as a floating-point number. `Trunc(-0.4)` keeps its sign and is `-0.0`. Infinities and NaNs pass through unchanged. ## Example ```rux import Math::Trunc; func Main() -> int { let a = Trunc(3.9); // 3.0 let b = Trunc(-3.9); // -3.0 return 0; } ``` ## See also - [`Math`](https://rux-lang.dev/docs/api/math) — the package overview - [`Floor`](https://rux-lang.dev/docs/api/math/floor) — round down - [`Ceil`](https://rux-lang.dev/docs/api/math/ceil) — round up - [`Round`](https://rux-lang.dev/docs/api/math/round) — round to the nearest integer - [`Mod`](https://rux-lang.dev/docs/api/math/mod) — the remainder built on this truncation # Alloc Allocates an uninitialized block of memory. **Package:** `Memory` ## Signature ```rux func Alloc( size: uint ) -> *var opaque; ``` ## Parameters | Name | Type | Description | | ------ | ------ | -------------------------------- | | `size` | `uint` | The number of bytes to allocate. | ## Returns A pointer to the first byte of the block, or `null` if the platform could not satisfy the request. The bytes are **not** zeroed — they hold whatever was last left there, so read them only after writing them, or clear the block with [`Zero`](https://rux-lang.dev/docs/api/memory/zero) first. The block belongs to the caller and must be released exactly once with [`Free`](https://rux-lang.dev/docs/api/memory/free). Every successful call returns a distinct block, and the pointer stays valid until it is passed to [`Free`](https://rux-lang.dev/docs/api/memory/free) or moved by [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc). ## Example ```rux import Memory::{ Alloc, Free, Zero }; func Main() -> int { let buffer = Alloc(1024); if buffer == null { return 1; } Zero(buffer, 1024); Free(buffer); return 0; } ``` ## See also - [`Memory`](https://rux-lang.dev/docs/api/memory) — the package overview - [`Free`](https://rux-lang.dev/docs/api/memory/free) — release the block when it is no longer needed - [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) — resize a block that already exists - [`Zero`](https://rux-lang.dev/docs/api/memory/zero) — clear the uninitialized bytes # Compare Finds the offset at which two blocks of memory first differ. **Package:** `Memory` ## Signature ```rux func Compare( lhs: *opaque, rhs: *opaque, length: uint ) -> uint; ``` ## Parameters | Name | Type | Description | | -------- | --------- | ------------------------------- | | `lhs` | `*opaque` | The first block. | | `rhs` | `*opaque` | The second block. | | `length` | `uint` | The number of bytes to compare. | ## Returns The offset of the first byte that differs, or `length` if the first `length` bytes are equal. This is **not** C's `memcmp`: there is no sign, and equality is reported as `length` rather than as `0`. The two comparisons that trip people up: - **Equal blocks return `length`,** so the equality test is `Compare(a, b, n) == n`, and never `== 0`. - **`0` means the blocks differ at the very first byte** — except when `length` is `0`, where nothing is compared and `0` means trivially equal. The comparison stops at `length`, so bytes that differ beyond it are invisible. Both blocks must be at least `length` bytes long. ## Example ```rux import Memory::{ Alloc, Compare, Free, Set }; func Main() -> int { let lhs = Alloc(16); let rhs = Alloc(16); Set(lhs, 16, 0x5A); Set(rhs, 16, 0x5A); Compare(lhs, rhs, 16); // 16 -- equal, so the full length let bytes = rhs as *var uint8; *(bytes + 5) = 0x00; Compare(lhs, rhs, 16); // 5 -- the first difference Compare(lhs, rhs, 5); // 5 -- equal, the difference is out of range Free(rhs); Free(lhs); return 0; } ``` ## See also - [`Memory`](https://rux-lang.dev/docs/api/memory) — the package overview - [`Copy`](https://rux-lang.dev/docs/api/memory/copy) — make one block equal to another - [`Set`](https://rux-lang.dev/docs/api/memory/set) — fill a block with a known byte before comparing # Copy Copies bytes from one block of memory to another. **Package:** `Memory` ## Signature ```rux func Copy( dest: *var opaque, src: *opaque, length: uint ); ``` ## Parameters | Name | Type | Description | | -------- | ------------- | ---------------------------- | | `dest` | `*var opaque` | The block to copy into. | | `src` | `*opaque` | The block to copy from. | | `length` | `uint` | The number of bytes to copy. | ## Remarks Copies `length` bytes from `src` to `dest`. The two regions **must not overlap** — the copy runs forward, byte by byte, so an overlapping destination that starts inside the source will read bytes it has already overwritten. There is no `Move` counterpart yet; to shift bytes within one block, copy through a scratch block from [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc). Both blocks must be at least `length` bytes long. A `length` of `0` copies nothing. ## Example ```rux import Memory::{ Alloc, Compare, Copy, Free, Set }; func Main() -> int { let src = Alloc(16); let dest = Alloc(16); Set(src, 16, 0x5A); Copy(dest, src, 16); Compare(dest, src, 16); // 16 -- the blocks are equal Free(dest); Free(src); return 0; } ``` ## See also - [`Memory`](https://rux-lang.dev/docs/api/memory) — the package overview - [`Compare`](https://rux-lang.dev/docs/api/memory/compare) — check that two blocks hold the same bytes - [`Set`](https://rux-lang.dev/docs/api/memory/set) — fill a block with a byte instead of copying one - [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) — resize a block, which copies the bytes for you # Free Releases a block of memory back to the platform. **Package:** `Memory` ## Signature ```rux func Free(ptr: *opaque); ``` ## Parameters | Name | Type | Description | | ----- | --------- | -------------------------------------------------------------------------------------------------------------------------------- | | `ptr` | `*opaque` | A block from [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) or [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc). | ## Remarks Passing `null` is a no-op, so a caller needs no guard around the common "allocate, maybe fail, clean up" path. Anything else is undefined behavior and is not diagnosed: releasing the same block twice, releasing a pointer that [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) has already moved, or passing a pointer this package did not produce. After the call the block is gone and the pointer must not be read, written, or freed again. ## Example ```rux import Memory::{ Alloc, Free }; func Main() -> int { let buffer = Alloc(256); if buffer == null { return 1; } Free(buffer); Free(null); // harmless return 0; } ``` ## See also - [`Memory`](https://rux-lang.dev/docs/api/memory) — the package overview - [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) — allocate the block in the first place - [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) — resize a block instead of releasing it # Memory Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: The package provides raw memory management — allocating, resizing, and releasing blocks, and filling, copying, and comparing the bytes inside them. **Package:** `Memory` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Memory](https://github.com/rux-lang/Rux/tree/main/Packages/Memory){rel=""nofollow""} Every function is implemented per target, so a call compiles down to the platform's own primitives rather than to a bundled allocator: the Win32 heap and the `Rtl*` intrinsics on Windows, and anonymous `mmap` mappings on Linux and macOS. The API is the same on all of them. ```rux import Memory::{ Alloc, Free, Set }; import Io::PrintLine; func Main() -> int { let buffer = Alloc(1024); if buffer == null { return 1; } Set(buffer, 1024, 0xFF); PrintLine(*(buffer as *uint8)); // 255 Free(buffer); return 0; } ``` ## Installation ```sh rux add Memory rux install ``` ## Platform support Implemented on BSD, Linux, macOS, and Windows. ## Memory model The block a caller receives is raw and owned: nothing tracks it, nothing frees it, and its contents are whatever the platform last left there. [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) does not zero it — use [`Zero`](https://rux-lang.dev/docs/api/memory/zero) when you need a clean block. Every block returned by [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) or [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) must be released exactly once with [`Free`](https://rux-lang.dev/docs/api/memory/free), and a pointer that [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) has moved must not be used again. Passing a pointer these functions did not produce, releasing the same block twice, or reading or writing outside the requested size is undefined behavior — it is not checked and it will not be diagnosed. ## Functions ### Allocation | Function | Description | | --------------------------------------------------------- | ---------------------------------------------- | | [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) | Allocate an uninitialized block of memory. | | [`Realloc`](https://rux-lang.dev/docs/api/memory/realloc) | Resize a block, preserving the bytes that fit. | | [`Free`](https://rux-lang.dev/docs/api/memory/free) | Release a block back to the platform. | ### Filling | Function | Description | | --------------------------------------------------- | ---------------------------------- | | [`Set`](https://rux-lang.dev/docs/api/memory/set) | Fill a block with a repeated byte. | | [`Zero`](https://rux-lang.dev/docs/api/memory/zero) | Fill a block with zero bytes. | ### Bulk operations | Function | Description | | --------------------------------------------------------- | ------------------------------------------------- | | [`Copy`](https://rux-lang.dev/docs/api/memory/copy) | Copy bytes between two non-overlapping blocks. | | [`Compare`](https://rux-lang.dev/docs/api/memory/compare) | Find the offset at which two blocks first differ. | # Realloc Resizes a block of memory, preserving the bytes that still fit. **Package:** `Memory` ## Signature ```rux func Realloc( ptr: *opaque, size: uint ) -> *var opaque; ``` ## Parameters | Name | Type | Description | | ------ | --------- | ------------------------------------------------------------------------------------------- | | `ptr` | `*opaque` | A block from [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) or `Realloc`, or `null`. | | `size` | `uint` | The new size in bytes. | ## Returns A pointer to the resized block, which **may differ from `ptr`** — the block is allowed to move, and once it has, the old pointer must not be used again. Two arguments are treated as shorthands rather than errors: - `Realloc(null, size)` allocates a fresh block, exactly like [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc). - `Realloc(ptr, 0)` releases the block and returns `null`, exactly like [`Free`](https://rux-lang.dev/docs/api/memory/free). Growing keeps every byte of the old block and leaves the bytes past the old size uninitialized; shrinking keeps the surviving prefix and discards the rest. The return is `null` if the platform could not satisfy the request. In that case the original block is **untouched and still valid**, so it must still be freed — assigning the result straight back over `ptr` leaks it. ## Example ```rux import Memory::{ Alloc, Free, Realloc, Set }; func Main() -> int { var buffer = Alloc(8); Set(buffer, 8, 0xAB); let grown = Realloc(buffer, 64); // the first 8 bytes are still 0xAB if grown == null { Free(buffer); // the old block survived the failure return 1; } buffer = grown; Free(buffer); return 0; } ``` ## See also - [`Memory`](https://rux-lang.dev/docs/api/memory) — the package overview - [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) — allocate the block in the first place - [`Free`](https://rux-lang.dev/docs/api/memory/free) — release the block when it is no longer needed - [`Copy`](https://rux-lang.dev/docs/api/memory/copy) — move bytes between blocks by hand # Set Fills a block of memory with a repeated byte. **Package:** `Memory` ## Signature ```rux func Set( ptr: *opaque, size: uint, value: int32 ); ``` ## Parameters | Name | Type | Description | | ------- | --------- | ------------------------------------- | | `ptr` | `*opaque` | The first byte of the block to fill. | | `size` | `uint` | The number of bytes to write. | | `value` | `int32` | The fill byte, in the low eight bits. | ## Remarks Writes `size` copies of `value` starting at `ptr`. Only the **low eight bits** of `value` reach memory — the parameter is wider only to match the C fill convention that the Win32 entry point expects, so `Set(ptr, size, 0x1FF)` fills with `0xFF`, not with a truncation error. A `size` of `0` writes nothing. Writing past the end of the block is undefined behavior; `size` must not exceed the size the block was allocated with. ## Example ```rux import Memory::{ Alloc, Free, Set }; func Main() -> int { let buffer = Alloc(16); Set(buffer, 16, 0xA5); // every byte is 0xA5 Set(buffer, 16, 0x1FF); // every byte is 0xFF -- only the low 8 bits land Free(buffer); return 0; } ``` ## See also - [`Memory`](https://rux-lang.dev/docs/api/memory) — the package overview - [`Zero`](https://rux-lang.dev/docs/api/memory/zero) — the common zero-fill case - [`Copy`](https://rux-lang.dev/docs/api/memory/copy) — fill a block from another block instead of from a byte # Zero Fills a block of memory with zero bytes. **Package:** `Memory` ## Signature ```rux func Zero(ptr: *opaque, size: uint); ``` ## Parameters | Name | Type | Description | | ------ | --------- | ------------------------------------- | | `ptr` | `*opaque` | The first byte of the block to clear. | | `size` | `uint` | The number of bytes to clear. | ## Remarks Writes `size` zero bytes starting at `ptr` — the same thing as [`Set(ptr, size, 0)`](https://rux-lang.dev/docs/api/memory/set), which is exactly how it is implemented everywhere except Windows, where it lowers to `RtlZeroMemory`. This is the usual companion to [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc), whose blocks come back uninitialized. A `size` of `0` writes nothing, and writing past the end of the block is undefined behavior. ## Example ```rux import Memory::{ Alloc, Free, Zero }; func Main() -> int { let buffer = Alloc(1024); Zero(buffer, 1024); // now safe to read before writing Free(buffer); return 0; } ``` ## See also - [`Memory`](https://rux-lang.dev/docs/api/memory) — the package overview - [`Set`](https://rux-lang.dev/docs/api/memory/set) — fill with a byte other than zero - [`Alloc`](https://rux-lang.dev/docs/api/memory/alloc) — the source of the uninitialized block # Assert Checks a condition at run time and aborts if it does not hold. **Package:** `Rux` ## Signature ```rux intrinsic func Assert(condition: bool, message: Slice); intrinsic func DebugAssert(condition: bool, message: Slice); ``` ## Description `Assert` evaluates `condition` and, when it is `false`, prints `message` and terminates the program the way [`Panic`](https://rux-lang.dev/docs/api/core/panic) does. It is a compiler intrinsic, so the check is emitted inline rather than called through a library. `DebugAssert` is the same check, but only in a build with debug assertions enabled (see [`#build.debugAssertions`](https://rux-lang.dev/docs/api/core/build)); in a release build it compiles to nothing, so it carries no run-time cost. Use `Assert` for invariants that must always hold, and `DebugAssert` for expensive checks you only want while developing. ## Example ```rux import Core::Assert; func Main() -> int { let count = 3; Assert(count > 0, "count must be positive"); return 0; } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`Panic`](https://rux-lang.dev/docs/api/core/panic) — the unconditional abort `Assert` uses - [`#build`](https://rux-lang.dev/docs/api/core/build) — whether debug assertions are enabled # #build Compile-time information about the active build. **Package:** `Rux` ## Definition ```rux intrinsic #build: Build; struct Build { profile: Slice; mode: BuildMode; optimization: OptimizationMode; debugAssertions: bool; debugInfo: bool; isTest: bool; outputKind: OutputKind; timestamp: uint64; date: Slice; time: Slice; } ``` `#build` is a compile-time value describing the profile and output of the current build. Read its fields in a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) to compile differently by mode, or embed the build date and time in a program. ## Fields | Field | Type | Description | | ----------------- | ------------------ | --------------------------------------------------------------------------------------- | | `profile` | `Slice` | Name of the active build profile. | | `mode` | `BuildMode` | `Debug` or `Release`. | | `optimization` | `OptimizationMode` | `None`, `Size`, or `Speed`. | | `debugAssertions` | `bool` | Whether [`DebugAssert`](https://rux-lang.dev/docs/api/core/assert) checks are compiled. | | `debugInfo` | `bool` | Whether debug information is emitted. | | `isTest` | `bool` | Whether this is a test build. | | `outputKind` | `OutputKind` | `Executable`, `SharedLibrary`, `StaticLibrary`, or `SourceLibrary`. | | `timestamp` | `uint64` | Build time as a Unix timestamp. | | `date` | `Slice` | Build date as text. | | `time` | `Slice` | Build time of day as text. | ## Enums ```rux enum BuildMode: uint8 { Debug = 0, Release = 1 } enum OptimizationMode: uint8 { None = 0, Size = 1, Speed = 2 } enum OutputKind: uint8 { Executable = 0, SharedLibrary = 1, StaticLibrary = 2, SourceLibrary = 3 } ``` A standalone SourceLibrary check reports `SourceLibrary`. Source compiled into a dependent package reports the consuming package's output kind. ## Example ```rux import Core::{ #build }; import Io::PrintLine; func Main() -> int { when #build.mode { .Debug => PrintLine("debug build"), .Release => PrintLine("release build") } return 0; } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`#target`](https://rux-lang.dev/docs/api/core/target) / [`#compiler`](https://rux-lang.dev/docs/api/core/compiler) / [`#config`](https://rux-lang.dev/docs/api/core/config) / [`#source`](https://rux-lang.dev/docs/api/core/source) — the other compile-time values - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — using the build context in `when` # #compiler Compile-time information about the compiler performing the build. **Package:** `Rux` ## Definition ```rux intrinsic #compiler: Compiler; struct Compiler { version: SemanticVersion; } extend Compiler { intrinsic func HasFeature(self, feature: Slice) -> bool; } ``` `#compiler` is a compile-time value describing the compiler itself: its `version`, and a `HasFeature` query for capabilities that a program may want to gate on. ## `SemanticVersion` ```rux struct SemanticVersion { major: uint; minor: uint; patch: uint; } ``` `SemanticVersion` carries the three numeric components of a semantic version and orders them by precedence. It provides `New`, a `Compare` returning `-1`, `0`, or `1`, the named comparisons `IsEqualTo` / `IsLessThan` / `IsAtMost` / `IsGreaterThan` / `IsAtLeast`, and the comparison operators `==`, `!=`, `<`, `<=`, `>`, `>=`. ## Example ```rux import Core::{ #compiler, SemanticVersion }; import Io::PrintLine; func Main() -> int { if #compiler.version >= SemanticVersion::New(0, 3, 0) { PrintLine("compiler is new enough"); } return 0; } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`#build`](https://rux-lang.dev/docs/api/core/build) / [`#target`](https://rux-lang.dev/docs/api/core/target) / [`#config`](https://rux-lang.dev/docs/api/core/config) / [`#source`](https://rux-lang.dev/docs/api/core/source) — the other compile-time values - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — using the build context in `when` # #config Compile-time access to user-defined build values. **Package:** `Rux` ## Definition ```rux intrinsic #config: Config; struct Config {} extend Config { intrinsic func Get(self, name: Slice) -> Slice; intrinsic func Has(self, name: Slice) -> bool; } ``` `#config` is a compile-time value exposing the definitions supplied through the manifest's `[Build.Defines]` table and the `--define` command-line flag. `Has` reports whether a name is defined and `Get` returns its value as text, so a program can specialize at build time on values the build passes in. ## Methods | Method | Returns | Description | | ------------------------- | -------------- | -------------------------------------- | | `Get(name: Slice)` | `Slice` | The value defined for `name`, as text. | | `Has(name: Slice)` | `bool` | Whether `name` is defined. | ## Example ```rux import Core::{ #config }; import Io::PrintLine; func Main() -> int { when #config.Has("FeatureX") { PrintLine("FeatureX is enabled"); } return 0; } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`#build`](https://rux-lang.dev/docs/api/core/build) / [`#compiler`](https://rux-lang.dev/docs/api/core/compiler) / [`#target`](https://rux-lang.dev/docs/api/core/target) / [`#source`](https://rux-lang.dev/docs/api/core/source) — the other compile-time values - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — using the build context in `when` # #Error Emits a compilation error. **Package:** `Rux` ## Signature ```rux intrinsic func #Error(message: Slice); ``` ## Description `#Error` stops compilation and reports `message` as a compile-time error. It runs at compile time, not run time, so it is the way to reject an unsupported configuration while the program is being built — most often the `else` arm of a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) over the target, so that an unsupported platform fails to build rather than misbehaving. ## Example ```rux import Core::{ #target, #Error }; when #target.os { .Linux => { /* ... */ }, .Windows => { /* ... */ }, else => #Error("Unsupported operating system") } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`#Warn`](https://rux-lang.dev/docs/api/core/warn) — report a compile-time warning without stopping the build - [`#target`](https://rux-lang.dev/docs/api/core/target) — the target that is usually being checked - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — `when` and the build context # Core Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: The core language package: the fundamental types and the compiler intrinsics every program can rely on. **Package:** `Core` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Core](https://github.com/rux-lang/Rux/tree/main/Packages/Core){rel=""nofollow""} `Core` is the root of the package graph — every other package depends on it. It defines the primitive types, the fundamental generic types ([`Result`](https://rux-lang.dev/docs/api/core/result), [`Slice`](https://rux-lang.dev/docs/api/core/slice), the [ranges](https://rux-lang.dev/docs/api/core/ranges)), and the compiler intrinsics for diagnostics and for reading the build, compiler, target, and source context at compile time. Its symbols are available without an explicit dependency line. ## Installation `Core` is an implicit dependency of every package, so it needs no `rux add`. Import the symbols you use: ```rux import Core::{ Result, Slice }; import Core::{ #target, #Error }; ``` ## Types | Type | Description | | ----------------------------------------------------- | ------------------------------------------------ | | [`Result`](https://rux-lang.dev/docs/api/core/result) | The return type of an operation that can fail. | | [`Slice`](https://rux-lang.dev/docs/api/core/slice) | A view over a contiguous sequence of elements. | | [`Ranges`](https://rux-lang.dev/docs/api/core/ranges) | The range types produced by the range operators. | The package also defines the primitive types — the [signed](https://rux-lang.dev/docs/lang/types/integers) and [unsigned](https://rux-lang.dev/docs/lang/types/integers) integers, [floating-point](https://rux-lang.dev/docs/lang/types/floating-point), [boolean](https://rux-lang.dev/docs/lang/types/booleans), and [character](https://rux-lang.dev/docs/lang/types/characters) families — which are covered in the language reference. ## Diagnostics | Function | Description | | ----------------------------------------------------- | ---------------------------------------------------- | | [`Assert`](https://rux-lang.dev/docs/api/core/assert) | Check a condition at run time, aborting if it fails. | | [`Panic`](https://rux-lang.dev/docs/api/core/panic) | Terminate the program immediately with a message. | | [`#Error`](https://rux-lang.dev/docs/api/core/error) | Emit a compilation error. | | [`#Warn`](https://rux-lang.dev/docs/api/core/warn) | Emit a compilation warning. | ## Compile-time context | Value | Description | | ---------------------------------------------------------- | ----------------------------------------------------- | | [`#target`](https://rux-lang.dev/docs/api/core/target) | Information about the compilation target. | | [`#build`](https://rux-lang.dev/docs/api/core/build) | Information about the active build. | | [`#compiler`](https://rux-lang.dev/docs/api/core/compiler) | Information about the compiler performing the build. | | [`#config`](https://rux-lang.dev/docs/api/core/config) | User-defined values from the manifest and `--define`. | | [`#source`](https://rux-lang.dev/docs/api/core/source) | Location of the expression that reads it. | ## See also - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — using the compile-time context in `when` - [The `Result` Type](https://rux-lang.dev/docs/lang/errors/overview) — the recoverable-error model - [Slices](https://rux-lang.dev/docs/lang/slices/overview) and [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) — the language-reference treatment # Panic Terminates the program immediately with a message. **Package:** `Rux` ## Signature ```rux intrinsic func Panic(message: Slice); ``` ## Description `Panic` prints `message` and stops the program at once. It does not return. Reach for it on a condition a caller is not expected to recover from — a broken invariant, an unreachable branch, a state the program cannot continue from. For failures a caller *should* handle, return a [`Result`](https://rux-lang.dev/docs/api/core/result) instead. `Panic` is a compiler intrinsic, so it is available without a run-time library and can be used from the lowest levels of a program. ## Example ```rux import Core::Panic; func Main() -> int { let ok = false; if !ok { Panic("unreachable state reached"); } return 0; } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`Assert`](https://rux-lang.dev/docs/api/core/assert) — a conditional panic on a checked invariant - [`Result`](https://rux-lang.dev/docs/api/core/result) — the recoverable-error alternative - [Fatal Errors](https://rux-lang.dev/docs/lang/errors/panics) — when to panic versus return an error # Ranges The range types produced by the range operators. **Package:** `Rux` Each range operator builds one of these structs. A range with a start is iterable in a [`for`](https://rux-lang.dev/docs/lang/statements/loops#for) loop; every range can slice a collection. ## Definitions ```rux struct Range { // a..b — start up to, excluding, end start: T; end: T; } struct RangeInclusive { // a..=b — start up to, including, end start: T; end: T; } struct RangeFrom { // a.. — start onward start: T; } struct RangeTo { // ..b — up to, excluding, end end: T; } struct RangeToInclusive { // ..=b — up to, including, end end: T; } struct RangeFull {} // .. — the whole extent ``` | Type | Operator | Bounds | Iterable | | --------------------- | -------- | ---------------------------------- | :------: | | `Range` | `a..b` | `start` inclusive, `end` exclusive | ✓ | | `RangeInclusive` | `a..=b` | both inclusive | ✓ | | `RangeFrom` | `a..` | `start` inclusive, unbounded end | ✓ | | `RangeTo` | `..b` | unbounded start, `end` exclusive | — | | `RangeToInclusive` | `..=b` | unbounded start, `end` inclusive | — | | `RangeFull` | `..` | the whole extent | — | The start-bearing ranges (`Range`, `RangeInclusive`, `RangeFrom`) can drive a `for` loop; the startless ones are for slicing only. ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [Ranges](https://rux-lang.dev/docs/lang/ranges/overview) — the language-reference treatment - [`Slice`](https://rux-lang.dev/docs/api/core/slice) — what range-indexing a collection yields # Result The return type of an operation that can fail. **Package:** `Rux` ## Definition ```rux enum Result { Success(T), Error(E) } ``` A `Result` holds either a `Success` carrying the value `T` an operation produced, or an `Error` carrying the failure `E`. Because the error is part of the return type, a caller cannot read the value without acknowledging that the call might have failed — the usual way is to [`match`](https://rux-lang.dev/docs/lang/patterns/match) on the two variants. ```rux import Core::Result; match ParseInt64("42") { .Success(n) => PrintLine(n), .Error(e) => PrintLine("bad input") } ``` `Result` is the recoverable-error half of Rux's error model; for conditions a caller is not expected to recover from, see [`Panic`](https://rux-lang.dev/docs/api/core/panic). ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [The `Result` Type](https://rux-lang.dev/docs/lang/errors/overview) — the language-reference treatment - [`match`](https://rux-lang.dev/docs/lang/patterns/match) — destructuring `Success` and `Error` - [`Panic`](https://rux-lang.dev/docs/api/core/panic) — for unrecoverable errors # Slice A view over a contiguous sequence of elements. **Package:** `Rux` ## Definition ```rux struct Slice { data: *T; length: uint; } ``` A `Slice` is a fat pointer — a `data` pointer paired with a `length` — that records *where* a run of `T` elements lives and *how many* there are. It does not own the elements; the storage belongs to an array, a heap allocation, or a static in the binary. A string literal is a `Slice`, and an array coerces to a slice. The `data` field is a read-only `*T`. Reach for a slice whenever a function needs to take a sequence of elements without caring where they are stored. ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [Slices](https://rux-lang.dev/docs/lang/slices/overview) — the language-reference treatment - [`Range`](https://rux-lang.dev/docs/api/core/ranges) — indexing a slice with a range produces another slice # #source Compile-time location of the expression that reads it. **Package:** `Rux` ## Definition ```rux intrinsic #source: Source; struct Source { line: uint; column: uint; file: Slice; fileName: Slice; filePath: Slice; function: Slice; module: Slice; } ``` `#source` is a compile-time value describing where in the code it is read. Each reference resolves to the position of that reference, which makes it useful for logging, assertions, and diagnostics that want to report their own location. ## Fields | Field | Type | Description | | ---------- | -------------- | ------------------------------- | | `line` | `uint` | Line number of the reference. | | `column` | `uint` | Column number of the reference. | | `file` | `Slice` | Full file identifier. | | `fileName` | `Slice` | File name alone. | | `filePath` | `Slice` | Path to the file. | | `function` | `Slice` | Enclosing function's name. | | `module` | `Slice` | Enclosing module's name. | ## Example ```rux import Core::{ #source }; import Io::PrintLine; func Main() -> int { PrintLine("{}:{}", #source.fileName, #source.line); return 0; } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`#build`](https://rux-lang.dev/docs/api/core/build) / [`#compiler`](https://rux-lang.dev/docs/api/core/compiler) / [`#config`](https://rux-lang.dev/docs/api/core/config) / [`#target`](https://rux-lang.dev/docs/api/core/target) — the other compile-time values - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — the compile-time context # #target Compile-time information about the compilation target. **Package:** `Rux` ## Definition ```rux intrinsic #target: Target; struct Target { os: OperatingSystem; arch: Architecture; abi: ApplicationBinaryInterface; endian: Endianness; pointerBits: uint; dataModel: DataModel; objectFormat: ObjectFormat; triple: Slice; } extend Target { intrinsic func HasFeature(self, feature: TargetFeature) -> bool; } ``` `#target` is a compile-time value describing the platform the code is being compiled for. It is the primary input to [conditional compilation](https://rux-lang.dev/docs/lang/comptime/conditional): match on `#target.os` or `#target.arch` in a [`when`](https://rux-lang.dev/docs/lang/comptime/conditional) to select platform-specific code, and use `HasFeature` to gate on CPU features. ## Fields | Field | Type | Description | | -------------- | ---------------------------- | ------------------------------- | | `os` | `OperatingSystem` | Target operating system. | | `arch` | `Architecture` | Target processor architecture. | | `abi` | `ApplicationBinaryInterface` | Calling convention and ABI. | | `endian` | `Endianness` | Byte order. | | `pointerBits` | `uint` | Width of a pointer in bits. | | `dataModel` | `DataModel` | Integer and pointer size model. | | `objectFormat` | `ObjectFormat` | Object-file format emitted. | | `triple` | `Slice` | Target triple as text. | `HasFeature(feature: TargetFeature) -> bool` reports whether the target enables a given CPU feature. ## Enums ```rux enum OperatingSystem: uint8 { Unknown, AIX, Android, DragonFlyBSD, FreeBSD, Fuchsia, Haiku, Illumos, IOS, Linux, MacOS, NetBSD, OpenBSD, QNX, Redox, Solaris, Windows } enum Architecture: uint8 { Unknown, ARM32, AArch64, RISCV32, RISCV64, X86Bit32, X86_64 } enum ApplicationBinaryInterface: uint8 { Unknown, AAPCS, AAPCS64, RiscvIlp32, RiscvLp64, SystemV, WindowsX64, WindowsX86 } enum TargetFeature: uint8 { AVX, AVX2, AVX512, NEON, RVV, SSE2, SSE3, SSE41, SSE42, SSSE3, SVE } enum Endianness: uint8 { Big, Little } enum DataModel: uint8 { Unknown, ILP32, LLP64, LP64 } enum ObjectFormat: uint8 { Unknown, COFF, ELF, MachO, Wasm } ``` ## Example ```rux import Core::{ #target }; func Main() -> int { when #target.os { .Linux => { /* Linux path */ }, .Windows => { /* Windows path */ }, else => {} } return 0; } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`#build`](https://rux-lang.dev/docs/api/core/build) / [`#compiler`](https://rux-lang.dev/docs/api/core/compiler) / [`#config`](https://rux-lang.dev/docs/api/core/config) / [`#source`](https://rux-lang.dev/docs/api/core/source) — the other compile-time values - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — selecting code by target # #Warn Emits a compilation warning. **Package:** `Rux` ## Signature ```rux intrinsic func #Warn(message: Slice); ``` ## Description `#Warn` reports `message` as a compile-time warning and lets compilation continue. Use it to flag a questionable but permitted configuration — a deprecated path, an untested target — without failing the build the way [`#Error`](https://rux-lang.dev/docs/api/core/error) does. ## Example ```rux import Core::{ #target, #Warn }; when #target.arch { .X86_64 => { /* optimized path */ }, else => #Warn("using the portable fallback for this architecture") } ``` ## See also - [`Core`](https://rux-lang.dev/docs/api/core) — the package overview - [`#Error`](https://rux-lang.dev/docs/api/core/error) — stop the build instead of warning - [Conditional Compilation](https://rux-lang.dev/docs/lang/comptime/conditional) — `when` and the build context # At Returns the byte at an index. **Package:** `Text` ## Signature ```rux func At(self, index: uint) -> char8; ``` ## Parameters | Name | Type | Description | | ------- | ------ | --------------------------------- | | `index` | `uint` | The offset of the byte, in bytes. | ## Returns The byte at `index`, or the null character (`'\0'`) when `index` is at or past [`Length`](https://rux-lang.dev/docs/api/text/string/length) — an out of range read reports rather than running off the end of the block. A null character is therefore ambiguous: it is also what a string that really holds one reports. The index is a **byte** offset, not a character offset, so indexing into a multi-byte UTF-8 sequence yields one byte of it rather than the character. ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Rux"); text.At(0); // 'R' text.At(2); // 'x' text.At(9); // '\0' -- out of range text.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`Length`](https://rux-lang.dev/docs/api/text/string/length) — the bound the index is checked against - [`Substring`](https://rux-lang.dev/docs/api/text/string/substring) — copy a range of bytes rather than read one - [`Data`](https://rux-lang.dev/docs/api/text/string/data) — the bytes without the bounds check # Clone Returns an independent copy of the string. **Package:** `Text` ## Signature ```rux func Clone(self) -> String; ``` ## Returns A `String` holding the same bytes in a block of its own. Cloning the empty string allocates nothing. Assignment copies the struct, not the block, so two `String` values can name the same allocation — freeing both is a double free. `Clone` is what to reach for when both of them have to be freed, and each copy is then released with its own [`Free`](https://rux-lang.dev/docs/api/text/string/free). ## Example ```rux import Text::String; func Main() -> int { var original = String::From("Rux"); var shared = original; // same block; free one of them, not both var copy = original.Clone(); // its own block copy == original; // true -- Equals compares bytes, not addresses copy.Free(); original.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`Free`](https://rux-lang.dev/docs/api/text/string/free) — release the block a copy owns - [`Equals`](https://rux-lang.dev/docs/api/text/string/equals) — compare two strings by their contents # Contains Reports whether a substring occurs anywhere in the string. **Package:** `Text` ## Signature ```rux func Contains(self, needle: String) -> bool; ``` ## Parameters | Name | Type | Description | | -------- | -------- | ------------------------ | | `needle` | `String` | The bytes to search for. | ## Returns `true` when the bytes of `needle` occur anywhere in the string. The empty needle is contained in every string, and a needle longer than the string is contained in none. This is [`IndexOf`](https://rux-lang.dev/docs/api/text/string/indexof) with the offset thrown away, and it costs the same — ask [`IndexOf`](https://rux-lang.dev/docs/api/text/string/indexof) instead when the position matters. ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Hello, Rux!"); var needle = String::From("Rux"); text.Contains(needle); // true needle.Free(); text.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`IndexOf`](https://rux-lang.dev/docs/api/text/string/indexof) — the same search, reporting where the match is - [`StartsWith`](https://rux-lang.dev/docs/api/text/string/startswith) — the cheaper test when the match has to be at the front # Data Returns the pointer to the underlying bytes. **Package:** `Text` ## Signature ```rux func Data(self) -> *char8; ``` ## Returns A pointer to the first byte of the block, or `null` for the empty string. The block stays the string's to free — this hands out the bytes, not ownership of them. It carries **no null terminator**, so pair it with [`Length`](https://rux-lang.dev/docs/api/text/string/length) rather than letting anything scan for a terminator that is not there. The string is immutable, and the pointer stays valid until the string is passed to [`Free`](https://rux-lang.dev/docs/api/text/string/free). ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Rux"); let bytes = text.Data(); let count = text.Length(); // 3 -- the bytes end here, not at a null text.Free(); // `bytes` is now dangling return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`Length`](https://rux-lang.dev/docs/api/text/string/length) — how many bytes the pointer covers - [`At`](https://rux-lang.dev/docs/api/text/string/at) — read one byte with a bounds check # EndsWith Reports whether the string closes with a suffix. **Package:** `Text` ## Signature ```rux func EndsWith(self, suffix: String) -> bool; ``` ## Parameters | Name | Type | Description | | -------- | -------- | --------------------------------- | | `suffix` | `String` | The bytes to look for at the end. | ## Returns `true` when the string's last bytes are the bytes of `suffix`. Every string ends with the empty one, and none ends with a suffix longer than itself. The comparison is byte for byte and case-sensitive, on the same terms as [`StartsWith`](https://rux-lang.dev/docs/api/text/string/startswith). ## Example ```rux import Text::String; func Main() -> int { var file = String::From("main.rux"); var ext = String::From(".rux"); file.EndsWith(ext); // true ext.Free(); file.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`StartsWith`](https://rux-lang.dev/docs/api/text/string/startswith) — the same test at the other end - [`Contains`](https://rux-lang.dev/docs/api/text/string/contains) — look for the bytes anywhere # Equals Reports whether two strings hold the same bytes. **Package:** `Text` ## Signature ```rux func Equals(self, other: String) -> bool; func ==(self, other: String) -> bool; func !=(self, other: String) -> bool; ``` ## Parameters | Name | Type | Description | | ------- | -------- | ------------------------- | | `other` | `String` | The string to compare to. | ## Returns `true` when both strings hold the same bytes. The comparison is on **contents, not addresses**: two strings holding the same bytes in separate blocks are equal, and so are two empty strings. Strings of different lengths are never equal, which is checked first, so the bytes are only walked when the lengths match. The `==` and `!=` operators route to `Equals`, so they compare the same way. The comparison is byte for byte. It does not fold case and does not normalize UTF-8, so two strings that a reader would call the same can still differ here. ## Example ```rux import Text::String; func Main() -> int { var a = String::From("Rux"); var b = String::From("Rux"); // a separate block, the same bytes var c = String::From("rux"); a.Equals(b); // true a == b; // true a != c; // true -- the comparison is case-sensitive c.Free(); b.Free(); a.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`StartsWith`](https://rux-lang.dev/docs/api/text/string/startswith) — compare only the front - [`Contains`](https://rux-lang.dev/docs/api/text/string/contains) — look for the bytes anywhere - [`Memory::Compare`](https://rux-lang.dev/docs/api/memory/compare) — the comparison this is built on # Free Releases the block the string owns. **Package:** `Text` ## Signature ```rux func Free(self); ``` ## Remarks There are no destructors, so ownership is released by hand: every `String` this package returns has to be freed exactly once. The empty string holds a null block, and [`Memory::Free`](https://rux-lang.dev/docs/api/memory/free) ignores null, which makes this safe to call on any `String` at all. It leaves an empty string behind — [`Data`](https://rux-lang.dev/docs/api/text/string/data) is `null` and [`Length`](https://rux-lang.dev/docs/api/text/string/length) is `0` — so a second call is a no-op rather than a double free, and the value stays usable as the empty string. What it cannot see is a block shared by two `String` values, which is what plain assignment produces. Freeing each of them **is** a double free; take a [`Clone`](https://rux-lang.dev/docs/api/text/string/clone) when both have to be released. ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Rux"); text.Free(); text.IsEmpty(); // true text.Free(); // harmless -- there is nothing left to release return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`Clone`](https://rux-lang.dev/docs/api/text/string/clone) — take a copy that can be freed on its own - [`Memory::Free`](https://rux-lang.dev/docs/api/memory/free) — the release this is built on # From Creates a string by copying bytes. **Package:** `Text` ## Signature ```rux func From(str: *char8, length: uint) -> String; func From(str: Slice) -> String; ``` ## Parameters | Name | Type | Description | | -------- | ------------------------- | --------------------------------------------- | | `str` | `*char8` / `Slice` | The bytes to copy. | | `length` | `uint` | How many bytes to copy, for the raw overload. | ## Returns A `String` owning a block of its own, holding a copy of the bytes. A `length` of `0`, or an empty slice, allocates nothing and yields the empty string. The slice overload is how a string literal becomes a `String`: the literal is copied, and the read-only page it lives on is left alone. The raw overload is the entry point for bytes that are already in memory — the caller vouches that `length` of them are really there, and the block behind `str` stays the caller's to keep or release. The result has to be released with [`Free`](https://rux-lang.dev/docs/api/text/string/free). ## Example ```rux import Text::String; func Main() -> int { var literal = String::From("Hello, Rux!"); literal.Length(); // 11 var raw = String::From(literal.Data(), 5); // "Hello" raw.Free(); literal.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`New`](https://rux-lang.dev/docs/api/text/string/new) — the empty string, without a copy - [`Clone`](https://rux-lang.dev/docs/api/text/string/clone) — copy a `String` rather than a buffer - [`Free`](https://rux-lang.dev/docs/api/text/string/free) — release the block when the string is finished with # IndexOf Returns the offset of the first occurrence of a substring. **Package:** `Text` ## Signature ```rux func IndexOf(self, needle: String) -> int; ``` ## Parameters | Name | Type | Description | | -------- | -------- | ------------------------ | | `needle` | `String` | The bytes to search for. | ## Returns The byte offset of the first match, or `-1` when there is none. The empty needle matches at the front and reports `0`, and a needle longer than the string never matches. The offset is in **bytes**, so it can be handed straight to [`Substring`](https://rux-lang.dev/docs/api/text/string/substring), and it can land inside a multi-byte UTF-8 sequence when the needle does. The scan is the plain one — every starting offset is tried in turn — so the worst case costs the product of the two lengths. Reach for [`Contains`](https://rux-lang.dev/docs/api/text/string/contains) when only the answer matters and not the position. ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Hello, Rux!"); var needle = String::From("Rux"); var missing = String::From("C"); text.IndexOf(needle); // 7 text.IndexOf(missing); // -1 missing.Free(); needle.Free(); text.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`Contains`](https://rux-lang.dev/docs/api/text/string/contains) — the same search when the position does not matter - [`Substring`](https://rux-lang.dev/docs/api/text/string/substring) — copy the bytes the offset points at - [`StartsWith`](https://rux-lang.dev/docs/api/text/string/startswith) — the cheaper test when the match has to be at the front # IsEmpty Reports whether the string holds no bytes. **Package:** `Text` ## Signature ```rux func IsEmpty(self) -> bool; ``` ## Returns `true` when the string holds no bytes, which is the case for [`New`](https://rux-lang.dev/docs/api/text/string/new), for anything [`Free`](https://rux-lang.dev/docs/api/text/string/free) has left behind, and for a transformation that had nothing to give back. ## Example ```rux import Text::String; func Main() -> int { String::New().IsEmpty(); // true var text = String::From("Rux"); text.IsEmpty(); // false text.Free(); text.IsEmpty(); // true -- Free leaves an empty string behind return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`Length`](https://rux-lang.dev/docs/api/text/string/length) — the byte count this tests against zero - [`New`](https://rux-lang.dev/docs/api/text/string/new) — the empty string # Length Returns the length of the string in bytes. **Package:** `Text` ## Signature ```rux func Length(self) -> uint; ``` ## Returns The number of bytes the string holds, and `0` for the empty string. This counts **bytes, not characters**: a multi-byte UTF-8 sequence counts once per byte, so the length of a string is not the number of characters a reader would see in it. ## Example ```rux import Text::String; func Main() -> int { var ascii = String::From("Rux"); ascii.Length(); // 3 var utf8 = String::From("héllo"); utf8.Length(); // 6 -- five characters, six bytes utf8.Free(); ascii.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`IsEmpty`](https://rux-lang.dev/docs/api/text/string/isempty) — the `Length() == 0` case - [`Data`](https://rux-lang.dev/docs/api/text/string/data) — the bytes this length covers # New Creates an empty string. **Package:** `Text` ## Signature ```rux func New() -> String; ``` ## Returns An empty `String`. It owns no block, so this allocates nothing, and every method treats it as a string of length zero rather than as a missing value: [`Length`](https://rux-lang.dev/docs/api/text/string/length) is `0`, [`IsEmpty`](https://rux-lang.dev/docs/api/text/string/isempty) is `true`, [`Data`](https://rux-lang.dev/docs/api/text/string/data) is `null`, and [`Free`](https://rux-lang.dev/docs/api/text/string/free) is a no-op. It is also what the transformations return when there is nothing to give back — [`Substring`](https://rux-lang.dev/docs/api/text/string/substring) past the end, [`Trim`](https://rux-lang.dev/docs/api/text/string/trim) of nothing but whitespace, [`Repeat`](https://rux-lang.dev/docs/api/text/string/repeat) zero times. ## Example ```rux import Text::String; func Main() -> int { var empty = String::New(); empty.IsEmpty(); // true empty.Length(); // 0 empty.Free(); // safe, and not required return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`From`](https://rux-lang.dev/docs/api/text/string/from) — create a string that holds bytes - [`IsEmpty`](https://rux-lang.dev/docs/api/text/string/isempty) — test for the empty string # Repeat Returns a copy of the contents laid down N times. **Package:** `Text` ## Signature ```rux func Repeat(self, count: uint) -> String; ``` ## Parameters | Name | Type | Description | | ------- | ------ | ------------------------------------- | | `count` | `uint` | How many times to lay the bytes down. | ## Returns A new `String` holding the receiver's bytes `count` times over, in a block of its own. The receiver is left untouched. Repeating zero times, or repeating the empty string, yields the empty string. The block is sized up front — it goes through a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) built with [`WithCapacity`](https://rux-lang.dev/docs/api/text/stringbuilder/withcapacity) — so this costs **one** allocation rather than one per round. ## Example ```rux import Text::String; func Main() -> int { var dash = String::From("-"); var rule = dash.Repeat(20); // "--------------------" rule.Length(); // 20 rule.Free(); dash.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`+`](https://rux-lang.dev/docs/api/text/string/plus) — join two different strings - [`StringBuilder::WithCapacity`](https://rux-lang.dev/docs/api/text/stringbuilder/withcapacity) — the one allocation this leans on # StartsWith Reports whether the string opens with a prefix. **Package:** `Text` ## Signature ```rux func StartsWith(self, prefix: String) -> bool; ``` ## Parameters | Name | Type | Description | | -------- | -------- | ----------------------------------- | | `prefix` | `String` | The bytes to look for at the front. | ## Returns `true` when the string's first bytes are the bytes of `prefix`. Every string starts with the empty one, and none starts with a prefix longer than itself. The comparison is byte for byte and case-sensitive, on the same terms as [`Equals`](https://rux-lang.dev/docs/api/text/string/equals). ## Example ```rux import Text::String; func Main() -> int { var path = String::From("/usr/local/bin"); var root = String::From("/usr"); path.StartsWith(root); // true path.StartsWith(String::New()); // true -- the empty prefix always matches root.Free(); path.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`EndsWith`](https://rux-lang.dev/docs/api/text/string/endswith) — the same test at the other end - [`IndexOf`](https://rux-lang.dev/docs/api/text/string/indexof) — find the bytes anywhere, not only at the front - [`Equals`](https://rux-lang.dev/docs/api/text/string/equals) — compare the whole string # Substring Returns a copy of the bytes in a range. **Package:** `Text` ## Signature ```rux func Substring( self, start: uint, length: uint ) -> String; ``` ## Parameters | Name | Type | Description | | -------- | ------ | ----------------------------- | | `start` | `uint` | The byte offset to copy from. | | `length` | `uint` | How many bytes to copy. | ## Returns A new `String` holding `length` bytes from `start`, in a block of its own. The receiver is left untouched. A range that runs past the end is **clamped** to what is there, so the result is never longer than the receiver and a length past the end is not an error. A `start` at or past the end yields the empty string, and so does a `length` of `0`. Both arguments are in **bytes**, so a range that begins or ends inside a multi-byte UTF-8 sequence cuts it in half. ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Hello, Rux!"); var hello = text.Substring(0, 5); // "Hello" var rux = text.Substring(7, 3); // "Rux" var tail = text.Substring(7, 999); // "Rux!" -- clamped to what is there var none = text.Substring(99, 3); // "" -- start is past the end none.Free(); tail.Free(); rux.Free(); hello.Free(); text.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`IndexOf`](https://rux-lang.dev/docs/api/text/string/indexof) — find the offset to copy from - [`At`](https://rux-lang.dev/docs/api/text/string/at) — read one byte instead of copying a range - [`Trim`](https://rux-lang.dev/docs/api/text/string/trim) — the common case of dropping the ends # ToLower Returns a copy with ASCII letters lowercased. **Package:** `Text` ## Signature ```rux func ToLower(self) -> String; ``` ## Returns A new `String` in which every byte from `A` to `Z` is lowercased, in a block of its own. The receiver is left untouched, and the empty string yields the empty string. The fold is **ASCII only**, on the same terms as [`ToUpper`](https://rux-lang.dev/docs/api/text/string/toupper): every other byte is copied through untouched, so the multi-byte sequences of a UTF-8 string survive intact but do not case fold. ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Hello, Rux!"); var quiet = text.ToLower(); // "hello, rux!" quiet.Free(); text.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`ToUpper`](https://rux-lang.dev/docs/api/text/string/toupper) — the fold in the other direction - [`Equals`](https://rux-lang.dev/docs/api/text/string/equals) — a comparison that does not fold case # ToUpper Returns a copy with ASCII letters uppercased. **Package:** `Text` ## Signature ```rux func ToUpper(self) -> String; ``` ## Returns A new `String` in which every byte from `a` to `z` is uppercased, in a block of its own. The receiver is left untouched, and the empty string yields the empty string. The fold is **ASCII only**. Every other byte is copied through untouched, so the multi-byte sequences of a UTF-8 string survive intact but do not case fold — `é` comes back as `é`, not `É`. ## Example ```rux import Text::String; func Main() -> int { var text = String::From("Hello, Rux!"); var shout = text.ToUpper(); // "HELLO, RUX!" shout.Free(); text.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`ToLower`](https://rux-lang.dev/docs/api/text/string/tolower) — the fold in the other direction - [`Equals`](https://rux-lang.dev/docs/api/text/string/equals) — a comparison that does not fold case # Trim Returns a copy with the surrounding whitespace dropped. **Package:** `Text` ## Signature ```rux func Trim(self) -> String; ``` ## Returns A new `String` with the leading and trailing whitespace removed, in a block of its own. The receiver is left untouched. Only the ends are trimmed — whitespace inside the string is kept. A string that is nothing but whitespace trims down to the empty string, and so does the empty string. Whitespace is space, tab, newline, and carriage return, which is exactly what [`IsSpace`](https://rux-lang.dev/docs/api/text/isspace) reports. ## Example ```rux import Text::String; func Main() -> int { var padded = String::From(" Hello, Rux! \n"); var text = padded.Trim(); // "Hello, Rux!" text.Length(); // 11 text.Free(); padded.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`IsSpace`](https://rux-lang.dev/docs/api/text/isspace) — the bytes this treats as whitespace - [`Substring`](https://rux-lang.dev/docs/api/text/string/substring) — drop bytes from the ends by offset instead # + Joins two strings into a new one. **Package:** `Text` ## Signature ```rux func +(self, other: String) -> String; func +(self, other: Slice) -> String; ``` ## Parameters | Name | Type | Description | | ------- | ------------------------- | -------------------- | | `other` | `String` / `Slice` | The bytes to append. | ## Returns A new `String` holding the bytes of both operands, in a block of its own. Both operands are left as they were, and joining two empty strings allocates nothing. The slice overload lets a literal be joined on without wrapping it in a `String` first. The result owns its block and has to be released with [`Free`](https://rux-lang.dev/docs/api/text/string/free) — including the intermediate results of a chain, which is what makes `a + b + c` three allocations and two leaks if only the last one is freed. In a loop it is a chain of allocations: accumulate with a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) instead. ## Example ```rux import Text::String; func Main() -> int { var greeting = String::From("Hello"); var name = String::From("Rux"); var comma = greeting + ", "; // "Hello, " var full = comma + name; // "Hello, Rux" full.Free(); comma.Free(); // the intermediate owns a block too name.Free(); greeting.Free(); return 0; } ``` ## See also - [`String`](https://rux-lang.dev/docs/api/text/string) — the string type - [`StringBuilder::Append`](https://rux-lang.dev/docs/api/text/stringbuilder/append) — join without allocating on every step - [`Repeat`](https://rux-lang.dev/docs/api/text/string/repeat) — join a string to itself N times, in one allocation - [`Free`](https://rux-lang.dev/docs/api/text/string/free) — release the block the result owns # String An immutable, heap-allocated string. **Package:** `Text` ## Struct ```rux struct String { data: *char8; length: uint; } ``` A `String` owns the block of `length` bytes behind `data` and never writes to it again: every transformation below returns a fresh `String` and leaves the receiver untouched. The fields are an implementation detail — read them with [`Data`](https://rux-lang.dev/docs/api/text/string/data) and [`Length`](https://rux-lang.dev/docs/api/text/string/length). The bytes are not terminated by a null, and `length` counts bytes rather than characters, so a multi-byte UTF-8 sequence counts for more than one. ## Ownership Assignment copies the struct, not the block, so two `String` values can name the same allocation. Take [`Clone`](https://rux-lang.dev/docs/api/text/string/clone) when both of them have to be freed, and pass each one to [`Free`](https://rux-lang.dev/docs/api/text/string/free) exactly once. The empty `String` owns no block, which is why [`New`](https://rux-lang.dev/docs/api/text/string/new) allocates nothing and [`Free`](https://rux-lang.dev/docs/api/text/string/free) can be called on anything this package returns. Because each transformation allocates, `+` in a loop is a chain of allocations. Accumulate with a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) instead. ## Methods ### Construction | Method | Description | | ---------------------------------------------------------- | -------------------------------------------------- | | [`New`](https://rux-lang.dev/docs/api/text/string/new) | Creates an empty string, without allocating. | | [`From`](https://rux-lang.dev/docs/api/text/string/from) | Creates a string by copying a literal or a buffer. | | [`Clone`](https://rux-lang.dev/docs/api/text/string/clone) | Returns an independent copy, with its own block. | | [`Free`](https://rux-lang.dev/docs/api/text/string/free) | Releases the block the string owns. | ### Accessors | Method | Description | | -------------------------------------------------------------- | ------------------------------------ | | [`Data`](https://rux-lang.dev/docs/api/text/string/data) | The pointer to the underlying bytes. | | [`Length`](https://rux-lang.dev/docs/api/text/string/length) | The length in bytes. | | [`IsEmpty`](https://rux-lang.dev/docs/api/text/string/isempty) | Whether the string holds no bytes. | | [`At`](https://rux-lang.dev/docs/api/text/string/at) | The byte at an index. | ### Comparison | Method | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | | [`Equals`](https://rux-lang.dev/docs/api/text/string/equals) | Whether two strings hold the same bytes. Also `==` and `!=`. | ### Search | Method | Description | | -------------------------------------------------------------------- | ---------------------------------------- | | [`StartsWith`](https://rux-lang.dev/docs/api/text/string/startswith) | Whether the string opens with a prefix. | | [`EndsWith`](https://rux-lang.dev/docs/api/text/string/endswith) | Whether the string closes with a suffix. | | [`IndexOf`](https://rux-lang.dev/docs/api/text/string/indexof) | The offset of the first match, or `-1`. | | [`Contains`](https://rux-lang.dev/docs/api/text/string/contains) | Whether a substring occurs anywhere. | ### Transformation Each returns a **new** `String` and leaves the receiver unchanged. | Method | Description | | ------------------------------------------------------------------ | --------------------------------------------- | | [`+`](https://rux-lang.dev/docs/api/text/string/plus) | Joins two strings, or a string and a literal. | | [`Substring`](https://rux-lang.dev/docs/api/text/string/substring) | A copy of the bytes in a range. | | [`ToUpper`](https://rux-lang.dev/docs/api/text/string/toupper) | A copy with ASCII letters uppercased. | | [`ToLower`](https://rux-lang.dev/docs/api/text/string/tolower) | A copy with ASCII letters lowercased. | | [`Trim`](https://rux-lang.dev/docs/api/text/string/trim) | A copy with surrounding whitespace dropped. | | [`Repeat`](https://rux-lang.dev/docs/api/text/string/repeat) | A copy of the contents laid down N times. | ## Example ```rux import Io::PrintLine; import Text::String; func Main() -> int { var greeting = String::From("Hello, Rux!"); var needle = String::From("Rux"); PrintLine(greeting.Length()); // 11 PrintLine(greeting.Contains(needle)); // true PrintLine(greeting.IndexOf(needle)); // 7 var shout = greeting.ToUpper(); // "HELLO, RUX!" shout.Free(); needle.Free(); greeting.Free(); return 0; } ``` ## See also - [`Text`](https://rux-lang.dev/docs/api/text) — the package overview - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — build a string without allocating on every step # Append Writes bytes to the end of the builder. **Package:** `Text` ## Signature ```rux func Append(self, ch: char8); func Append(self, str: Slice); func Append(self, str: String); ``` ## Parameters | Name | Type | Description | | ------------ | ----------------------------------- | ------------------- | | `ch` / `str` | `char8` / `Slice` / `String` | The bytes to write. | ## Remarks Writes the bytes at the end of what is already there, growing the block if they do not fit — the growth doubles the capacity, so a run of appends costs an amortized constant rather than a reallocation each time. Appending nothing (an empty slice or an empty string) does nothing at all. The bytes are **copied**, so an appended [`String`](https://rux-lang.dev/docs/api/text/string) is left untouched and is still the caller's to free. The slice overload lets a literal be appended without wrapping it in a `String` first, and the `char8` overload writes a single byte. Growing may move the block, so a pointer taken from [`Data`](https://rux-lang.dev/docs/api/text/stringbuilder/data) before an append must not be used after one. ## Example ```rux import Text::{ String, StringBuilder }; func Main() -> int { var name = String::From("Rux"); var builder = StringBuilder::New(); builder.Append("Hello, "); // a literal builder.Append(name); // a String, copied builder.Append(c8'!'); // a single byte var greeting = builder.IntoString(); // "Hello, Rux!" greeting.Free(); name.Free(); // the append did not take it return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Reserve`](https://rux-lang.dev/docs/api/text/stringbuilder/reserve) — make room before a run of appends - [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) — take the result once the appends are done - [`String::+`](https://rux-lang.dev/docs/api/text/string/plus) — join two strings, at the cost of an allocation each time # Capacity Returns how many bytes fit before the block has to grow. **Package:** `Text` ## Signature ```rux func Capacity(self) -> uint; ``` ## Returns The size of the block in bytes, and `0` for a builder that has not taken one yet. Everything up to this can be written without reallocating; the first byte past it doubles the block. Capacity only ever grows on its own — [`Clear`](https://rux-lang.dev/docs/api/text/stringbuilder/clear) keeps it, and [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) is what hands it back. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Capacity(); // 0 builder.Append("Rux"); builder.Capacity(); // 16 -- the floor the first append takes builder.Clear(); builder.Capacity(); // 16 still -- Clear keeps the block builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) — how much of the capacity is used - [`Reserve`](https://rux-lang.dev/docs/api/text/stringbuilder/reserve) — ask for more room up front - [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) — hand back what is unused # Clear Forgets the contents but keeps the block. **Package:** `Text` ## Signature ```rux func Clear(self); ``` ## Remarks Sets [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) back to `0` while leaving the block and its [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) alone, so the builder can be filled again without a fresh allocation. This is what to reach for when a builder is reused in a loop. The bytes are not erased, only forgotten — they sit in the block until they are overwritten, and no method will read them back. Use [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) to release the block itself. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); for i in 0..3 { builder.Append("line"); // ... use builder.Data() and builder.Length() here builder.Clear(); // ready for the next round, same block } builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) — release the block rather than reuse it - [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) — the room this keeps hold of # Data Returns the pointer to the bytes written so far. **Package:** `Text` ## Signature ```rux func Data(self) -> *char8; ``` ## Returns A pointer to the first byte of the block, or `null` for a builder that has not taken one yet. Unlike [`String::Data`](https://rux-lang.dev/docs/api/text/string/data), this points at memory a later append **may move**: growing the block reallocates it. Do not hold the pointer across an [`Append`](https://rux-lang.dev/docs/api/text/stringbuilder/append), a [`Reserve`](https://rux-lang.dev/docs/api/text/stringbuilder/reserve), a [`Grow`](https://rux-lang.dev/docs/api/text/stringbuilder/grow), or a [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) — read it again afterwards. Only the first [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) bytes have been written; the rest of the capacity is uninitialized. There is no null terminator, so pair the pointer with [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length). ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Append("Rux"); let bytes = builder.Data(); // valid for Length() bytes builder.Append(" rocks"); // may reallocate -- `bytes` is now suspect builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) — how many of the bytes have been written - [`ToString`](https://rux-lang.dev/docs/api/text/stringbuilder/tostring) — take a copy that will not move - [`String::Data`](https://rux-lang.dev/docs/api/text/string/data) — the same pointer on a string, which never moves # Free Releases the block the builder owns. **Package:** `Text` ## Signature ```rux func Free(self); ``` ## Remarks There are no destructors, so ownership is released by hand, as it is for [`String::Free`](https://rux-lang.dev/docs/api/text/string/free). It leaves an empty builder behind — [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) and [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) back to `0` — so a second call is a no-op, and the builder stays usable and will take a fresh block on the next append. It is safe on a builder that owns nothing, which is what a builder is after [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) has given its block away. Calling it there releases nothing and is not a double free. To keep the block and only forget the contents, use [`Clear`](https://rux-lang.dev/docs/api/text/stringbuilder/clear). ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Append("Rux"); builder.Free(); builder.IsEmpty(); // true builder.Append("again"); // fine -- the builder takes a new block builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Clear`](https://rux-lang.dev/docs/api/text/stringbuilder/clear) — forget the contents but keep the block - [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) — give the block away instead of releasing it - [`String::Free`](https://rux-lang.dev/docs/api/text/string/free) — the same by-hand release for a string # Grow Grows the block to hold at least a given number of bytes. **Package:** `Text` ## Signature ```rux func Grow(self, required: uint); ``` ## Parameters | Name | Type | Description | | ---------- | ------ | ---------------------------------------------- | | `required` | `uint` | The total number of bytes the block must hold. | ## Remarks The size is a **total**, not an increment — it counts the bytes already written. [`Reserve`](https://rux-lang.dev/docs/api/text/stringbuilder/reserve) is the same thing stated relative to [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length), and is usually what you want. A `required` that already fits is left alone: the block never shrinks here. Otherwise the capacity doubles from a 16 byte floor until it covers `required`, which is why it can end up larger than what was asked for, and why a run of [`Append`](https://rux-lang.dev/docs/api/text/stringbuilder/append) calls costs an amortized constant rather than a reallocation each time. Growing may move the block, so a pointer taken from [`Data`](https://rux-lang.dev/docs/api/text/stringbuilder/data) beforehand must not be used after. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Grow(20); builder.Capacity(); // 32 -- doubled from 16 until it covered 20 builder.Length(); // 0 -- growing writes nothing builder.Grow(8); builder.Capacity(); // 32 still -- the block never shrinks here builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Reserve`](https://rux-lang.dev/docs/api/text/stringbuilder/reserve) — the same, counted from what is already written - [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) — the way back down - [`Memory::Realloc`](https://rux-lang.dev/docs/api/memory/realloc) — the resize this is built on # StringBuilder A growable buffer for building a string in steps. **Package:** `Text` ## Struct ```rux struct StringBuilder { data: *char8; length: uint; capacity: uint; } ``` The mutable counterpart to [`String`](https://rux-lang.dev/docs/api/text/string). Appending to a `String` allocates on every step, since each transformation returns a fresh one; accumulation happens here instead. The builder keeps spare capacity and doubles it when it runs out, which is what the third field buys over a `String`, and what makes a run of appends cost an amortized constant rather than a reallocation each time. The fields are an implementation detail — read them with [`Data`](https://rux-lang.dev/docs/api/text/stringbuilder/data), [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length), and [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity). ## Ownership A builder owns its block and has to be passed to [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) exactly once, on the same terms as a `String`. The exception is [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring), which hands the block over to the `String` it returns and leaves the builder empty and owning nothing — after that only the `String` has to be freed, and [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) on the drained builder is a harmless no-op. Take the result with [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) once the builder is finished with, and with [`ToString`](https://rux-lang.dev/docs/api/text/stringbuilder/tostring) when the builder has to stay usable — that one copies, and leaves both to be freed. ## Methods ### Construction | Method | Description | | ------------------------------------------------------------------------------- | --------------------------------------------- | | [`New`](https://rux-lang.dev/docs/api/text/stringbuilder/new) | Creates an empty builder, without allocating. | | [`WithCapacity`](https://rux-lang.dev/docs/api/text/stringbuilder/withcapacity) | Creates an empty builder with room reserved. | | [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) | Releases the block the builder owns. | ### Accessors | Method | Description | | ----------------------------------------------------------------------- | ------------------------------------------ | | [`Data`](https://rux-lang.dev/docs/api/text/stringbuilder/data) | The pointer to the bytes written so far. | | [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) | How many bytes have been written. | | [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) | How many bytes fit before the block grows. | | [`IsEmpty`](https://rux-lang.dev/docs/api/text/stringbuilder/isempty) | Whether anything has been written. | ### Building | Method | Description | | --------------------------------------------------------------------- | -------------------------------------------- | | [`Append`](https://rux-lang.dev/docs/api/text/stringbuilder/append) | Writes a byte, a literal, or a string. | | [`Reserve`](https://rux-lang.dev/docs/api/text/stringbuilder/reserve) | Makes room for N more bytes. | | [`Grow`](https://rux-lang.dev/docs/api/text/stringbuilder/grow) | Grows the block to hold at least N bytes. | | [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) | Drops the capacity the builder is not using. | | [`Clear`](https://rux-lang.dev/docs/api/text/stringbuilder/clear) | Forgets the contents but keeps the block. | ### Conversion | Method | Description | | --------------------------------------------------------------------------- | --------------------------------------------------- | | [`ToString`](https://rux-lang.dev/docs/api/text/stringbuilder/tostring) | Copies the contents out into a `String`. | | [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) | Hands the block over to a `String`, without a copy. | ## Example ```rux import Io::PrintLine; import Text::{ String, StringBuilder }; func Main() -> int { var builder = StringBuilder::New(); for i in 0..3 { builder.Append("ab"); builder.Append(c8'-'); } PrintLine(builder.Length()); // 9 var text = builder.IntoString(); // "ab-ab-ab-", and the builder is empty text.Free(); return 0; } ``` ## See also - [`Text`](https://rux-lang.dev/docs/api/text) — the package overview - [`String`](https://rux-lang.dev/docs/api/text/string) — the immutable string a builder produces # IntoString Hands the block over to a string, without copying it. **Package:** `Text` ## Signature ```rux func IntoString(self) -> String; ``` ## Returns A [`String`](https://rux-lang.dev/docs/api/text/string) owning the builder's block, which costs no copy. The block is [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink)ed first, so the `String` owns one sized to its contents rather than to the builder's high-water mark. The builder is left **empty and owning nothing**, so only the `String` has to be freed — a [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) on the drained builder releases nothing and is not a double free. The builder stays usable and will take a fresh block on the next append. Draining an empty builder yields the empty string. This is how to take the result once the builder is finished with. Use [`ToString`](https://rux-lang.dev/docs/api/text/stringbuilder/tostring) when the builder has to keep its contents. ## Example ```rux import Text::{ String, StringBuilder }; func Main() -> int { var builder = StringBuilder::WithCapacity(1024); builder.Append("Hello, "); builder.Append("Rux!"); var greeting = builder.IntoString(); // "Hello, Rux!", the block handed over greeting.Length(); // 11 -- and the block is 11 bytes, not 1024 builder.IsEmpty(); // true -- it owns nothing now greeting.Free(); // the only Free needed return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`ToString`](https://rux-lang.dev/docs/api/text/stringbuilder/tostring) — copy the contents out and keep the builder's block - [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) — the trim this does first - [`String::Free`](https://rux-lang.dev/docs/api/text/string/free) — release the block the string now owns # IsEmpty Reports whether anything has been written. **Package:** `Text` ## Signature ```rux func IsEmpty(self) -> bool; ``` ## Returns `true` when [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) is `0`. It says nothing about the [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity): a builder that has been [`Clear`](https://rux-lang.dev/docs/api/text/stringbuilder/clear)ed is empty but still holds its block, and so is one built with [`WithCapacity`](https://rux-lang.dev/docs/api/text/stringbuilder/withcapacity) that has not been appended to. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::WithCapacity(64); builder.IsEmpty(); // true -- room reserved, nothing written builder.Append("Rux"); builder.IsEmpty(); // false builder.Clear(); builder.IsEmpty(); // true, and the 64 bytes are still there builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) — the byte count this tests against zero - [`Clear`](https://rux-lang.dev/docs/api/text/stringbuilder/clear) — empty the builder without releasing the block # Length Returns how many bytes have been written. **Package:** `Text` ## Signature ```rux func Length(self) -> uint; ``` ## Returns The number of bytes written so far, which is **not** the [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) — a builder holds a block that is usually bigger than what has been put in it. The count is in bytes, so a multi-byte UTF-8 sequence counts once per byte. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::WithCapacity(64); builder.Append("Rux"); builder.Length(); // 3 builder.Capacity(); // 64 builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) — how much room the block has - [`IsEmpty`](https://rux-lang.dev/docs/api/text/stringbuilder/isempty) — the `Length() == 0` case # New Creates an empty builder. **Package:** `Text` ## Signature ```rux func New() -> StringBuilder; ``` ## Returns An empty `StringBuilder`. It holds no block until the first append, so this allocates nothing: [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) and [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) are both `0`, and [`Data`](https://rux-lang.dev/docs/api/text/stringbuilder/data) is `null`. The first [`Append`](https://rux-lang.dev/docs/api/text/stringbuilder/append) takes a block of 16 bytes and doubles from there. Reach for [`WithCapacity`](https://rux-lang.dev/docs/api/text/stringbuilder/withcapacity) instead when the final size is known up front. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Capacity(); // 0 -- nothing is allocated yet builder.Append("Rux"); builder.Capacity(); // 16 -- the first append takes a block builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`WithCapacity`](https://rux-lang.dev/docs/api/text/stringbuilder/withcapacity) — reserve the room up front - [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) — release the block when the builder is finished with # Reserve Makes room for more bytes. **Package:** `Text` ## Signature ```rux func Reserve(self, additional: uint); ``` ## Parameters | Name | Type | Description | | ------------ | ------ | ---------------------------------------------------------------- | | `additional` | `uint` | How many more bytes to make room for, on top of what is written. | ## Remarks Ensures the block can hold `additional` bytes **beyond** the current [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length), so the appends that follow do not reallocate. It is [`Grow`](https://rux-lang.dev/docs/api/text/stringbuilder/grow) with the bookkeeping done for you — the argument is relative to what is written, not the total. Capacity only ever grows, so this leaves a builder that is already big enough alone; the capacity that goes unused is handed back by [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink), not by reserving less. Reserving may move the block, so a pointer taken from [`Data`](https://rux-lang.dev/docs/api/text/stringbuilder/data) beforehand must not be used after. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Append("Rux"); // Length is 3 builder.Reserve(100); // room for 100 more, so at least 103 in all builder.Capacity(); // >= 103, and no append below that reallocates builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Grow`](https://rux-lang.dev/docs/api/text/stringbuilder/grow) — ask for a total size instead of an increment - [`WithCapacity`](https://rux-lang.dev/docs/api/text/stringbuilder/withcapacity) — reserve up front, before anything is written - [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) — hand back what went unused # Shrink Drops the capacity the builder is not using. **Package:** `Text` ## Signature ```rux func Shrink(self); ``` ## Remarks Resizes the block down to exactly [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) bytes, so the builder holds no more memory than its contents need. The contents are kept. A builder that is already exactly full is left alone, and one that is empty releases its block outright, as [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) would. [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) leans on this, which is why the `String` it hands out owns a block sized to its contents rather than to the builder's high-water mark. The resize may move the block, so a pointer taken from [`Data`](https://rux-lang.dev/docs/api/text/stringbuilder/data) beforehand must not be used after. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::WithCapacity(1024); builder.Append("Rux"); builder.Capacity(); // 1024 builder.Shrink(); builder.Capacity(); // 3 -- the 1021 unused bytes are handed back builder.Length(); // 3 -- the contents are kept builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`Grow`](https://rux-lang.dev/docs/api/text/stringbuilder/grow) — the way back up - [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) — shrinks before it hands the block over - [`Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free) — release the block instead of trimming it # ToString Copies the contents out into a string. **Package:** `Text` ## Signature ```rux func ToString(self) -> String; ``` ## Returns A [`String`](https://rux-lang.dev/docs/api/text/string) holding a copy of the bytes written so far, in a block of its own. An empty builder yields the empty string. The builder keeps its own block and stays usable, so **both** it and the `String` own a block and both have to be freed. Reach for [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) instead once the builder is finished with: that one hands the block over rather than copying it. Taking a snapshot mid-build is what this is for — the `String` will not follow the builder's later appends. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::New(); builder.Append("Rux"); var snapshot = builder.ToString(); // "Rux", its own block builder.Append(" rocks"); // the snapshot does not change snapshot.Length(); // 3 snapshot.Free(); builder.Free(); // both own a block return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) — hand the block over instead of copying it - [`String::Clone`](https://rux-lang.dev/docs/api/text/string/clone) — the same copy, from a string # WithCapacity Creates an empty builder with room reserved up front. **Package:** `Text` ## Signature ```rux func WithCapacity( capacity: uint ) -> StringBuilder; ``` ## Parameters | Name | Type | Description | | ---------- | ------ | -------------------------- | | `capacity` | `uint` | How many bytes to reserve. | ## Returns An empty `StringBuilder` holding a block of `capacity` bytes. It is empty — [`Length`](https://rux-lang.dev/docs/api/text/stringbuilder/length) is `0` — but [`Capacity`](https://rux-lang.dev/docs/api/text/stringbuilder/capacity) is what was asked for, and appends that stay within it do not reallocate. A `capacity` of `0` allocates nothing, which is [`New`](https://rux-lang.dev/docs/api/text/stringbuilder/new). This is worth reaching for when the final size is known: [`String::Repeat`](https://rux-lang.dev/docs/api/text/string/repeat) sizes its builder this way, which is what makes it one allocation rather than one per round. ## Example ```rux import Text::StringBuilder; func Main() -> int { var builder = StringBuilder::WithCapacity(64); builder.Length(); // 0 builder.Capacity(); // 64 builder.Append("Rux"); builder.Capacity(); // 64 still -- the append fit builder.Free(); return 0; } ``` ## See also - [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) — the builder type - [`New`](https://rux-lang.dev/docs/api/text/stringbuilder/new) — an empty builder that allocates nothing - [`Reserve`](https://rux-lang.dev/docs/api/text/stringbuilder/reserve) — reserve more room on a builder that already exists - [`Shrink`](https://rux-lang.dev/docs/api/text/stringbuilder/shrink) — hand back the capacity that went unused # IsSpace Reports whether a byte is whitespace. **Package:** `Text` ## Signature ```rux func IsSpace(ch: char8) -> bool; ``` ## Parameters | Name | Type | Description | | ---- | ------- | ----------------- | | `ch` | `char8` | The byte to test. | ## Returns `true` for space (`' '`), tab (`'\t'`), newline (`'\n'`), and carriage return (`'\r'`), and `false` for every other byte. These four are exactly the bytes [`String::Trim`](https://rux-lang.dev/docs/api/text/string/trim) removes. The test is on one byte, so it says nothing about the whitespace characters that UTF-8 encodes in more than one — a non-breaking space is not whitespace here. ## Example ```rux import Text::IsSpace; func Main() -> int { IsSpace(c8' '); // true IsSpace(c8'\n'); // true IsSpace(c8'a'); // false return 0; } ``` ## See also - [`Text`](https://rux-lang.dev/docs/api/text) — the package overview - [`String::Trim`](https://rux-lang.dev/docs/api/text/string/trim) — drop the whitespace at both ends of a string # Text Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: The package provides strings and fundamental text manipulation — an immutable [`String`](https://rux-lang.dev/docs/api/text/string), and a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) to accumulate one. **Package:** `Text` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Text](https://github.com/rux-lang/Rux/tree/main/Packages/Text){rel=""nofollow""} Both types hold a heap block they own, and every block comes from the [`Memory`](https://rux-lang.dev/docs/api/memory) package, which is what this one is built on. The bytes are whatever was put there — the package reads them as UTF-8 only where it has to, and says so where it does. ```rux import Io::PrintLine; import Text::{ String, StringBuilder }; func Main() -> int { var name = String::From(" Rux ").Trim(); // "Rux" var builder = StringBuilder::New(); builder.Append("Hello, "); builder.Append(name); builder.Append(c8'!'); var greeting = builder.IntoString(); // "Hello, Rux!" PrintLine(greeting.Length()); // 11 greeting.Free(); name.Free(); return 0; } ``` ## Installation ```sh rux add Text rux install ``` ## Platform support Every allocation goes through [`Memory`](https://rux-lang.dev/docs/api/memory), so this package runs wherever that one does: BSD, Linux, macOS, and Windows. ## Ownership There are no destructors, so a block is released by hand. Every `String` and `StringBuilder` this package hands out owns its block and has to be passed to `Free` exactly once — [`String::Free`](https://rux-lang.dev/docs/api/text/string/free) or [`StringBuilder::Free`](https://rux-lang.dev/docs/api/text/stringbuilder/free), not [`Memory::Free`](https://rux-lang.dev/docs/api/memory/free). Assignment copies the struct, not the block, so two `String` values can name the same allocation and freeing both is a double free. Take [`Clone`](https://rux-lang.dev/docs/api/text/string/clone) when both have to be freed. `Free` ignores an empty value and leaves an empty value behind, so calling it twice is harmless, and it is safe on anything the package returns. A `String` never changes once built: every transformation returns a fresh one and leaves the receiver alone. That is what makes the value semantics safe, since a `String` built from a literal would otherwise be written through a read-only page. It also makes `+` in a loop a chain of allocations — build with a [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) instead, and hand the result over with [`IntoString`](https://rux-lang.dev/docs/api/text/stringbuilder/intostring) to avoid a final copy. ## Text encoding A `String` holds bytes, and [`Length`](https://rux-lang.dev/docs/api/text/string/length) counts bytes rather than characters, so a multi-byte UTF-8 sequence counts for more than one. [`At`](https://rux-lang.dev/docs/api/text/string/at) and [`Substring`](https://rux-lang.dev/docs/api/text/string/substring) index by byte and will happily cut a sequence in half. [`ToUpper`](https://rux-lang.dev/docs/api/text/string/toupper) and [`ToLower`](https://rux-lang.dev/docs/api/text/string/tolower) fold ASCII only and copy everything else through untouched. The block carries no null terminator. Pair [`Data`](https://rux-lang.dev/docs/api/text/string/data) with [`Length`](https://rux-lang.dev/docs/api/text/string/length) when handing the bytes to something that reads them directly, rather than letting it scan for a terminator that is not there. ## Types | Type | Description | | ------------------------------------------------------------------- | ---------------------------------------------------- | | [`String`](https://rux-lang.dev/docs/api/text/string) | An immutable string, owning the block behind it. | | [`StringBuilder`](https://rux-lang.dev/docs/api/text/stringbuilder) | A growable buffer, for building a `String` in steps. | ## Functions | Function | Description | | ------------------------------------------------------- | ---------------------------------------------- | | [`IsSpace`](https://rux-lang.dev/docs/api/text/isspace) | Whether a byte is one of the whitespace bytes. | # AllocConsole Allocates a console for the calling process. **Package:** `Windows` **Microsoft documentation:** [`AllocConsole`](https://learn.microsoft.com/en-us/windows/console/allocconsole){rel=""nofollow""} ## Signature ```rux func AllocConsole() -> bool32; ``` ## Returns `bool32` — nonzero on success or zero on failure. Call [`GetLastError`](https://rux-lang.dev/docs/api/windows/getlasterror) after failure. ## Description A process can be associated with at most one console. After allocation, obtain the console handles with [`GetStdHandle`](https://rux-lang.dev/docs/api/windows/getstdhandle). ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview - [`GetStdHandle`](https://rux-lang.dev/docs/api/windows/getstdhandle) — retrieve a standard device handle # Beep Generates a simple tone on the speaker. **Package:** `Windows` **Microsoft documentation:** [`Beep`](https://learn.microsoft.com/en-us/windows/win32/docs/api/utilapiset/nf-utilapiset-beep){rel=""nofollow""} ## Signature ```rux func Beep( freq: uint32, duration: uint32 ) -> bool32; ``` ## Parameters | Name | Type | Description | | ---------- | -------- | ----------------------------- | | `freq` | `uint32` | Frequency in hertz, 37–32767. | | `duration` | `uint32` | Duration in milliseconds. | ## Returns `bool32` — nonzero on success or zero on failure. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # CloseHandle Closes an open kernel object handle. **Package:** `Windows` **Microsoft documentation:** [`CloseHandle`](https://learn.microsoft.com/en-us/windows/win32/docs/api/handleapi/nf-handleapi-closehandle){rel=""nofollow""} ## Signature ```rux func CloseHandle(handle: *opaque) -> bool32; ``` ## Parameters | Name | Type | Description | | -------- | --------- | ------------------- | | `handle` | `*opaque` | Open object handle. | ## Returns `bool32` — nonzero on success or zero on failure. Do not use the handle after success. Search handles require [`FindClose`](https://rux-lang.dev/docs/api/windows/findclose), and pseudo-handles must not be passed here. ## See also - [`GetLastError`](https://rux-lang.dev/docs/api/windows/getlasterror) — retrieve failure details - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # CodePage Identifies a Windows character encoding. **Package:** `Windows` **Microsoft documentation:** [`Code page identifiers`](https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers){rel=""nofollow""} ```rux enum CodePage: uint32 ``` The enum exposes the Windows code-page identifiers accepted by [`MultiByteToWideChar`](https://rux-lang.dev/docs/api/windows/multibytetowidechar) and [`WideCharToMultiByte`](https://rux-lang.dev/docs/api/windows/widechartomultibyte). Common members are: | Member | Value | Encoding | | ------------- | ------: | --------------------------- | | `Utf8` | `65001` | UTF-8. | | `Utf7` | `65000` | UTF-7; avoid for new data. | | `Utf16Le` | `1200` | UTF-16 little-endian. | | `Utf16Be` | `1201` | UTF-16 big-endian. | | `Utf32Le` | `12000` | UTF-32 little-endian. | | `Utf32Be` | `12001` | UTF-32 big-endian. | | `UsAscii` | `20127` | 7-bit US ASCII. | | `Windows1250` | `1250` | Windows Central European. | | `Windows1251` | `1251` | Windows Cyrillic. | | `Windows1252` | `1252` | Windows Western European. | | `ShiftJis` | `932` | Japanese Shift-JIS. | | `Gb2312` | `936` | Simplified Chinese. | | `KsC5601` | `949` | Korean. | | `Big5` | `950` | Traditional Chinese. | | `Gb18030` | `54936` | GB18030 Simplified Chinese. | The enum also includes the OEM, EBCDIC, Macintosh, ISO-8859, ISO-2022, EUC, KOI8, ISCII, and legacy East Asian identifiers declared by Windows. Prefer `CodePage.Utf8` for new interoperable text. ::warning A code-page identifier does not make an `A`-suffixed API UTF-8. Those APIs use the process ANSI conventions. Convert explicitly when Unicode correctness is required. :: # CopyFileA Copies an existing file to a new path. **Package:** `Windows` **Microsoft documentation:** [`CopyFileA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/winbase/nf-winbase-copyfilea){rel=""nofollow""} ## Signature ```rux func CopyFileA( existingFileName: *char8, newFileName: *char8, failIfExists: bool32 ) -> bool32; ``` Both names must be null-terminated ANSI paths. When `failIfExists` is nonzero, the call fails rather than overwriting an existing destination. Returns nonzero on success. ## See also - [`MoveFileA`](https://rux-lang.dev/docs/api/windows/movefilea) — move or rename - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # CreateDirectoryA Creates a directory. **Package:** `Windows` **Microsoft documentation:** [`CreateDirectoryA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-createdirectorya){rel=""nofollow""} ## Signature ```rux func CreateDirectoryA( pathName: *char8, securityAttributes: *opaque ) -> bool32; ``` `pathName` must be a null-terminated ANSI path. Pass `null` for default security attributes. The function creates one directory and does not create missing parents. Returns nonzero on success. ## See also - [`RemoveDirectoryA`](https://rux-lang.dev/docs/api/windows/removedirectorya) — remove an empty directory - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # CreateFileA Creates or opens a file or I/O device. **Package:** `Windows` **Microsoft documentation:** [`CreateFileA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-createfilea){rel=""nofollow""} ## Signature ```rux func CreateFileA( fileName: *char8, desiredAccess: uint32, shareMode: uint32, securityAttributes: *opaque, creationDisposition: uint32, flagsAndAttributes: uint32, templateFile: *opaque ) -> *opaque; ``` ## Parameters | Name | Description | | --------------------- | ----------------------------------- | | `fileName` | Null-terminated ANSI path. | | `desiredAccess` | Requested read and/or write access. | | `shareMode` | Sharing permitted to later opens. | | `securityAttributes` | Security attributes, or `null`. | | `creationDisposition` | Create/open behavior. | | `flagsAndAttributes` | File attributes and Win32 flags. | | `templateFile` | Template handle, normally `null`. | ## Returns `*opaque` — an open handle on success, or the invalid-handle sentinel (`-1` as a handle) on failure. Close a successful result with [`CloseHandle`](https://rux-lang.dev/docs/api/windows/closehandle). ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview - [`Types and constants`](https://rux-lang.dev/docs/api/windows/types) — file enums # DeleteFileA Marks an existing file for deletion. **Package:** `Windows` **Microsoft documentation:** [`DeleteFileA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-deletefilea){rel=""nofollow""} ## Signature ```rux func DeleteFileA(fileName: *char8) -> bool32; ``` `fileName` must be a null-terminated ANSI path. Returns nonzero on success or zero on failure. Actual removal can be deferred until all open handles permit deletion and are closed. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # ExitProcess Terminates the calling process and all of its threads. **Package:** `Windows` **Microsoft documentation:** [`ExitProcess`](https://learn.microsoft.com/en-us/windows/win32/docs/api/processthreadsapi/nf-processthreadsapi-exitprocess){rel=""nofollow""} ## Signature ```rux func ExitProcess(exitCode: uint32); ``` ## Parameters | Name | Type | Description | | ---------- | -------- | -------------------------------- | | `exitCode` | `uint32` | Status reported for the process. | ## Description `ExitProcess` does not return. Normal stack unwinding and cleanup in the caller do not occur. Prefer returning from `Main` for normal termination. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # FindClose Closes a file-search handle. **Package:** `Windows` **Microsoft documentation:** [`FindClose`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-findclose){rel=""nofollow""} ## Signature ```rux func FindClose(findFile: *opaque) -> bool32; ``` Returns nonzero on success or zero on failure. Call it exactly once for every successful [`FindFirstFileA`](https://rux-lang.dev/docs/api/windows/findfirstfilea), including when enumeration stops early. Search handles must not be passed to `CloseHandle`. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # FindFirstFileA Starts a file search and returns its first matching entry. **Package:** `Windows` **Microsoft documentation:** [`FindFirstFileA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-findfirstfilea){rel=""nofollow""} ## Signature ```rux func FindFirstFileA( fileName: *char8, findFileData: *Win32FindDataA ) -> *opaque; ``` `fileName` is a null-terminated ANSI path pattern and may contain wildcards. `findFileData` receives the first result. Returns a search handle on success, or the invalid-handle sentinel (`-1` as a handle) on failure. Release the search handle with [`FindClose`](https://rux-lang.dev/docs/api/windows/findclose), not `CloseHandle`. ## See also - [`FindNextFileA`](https://rux-lang.dev/docs/api/windows/findnextfilea) — continue the search - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # FindNextFileA Retrieves the next entry from a file search. **Package:** `Windows` **Microsoft documentation:** [`FindNextFileA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-findnextfilea){rel=""nofollow""} ## Signature ```rux func FindNextFileA( findFile: *opaque, findFileData: *Win32FindDataA ) -> bool32; ``` Returns nonzero when another result was written. A zero return with `ERROR_NO_MORE_FILES` from [`GetLastError`](https://rux-lang.dev/docs/api/windows/getlasterror) means enumeration is complete; other error codes indicate failure. ## See also - [`FindFirstFileA`](https://rux-lang.dev/docs/api/windows/findfirstfilea) — start a search - [`FindClose`](https://rux-lang.dev/docs/api/windows/findclose) — release the search handle # FreeLibrary Releases a reference to a loaded DLL. **Package:** `Windows` **Microsoft documentation:** [`FreeLibrary`](https://learn.microsoft.com/en-us/windows/win32/docs/api/libloaderapi/nf-libloaderapi-freelibrary){rel=""nofollow""} ## Signature ```rux func FreeLibrary(module_arg: *opaque) -> bool32; ``` ## Parameters | Name | Description | | ------------ | --------------------- | | `module_arg` | Loaded module handle. | ## Returns `bool32` — nonzero on success or zero on failure. When the reference count reaches zero, the DLL is unloaded and addresses returned by `GetProcAddress` become invalid. ## See also - [`LoadLibraryA`](https://rux-lang.dev/docs/api/windows/loadlibrarya) — load a module - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetCurrentDirectoryA Retrieves the process current directory. **Package:** `Windows` **Microsoft documentation:** [`GetCurrentDirectoryA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/winbase/nf-winbase-getcurrentdirectorya){rel=""nofollow""} ## Signature ```rux func GetCurrentDirectoryA( bufferLength: uint32, buffer: *char8 ) -> uint32; ``` ## Returns When the buffer is large enough, returns the path length excluding the null terminator. If too small, returns the required size including the terminator. Returns `0` on failure. `bufferLength` is measured in narrow characters. ## See also - [`SetCurrentDirectoryA`](https://rux-lang.dev/docs/api/windows/setcurrentdirectorya) — change the current directory - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetCurrentProcessId Returns the identifier of the calling process. **Package:** `Windows` **Microsoft documentation:** [`GetCurrentProcessId`](https://learn.microsoft.com/en-us/windows/win32/docs/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid){rel=""nofollow""} ## Signature ```rux func GetCurrentProcessId() -> uint32; ``` ## Returns `uint32` — the current process identifier. Windows may reuse the identifier after the process terminates. ## See also - [`GetCurrentThreadId`](https://rux-lang.dev/docs/api/windows/getcurrentthreadid) — calling thread identifier - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetCurrentThreadId Returns the identifier of the calling thread. **Package:** `Windows` **Microsoft documentation:** [`GetCurrentThreadId`](https://learn.microsoft.com/en-us/windows/win32/docs/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid){rel=""nofollow""} ## Signature ```rux func GetCurrentThreadId() -> uint32; ``` ## Returns `uint32` — the current thread identifier. Windows may reuse the identifier after the thread terminates. ## See also - [`GetCurrentProcessId`](https://rux-lang.dev/docs/api/windows/getcurrentprocessid) — calling process identifier - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetFileAttributesA Retrieves attributes for a file or directory. **Package:** `Windows` **Microsoft documentation:** [`GetFileAttributesA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-getfileattributesa){rel=""nofollow""} ## Signature ```rux func GetFileAttributesA( fileName: *char8 ) -> uint32; ``` ## Returns `uint32` — an attribute bitmask on success or `0xFFFFFFFF` on failure. Test individual attributes with bitwise operations because several can be set. ## See also - [`SetFileAttributesA`](https://rux-lang.dev/docs/api/windows/setfileattributesa) — change attributes - [`Windows` types](https://rux-lang.dev/docs/api/windows/types) — shared type definitions # GetFileSizeEx Retrieves the size of an open file. **Package:** `Windows` **Microsoft documentation:** [`GetFileSizeEx`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-getfilesizeex){rel=""nofollow""} ## Signature ```rux func GetFileSizeEx( file: *opaque, size: *int64 ) -> bool32; ``` ## Parameters | Name | Description | | ------ | -------------------------------- | | `file` | Open file handle. | | `size` | Receives the file size in bytes. | ## Returns `bool32` — nonzero on success or zero on failure. ## See also - [`SetFilePointerEx`](https://rux-lang.dev/docs/api/windows/setfilepointerex) — move the file pointer - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetLastError Retrieves the calling thread's last-error code. **Package:** `Windows` **Microsoft documentation:** [`GetLastError`](https://learn.microsoft.com/en-us/windows/win32/docs/api/errhandlingapi/nf-errhandlingapi-getlasterror){rel=""nofollow""} ## Signature ```rux func GetLastError() -> uint32; ``` ## Returns `uint32` — the thread-local Win32 error code. ## Description Call `GetLastError` immediately after a function reports failure and only when that function's contract defines a last-error value. Another API call may overwrite the code. A return value of `0` does not prove that the preceding operation succeeded. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetLocalTime Retrieves the current local date and time. **Package:** `Windows` **Microsoft documentation:** [`GetLocalTime`](https://learn.microsoft.com/en-us/windows/win32/docs/api/sysinfoapi/nf-sysinfoapi-getlocaltime){rel=""nofollow""} ## Signature ```rux func GetLocalTime(time: *SystemTime); ``` ## Parameters | Name | Type | Description | | ------ | ------------- | ------------------------------------ | | `time` | `*SystemTime` | Writable destination for local time. | The pointer must be non-null and writable. The function does not return a status value. ## See also - [`GetSystemTime`](https://rux-lang.dev/docs/api/windows/getsystemtime) — retrieve UTC - [`SystemTime`](https://rux-lang.dev/docs/api/windows/types#systemtime) — result structure # GetProcAddress Retrieves the address of an exported DLL symbol. **Package:** `Windows` **Microsoft documentation:** [`GetProcAddress`](https://learn.microsoft.com/en-us/windows/win32/docs/api/libloaderapi/nf-libloaderapi-getprocaddress){rel=""nofollow""} ## Signature ```rux func GetProcAddress( module_arg: *opaque, procName: *char8 ) -> *opaque; ``` ## Parameters | Name | Description | | ------------ | -------------------------------------------- | | `module_arg` | Loaded module handle. | | `procName` | Null-terminated, case-sensitive export name. | ## Returns `*opaque` — the export address, or `null` when not found. Cast it to a function pointer with the exact native signature and calling convention before use. The address becomes invalid when its module is unloaded. ## See also - [`LoadLibraryA`](https://rux-lang.dev/docs/api/windows/loadlibrarya) — load a module - [`FreeLibrary`](https://rux-lang.dev/docs/api/windows/freelibrary) — release a module reference # GetProcessHeap Retrieves the calling process's default heap. **Package:** `Windows` **Microsoft documentation:** [`GetProcessHeap`](https://learn.microsoft.com/en-us/windows/win32/docs/api/heapapi/nf-heapapi-getprocessheap){rel=""nofollow""} ## Signature ```rux func GetProcessHeap() -> *opaque; ``` ## Returns `*opaque` — the process heap handle. The caller must not close or destroy this handle. ## See also - [`HeapAlloc`](https://rux-lang.dev/docs/api/windows/heapalloc) — allocate from a heap - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetStdHandle Retrieves a handle to a standard device. **Package:** `Windows` **Microsoft documentation:** [`GetStdHandle`](https://learn.microsoft.com/en-us/windows/console/getstdhandle){rel=""nofollow""} ## Signature ```rux func GetStdHandle(stdHandle: uint32) -> *opaque; ``` ## Parameters | Name | Description | | ----------- | ---------------------------------------------------------------- | | `stdHandle` | One of `StdInputHandle`, `StdOutputHandle`, or `StdErrorHandle`. | ## Returns `*opaque` — the current standard-device handle. The result can be `null` or the invalid-handle sentinel (`-1` as a handle) when no usable handle is available. The returned handle is process-owned and normally must not be closed by the caller. ## See also - [`Types and constants`](https://rux-lang.dev/docs/api/windows/types#standard-device-handles) — `StdHandle` values - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # GetSystemTime Retrieves the current UTC date and time. **Package:** `Windows` **Microsoft documentation:** [`GetSystemTime`](https://learn.microsoft.com/en-us/windows/win32/docs/api/sysinfoapi/nf-sysinfoapi-getsystemtime){rel=""nofollow""} ## Signature ```rux func GetSystemTime(time: *SystemTime); ``` ## Parameters | Name | Type | Description | | ------ | ------------- | ---------------------------------- | | `time` | `*SystemTime` | Writable destination for UTC time. | The pointer must be non-null and writable. The function does not return a status value. ## See also - [`GetLocalTime`](https://rux-lang.dev/docs/api/windows/getlocaltime) — retrieve local time - [`SystemTime`](https://rux-lang.dev/docs/api/windows/types#systemtime) — result structure # GetTickCount64 Returns milliseconds elapsed since system startup. **Package:** `Windows` **Microsoft documentation:** [`GetTickCount64`](https://learn.microsoft.com/en-us/windows/win32/docs/api/sysinfoapi/nf-sysinfoapi-gettickcount64){rel=""nofollow""} ## Signature ```rux func GetTickCount64() -> uint64; ``` ## Returns `uint64` — the monotonic system tick count in milliseconds. Use differences between readings to measure elapsed time. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # HeapAlloc Allocates a block from a Win32 heap. **Package:** `Windows` **Microsoft documentation:** [`HeapAlloc`](https://learn.microsoft.com/en-us/windows/win32/docs/api/heapapi/nf-heapapi-heapalloc){rel=""nofollow""} ## Signature ```rux func HeapAlloc( heap: *opaque, flags: uint32, bytes: uint ) -> *var opaque; ``` ## Parameters | Name | Description | | ------- | ----------------------------------------------- | | `heap` | Heap handle returned by `GetProcessHeap`. | | `flags` | Win32 heap flags; use `0` for default behavior. | | `bytes` | Requested allocation size. | ## Returns `*opaque` — the allocated block, or `null` on failure. The memory is uninitialized unless a zero-memory flag is supplied. Release it with [`HeapFree`](https://rux-lang.dev/docs/api/windows/heapfree) using the same heap. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # HeapFree Releases a block allocated from a Win32 heap. **Package:** `Windows` **Microsoft documentation:** [`HeapFree`](https://learn.microsoft.com/en-us/windows/win32/docs/api/heapapi/nf-heapapi-heapfree){rel=""nofollow""} ## Signature ```rux func HeapFree( heap: *opaque, flags: uint32, mem: *opaque ) -> bool32; ``` ## Parameters | Name | Description | | ------- | ------------------------------------- | | `heap` | The same heap used to allocate `mem`. | | `flags` | Heap flags; normally `0`. | | `mem` | Allocated block to release. | ## Returns `bool32` — nonzero on success or zero on failure. The pointer is invalid after a successful call. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # HeapReAlloc Resizes a block allocated from a Win32 heap. **Package:** `Windows` **Microsoft documentation:** [`HeapReAlloc`](https://learn.microsoft.com/en-us/windows/win32/docs/api/heapapi/nf-heapapi-heaprealloc){rel=""nofollow""} ## Signature ```rux func HeapReAlloc( heap: *opaque, flags: uint32, mem: *opaque, bytes: uint ) -> *var opaque; ``` ## Returns `*opaque` — the resized block, or `null` on failure. On failure, `mem` remains valid and must still be freed. Use the same heap that allocated the block. ::warning Do not overwrite the only copy of `mem` before checking the returned pointer. :: ## See also - [`HeapAlloc`](https://rux-lang.dev/docs/api/windows/heapalloc) — allocate a block - [`HeapFree`](https://rux-lang.dev/docs/api/windows/heapfree) — release a block - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # Windows Package ::warning **Unstable API**:br The package is under active development and its API is **not yet stable**. Names, signatures, and behavior may change between releases, and this documentation will be updated to match. :: Direct Win32 API bindings for Rux programs. **Package:** `Windows` **Source:** [github.com/rux-lang/Rux/tree/main/Packages/Windows](https://github.com/rux-lang/Rux/tree/main/Packages/Windows){rel=""nofollow""} The package imports a focused set of functions from `kernel32.dll`, covering console and file I/O, heap and memory operations, processes, time, filesystem operations, text conversion, directory enumeration, and dynamic libraries. ## Requirements - Windows - A Rux compiler with the `kernel32.dll` link support The functions are declared in a `#Link("Kernel32.dll") extern { ... }` block, so they resolve at link time against the system DLL. These bindings mirror Win32 closely and are not portable — reach for the cross-platform packages ([`Io`](https://rux-lang.dev/docs/api/io), [`Memory`](https://rux-lang.dev/docs/api/memory)) when they provide the operation you need. ## Installation ```sh rux add Windows rux install ``` Then import the symbols you need: ```rux import Windows::{ GetLastError, GetTickCount64 }; ``` ## Calling Conventions Most fallible functions return `bool32`: nonzero means success and zero means failure. Call [`GetLastError`](https://rux-lang.dev/docs/api/windows/getlasterror) immediately after a failure when the function's contract defines a last-error value. Pointer-returning functions use `null` as their failure sentinel unless a page states otherwise. Functions ending in `A` take narrow, null-terminated strings interpreted by the Windows ANSI API; they are not inherently UTF-8. Functions ending in `W` take UTF-16 `char16` data. Buffer lengths are measured in bytes or characters as each function states. ::warning **Raw bindings**:br These APIs do not automatically close handles, free heap blocks, retry partial I/O, validate pointers, or preserve `GetLastError`. The caller owns those responsibilities. :: ## Functions ### Console | Function | Description | | ---------------------------------------------------------------------- | ------------------------------------- | | [`AllocConsole`](https://rux-lang.dev/docs/api/windows/allocconsole) | Allocate a console for the process. | | [`GetStdHandle`](https://rux-lang.dev/docs/api/windows/getstdhandle) | Get a standard device handle. | | [`ReadConsoleA`](https://rux-lang.dev/docs/api/windows/readconsolea) | Read characters from the console. | | [`WriteConsoleA`](https://rux-lang.dev/docs/api/windows/writeconsolea) | Write a narrow string to the console. | | [`WriteConsoleW`](https://rux-lang.dev/docs/api/windows/writeconsolew) | Write a UTF-16 string to the console. | | [`Beep`](https://rux-lang.dev/docs/api/windows/beep) | Sound a tone on the speaker. | ### File I/O | Function | Description | | ---------------------------------------------------------------------------- | --------------------------------- | | [`CreateFileA`](https://rux-lang.dev/docs/api/windows/createfilea) | Create or open a file or device. | | [`ReadFile`](https://rux-lang.dev/docs/api/windows/readfile) | Read bytes from a file or device. | | [`WriteFile`](https://rux-lang.dev/docs/api/windows/writefile) | Write bytes to a file or device. | | [`GetFileSizeEx`](https://rux-lang.dev/docs/api/windows/getfilesizeex) | Get the size of a file. | | [`SetFilePointerEx`](https://rux-lang.dev/docs/api/windows/setfilepointerex) | Move the file pointer. | ### Filesystem | Function | Description | | ------------------------------------------------------------------------------------ | ----------------------------- | | [`CopyFileA`](https://rux-lang.dev/docs/api/windows/copyfilea) | Copy a file. | | [`MoveFileA`](https://rux-lang.dev/docs/api/windows/movefilea) | Move a file or directory. | | [`DeleteFileA`](https://rux-lang.dev/docs/api/windows/deletefilea) | Delete a file. | | [`CreateDirectoryA`](https://rux-lang.dev/docs/api/windows/createdirectorya) | Create a directory. | | [`RemoveDirectoryA`](https://rux-lang.dev/docs/api/windows/removedirectorya) | Remove an empty directory. | | [`GetFileAttributesA`](https://rux-lang.dev/docs/api/windows/getfileattributesa) | Read a file's attributes. | | [`SetFileAttributesA`](https://rux-lang.dev/docs/api/windows/setfileattributesa) | Set a file's attributes. | | [`GetCurrentDirectoryA`](https://rux-lang.dev/docs/api/windows/getcurrentdirectorya) | Read the current directory. | | [`SetCurrentDirectoryA`](https://rux-lang.dev/docs/api/windows/setcurrentdirectorya) | Change the current directory. | ### File enumeration | Function | Description | | ------------------------------------------------------------------------ | ---------------------------- | | [`FindFirstFileA`](https://rux-lang.dev/docs/api/windows/findfirstfilea) | Begin a directory search. | | [`FindNextFileA`](https://rux-lang.dev/docs/api/windows/findnextfilea) | Continue a directory search. | | [`FindClose`](https://rux-lang.dev/docs/api/windows/findclose) | Close a search handle. | ### Heap and memory | Function | Description | | ---------------------------------------------------------------------------- | ------------------------------- | | [`GetProcessHeap`](https://rux-lang.dev/docs/api/windows/getprocessheap) | Get the process's default heap. | | [`HeapAlloc`](https://rux-lang.dev/docs/api/windows/heapalloc) | Allocate a heap block. | | [`HeapReAlloc`](https://rux-lang.dev/docs/api/windows/heaprealloc) | Resize a heap block. | | [`HeapFree`](https://rux-lang.dev/docs/api/windows/heapfree) | Free a heap block. | | [`RtlCopyMemory`](https://rux-lang.dev/docs/api/windows/rtlcopymemory) | Copy bytes between blocks. | | [`RtlFillMemory`](https://rux-lang.dev/docs/api/windows/rtlfillmemory) | Fill a block with a byte. | | [`RtlZeroMemory`](https://rux-lang.dev/docs/api/windows/rtlzeromemory) | Zero a block. | | [`RtlCompareMemory`](https://rux-lang.dev/docs/api/windows/rtlcomparememory) | Compare two blocks. | ### Process and thread | Function | Description | | ---------------------------------------------------------------------------------- | --------------------------- | | [`ExitProcess`](https://rux-lang.dev/docs/api/windows/exitprocess) | Terminate the process. | | [`Sleep`](https://rux-lang.dev/docs/api/windows/sleep) | Suspend the current thread. | | [`GetCurrentProcessId`](https://rux-lang.dev/docs/api/windows/getcurrentprocessid) | Get the process ID. | | [`GetCurrentThreadId`](https://rux-lang.dev/docs/api/windows/getcurrentthreadid) | Get the thread ID. | ### Time | Function | Description | | ------------------------------------------------------------------------ | -------------------------------- | | [`GetTickCount64`](https://rux-lang.dev/docs/api/windows/gettickcount64) | Milliseconds since system start. | | [`GetLocalTime`](https://rux-lang.dev/docs/api/windows/getlocaltime) | Current local date and time. | | [`GetSystemTime`](https://rux-lang.dev/docs/api/windows/getsystemtime) | Current UTC date and time. | ### Text conversion | Function | Description | | ---------------------------------------------------------------------------------- | ------------------------------------ | | [`MultiByteToWideChar`](https://rux-lang.dev/docs/api/windows/multibytetowidechar) | Convert a narrow string to UTF-16. | | [`WideCharToMultiByte`](https://rux-lang.dev/docs/api/windows/widechartomultibyte) | Convert UTF-16 to another code page. | ### Dynamic libraries | Function | Description | | ------------------------------------------------------------------------ | --------------------------- | | [`LoadLibraryA`](https://rux-lang.dev/docs/api/windows/loadlibrarya) | Load a DLL. | | [`FreeLibrary`](https://rux-lang.dev/docs/api/windows/freelibrary) | Unload a DLL. | | [`GetProcAddress`](https://rux-lang.dev/docs/api/windows/getprocaddress) | Resolve an exported symbol. | ### Handles and errors | Function | Description | | -------------------------------------------------------------------- | ------------------------- | | [`CloseHandle`](https://rux-lang.dev/docs/api/windows/closehandle) | Close an object handle. | | [`GetLastError`](https://rux-lang.dev/docs/api/windows/getlasterror) | Read the last-error code. | ## Types and constants The standard handle constants, the [`CodePage`](https://rux-lang.dev/docs/api/windows/codepage) and `CreationDisposition` enums, and the `FileTime`, `SystemTime`, and `Win32FindDataA` structures are listed on the [types and constants](https://rux-lang.dev/docs/api/windows/types) page. ## Example ```rux import Windows::{ GetStdHandle, StdOutputHandle, WriteFile }; func Main() -> int { let output = GetStdHandle(StdOutputHandle); let text = "Hello, Windows!\n"; var written: uint32 = 0; let ok = WriteFile(output, text.data, text.length as uint32, @written, null); return ok != 0 && written == text.length as uint32 ? 0 : 1; } ``` # LoadLibraryA Loads a DLL into the calling process. **Package:** `Windows` **Microsoft documentation:** [`LoadLibraryA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/libloaderapi/nf-libloaderapi-loadlibrarya){rel=""nofollow""} ## Signature ```rux func LoadLibraryA( libFileName: *char8 ) -> *opaque; ``` ## Parameters | Name | Description | | ------------- | ----------------------------------- | | `libFileName` | Null-terminated ANSI DLL path/name. | ## Returns `*opaque` — a module handle on success or `null` on failure. Release a successful handle with [`FreeLibrary`](https://rux-lang.dev/docs/api/windows/freelibrary). Use a trusted, explicit path when the name is configurable to avoid unintended DLL search results. ## See also - [`GetProcAddress`](https://rux-lang.dev/docs/api/windows/getprocaddress) — resolve an export - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # MoveFileA Moves or renames a file or directory. **Package:** `Windows` **Microsoft documentation:** [`MoveFileA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/winbase/nf-winbase-movefilea){rel=""nofollow""} ## Signature ```rux func MoveFileA( existingFileName: *char8, newFileName: *char8 ) -> bool32; ``` Both names must be null-terminated ANSI paths. Returns nonzero on success or zero on failure. `MoveFileA` does not provide replacement flags for an existing destination. ## See also - [`CopyFileA`](https://rux-lang.dev/docs/api/windows/copyfilea) — copy a file - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # MultiByteToWideChar Converts multibyte text to UTF-16. **Package:** `Windows` **Microsoft documentation:** [`MultiByteToWideChar`](https://learn.microsoft.com/en-us/windows/win32/docs/api/stringapiset/nf-stringapiset-multibytetowidechar){rel=""nofollow""} ## Signatures ```rux func MultiByteToWideChar(codePage: CodePage, flags: uint32, multiByteStr: *char8, multiByte: int32, wideCharStr: *char16, wideChar: int32) -> int32; func MultiByteToWideChar(codePage: uint32, flags: uint32, multiByteStr: *char8, multiByte: int32, wideCharStr: *char16, wideChar: int32) -> int32; ``` ## Parameters | Name | Description | | -------------- | ------------------------------------------------- | | `codePage` | Source encoding. | | `flags` | Conversion flags. | | `multiByteStr` | Source bytes. | | `multiByte` | Byte count, or `-1` to include a null terminator. | | `wideCharStr` | UTF-16 destination, or `null` for a size query. | | `wideChar` | Destination capacity in UTF-16 code units. | ## Returns `int32` — UTF-16 code units written or required, or `0` on failure. For a size query, pass `null` and `0` as the destination. The `uint32` overload exists for `Std` compatibility; prefer `CodePage` in application code. ## See also - [`WideCharToMultiByte`](https://rux-lang.dev/docs/api/windows/widechartomultibyte) — convert from UTF-16 - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # ReadConsoleA Reads narrow characters from a console input buffer. **Package:** `Windows` **Microsoft documentation:** [`ReadConsole`](https://learn.microsoft.com/en-us/windows/console/readconsole){rel=""nofollow""} ## Signature ```rux func ReadConsoleA( consoleInput: *opaque, buffer: *opaque, numberOfCharsToRead: uint32, numberOfCharsRead: *uint32, inputControl: *opaque ) -> bool32; ``` ## Parameters | Name | Description | | --------------------- | ---------------------------------------------- | | `consoleInput` | Console input handle. | | `buffer` | Writable output buffer. | | `numberOfCharsToRead` | Maximum characters to read. | | `numberOfCharsRead` | Receives the actual character count. | | `inputControl` | Input control data; use `null` for ANSI input. | ## Returns `bool32` — nonzero on success or zero on failure. The function requires a console handle; use [`ReadFile`](https://rux-lang.dev/docs/api/windows/readfile) for redirected standard input. ## See also - [`GetStdHandle`](https://rux-lang.dev/docs/api/windows/getstdhandle) — retrieve standard input - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # ReadFile Reads bytes from a file or I/O device. **Package:** `Windows` **Microsoft documentation:** [`ReadFile`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-readfile){rel=""nofollow""} ## Signature ```rux func ReadFile( file: *opaque, buffer: *opaque, numberOfBytesToRead: uint32, numberOfBytesRead: *uint32, overlapped: *opaque ) -> bool32; ``` ## Parameters | Name | Description | | --------------------- | ------------------------------------------------ | | `file` | Open readable handle. | | `buffer` | Writable destination buffer. | | `numberOfBytesToRead` | Maximum requested byte count. | | `numberOfBytesRead` | Receives the actual count for synchronous I/O. | | `overlapped` | Overlapped state, or `null` for synchronous I/O. | ## Returns `bool32` — nonzero on completed success or zero on failure/pending overlapped I/O. A successful synchronous read can return fewer bytes than requested; zero bytes can indicate end of file. ## See also - [`WriteFile`](https://rux-lang.dev/docs/api/windows/writefile) — write bytes - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # RemoveDirectoryA Removes an empty directory. **Package:** `Windows` **Microsoft documentation:** [`RemoveDirectoryA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-removedirectorya){rel=""nofollow""} ## Signature ```rux func RemoveDirectoryA( pathName: *char8 ) -> bool32; ``` `pathName` must be a null-terminated ANSI path. Returns nonzero on success or zero on failure. The directory must be empty. ## See also - [`CreateDirectoryA`](https://rux-lang.dev/docs/api/windows/createdirectorya) — create a directory - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # RtlCompareMemory Counts matching bytes from the start of two memory ranges. **Package:** `Windows` **Microsoft documentation:** [`RtlCompareMemory`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlcomparememory){rel=""nofollow""} ## Signature ```rux func RtlCompareMemory( source1: *opaque, source2: *opaque, length: uint ) -> uint; ``` ## Returns `uint` — matching bytes before the first difference, up to `length`. A result equal to `length` means the ranges are equal. This is not lexicographic compare. Both pointers must be readable for `length` bytes. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # RtlCopyMemory Copies bytes between non-overlapping memory ranges. **Package:** `Windows` **Microsoft documentation:** [`RtlCopyMemory`](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa366535%28v=vs.85%29){rel=""nofollow""} ## Signature ```rux func RtlCopyMemory( destination: *var opaque, source: *opaque, length: uint ); ``` Both ranges must be valid for `length` bytes and must not overlap. Invalid pointers, undersized buffers, or overlap can corrupt memory. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # RtlFillMemory Fills a memory range with a byte value. **Package:** `Windows` **Microsoft documentation:** [`RtlFillMemory`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlfillmemory){rel=""nofollow""} ## Signature ```rux func RtlFillMemory( destination: *opaque, length: uint, fill: int32 ); ``` Writes the low 8 bits of `fill` to each of `length` bytes. `destination` must be writable for the complete range. ## See also - [`RtlZeroMemory`](https://rux-lang.dev/docs/api/windows/rtlzeromemory) — fill with zero - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # RtlZeroMemory Fills a memory range with zeros. **Package:** `Windows` **Microsoft documentation:** [`RtlZeroMemory`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlzeromemory){rel=""nofollow""} ## Signature ```rux func RtlZeroMemory( destination: *opaque, length: uint ); ``` `destination` must be writable for `length` bytes. ## See also - [`RtlFillMemory`](https://rux-lang.dev/docs/api/windows/rtlfillmemory) — fill with another byte value - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # SetCurrentDirectoryA Changes the process current directory. **Package:** `Windows` **Microsoft documentation:** [`SetCurrentDirectoryA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/winbase/nf-winbase-setcurrentdirectorya){rel=""nofollow""} ## Signature ```rux func SetCurrentDirectoryA( pathName: *char8 ) -> bool32; ``` `pathName` must be a null-terminated ANSI path. Returns nonzero on success. The current directory is process-wide state, so changing it can affect other threads and relative-path operations. ## See also - [`GetCurrentDirectoryA`](https://rux-lang.dev/docs/api/windows/getcurrentdirectorya) — retrieve the current directory - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # SetFileAttributesA Sets attributes for a file or directory. **Package:** `Windows` **Microsoft documentation:** [`SetFileAttributesA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-setfileattributesa){rel=""nofollow""} ## Signature ```rux func SetFileAttributesA( fileName: *char8, fileAttributes: uint32 ) -> bool32; ``` `fileName` must be a null-terminated ANSI path. `fileAttributes` is a bitmask of the Win32 `FILE_ATTRIBUTE_*` values; the normal-file flag must be used alone. Returns nonzero on success or zero on failure. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview - [`GetFileAttributesA`](https://rux-lang.dev/docs/api/windows/getfileattributesa) — retrieve attributes # SetFilePointerEx Moves the file pointer of an open file. **Package:** `Windows` **Microsoft documentation:** [`SetFilePointerEx`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-setfilepointerex){rel=""nofollow""} ## Signature ```rux func SetFilePointerEx( file: *opaque, distance: int64, newPos: *int64, moveMethod: SeekOrigin ) -> bool32; ``` ## Parameters | Name | Description | | ------------ | --------------------------------------------- | | `file` | Open seekable handle. | | `distance` | Signed displacement from the selected origin. | | `newPos` | Receives the absolute position, or `null`. | | `moveMethod` | `Begin`, `Current`, or `End`. | ## Returns `bool32` — nonzero on success or zero on failure. ## See also - [`Windows` types](https://rux-lang.dev/docs/api/windows/types) — shared type definitions - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # Sleep Suspends execution of the current thread. **Package:** `Windows` **Microsoft documentation:** [`Sleep`](https://learn.microsoft.com/en-us/windows/win32/docs/api/synchapi/nf-synchapi-sleep){rel=""nofollow""} ## Signature ```rux func Sleep(milliseconds: uint32); ``` ## Parameters | Name | Type | Description | | -------------- | -------- | ---------------------------- | | `milliseconds` | `uint32` | Minimum suspension interval. | The actual delay depends on system timer resolution and scheduling. A value of `0` yields the remainder of the thread's time slice to another ready thread. ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # Types and Constants Types and constants exported by the `Windows` package. **Package:** `Windows` ## Standard Device Handles Pass one of these to [`GetStdHandle`](https://rux-lang.dev/docs/api/windows/getstdhandle) to obtain a handle to the corresponding standard device. | Name | Type | Value | Description | | ----------------- | -------- | ------------ | ----------------------- | | `StdInputHandle` | `uint32` | `0xFFFFFFF6` | Standard input device. | | `StdOutputHandle` | `uint32` | `0xFFFFFFF5` | Standard output device. | | `StdErrorHandle` | `uint32` | `0xFFFFFFF4` | Standard error device. | ## `CreationDisposition` **Microsoft documentation:** [`CreateFileA`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-createfilea){rel=""nofollow""} ```rux enum CreationDisposition: uint32 { CreateNew = 1, CreateAlways = 2, OpenExisting = 3, OpenAlways = 4, TruncateExisting = 5 } ``` | Member | Value | Behavior | | ------------------ | ----: | ------------------------------------------- | | `CreateNew` | `1` | Create only when the target does not exist. | | `CreateAlways` | `2` | Create or overwrite. | | `OpenExisting` | `3` | Open only when the target exists. | | `OpenAlways` | `4` | Open or create. | | `TruncateExisting` | `5` | Open and truncate an existing file. | ## `FileTime` **Microsoft documentation:** [`FILETIME structure`](https://learn.microsoft.com/en-us/windows/win32/docs/api/minwinbase/ns-minwinbase-filetime){rel=""nofollow""} ```rux struct FileTime { lowDateTime: uint32; highDateTime: uint32; } ``` The two fields form an unsigned 64-bit count of 100-nanosecond intervals since January 1, 1601 UTC. `lowDateTime` contains the low-order bits. ## `SystemTime` **Microsoft documentation:** [`SYSTEMTIME structure`](https://learn.microsoft.com/en-us/windows/win32/docs/api/minwinbase/ns-minwinbase-systemtime){rel=""nofollow""} ```rux struct SystemTime { year: uint16; month: uint16; dayOfWeek: uint16; day: uint16; hour: uint16; minute: uint16; second: uint16; milliseconds: uint16; } ``` | Field | Typical range | Description | | -------------- | ------------- | ------------------------------ | | `year` | Full year | Year. | | `month` | `1`–`12` | January is 1. | | `dayOfWeek` | `0`–`6` | Sunday is 0. | | `day` | `1`–`31` | Day of month. | | `hour` | `0`–`23` | Hour. | | `minute` | `0`–`59` | Minute. | | `second` | `0`–`59` | Second. | | `milliseconds` | `0`–`999` | Millisecond within the second. | Filled by [`GetLocalTime`](https://rux-lang.dev/docs/api/windows/getlocaltime) (local time) and [`GetSystemTime`](https://rux-lang.dev/docs/api/windows/getsystemtime) (UTC). ## `Win32FindDataA` **Microsoft documentation:** [`WIN32_FIND_DATAA structure`](https://learn.microsoft.com/en-us/windows/win32/docs/api/minwinbase/ns-minwinbase-win32_find_dataa){rel=""nofollow""} ```rux struct Win32FindDataA { fileAttributes: uint32; creationTime: FileTime; lastAccessTime: FileTime; lastWriteTime: FileTime; fileSizeHigh: uint32; fileSizeLow: uint32; reserved0: uint32; reserved1: uint32; fileName: char8[260]; alternateFileName: char8[14]; } ``` `fileSizeHigh` and `fileSizeLow` form the high and low halves of the unsigned 64-bit file size. The filename arrays hold null-terminated ANSI strings, and applications must not depend on the reserved fields. Populated by [`FindFirstFileA`](https://rux-lang.dev/docs/api/windows/findfirstfilea) and [`FindNextFileA`](https://rux-lang.dev/docs/api/windows/findnextfilea). ## See also - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview - [`CodePage`](https://rux-lang.dev/docs/api/windows/codepage) — text-encoding identifiers for the conversion functions # WideCharToMultiByte Converts UTF-16 text to a multibyte encoding. **Package:** `Windows` **Microsoft documentation:** [`WideCharToMultiByte`](https://learn.microsoft.com/en-us/windows/win32/docs/api/stringapiset/nf-stringapiset-widechartomultibyte){rel=""nofollow""} ## Signature ```rux func WideCharToMultiByte( codePage: CodePage, flags: uint32, wideCharStr: *const char16, wideChar: int32, multiByteStr: *char8, multiByte: int32, defaultChar: *char8, usedDefaultChar: *bool32 ) -> int32; ``` ## Parameters | Name | Description | | ----------------- | -------------------------------------------------- | | `codePage` | Destination encoding. | | `flags` | Conversion flags. | | `wideCharStr` | UTF-16 source. | | `wideChar` | Code-unit count, or `-1` to include a terminator. | | `multiByteStr` | Byte destination, or `null` for a size query. | | `multiByte` | Destination capacity in bytes. | | `defaultChar` | Substitution byte, or `null`. | | `usedDefaultChar` | Receives whether substitution occurred, or `null`. | ## Returns `int32` — bytes written or required, or `0` on failure. For a size query, pass `null` and `0` as the destination. For UTF-8, pass `null` for both substitution parameters. ## See also - [`MultiByteToWideChar`](https://rux-lang.dev/docs/api/windows/multibytetowidechar) — convert to UTF-16 - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # WriteConsoleA Writes narrow characters to a console screen buffer. **Package:** `Windows` **Microsoft documentation:** [`WriteConsole`](https://learn.microsoft.com/en-us/windows/console/writeconsole){rel=""nofollow""} ## Signature ```rux func WriteConsoleA( consoleOutput: *opaque, buffer: *char8, numberOfCharsToWrite: uint32, numberOfCharsWritten: *uint32, reserved: *opaque ) -> bool32; ``` ## Parameters | Name | Description | | ---------------------- | ------------------------------------ | | `consoleOutput` | Console screen-buffer handle. | | `buffer` | Narrow characters to write. | | `numberOfCharsToWrite` | Requested character count. | | `numberOfCharsWritten` | Receives the actual character count. | | `reserved` | Must be `null`. | ## Returns `bool32` — nonzero on success or zero on failure. This call fails for redirected output; use [`WriteFile`](https://rux-lang.dev/docs/api/windows/writefile) for that case. ## See also - [`WriteConsoleW`](https://rux-lang.dev/docs/api/windows/writeconsolew) — write UTF-16 characters - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # WriteConsoleW Writes UTF-16 characters to a console screen buffer. **Package:** `Windows` **Microsoft documentation:** [`WriteConsole`](https://learn.microsoft.com/en-us/windows/console/writeconsole){rel=""nofollow""} ## Signature ```rux func WriteConsoleW( consoleOutput: *opaque, buffer: *const char16, numberOfCharsToWrite: uint32, numberOfCharsWritten: *uint32, reserved: *opaque ) -> bool32; ``` ## Parameters | Name | Description | | ---------------------- | ---------------------------------- | | `consoleOutput` | Console screen-buffer handle. | | `buffer` | UTF-16 code units to write. | | `numberOfCharsToWrite` | Requested UTF-16 code-unit count. | | `numberOfCharsWritten` | Receives the actual count written. | | `reserved` | Must be `null`. | ## Returns `bool32` — nonzero on success or zero on failure. This call fails for redirected output; use [`WriteFile`](https://rux-lang.dev/docs/api/windows/writefile) for that case. ## See also - [`WriteConsoleA`](https://rux-lang.dev/docs/api/windows/writeconsolea) — write narrow characters - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # WriteFile Writes bytes to a file or I/O device. **Package:** `Windows` **Microsoft documentation:** [`WriteFile`](https://learn.microsoft.com/en-us/windows/win32/docs/api/fileapi/nf-fileapi-writefile){rel=""nofollow""} ## Signature ```rux func WriteFile( file: *opaque, buffer: *opaque, bytesToWrite: uint32, bytesWritten: *uint32, overlapped: *opaque ) -> bool32; ``` ## Parameters | Name | Description | | -------------- | ------------------------------------------------ | | `file` | Open writable handle. | | `buffer` | Source buffer. | | `bytesToWrite` | Requested byte count. | | `bytesWritten` | Receives the actual count for synchronous I/O. | | `overlapped` | Overlapped state, or `null` for synchronous I/O. | ## Returns `bool32` — nonzero on completed success or zero on failure/pending overlapped I/O. A successful device write can report fewer bytes than requested. ## See also - [`ReadFile`](https://rux-lang.dev/docs/api/windows/readfile) — read bytes - [`Windows`](https://rux-lang.dev/docs/api/windows) — the package overview # API Reference 1. [Introduction](https://rux-lang.dev/docs/api/introduction) 2. Cross-Platform Packages - 2.1. [Format](https://rux-lang.dev/docs/api/format) - 2.2. [Io](https://rux-lang.dev/docs/api/io) - 2.3. [Math](https://rux-lang.dev/docs/api/math) - 2.4. [Memory](https://rux-lang.dev/docs/api/memory) - 2.5. [Text](https://rux-lang.dev/docs/api/text) 3. Platform-Dependent Packages - 3.1. [BSD](https://rux-lang.dev/docs/api/bsd) - 3.2. [Linux](https://rux-lang.dev/docs/api/linux) - 3.3. [MacOS](https://rux-lang.dev/docs/api/macos) - 3.4. [Windows](https://rux-lang.dev/docs/api/windows)