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

Use

use imports a name into the current scope. It follows the same rules as Rust 2018 paths: every name in scope arrived either by an explicit declaration (let, type, mod), an explicit use, or one of the two preludes described below. Nothing else is implicit.

// fully qualified call
sys::net::subscribe(...);
// import the name `net`
use sys::net;
// now `net` is in scope
net::subscribe(...);
// import the item itself
use sys::net::subscribe;
// same function
subscribe(...)

Note that use sys::net imports the single name net — it does not bring the contents of sys::net into scope. To import an item, name the item. An imported name covers every declaration kind that shares it: if a module exports both a type Color and a value Color, one use m::Color imports both.

Forms

// one name
use tui::line;
// group
use tui::{line, span, style};
// groups nest; self names the prefix
use sys::{fs::{self, watch}, io};
// rename
use tui::line as tline;
// glob: import everything array exports
use array::*;
// from the parent module
use super::{answer, identicon};
// from the current package's root
use package::tui::pump;

self inside a group names the prefix itself: use sys::{self, net} is use sys plus use sys::net. A trailing comma is allowed. The same syntax works in .gxi interface files.

Globs are legal but discouraged outside test modules — an explicit list documents where every name comes from, and two globs that both provide a name make that name an error at its first use.

In the UI packages a widget module and its main function share a name. Import the function by its path, use tui::text::text, and the module’s other items the same way, use tui::overlay::{Layer, layer, overlay}. A group may name the module (self) or the function, not both: use tui::overlay::{self, overlay} is refused, because both are overlay. Collect everything from one root in one statement:

use tui::{
  line, style,
  block::block,
  input_handler::{Event, input_handler, on_press},
  layout::{child, layout}
};

Path roots

The leading segment of any path — in use, in expressions, and in type annotations — is one of:

  • a package name: array::len, tui::line. Every installed package’s name is usable as a path root anywhere, with no use (this is the package prelude; only the name is implicit, not the package’s items).
  • self — the current module: self::helper.
  • super — the parent module, chainable: use super::super::x. In a program file’s top-level submodules, super reaches the bindings at the top of the file.
  • package — the root of the current package (like Rust’s crate): use package::tui::pump.

The same grammar works outside use: super::helper(x) and let p: package::tui::Pump = ... are both valid.

The core prelude

The root items of the core package (print, println, cast, error, filter, once, never, range, Any, Result, …) and the primitive type names are implicitly imported into every module. They can be shadowed by explicit declarations and imports.

Scoping

use is a statement, valid in any block or module body (not in expression position), and imports into the enclosing lexical scope only:

let list = {
  use array::map;
  map([1, 2, 3, 4, 5], |x| x * 2)
};
list

will print [2, 4, 6, 8, 10]

let list = {
  use array::map;
  map([1, 2, 3, 4, 5], |x| x * 2)
};
map(list, |x| x * 2)

will not compile — the import is local to the block:

$ graphix test.gx
Error: in file "test.gx"

Caused by:
    0: at: line: 5, column: 1 in file test.gx, in: map
    1: map not defined

Unlike declarations, imports carry no ordering constraint within a module: use self::sub::x may stand above mod sub;. Module and type names are visible whatever their order; a value is not, because a module’s body runs where its mod statement stands, so a sub-module that imports a parent’s value must be declared below that value.

Shadowing and collisions

Within one scope, names resolve in precedence order: an explicit declaration beats an import, an import beats a glob, a glob beats the package prelude, and the package prelude beats the core prelude. Inner scopes shadow outer scopes as usual, so a block-local use shadows an outer let:

let map = |a, f| "hello you called map!";
let list = {
  use array::map;
  map([1, 2, 3, 4, 5], |x| x * 2)
};
(list, map(list, |x| x * 2))

prints

$ graphix test.gx
([2, 4, 6, 8, 10], "hello you called map!")

The error cases:

  • a declaration and an import of the same name in the same scope: error at the use if the declaration comes first; if the declaration comes after, it shadows the import (rebinding a name with let is idiomatic Graphix and imports are no different)
  • two explicit imports of the same name: error — rename one
  • two globs that both provide a name: allowed at import, error at the first use of the ambiguous name, naming both sources