Module Bindings and Re-exports
This section specifies how the result of @import is bound to a name, the re-export idiom (pub const m = @import(...)), and the (non-)status of modules as runtime values. The imported module may be bound to any name (rule 4.13:87).
Binding Forms
The result of @import MAY be bound at the top level of a file with a const item, or locally inside a function body with a let statement. Member access through either binding form has the same semantics.
const math = @import("math.rue"); // top-level const binding
fn main() -> i32 {
let m = @import("math.rue"); // local let binding
math.add(1, m.add(2, 3)) // equivalent access through either
}
Per-File Scoping
A top-level const module binding is scoped to the file that declares it, like every other top-level name (10.5:2). Two files MAY bind the same name — whether to the same module or to different modules — without conflict; references in each file resolve to that file's own binding. A local binding of the same name shadows the file's module binding by the ordinary shadowing rules (5.1:10–5.1:12): after let lib = 5;, the path lib.Color.Green is field access on that local and not a module-qualified path, in every position. A local binding whose value is a module (let m = lib.geo;) names that module and member access continues through it, since a module is not a runtime value (10.4:6). Declaring two module bindings with the same name in one file remains a compile-time error (E0418).
// a.rue
const utils = @import("utils"); // fine
pub fn from_a() -> i32 { utils.one() }
// b.rue
const utils = @import("utils"); // fine: per-file, no collision
pub fn from_b() -> i32 { utils.one() }
Re-exports
A top-level const whose initializer is an @import expression makes the bound name a member of the enclosing module — a re-export. Accessing that member through an import of the enclosing module yields the inner imported module, and such access chains through multiple levels of re-export.
A const re-export follows the same visibility rules as any other item (10.3): a pub const re-export is accessible from any directory, while a non-pub re-export is private to the directory of its defining file.
// outer/_outer.rue
pub const inner = @import("inner.rue"); // pub re-export
const hidden = @import("inner.rue"); // private outside outer/
// outer/inner.rue
pub fn value() -> i32 { 42 }
// main.rue
fn main() -> i32 {
let outer = @import("outer");
outer.inner.value() // 42, chained through the re-export
// outer.hidden.value() // error: `hidden` is private (E0706)
}
Module Aliases
A top-level const whose initializer evaluates to a module — an alias of another module binding in the same file (const m2 = m;) or a member-access chain ending at a module (const math = std.math;) — is itself a module binding, with the same per-file scoping (10.4:8) and re-export semantics (10.4:3) as a direct @import binding.
const std = @import("std");
const math = std.math; // alias of a nested re-export
const m2 = math; // alias of an alias
fn main() -> i32 {
math.abs(-7) - m2.abs(7) // 0: all three name the same module
}
Value Constants as Module Members
A value constant declared at the top level of a module's file is a member of that module. Accessing it through the module yields the constant's value, with the constant's declared type — a value constant always carries an explicit type annotation (6.5:4). The access is itself compile-time evaluable, so it may appear in another constant's initializer.
A value constant member follows the same visibility rules as any other item (10.3): a pub const is accessible from any directory, while a non-pub constant is private to the directory of its defining file (E0706).
// math.rue
pub const ANSWER: i32 = 42;
const SECRET: i32 = 99; // private outside math.rue's directory
// main.rue (same directory)
const math = @import("math.rue");
const COPY: i32 = math.ANSWER; // const-evaluable member access
fn main() -> i32 { math.ANSWER + COPY } // 84
Types, Enum Variants, and Associated Functions Through a Module
The preceding sections cover functions and value constants as module members. This section specifies how a module's types, enum variants, and associated functions (6.4:11) are referenced through a module binding, and how privacy applies to each form.
A type, enum variant, or associated function of a module MAY be named through a module binding in an expression or pattern position by writing the binding, a ., and the item's path: a struct type in a struct-literal expression (m.Point { .. }), an enum variant in a variant expression or a match pattern (m.Color.Red), and an associated function in a call (m.Point.origin()). Each of these forms is accepted wherever the corresponding bare path — Point { .. }, Color.Red, Point.origin() — is accepted, and has the same meaning.
A module-qualified path MAY be used in a type position — a type annotation, a field type, or a parameter or return type. In those positions, m.Type names the type member reached through the module binding m, and m.Type(T) applies a module-qualified type constructor. This is the preferred form for referring to standard-library types such as std.option.Option(i64).
The module qualifier is load-bearing for every item kind. In the forms of 10.4:15 the type, variant, or associated function resolves as a member of the named module's file — exactly as functions and value constants do (10.4:3, 10.4:12). If the named module's file does not define the item, the reference is an unknown-member error even when another loaded file defines an item of that name; same-named types in distinct modules are distinct nominal types (10.5:2).
Privacy of module-qualified access is uniform across item kinds (10.3:7) and reports one member-access diagnostic: accessing a private member through a module binding from a source file in another directory is a compile-time error E0706 — for a struct named in a qualified struct literal or type annotation, for an enum's variant, for an associated function's enclosing type, and for functions and constants alike. A module binding named as an intermediate segment of a longer path (lib.inner.E.B, lib.inner.Point.origin(), let e: lib.inner.E) is itself a const member of the preceding module (10.4:1), so crossing a private one is this same E0706 in every position — expression, pattern, associated-function receiver, struct literal, and type annotation alike. The one exception is the comptime type-constructor application form in a type position (10.4:16), which reports E0460 (10.3:7).
An associated function's visibility follows the visibility of its enclosing type (10.3:7). A module-qualified call on a private type from another directory (lib.Secret.make()) is a compile-time error E0706 (10.4:18). An unqualified call (Secret.make()) never reaches the visibility check from another file: the bare type name does not resolve outside its defining module (10.3:8), so the reference is a name-resolution error. Binding the type value first does not launder privacy: the binding (let P = lib.Secret;) is itself a module-member access and reports E0706 at the binding site.
// sub/lib.rue
pub struct Point {
x: i32,
y: i32,
fn origin() -> Point { Point { x: 30, y: 12 } } // associated fn
}
pub enum Color { Red, Green, Blue, }
enum Hidden { A, B, } // private outside sub/
// main.rue — a different directory
const lib = @import("sub/lib.rue");
fn main() -> i32 {
let p = lib.Point { x: 40, y: 2 }; // qualified struct literal
let q = lib.Point.origin(); // qualified associated fn
let c = lib.Color.Green; // qualified variant expression
let typed: lib.Point = p; // qualified type annotation
// let h = lib.Hidden.A; // error E0706: `Hidden` is private outside sub/
let base = typed.x + typed.y + q.x + q.y; // 40 + 2 + 30 + 12 = 84
match c {
lib.Color.Red => 0, // qualified variant pattern
lib.Color.Green => base, // 84
lib.Color.Blue => 0,
}
}
A top-level const whose initializer evaluates to a type — a type alias, such as pub const R = Result(u64, E); — is a type member of its module. It is named through a module binding in every form of 10.4:15 and 10.4:16, wherever the aliased type itself may be named: as a qualified struct literal, an associated-function receiver, a variant expression, a match-pattern head, and a type annotation. The alias denotes the type its initializer evaluated to and introduces no new nominal type (10.5:2), so m.Alias.Variant and m.Declared.Variant name the same variant of the same enum.
A module-qualified reference to a type alias names the constant, not the declaration behind it, so the constant's own visibility governs the access (10.4:13): a pub alias of a private type is accessible from another directory and re-exports that type, while a private alias named from another directory is a compile-time error E0706 reporting the constant. This does not weaken 10.4:19: naming the private declaration itself (lib.Secret, including through a client-side binding let P = lib.Secret;) remains E0706, because that reference does name the declaration.
// sub/lib.rue
enum Hidden { A, B, }
pub const H = Hidden; // pub alias re-exports a private enum
pub struct Point { x: i32, y: i32, }
pub const P = Point;
// main.rue - a different directory
const lib = @import("sub/lib.rue");
fn main() -> i32 {
let p = lib.P { x: 40, y: 2 }; // alias as a struct-literal head
let h = lib.H.A; // alias as a variant expression
match h {
lib.H.A => p.x + p.y, // alias as a match-pattern head: 42
lib.H.B => 0,
}
}
Modules Are Not Runtime Values
A module is not a runtime value. It is a compile-time error to use a module binding where a value of some type is expected — as a function argument, as a struct field value, as an operand of an operator such as ==, or as an expression whose type must match an expected type. This includes the expected type (): a module expression is never treated as the unit value. The diagnostic is a type mismatch (E0206) reporting <module> as the found type.
A module expression in statement position — a bare expression statement such as m;, or a block statement whose tail is a module expression — is accepted. No type is expected there, so the module is discarded without being materialized as a value. This is not an exception to 10.4:6: no module value is produced.