Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Names and scopes

Module declarations are visible throughout their module. Functions can call later functions or recurse mutually; aliases and data declarations can refer forward. Duplicate declarations and redefinitions are rejected.

Function parameters and local bindings occupy lexical scopes. A declaration in a branch does not escape that branch: using the name afterwards is an unknown binding, and the diagnostic says that the earlier binding ended with its block. Bind a mut local to a value before the block and assign it inside instead. Assigning an existing mutable local changes that local across a branch join. Pattern bindings belong to their arm and may shadow outer bindings. A new binding cannot read itself in its initializer.

Type lookup is separate from local expression names. A local can shadow a numeric cast name without changing type annotations. A binding shadowing an enum type qualifier is diagnosed rather than selecting a different constructor.

The built-in prelude is available without an import. It provides primitive and container type names (including Box and Shared), standard sums and variants (Option, Result, Some, Nothing, Ok, Err), callable and generator types, and facility names for files, errors, threads, channels, executors, and cancellation. FormatError, the error of user formatting methods and of f-strings that run them, names std.fmt.FormatError; a module that declares or imports its own FormatError, such as std.report’s, uses that one instead. Built-in operations include copy, drop, len, range, text I/O, worker submission, and channel construction and selection. Their contracts appear under facilities and the relevant language section. Top-level declarations cannot redefine built-ins. Locals can shadow expression names; unqualified standard variant patterns still denote the standard variants.

Imported names are explicit private bindings. Importing a module or protocol does not activate methods or introduce transitive names.

An import binds its alias, or else its item or module path. That name cannot be a built-in, a module declaration, or a name an earlier import bound. Importing a standard declaration that is itself the built-in, such as from std.concurrent import ThreadPoolExecutor, is allowed. The diagnostic points at the bound name, says whether it conflicts with a prelude built-in or with a declaration or import on a given line, and suggests renaming, binding with as, or removing a redundant import. Function-local bindings may still shadow imported names, as math = 3 inside a function can after import std.math as math.