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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions