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:
| Declaration | Default | Notes |
|---|---|---|
func, const, type, struct, union, enum, variant, interface | package-private | at the root or in a module |
module | package-private | pub module A::B makes both A and B public |
| struct and union fields | package-private | each field separately |
methods, constructors, operators in extend | package-private | each member separately; extend itself takes no pub |
extern func, extern block members | package-private | pub before a member, or pub extern { … } for all |
| enum members, variant cases | inherit | public exactly when their type is |
| interface requirements | inherit | public 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 program | Error |
|---|---|
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
pubto be used from another package. Destructors are called by the compiler whatever their visibility, and normally stay private. - Field visibility applies inside a
whencondition too:when #build.debugAssertionsworks becauseCoredeclares that fieldpub. rux docdocuments only effectively public declarations unless asked for private ones.
See also
- Packages and modules — the namespaces
pubapplies to - Imports — how a dependent package names public items
- Structs and Constructors — fields and the functions that build them
- Learn: Visibility, Documentation