all_lessons/Rust/21 · Crates, Cargo, and privacylesson 22 / 23

Crates, Cargo, and privacy — promises that travel

Lesson 20's proof that MyVec is sound rests on one sentence: only MyVec's own code writes len. Mark that field pub and a program with no unsafe in it compiles and reads memory nobody wrote. This lesson derives the rules that keep the sentence true. Privacy bounds the code a proof must read; modules and crates are the units of that bound; Cargo, semver and editions carry the promises to teams who never read your source, including one, Send, that a private field can quietly withdraw and a test can catch. The payoff: invariants that survive being depended on.

The thesis, here
Privacy is not etiquette. An unsafe block's proof holds only while every line that can touch its invariant is known; the Rustonomicon's conclusion is that unsafe code's assumptions reach its whole module, and the module boundary, enforced by privacy, is the one reliable limit on them. Crates, Cargo, semver, editions and tests scale that boundary up to people who see only your signatures.
Linear position
Forced by: A sound abstraction survives only while its invariants stay private. How is code organized into modules and crates so that privacy is enforced, dependencies are managed, and other teams can rely on what you promise?
New idea: privacy is the soundness boundary: an item's visibility names a finite region of code (its module, a parent's subtree, the crate), and only that region can break the invariant the item guards. The crate is the unit compiled, shared and versioned; semver and editions say what may change across it.
Forces next: We now have every mechanism. What does the whole language look like assembled in one program, and what does the bargain honestly cost — where is it the wrong tool, and what should a careful engineer still weigh?
The plan
Seven moves. (1) Make len public and derive visibility from what the proof loses. (2) Name the units: module, crate, package. (3) Compute who can see an item. (4) Cargo: version ranges, frozen in a lock file. (5) Semver as a promise about types, including one no signature shows. (6) Editions: change the language without breaking a crate. (7) Tests and tools: promises the compiler runs.

1 · The invariant anyone can write

Lesson 20's MyVec is three words, a pointer, a capacity and a length, and its unsafe blocks lean on what they say: len ≤ cap, and the first len slots are initialized (I1 and I2 there). The clearest case is the block that lends out len elements (Deref there, as_slice in this i32 cut-down). Now declare len with pub, as one of Lesson 20's mutants did:

mod myvec {
    use std::{alloc::{alloc, Layout}, ptr::NonNull};
    pub struct MyVec {
        ptr: NonNull<i32>,
        cap: usize,
        pub len: usize,                  // the mistake: the invariant's state is public
    }
    impl MyVec {
        pub fn with_capacity(cap: usize) -> MyVec {   // push, pop, Drop: as in Lesson 20
            let p = unsafe { alloc(Layout::array::<i32>(cap).unwrap()) }.cast::<i32>();
            MyVec { ptr: NonNull::new(p).unwrap(), cap, len: 0 }
        }
        pub fn as_slice(&self) -> &[i32] {
            // SAFETY: len <= cap, and the first len slots are initialized (the invariant)
            unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
        }
    }
}
fn main() {
    let mut v = myvec::MyVec::with_capacity(4);
    v.len = 1000;                        // safe code: no `unsafe` in main
    println!("{}", v.as_slice()[999]);   // reads far past a four-slot allocation
}

It compiles, with no unsafe in main, and was not run: reading slot 999 of a four-slot allocation is undefined behavior, so safe code has broken the promise. The fault is in the proof: its SAFETY comment holds only if every function that writes len keeps the invariant, and with pub that is every function, in every crate, that can name a MyVec, including crates not yet written. A proof over code nobody can list is not a proof: the code that can write len must be a finite, known set.

Three roads lead there. A check of len on every access costs a compare per call, and checks against what? cap is just as writable. A comment saying "never write len" is a promise, C++'s answer, and C++'s private hides the name, not the bytes:

class MyVec {
    int* ptr = nullptr; std::size_t cap = 0; std::size_t len = 0;   // private
};
void hostile(MyVec& v) {
    // v.len = 1000;                                // rejected: len is private
    reinterpret_cast<std::size_t*>(&v)[2] = 1000;   // compiles: the bytes are reachable anyway
}

That compiles (Apple clang 21, -std=c++20): a cast reaches what the name protected, Lesson 01's type-punning family. The third road is free at run time: make the field unnameable outside code the author controls. In safe Rust that is a wall, because safe code has no such cast; it may build a raw pointer, but following one takes unsafe. Delete pub:

