We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modules, Visibility, Testing and Docs step 17 of 22
SemVer for Rust APIs: what actually breaks
Compare two simplified descriptions of a Rust API and classify the change
as "major", "minor" or "patch".
pub fn classify(before: Vec<String>, after: Vec<String>) -> String
Apply the rules below and return the most severe result:
major takes precedence over minor, which takes precedence over patch.
These rules define this exercise's model. They are not a complete predictor
of compatibility for arbitrary Rust code.
Parse entities, not whole lines
Each description contains facts in this format:
struct Name exhaustive
struct Name non_exhaustive
enum Name exhaustive
enum Name non_exhaustive
field Struct.name pub
field Struct.name private
variant Enum.Name
trait Name
method Trait.name required
method Trait.name defaulted
fn name Clone+Send
fn name -
A type has one of the two exhaustiveness markers; a field has one visibility;
a method is either required or defaulted. - means no function bounds.
Bound names separated by + form a set, so Clone+Send and Send+Clone
are equivalent. Treat bound names as independent labels rather than resolving
Rust trait implications.
Inputs use the listed forms with consistent entity definitions and owners. Whitespace between tokens and line order do not affect the classification. An empty description is allowed. Identical repeated facts have no effect. The model includes private fields because their presence affects whether outside code could construct a struct literal.
Identify a type by its kind and name, a field/variant/method by its path,
and a trait or function by its name. For example, changing
struct S exhaustive to struct S non_exhaustive changes one type's
attribute. It does not remove that type and introduce an unrelated one.
Apply the exercise's rules
Start with patch. Any of the following findings can raise that level.
| Change | Classification |
|---|---|
| Remove any recorded entity, including a private field | major |
| Add a type, trait or function | minor |
| Add a field to a newly introduced struct | minor |
| Add a field to an existing struct that previously had a private field | minor |
| Add a field when the resulting struct is non-exhaustive | minor |
| Add any other field to an existing struct | major |
| Add a variant to a new enum or a resulting non-exhaustive enum | minor |
| Add a variant to an existing exhaustive enum | major |
| Add a required method to an existing trait | major |
| Add a defaulted method, or any method to a new trait | minor |
| Change a method from required to defaulted | minor |
| Change a method from defaulted to required | major |
| Change a type from exhaustive to non-exhaustive | major |
| Change a type from non-exhaustive to exhaustive | minor |
| Change a field from public to private | major |
| Change a field from private to public | minor |
For a function that exists in both descriptions, compare its bound sets:
- Equal sets: no change.
- The new set is a proper subset of the old one: minor.
- Any other change: major. This includes adding a bound or replacing one bound with another.
All rules apply together. Adding a field may qualify as minor because the resulting struct is non-exhaustive, but adding that attribute at the same time is itself major. The combined answer is therefore major.
If no rule reports a change, return patch. Reordering lines or bound names
alone should not raise the level.
Why the structural distinctions matter
Adding a field to an all-public exhaustive struct can break outside struct
literals and patterns that list all fields without ... If the struct
already has a private field, outside code already cannot construct it with
a literal. A non-exhaustive struct also prevents that construction outside
its defining crate.
Adding a variant to an exhaustive enum can break a match that previously covered every variant. A non-exhaustive enum already requires an outside match to allow for unknown variants. Inside the defining crate, this attribute does not impose those external construction and matching limits.
These explain the exercise's rules, but do not establish that a private field change is always compatible in a real library.
Where real compatibility needs more information
A public API includes more than reachable names: signatures, trait implementations, auto traits and documented behavior can also matter. This input language does not describe all of them.
For example, adding a private Rc field can make a public type stop being
Send, breaking callers that move it between threads. The classifier cannot
detect this because field types are absent. Conversely, removing a private
field does not necessarily remove any callable public path; the model's
blanket removal rule deliberately does not analyze that distinction.
The model classifies an added defaulted method as minor. In real code it can introduce ambiguity between methods from two traits in scope. A matching inherent method generally takes precedence rather than automatically causing that ambiguity. Trait-object compatibility is another consideration, as discussed in the sealed-trait exercise.
The minimum supported Rust version is also outside this model. Raising it
can exclude users of older compilers. Cargo's rust-version metadata and
resolver policy help select compatible dependency versions when available;
they do not guarantee that every dependency constraint has a compatible
solution or decide your project's release policy.
Use compatibility tools, downstream compile checks and behavior tests as complementary evidence. No structural classifier proves every update safe. Consult the Cargo compatibility guide when reviewing an actual release.
Repair the starter
The starter compares sets of complete lines. It mistakes an attribute or bound change for a removal, and treats every addition as minor. Parse the facts into maps and sets, then apply the entity-specific rules.
Passing requires compilation, expected classifications and Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.