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.

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:

DeclarationDefaultNotes
func, const, type, struct, union, enum, variant, interfacepackage-privateat the root or in a module
modulepackage-privatepub module A::B makes both A and B public
struct and union fieldspackage-privateeach field separately
methods, constructors, operators in extendpackage-privateeach member separately; extend itself takes no pub
extern func, extern block memberspackage-privatepub before a member, or pub extern { … } for all
enum members, variant casesinheritpublic exactly when their type is
interface requirementsinheritpublic 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:

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:

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 programError
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 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:

module Hidden {
    pub func Inside() -> int { return 4; }
}
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:

struct Secret { value: int; }

pub func Reveal() -> Secret { return Secret { value: 1 }; }
pub struct Holder { pub inner: Secret; }
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 but not directly on the concrete type:

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 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 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