mod myvec {
    pub struct MyVec {
        len: usize,                      // private again (ptr and cap as before)
    }
    impl MyVec {
        pub fn new() -> MyVec { MyVec { len: 0 } }
        pub fn len(&self) -> usize { self.len }
    }
}
fn main() {
    let mut v = myvec::MyVec::new();
    v.len = 1000;                        // the write from above
    println!("{}", v.len);               // and a read
}
Reading the error
error[E0616]: field `len` of struct `MyVec` is private
  --> src/main.rs:12:7
   |
12 |     v.len = 1000;                        // the write from above
   |       ^^^ private field

error[E0616]: field `len` of struct `MyVec` is private
  --> src/main.rs:13:22
help: a method `len` also exists, call it with parentheses
The message says what is private and where, and for the read it found a public method of that name. It cannot know why nothing public writes len: the missing setter is the design. rustc --explain E0616 offers two fixes, make the field public or add a getter; for MyVec the first is Lesson 20's mutant. A privacy error asks which public method keeps the invariant (push? truncate?), not which field to widen.

The need decides three design questions. The default? If items were public until marked, one forgotten keyword would reopen a proof; private by default makes forgetting safe. The unit? Not the type: an invariant often spans types that must all see the fields, a vector and its iterator, and unsafe code's assumptions reach the whole module. So an item is private to its module, nested modules included: a child sees everything in its ancestors, a parent nothing private in its children, which is why a tests module inside myvec may check len. Widening? In steps, each naming a region a proof would have to read:

WrittenWho may name the itemWhat a proof about it must read
nothing (private)its module, and the modules inside itone module
pub(super)the parent module, and inside itthe parent's subtree
pub(in crate::a)the enclosing module crate::a, and inside ita's subtree
pub(crate)every module of this crate; no other cratethe crate
pubany code that can reach every module on its path, in any crateeveryone, from now on

Fields, methods and associated functions carry their own visibility: pub struct opens the name, not the fields. That shuts the second door, a struct literal that skips the constructor making the invariant true:

mod myvec {
    pub struct MyVec { cap: usize, len: usize }
    impl MyVec {
        pub fn with_capacity(cap: usize) -> MyVec { MyVec { cap, len: 0 } }   // makes the invariant true
    }
}
fn main() {
    let honest = myvec::MyVec::with_capacity(4);
    let forged = myvec::MyVec { cap: 4, len: 1000 };   // the same lie as `v.len = 1000`
}

So every MyVec starts life through a constructor. The remaining ways to fail, in one program:

mod collections {
    pub mod myvec {
        pub struct MyVec { len: usize }
        impl MyVec {
            pub fn new() -> MyVec { MyVec { len: 0 } }
            fn grow(&mut self) {}                              // private
            pub(super) fn audit(&self) -> usize { self.len }   // `collections` and below
        }
        mod raw { pub fn layout() {} }                         // `pub` inside a private module
    }
}
fn main() {
    let mut v = collections::myvec::MyVec::new();
    v.grow();                                                  // a private method
    v.audit();                                                 // main is in the root, not in `collections`
    collections::myvec::raw::layout();                         // a private module on the path
}

E0603 names the first private step of a path and stops: layout is pub, but its module is not. E0624 is a private method; pub(super) on audit reaches collections, not the root. Each rule shrinks a proof back to a region, and regions are drawn in modules and bounded by the crate, two units we have used without defining.

2 · Modules, crates, packages

A module is a named scope for items: mod myvec { … } inline, or mod myvec; to read myvec.rs (or the older myvec/mod.rs). Modules form a tree rooted at crate, the file compilation starts from, and a path names an item through it: absolute from crate::, relative with self:: or super::. A crate is the smallest unit the compiler takes in at a time, a library or a binary with a main, so it is the unit privacy is enforced across: pub(crate) stops at its edge, and another crate can use an item only if it is pub at every step from the root. A package is what Cargo builds: a Cargo.toml, at most one library crate and any number of binaries (src/lib.rs, src/main.rs).

use, which Lesson 02 gave as a recipe, imports no code: it binds a shorter name to an item that already exists, only in the module where it is written, and compiles to nothing. The root's use does not reach a child:

