We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 25 of 26
pub on structs and enums behaves differently
A shape module owns a rectangle type whose fields nobody outside may touch.
Two functions are graded.
pub fn rect_areas(dims: Vec<(i64, i64)>) -> Vec<Option<u64>>
pub fn orientations(dims: Vec<(i64, i64)>) -> Vec<String>
A rectangle is only valid if both sides are strictly positive and both fit in
a u32. rect_areas returns the area for valid dimensions and None
otherwise; orientations returns portrait, landscape, square, or
invalid.
Private fields are how you hold an invariant
Rect stores u32s and promises they are non-zero. That promise is only
worth anything if the only way to build a Rect is through
Rect::new, which checks. Private fields are what makes the constructor the
sole entry point:
mod shape {
pub struct Rect { w: u32, h: u32 } // pub type, private fields
impl Rect {
pub fn new(w: i64, h: i64) -> Option<Rect> { /* validates */ }
}
}
Outside the module, Rect { w: 3, h: 4 } is E0451 (“field w of struct
Rect is private”) and rect.w is E0616 (“field w of struct Rect is
private”). The starter ships the second one. Inside the module, both work
normally — privacy in Rust is module-scoped, and child modules can see
their ancestors’ private items.
This is the moment privacy stops feeling like bureaucracy. It is not about hiding things from colleagues; it is the only mechanism in the language for saying “this combination of field values cannot occur, and I have made it impossible to create one.”
The asymmetry
Here is the fact that catches everyone:
-
pub structleaves every field private unless you mark each onepub. -
pub enummakes every variant public, automatically, with no way to hide one.
That looks inconsistent until you ask what each would be for. A struct with
public fields has no invariants — anyone can write any value into any field —
so private-by-default is the useful default. An enum with private variants
would be unusable: you could not match on it, could not construct it, could
not do anything. The defaults differ because the useful answer differs.
A consequence worth knowing: ..Default::default() does not bypass
privacy. Struct update syntax is still a struct literal, and a struct
literal outside the defining module cannot name private fields, whether it
lists them or not.
pub, and the other visibilities
pub means “visible wherever the containing module is”. There is also
pub(crate) (this crate only — the workhorse for internal APIs),
pub(super) (the parent module) and pub(in path). Reaching for a private
item from outside is E0603, “module shape is private” or “function
new is private”.
Validating at the boundary
Rect::new takes i64 and returns Option<Rect> — wide input, narrow
storage, explicit failure. u32::try_from(w) is the tool: it returns Err
for negatives and for anything above u32::MAX, which is exactly the two
ways the caller can be wrong. Compare w as u32, which would silently turn
-1 into 4294967295.
Notice how the types make the shape of the design visible from outside:
the module’s public surface is new -> Option<Rect>, area -> u64,
orientation -> Orientation, and there is no fourth way to reach the data.
Lints for library authors
partial_pub_fields (allow-by-default) flags a struct where some fields
are pub and some are not, on the grounds that the type has not decided
whether it is a data bag or an abstraction. exhaustive_structs and
exhaustive_enums (both restriction) force you to opt into
#[non_exhaustive], which the next item covers.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.