Prompt and context
You maintain a parser library whose index ranges are copied by several lightweight handles, and whose tests should print values when a pattern fails. The project currently uses legacy core::ops ranges and matches!, and is upgrading to Rust 1.96. Explain the new range types, iteration semantics, public APIs, assertion macros, and release plan.
What the interviewer evaluates
- Whether you know that legacy Range implements
Iterator, while newcore::rangetypes useIntoIteratorand can beCopy. - Whether you avoid assuming the
0..nsyntax has already switched to the new type. - Whether you know
assert_matches!is not in the prelude and must be imported explicitly for diagnostics. - Whether you handle MSRV, documentation, macro expansion, Wasm linking changes, and rollback.
Clarifying questions to ask
- Is the library’s MSRV Rust 1.96, or must older compilers remain supported?
- Must a range be stored and iterated later, or consumed in one loop?
- Should the public API accept any
RangeBounds, or expose a concrete range type? - Does the Wasm build intentionally rely on undefined imports, and does Rust 1.96 require an explicit linker argument?
30-second answer framework
I would store copyable interval handles with core::range types and iterate through an explicit IntoIterator conversion. Generic APIs would prefer RangeBounds to avoid locking callers to one implementation. Tests would explicitly import core::assert_matches to retain pattern checks while printing actual values. Before migration I would pin the MSRV, verify that 0..n still creates a legacy type, add type and behavior tests, and separately validate Rust 1.96’s stricter handling of undefined symbols when linking Wasm.
Step-by-step deep dive
1. Separate the two range semantics
Rust 1.96 stabilizes core::range::Range, RangeFrom, RangeInclusive, and their associated iterators. The new types implement IntoIterator, so they can be stored in Copy structures; existing range syntax still creates legacy types for now and will change in a future edition. Code review must inspect type signatures rather than infer a type from 0..n.
2. Design library APIs and lifetimes
If an API only reads bounds, accept RangeBounds to support legacy and new ranges. If it stores and copies a range, use the new type and convert to an iterator at the boundary. Slice access still validates positions and character boundaries; Copy does not remove bounds checks. Document the MSRV so downstream users do not fail unexpectedly on an older toolchain.
3. Use pattern assertions for diagnostics
assertmatches! and debugassertmatches! are pattern assertions that show the value on failure. They are not in the prelude, so import them in the test module. Do not use debugassert_matches! as a production safety check because release builds remove debug assertions. Match narrow fields of error enums to avoid printing sensitive payloads.
4. Assess upgrade risk
Rust 1.96 also tightens linking for Wasm targets: --allow-undefined is no longer passed by default. If the project intentionally relies on imports, configure the linker argument explicitly or annotate the import module, then build Wasm in CI. Run the old-toolchain matrix, documentation examples, tests, and binary artifact checks; keep the compiler and lockfile rollback path ready.
High-quality sample answer
I would write the MSRV and supported targets into the release contract. A stored handle uses Rust 1.96’s core::range::Range because it is copyable and iterates through IntoIterator; generic functions accept RangeBounds so callers are not tied to one concrete type. The code does not assume that 0..n is already the new type; type checks and behavior tests prove the intended semantics. Test modules explicitly import core::assert_matches for narrow pattern diagnostics while retaining matches! for simple booleans. The migration also validates Wasm undefined symbols: if default linking now fails, only intentional imports receive explicit --allow-undefined and an import-module annotation. CI runs the MSRV, latest stable, Wasm build, and documentation examples, and any failure blocks release.
Common mistakes
- Assuming
0..nautomatically produces acore::rangetype in Rust 1.96. - Treating a new range as an
Iteratorand calling it directly despite itsIntoIteratordesign. - Relying on a prelude import for
assert_matches!. - Using debug assertions as production safety checks.
- Skipping bounds, overflow, or character-boundary validation because a range is
Copy. - Ignoring the Rust 1.96 change in Wasm undefined-symbol linking.
Follow-up questions and answers
Why not change every API to the new Range type?
A concrete type increases MSRV and compatibility pressure. RangeBounds keeps callers flexible; use the new type only where an internal structure must store and copy it.
When should assert_matches differ from matches?
Use matches! for a boolean result. Use assert_matches! when a test failure should show the actual value and pattern. Neither replaces business error handling or security validation.
What if a Wasm import must remain?
First prove that the import is an intentional contract rather than a missing linker configuration. Then restore it with explicit --allow-undefined and wasmimportmodule annotations, and test the symbol list and runtime behavior in CI.