use std::collections::HashMap;          // a name for std's HashMap, in this module only
mod report {
    pub fn tally(words: &[&str]) -> HashMap<String, u32> {   // not a name in `report`
        let mut counts = HashMap::new();
        for w in words { *counts.entry(w.to_string()).or_insert(0) += 1; }
        counts
    }
}
use crate::reports::tally;              // no module `reports`
fn main() {
    let t = report::tally(&["a", "b", "a"]);
    println!("{}", t.len() + totl);     // no value `totl`
}

Lesson 02 counted four name-resolution codes among the ten most common errors. Three are here, one lookup failing in three positions: a name used as a type or a value (E0425), a path's first segment (E0433), a use (E0432). The fourth, E0412, is gone: on rustc 1.98.1 a missing type is E0425. The reverse of hiding is re-exporting: pub use storage::disk::flush; gives an item a second, public path, so the module holding it can stay private and free to change.

Go chooses otherwise (the Go track's Lesson 12): the package is the unit of compilation and of encapsulation, capitalization the whole access rule, internal/ a hidden subtree. Rust separates the two: modules draw privacy as finely as an invariant needs, the crate bounds it. Whether an access compiles is then a walk down a path, one visibility per step, which is mechanical enough to compute.

3 · Who can see this item?

The widget runs §1's rule: an access from module F compiles when each module on the item's path, then the item, is visible from F (F lies inside the region its visibility names); otherwise it fails with the code rustc reports, stopping where rustc stops (a literal on a private path never reaches its fields). Checked against rustc 1.98.1 on 320 random module trees and 3,853 accesses, inside the crate and from another crate: no disagreement.

Who can see this item?
One crate's modules, another crate on top. Pick an access and slide the accessing module down the tree: each row says whether the access compiles there, so green rows are the code that can make it. The violet bar is the region the item's own visibility names.
rustc says
—
decided by
—
modules that may
—
another crate may
—
Show the core JS
function visibleFrom(tree, scope, from) {  // scope: 'world', or the module a visibility names
  if (scope === 'world') return true;  // pub
  if (from === EXT) return false;  // anything narrower never leaves the crate
  return isWithin(tree, from, scope);  // from is that module, or inside it
}
// what rustc reports for this access when it is the only code in module `from` (or in another crate)
function check(tree, from, pr) {
  var steps = stepsOf(tree, from, pr), bad = steps.filter(function (x) { return !x.ok; }), errors = [], masked = null;
  var path = bad.filter(function (x) { return x.path; }), own = bad.filter(function (x) { return !x.path; });
  function err(code, b) { return { code: code, msg: message(code, pr, findStruct(tree, pr), b) }; }
  if (path.length) errors.push(err('E0603', path));  // rustc names only the first private step
  if (own.length) {  // the field, method or literal itself
    var e = err({ field: 'E0616', method: 'E0624', assoc: 'E0624', build: 'E0451' }[pr.kind], own);
    if (e.code === 'E0451' && path.length) masked = e; else errors.push(e);  // a later pass, skipped once an error exists
  }
  return { ok: !errors.length, errors: errors, steps: steps, masked: masked, code: errors.map(function (x) { return x.code; }).join(' + ') };
}

What to try. On the first example drag the slider from 0 to 4: writing v.len fails with E0616 from another crate, crate and collections, and compiles from myvec and its tests: 2 of 5 modules. Set its visibility to pub(super): 3 of 5; pub(crate): 5 of 5, another crate still no; pub: everywhere, Lesson 20's mutant. Choose the access MyVec { ptr, cap, len }: E0451 outside myvec, while MyVec::with_capacity(…) compiles from all 5 modules and other crates. In the restaurant the root gets E0603 for module hosting, and front_of_house gets it for add_to_waitlist: a parent cannot see into a child's private items. In the source, pub mod hosting alone leaves 1 of 3; pub on the function too makes 3 of 3. The widget's "another crate" is a stand-in: which crates those are, and at which versions, is still open.

4 · Cargo, minimal

Privacy says what another crate may touch; Cargo says which crate, at which version:

[package]
name = "app"
version = "0.1.0"
edition = "2024"                  # this crate's edition (§6)

[dependencies]
geom = "1.4"                      # a requirement, not a version
cache = { path = "../cache", version = "1" }   # a local crate, held to the same rule

cargo new writes the [package] table (edition = "2024" on cargo 1.98.1); cargo build, run and test (§7) build, run and test the package with its dependencies. A requirement is a range: "1.4" is a caret requirement, >=1.4.0, <2.0.0; in a 0.x crate the first non-zero digit counts as major, so "0.2.3" means >=0.2.3, <0.3.0. The resolver picks the highest published version in range and records every choice in Cargo.lock. Later builds reuse the locked versions while they satisfy the manifest (Cargo.toml), so an upstream release changes nothing until cargo update (and --locked makes Cargo fail rather than touch the file). That makes an application's build reproducible: the Cargo guide says, when in doubt, commit the lock file (and let CI also try the newest versions).

Two requirements on one crate meet in the resolver: if semver-compatible ("1.2", "1.4"), Cargo unifies them to one version, built once, whose types both users share; if not ("0.6", "0.7"), it builds both. Go's minimal version selection answers the other way: modules state minimum versions, the build takes the highest minimum anyone requires and never a newer release, so the answer cannot drift and needs no lock file; an incompatible major changes the import path (/v2), making two majors two modules by name. Out of scope: workspaces, and features and publishing beyond one fact each (Cargo builds a crate once with the union of its users' features, so a feature may only add; a published version is never overwritten, and yanking only keeps new resolutions from choosing it). All of it assumes that a version number promises what may have changed.

