Skip to content

Eliminate unwrap() in production code and add clippy::unwrap_used lint #415

@jadamcrain

Description

@jadamcrain

Summary

Add clippy::unwrap_used = "deny" to the workspace Clippy lints and eliminate all .unwrap() / .expect() calls in non-test production code, replacing them with proper error handling. Mutex .lock().unwrap() sites would get explicit #[allow(clippy::unwrap_used)] annotations.

This follows the same philosophy as the existing unsafe_code = "forbid" lint — make the safe path the default and require explicit opt-in for exceptions.

Motivation

A review of the codebase found ~23 unwrap()/expect() sites in production code. Most are safe in practice, but 3 in outstation/session.rs (lines 1725, 1742, 1751) are on control response serialization and could panic if the TX buffer is smaller than needed to echo back a large control request. These are reachable from network input.

More importantly, adding the lint as a guardrail prevents future contributors from accidentally introducing unwraps in network-facing code paths.

Work required

Easy (mechanical changes):

  • outstation/session.rs:1725,1742,1751 — control response .unwrap() → propagate WriteError (also a real bug fix)
  • outstation/session.rs:413,458 — buffer get .unwrap() → return slice from cursor directly
  • outstation/session.rs:1457,1488,1493 — fixed-size writes .unwrap() → handle error
  • tcp/master/server.rs:266 — channel .expect() → map to error type and use ?
  • database/details/event/list.rs:95.unwrap() pair → destructure with match

Medium (requires some restructuring):

  • master/tasks/time.rs:120,269 — state machine .expect() → typestate pattern or encode invariant in types
  • transport/real/assembler.rs:55,69,147 — internal invariant .expect() → restructure size tracking

Allow-listed (idiomatic, keep as-is with #[allow]):

  • database/mod.rs — 7 Mutex::lock().unwrap() sites
  • tcp/master/server.rs:44,45 and static_db.rs:993 — constant literals (could also use const blocks)

Final state

# Cargo.toml
[workspace.lints.clippy]
unwrap_used = "deny"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions