Introduction

Clippy's dead_code lint warns about pub functions in binary crates (main.rs, bin/) that are never called from within the crate. Unlike library crates where pub means "visible to downstream users," binary crates have no downstream users, so pub functions that are not called internally are truly dead code. This creates friction when functions are marked pub for integration testing purposes.

Symptoms

  • warning: function 'helper' is never used on a pub fn
  • Clippy reports dead_code for functions used only in tests
  • #[allow(dead_code)] suppresses the warning but feels like a workaround
  • Functions are pub only so they can be tested from tests/ directory
  • Warning appears after refactoring removes callers but keeps the function

Common Causes

  • Functions marked pub for integration testing (tests/ directory)
  • Feature flags conditionally enable code paths
  • Functions intended for FFI export (#[no_mangle])
  • Refactoring left unused utility functions
  • CLI subcommands not yet wired into the main dispatch

Step-by-Step Fix

  1. 1.Suppress the warning for test-only functions:
  2. 2.```rust
  3. 3.// For functions only used in integration tests
  4. 4.#[allow(dead_code)]
  5. 5.pub fn calculate_metrics(data: &[f64]) -> Metrics {
  6. 6.// ...
  7. 7.}
  8. 8.`
  9. 9.**Use cfg attribute for test-visible functions**:
  10. 10.```rust
  11. 11.#[cfg_attr(test, pub)]
  12. 12.fn calculate_metrics(data: &[f64]) -> Metrics {
  13. 13.// In test builds: pub (visible to tests)
  14. 14.// In production builds: private (no dead_code warning)
  15. 15.}
  16. 16.`
  17. 17.Move testable code to a library crate:
  18. 18.```rust
  19. 19.// src/lib.rs - library crate, pub means visible to users and tests
  20. 20.pub fn calculate_metrics(data: &[f64]) -> Metrics { ... }

// src/main.rs - binary crate use my_crate::calculate_metrics;

fn main() { let metrics = calculate_metrics(&data); println!("{:?}", metrics); } ```

  1. 1.Suppress at the crate level for CLI tools:
  2. 2.```rust
  3. 3.// At the top of main.rs
  4. 4.#![allow(dead_code)]

// Or selectively in Cargo.toml [lints.clippy] dead_code = "allow" ```

  1. 1.Wire up CLI subcommands to eliminate dead code:
  2. 2.```rust
  3. 3.// Instead of unused pub fn, wire it into CLI dispatch
  4. 4.fn main() {
  5. 5.let cli = Cli::parse();
  6. 6.match cli.command {
  7. 7.Commands::Metrics { input } => {
  8. 8.let data = read_data(&input);
  9. 9.let metrics = calculate_metrics(&data);
  10. 10.println!("{:?}", metrics);
  11. 11.}
  12. 12.}
  13. 13.}
  14. 14.`

Prevention

  • Follow the binary + library pattern: put logic in lib.rs, call from main.rs
  • Use #[cfg(test)] for test-only helper functions
  • Run cargo clippy regularly and address warnings before they accumulate
  • Consider cargo deadlinks and cargo udeps for additional dead code detection
  • Document why #[allow(dead_code)] is used when it is necessary
  • Use feature flags to conditionally compile unused code paths