5 · Semver is a promise about types

A version is MAJOR.MINOR.PATCH, and Cargo reads it with one rule: versions are compatible when their first non-zero component is equal, so 1.4.0 → 1.9.2 must break no one and 1.9 → 2.0 may. What counts as breaking is listed in Cargo's SemVer chapter, whose guidelines mostly ask: would code that compiled against the old version stop compiling?

Change in a libraryClassWhy
rename, move or remove a public itemmajordownstream paths stop resolving
add a public itemminornothing named it (glob imports aside)
add a private field to a struct whose fields are all publicmajordownstream literals stop compiling (below)
add or remove private fields when one already existsminorno one outside could write a literal anyway
add a trait method without a defaultmajordownstream impls lack it (E0046, Lesson 13)
add an enum variant without #[non_exhaustive]majordownstream matches stop being exhaustive (E0004); #[non_exhaustive] makes other crates write a wildcard arm, so the variant is free
tighten a generic bound; loosen onemajor; minora caller meeting the old bound may miss the new

The third row, compiled: the library added a private field, and the downstream line did not change.

mod geom {                                      // version 1.1.0: one private field added
    pub struct Point { pub x: f64, pub y: f64, cached_norm: f64 }
}
fn main() {
    let p = geom::Point { x: 3.0, y: 4.0 };      // written against 1.0.0, unchanged
    println!("{}", p.x);
}
error: cannot construct `Point` with struct literal syntax due to private fields
  = note: ...and other private field `cached_norm` that was not provided

(No code this time; E0451 is for a literal that names a private field.) A struct marked #[non_exhaustive] from its first release, with a constructor, can grow fields in a minor version. Now the change no row lists. Lesson 16 showed that Send and Sync are auto traits, derived from every field, private ones included, so a private field is part of the public contract. Version 1.0.0 of a cache crate holds one private u64; an application moves a Cache into a thread. Version 1.1.0 adds a private field, minor by the rows above, and changes no signature:

$ cargo --version
cargo 1.98.1 (797e8a9bc 2026-08-05)
$ cat src/main.rs
use std::thread;
fn main() {
    let mut c = cache::Cache::new();
    let worker = thread::spawn(move || c.hit());   // needs Cache: Send
    println!("{}", worker.join().unwrap());
}
$ cargo run -q                       # cache 1.0.0: struct Cache { hits: u64 }
1
$ cargo build -q                     # cache 1.1.0 adds: last: Option<Rc<String>>, private
error[E0277]: `Rc<String>` cannot be sent between threads safely
 --> src/main.rs:4:32
note: required because it appears within the type `Option<Rc<String>>`
note: required because it appears within the type `Cache`
 --> …/cache/src/lib.rs:2:12
note: required by a bound in `spawn`

Neither the application nor anything it can name changed; its build did. Cargo's SemVer chapter has no entry for this, but the Rust lang team's design note on auto traits calls each auto trait a semver hazard for libraries, for exactly this reason. The flip is mechanical: the series' trait solver (Lesson 16) predicts it from field types alone, and on 120 random private field types checked for Send and Sync against rustc 1.98.1 (240 checks, 117 yes-to-no flips) it never disagreed. The defense: a test that stops compiling when the promise is withdrawn (§7).

