Visibility
In Module, nothing was marked pub and everything still worked, because every file of a package sees everything the package declares. pub matters at a different border: the one between packages. It marks what a package lets the packages that depend on it see, and everything without it stays private. That is how a library keeps a promise — here, that a counter never passes its limit — no matter what the code using it tries.
Two packages in one lesson
Showing a border between packages needs two of them. The program is the lesson's own package; the library, Tally, sits in a folder beside Src/ with its own manifest:
Visibility/
├── Rux.toml the program, which depends on Tally
├── Src/
│ └── Main.rux
└── Tally/
├── Rux.toml Type = "SourceLibrary"
└── Src/
└── Counter.rux
The program's Rux.toml names the library under [Dependencies]:
Tally = { Path = "Tally" }
That is a path dependency, and Dependency covers it properly. For now it is enough that one rux run compiles both packages, and that Main.rux can then write import Tally::Counter;.
pub, one declaration at a time
Visibility is chosen for each declaration separately, and a member does not inherit it from its type. In Tally/Src/Counter.rux:
pub struct Counter {
pub step: int;
count: int;
}
Counter is public and so is its step field, but count is not. The functions in the extend block are each marked too, and the limit and the helper that enforces it are left private:
Declaration in Tally | pub? | What Main.rux can do with it |
|---|---|---|
struct Counter | yes | import it and name the type |
field step | yes | read it and change it |
field count | no | nothing — not even read it |
Counter(step), Tick() | yes | call them |
Count() | yes | call it, to learn the count |
const Limit, Clamp(…) | no | nothing — not even import them |
A private field keeps a promise
The library's one promise is that count never goes past Limit. Every change to it goes through Tick, and Tick clamps:
pub func Tick(self: &var Counter) {
self.count = Clamp(self.count + self.step);
}
Because count is private, there is no other way in. The program ticks three times with a step of 4:
var counter = Counter(4);
counter.Tick();
counter.Tick();
counter.Tick();
Three ticks of 4 would make 12, but the program prints count 10. Nothing in Main.rux can push it further, because nothing in Main.rux can touch count. The public constructor is the only way to make a Counter at all: a struct literal would have to set count, so it is refused.
flowchart LR
main["Visibility<br/>Src/Main.rux"]
subgraph tally ["package Tally"]
direction TB
pubs["pub: Counter, step,<br/>Counter(), Tick(), Count()"]
privs["private: count,<br/>Limit, Clamp()"]
pubs -- "used inside<br/>the package" --> privs
end
main -- "allowed" --> pubs
main -.->|"refused"| privsThis is the usual shape of a good library: a small public surface, and private details that it is free to change later without breaking anyone.
The program
The whole lesson is one package in the Examples repository. Its comments explain every step.
// Inside one package, every file sees every declaration. `pub` matters at the boundary between
// packages: it marks what a package lets the packages that depend on it see.
//
// This lesson needs two packages to show that. The companion library lives in `Tally/`, beside
// `Src/`, with its own `Rux.toml`. This package's manifest lists it under `[Dependencies]` as
// `Tally = { Path = "Tally" }`, so building this program compiles the library as well.
//
// Visibility is chosen per declaration, and a member does not inherit it from its type. `Counter`
// is public, its `step` field is public, and its `count` field is not. A caller can read `step`,
// but can learn the count only by asking `Count()`, and can change it only through `Tick()`, which
// never lets it pass the library's private limit.
import Io::PrintLine;
import Tally::Counter;
// A private function cannot even be imported. Adding it to the import above is rejected:
//
// import Tally::{ Counter, Clamp };
//
// error: function 'Clamp' is private to package 'Tally'
// help: add 'pub' to the declaration of 'Clamp'
func Main() -> int {
// The public constructor is the only way to make a `Counter` here. A struct literal would have
// to set the private `count`, and is refused for that reason.
var counter = Counter(4);
counter.Tick();
counter.Tick();
counter.Tick();
// Three ticks of 4 would make 12; the library's private limit keeps it at 10.
PrintLine("step {}, count {}", counter.step, counter.Count());
// The private field itself is out of reach, for reading as much as for writing:
//
// counter.count = 0;
//
// error: struct field 'count' is private to package 'Tally'
// help: add 'pub' before the declaration of 'count'
return 0;
}
Run it
cd Examples/Packages/Visibility
rux run
step 4, count 10
Common mistakes
A private function cannot even be imported.
import Tally::{ Counter, Clamp }; fails with error: function 'Clamp' is private to package 'Tally', and the help line says "add 'pub' to the declaration of 'Clamp'". The private constant is refused the same way, as constant 'Limit' is private to package 'Tally'.counter.count = 0; fails with error: struct field 'count' is private to package 'Tally'. Reading it is refused just the same. Ask the type instead, through Count().Counter { step: 4, count: 0 } fails with error: struct 'Counter' cannot be initialized outside its package because it has private fields, and the help line says "use a public constructor instead". Call Counter(4).pub on a struct does not reach its methods. Remove it from Tick and the call fails with error: method 'Tick' is private to package 'Tally'. Take it off the struct instead, and even the import fails: error: type 'Counter' is private to package 'Tally'.Try it yourself
stepis public, so the program may change it. Setcounter.step = 1;before the ticks and predict the output.- Add a public
Resetmethod toCounterinTally/Src/Counter.ruxthat setscountback to 0, and call it fromMain. - Make
Limitpublic, import it next toCounter, and print it. Is the promise aboutcountany weaker now?
Learn more
- Items visibility in the Rux Reference
- Constructor and Mutating method — the two kinds of function
Counterexposes - Dependency — the
Pathline that connects the two packages - Documentation — describing the
pubitems for the people who use them