Representative interview topic

Rust interview: How would you design a Rust 1.97 warning policy for CI?

CodingHard
Offer.cc Editorial TeamPublished Updated

Question

Rust 1.97 adds explicit Cargo control for warnings from local crates and shows linker output by default. How would you design a CI warning policy that separates code lints, dependency warnings, and linker diagnostics while preserving build caches and supporting cross-compilation?

Prompt and scope

Rust 1.97 exposes build.warnings in Cargo: warn is the default, allow hides adjustable lints, and deny makes lint warnings from local packages fail the build. The behavior can also be changed with CARGO_BUILD_WARNINGS or --keep-going. The release also makes successful linker stderr visible by default and introduces the special linker_messages lint.

This targets engineers who maintain a Rust workspace, compiler toolchain, or release pipeline. Assume multiple local crates, third-party dependencies, Linux and Windows builds, and one cross-compiled target. The goal is not to turn every yellow line red; each signal needs an owner, a failure gate, and a path to resolution.

What the interviewer evaluates

  • Can you separate local-code lints, dependency output, linker diagnostics, and compilation errors?
  • Can you explain the boundaries of warn, allow, and deny instead of treating RUSTFLAGS=-Dwarnings as the only solution?
  • Can you balance fast local feedback, CI enforcement, and reproducibility across targets?
  • Can you keep exceptions auditable and explain the effects on caches, build scripts, and toolchain upgrades?

A weak answer says “use -D warnings in CI.” A strong answer classifies signals first, chooses a Cargo-level policy, and controls false positives with baselines, owners, and verification commands.

Questions to clarify first

  1. Should the gate cover only workspace crates, or also dependencies and linker output? Cargo’s build.warnings primarily applies to local packages; dependency warnings need separate observation.
  2. Does CI build multiple targets? Cross-compilation requires separate records for linker, sysroot, SDK, and target-specific diagnostics.
  3. Must one run collect every issue? If so, --keep-going helps gather related warnings and errors, but it does not make a failed build successful.
  4. Are temporary exceptions allowed? Record the lint or message, target, reason, owner, and review date, or allow becomes permanent silence.

A 30-second answer

“I would classify output into local adjustable lints, third-party dependency warnings, linker diagnostics, and compilation errors. Local development stays at warn for fast feedback; CI uses deny for workspace crates and --keep-going to collect the full result. Dependencies remain visible and tracked for upgrades instead of blocking every change because upstream has a warning. Linker output gets a baseline per target, and only verified harmless messages are explicitly allowed. Every exception has an owner and expiry date. I would validate the policy with a multi-target build, cache-hit measurements, and toolchain and dependency upgrade drills.”

Step-by-step solution

1. Define signal boundaries

Separate exit codes, stderr, and lint levels. Compilation errors always block. build.warnings controls adjustable lints in local packages. Dependency output stays in verbose logs so upstream maintenance is not silently assigned to this repository. Linker diagnostics are recorded by toolchain and target.

2. Layer the environments

Keep local development at warn: developers see problems without being stopped by the entire legacy backlog. Use deny for the repository’s own packages in CI so new code cannot add adjustable lints. The release pipeline also pins the toolchain, lockfile, and target matrix so that “works on my machine” is not a release criterion.

toml
[build]
warnings = "warn"

[lints.rust]
linker_messages = "allow"

The linker_messages = "allow" line is valid only after that platform’s message has been shown harmless; it is not a wildcard for all linker stderr. CI can change the local-package warning level with an environment variable:

text
CARGO_BUILD_WARNINGS=deny cargo check --workspace --all-targets --keep-going

3. Handle dependencies and caches

Put dependency warnings on an upgrade board or allowlist with the crate, version, target, and first-seen build. Do not hide upstream risk globally. Rust 1.97’s release notes state that changing the warning behavior does not invalidate the underlying build cache. Still monitor hit rate because changing targets, toolchains, or rustflags can create different cache keys.

4. Make cross-compilation explainable

For every target, retain compiler version, linker path, sysroot, SDK, build-script output, and warning baseline. If a platform needs a temporary linker exception, scope it to that target and recheck it when the linker changes. “Linux is quiet” is not evidence that Windows is safe.

5. Design exceptions and exit paths

An exception records four fields: exact lint or message, target, owner, and expiry date. Regenerate the warning report for every toolchain upgrade and release candidate. If exception count or recurrence rises, pause expansion of the build matrix and remove the cause first.

6. Verify that the policy works

Use a temporary branch that intentionally triggers a local lint to verify the difference between warn and deny; use a cross-platform linker sample to verify the baseline; and run a dependency upgrade drill to ensure upstream warnings remain visible. Track per-target failure causes, warning count, build time, cache hit rate, and exception age. Success means a new local lint blocks CI, dependency issues are not hidden, and approved linker messages remain traceable.

High-quality sample answer

“I would not start with a global -Dwarnings. First I would define whether the gate protects a workspace regression or every tool message. Rust 1.97’s build.warnings is a good boundary for local crates: keep development at warn, then use CARGO_BUILD_WARNINGS=deny with cargo check --workspace --all-targets --keep-going in CI to collect more results in one run. Dependency warnings go to an upgrade queue; they should remain visible and versioned, but an upstream warning should not automatically block application code.

“I would maintain a linker baseline per target. Only after confirming a message is harmless would I allow linker_messages in that target’s configuration, with the platform, toolchain, reason, owner, and review date recorded. Cross-compilation targets keep separate linker and sysroot records. Before release, I would use an intentional lint, one dependency upgrade, and one toolchain upgrade to verify exit codes, complete logs, cache behavior, and exception expiry. The policy then blocks accountable new regressions without hiding unknown output.”

Common mistakes

  • Mistake: Set global RUSTFLAGS=-Dwarnings. → Why it fails: It mixes compiler, build-script, and dependency boundaries, so an upgrade can turn unrelated output into an unexplained failure. → Fix: Use Cargo’s local-package warning policy and track dependencies and linkers separately.
  • Mistake: Use allow to remove every red line. → Why it fails: Adjustable lints, compilation errors, and non-lint linker stderr are different signals. → Fix: Allow only verified lints or messages and keep the raw log.
  • Mistake: Treat --keep-going as success. → Why it fails: It collects results; it does not turn errors into a passing build. → Fix: Check the final exit code and classify reports by crate and target.
  • Mistake: Validate only the default target. → Why it fails: Linkers, SDKs, and build scripts vary by platform. → Fix: Build an independent baseline and upgrade drill for every release target.

Follow-ups and responses

What if a dependency crate floods the release log with warnings?

Aggregate by crate, version, and target instead of silencing it. If the warning is fixed upstream, schedule a reversible upgrade. If an upgrade must wait, record the version range and risk owner, and keep repository lints separate from dependency observation. Block only when the dependency violates a stated release safety gate.

Why not handle everything with RUSTFLAGS=-Dwarnings?

Global rustflags affect build scripts, procedural macros, and target selection, making the boundary harder to explain and potentially changing cross-platform behavior and cache keys. Cargo’s build.warnings states the narrower rule: CI blocks adjustable lints from local packages, while other output follows its own audit path.

A target’s linker warning changes on every build. What do you do?

First pin the toolchain, linker, SDK, and build environment, then compare the raw stderr. If the message is stable and harmless, allow it for that target with a review date. If the text changes or appears with a link failure, remove the exception and fix the toolchain or build configuration.

Public sources

Related questions

Related interview tool

Use Screenshot for a coding prompt

Capture the problem, then work through the constraints, solution, code, edge cases, and complexity in order.

View the tool