One more consequence. In the Rust Book's quiz data, "semver dependency deduplication" went from 18% to 70% correct after one added paragraph, the largest gain of twelve interventions. When the graph needs two semver-incompatible versions of one crate, Cargo builds both (§4), and to the compiler they are two crates: same name, same definition, different types:

$ grep geom Cargo.toml
geom = { path = "../geom1", version = "1.4" }
geom2 = { path = "../geom2", version = "2.0", package = "geom" }
$ cargo build -q
error[E0308]: mismatched types
 --> src/main.rs:3:32
  |
3 |     println!("{}", geom2::norm(&p));           // expects geom 2.0.0's Point
  |                    ----------- ^^ expected `geom::Point`, found a different `geom::Point`
note: there are multiple different versions of crate `geom` in the dependency graph

Neither value is wrong; they are different types (cargo tree -d lists such duplicates): a major version is a new set of types. Semver governs changes to your crate; the language changes too, under crates nobody can edit all at once.

6 · Editions

Rust promises that a stable feature keeps working in every later release. Some improvements cannot keep that promise: making async a keyword breaks every variable named async. Such changes go into an edition, a per-crate opt-in set by the edition key (2015, 2018, 2021 or 2024; no key means 2015, with a warning). One function, two editions, one compiler:

use std::cell::RefCell;
fn len_of_borrow() -> usize {
    let c = RefCell::new(String::from("abc"));
    c.borrow().len()                    // a temporary `Ref` in the tail expression
}
fn main() { println!("{}", len_of_borrow()); }
use std::cell::RefCell;
fn len_of_borrow() -> usize {
    let c = RefCell::new(String::from("abc"));
    c.borrow().len()                    // a temporary `Ref` in the tail expression
}
fn main() { println!("{}", len_of_borrow()); }
3

Under 2021 (the first badge) the tail expression's temporary Ref outlives c; under 2024 it is dropped first. Editions do not fork the language: crates of different editions must interoperate, and all compile to the same internal representation, so migrating is each crate's private decision. A 2015 crate that named a function async, called from a 2024 crate through a raw identifier:

