Ruta graveolens  ·  notes from a language experiment  ·  cultivated since 2025

Visibility

This section specifies the pub visibility modifier and the intra-directory visibility rule: visibility boundaries in Rue are directories, not files.

A top-level function, struct, enum, or constant MAY be marked with the pub modifier. An item without pub is private.

A private item is visible throughout the directory that contains its defining file: code in any source file in the same directory may access it, including through a module import.

It is a compile-time error to access a private item through a module from a source file in a different directory than the item's defining file (error E0706).

A pub item is accessible through a module import from any directory.

A directory module's facade file lives inside the directory it fronts (10.1:3); the facade therefore has intra-directory access to the private items of the other files in that directory, while external importers of the directory module see only the facade's pub members.

// utils/_utils.rue — the facade
const strings = @import("strings");
pub fn format() -> i32 { strings.internal_format() }  // OK: same directory

// utils/strings.rue
fn internal_format() -> i32 { 42 }  // private

// main.rue — a different directory
fn main() -> i32 {
    let utils = @import("utils");
    utils.format()              // OK: `format` is pub
    // utils.internal_format()  // would be an error even if imported:
    //                          // private to the utils/ directory
}

Visibility is uniform across every multi-file compilation and every item kind: an item is usable outside its defining directory if and only if it is pub, whether its defining file was loaded via @import or listed explicitly in the compilation. Access to a private item through a module binding from another directory is error E0706 (10.4:18) — this is the diagnostic for privacy violations, since cross-module references are spelled through module bindings. Where an implementation still accepts an unqualified reference that names a cross-directory item directly (legacy forms removed with flat mode, ADR-0046), referencing a private item that way is error E0460.

Unqualified references resolve module-locally: a top-level name refers to an item of the referencing file (or a compiler builtin). A name defined only in other loaded files — imported or explicitly listed, pub or not, any directory — does not resolve unqualified; the reference is a name-resolution error (E0201 for variables/constants and enum type names in expressions, E0202 for functions, E0204 for types), never a silent resolution into another file. Cross-module access is spelled through a module binding (10.4:1).

// sub/lib.rue
fn secret() -> i32 { 99 }       // private to sub/
pub fn open() -> i32 { 7 }
struct Hidden { n: i32, }       // private to sub/
pub struct Shared { n: i32, }
pub const MAX: i32 = 16;

// main.rue — a different directory
const lib = @import("sub/lib");

fn main() -> i32 {
    // secret()                    // error E0202: does not resolve here (10.3:8)
    // Shared { n: 1 };            // error E0204: pub, but still module-scoped
    // lib.secret()                // error E0706: private to sub/ (10.4:18)
    // lib.Hidden { n: 1 };        // error E0706: private to sub/
    let s = lib.Shared { n: lib.MAX };  // OK: pub members through the binding
    lib.open() + s.n                    // 7 + 16 = 23
}