$ grep edition Cargo.toml ../oldlib/Cargo.toml
Cargo.toml:edition = "2024"
../oldlib/Cargo.toml:edition = "2015"
$ cat ../oldlib/src/lib.rs src/main.rs
pub fn async() -> &'static str { "written in 2015, when async was a plain name" }
fn main() {
    println!("{}", oldlib::r#async());   // a 2024 crate calling a 2015 crate
}
$ cargo run -q
written in 2015, when async was a plain name

Edition 2024 changes five things this track teaches, and cargo fix --edition applies what it can of a migration mechanically, printing what it cannot.

In edition 2024Where the track covers it
temporaries in a block's tail expression drop before its locals (above)only here
an if let's scrutinee temporaries drop before its else block17
a return-position impl Trait captures every lifetime in scope12
a reference to a static mut is denied17, 19
an unsafe fn's body needs its own unsafe { } blocks (a warning)19

An edition protects old crates from the language's changes. A test protects your users from yours.

7 · Tests, docs, tooling — promises the compiler runs

cargo test builds and runs three kinds of test. Unit tests sit in a #[cfg(test)] mod tests inside the module they test, compiled only for testing; as a child module they see its private items (§1). Integration tests in tests/ are separate crates and see only the public API. Doc tests are the code blocks in /// comments: rustdoc compiles and runs each one, so a stale example fails cargo test, and a compile_fail block must be rejected (a verdict the rustdoc book warns may change between releases). This track works the same way: every badge here is rustc 1.98.1's verdict, recomputed by a script that compiles each block.

/// Counts lookups that hit.
///
/// ```
/// let mut c = cache::Cache::new();
/// assert_eq!(c.hit(), 1);
/// ```
pub struct Cache {
    hits: u64,
}
impl Cache {
    pub fn new() -> Cache { Cache { hits: 0 } }
    pub fn hit(&mut self) -> u64 { self.hits += 1; self.hits }
}

#[cfg(test)]
mod tests {
    use super::*;                   // a child module sees its parent's private items
    #[test]
    fn starts_at_zero() { assert_eq!(Cache::new().hits, 0); }
    #[test]
    fn stays_send() { fn is_send<T: Send>() {} is_send::<Cache>(); }
}
$ cargo test                         # tests/api.rs holds counts_hits, a test of the public API
     Running unittests src/lib.rs (target/debug/deps/cache-0e7f0c23e947499a)
test tests::starts_at_zero ... ok
test tests::stays_send ... ok
     Running tests/api.rs (target/debug/deps/api-bae8d392ed329275)
test counts_hits ... ok
   Doc-tests cache
test src/lib.rs - Cache (line 3) ... ok

stays_send answers §5: add the Rc field and this crate's own cargo test fails to compile, with the same E0277, before any user sees it. Around the tests the default toolchain ships cargo fmt (layout, never meaning), cargo clippy (lints for likely mistakes; its correctness group is an error by default), cargo fix (applies suggestions with a clear fix) and rustc --explain. With privacy in place, Lesson 02's two kinds of error become three:

Local: read it, fix the line
E0425 (types too, now), E0433, E0432, E0308, E0599, E0277: one name, one line.
Law: change the design
E0382, E0499, E0502, E0505, E0506, E0597, E0515: several program points (Lessons 03–08).
Boundary: ask who owns it
The item exists, but not for you. Privacy (E0603, E0616, E0451, E0624): ask its author. Stability (E0658, below): the toolchain. Edition (gen is reserved in 2024): the manifest.
fn main() {
    let segs = ["crate", "collections", "myvec"];
    let path: String = segs.iter().copied().intersperse("::").collect();   // unstable as of 2026-09
    println!("{path}");
}
error[E0658]: use of unstable library feature `iter_intersperse`

Common mistakes / failure modes

"Privacy is for encapsulation style"
It is the soundness boundary: an unsafe proof must cover every line that can touch its invariant, and privacy makes that set finite. pub len let safe code cause undefined behavior (§1).
"pub(crate) is basically private"
It is crate-wide: every module may use the item, so a proof must read the whole crate; it only stops other crates (5 of 5 modules in the widget, §3).
"Version numbers are labels, and the lock file is optional"
A version is a promise the resolver acts on, taking the newest compatible release; Cargo.lock freezes the choice, so without it a fresh checkout of an application may build different code (§4).
"A private field cannot affect my API"
Auto traits are computed from every field: a private Rc removes Send and Sync with no signature change; a private field added to an all-public struct breaks literals (§5).
"Editions fork the language"
An edition is a per-crate opt-in, and crates of different editions call each other: a 2024 crate called a 2015 crate's async function (§6).
"use imports code"
It binds a name to an item that already exists, in one module, and compiles to nothing; a child module does not see its parent's use (§2).

Checkpoint exercise

Try it
In the widget's first example (access v.len = …), predict the narrowest visibility for len that lets crate::collections write it while keeping crate::app out, and how many modules it shades; then set it and check. Two spellings give the same region: say why. Next, in a scratch file, give collections a pub fn reset(v: &mut myvec::MyVec) that writes v.len = 0, compile it with that visibility, then move reset into app and read which code rustc prints. Finally, list the code a reviewer must now read to trust as_slice.

Where this points next

Every mechanism is on the table, from an owner and a law to the unsafe boundary and now the privacy, crates and versions that let proofs travel between teams. Each lesson priced its own piece; none has run them together or totalled the bill. Lesson 22 asks the closing question: built into one program, what do they look like, what does the whole bargain cost, and when is that the wrong price to pay?

Takeaway
An unsafe proof holds only while the code that can break its invariant is known, and privacy makes that set finite: an item is private to its module and the modules inside it unless widened (pub(super), pub(in path), pub(crate), pub), and safe code has no cast around it. A private field forbids outside writes (E0616) and literals (E0451). The crate is the unit compiled, shared and versioned. Cargo resolves version ranges to the newest compatible release and freezes the answer in Cargo.lock. Semver promises what may change, auto traits included: a private Rc field removes Send. Two incompatible versions of a crate are two crates. Editions change the language per crate and interoperate. Tests, doc tests and a Send assertion are promises the compiler runs.

Interview prompts

Companion reads: Go · 12 Packages and modules (capitalization, internal/, MVS), Go · 15 Testing and tooling, C++ · 11 Resource-safe class design (invariants kept by discipline), C++ · 16 const and constexpr (types as a tool).