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 usedon apub fn- Clippy reports
dead_codefor functions used only in tests #[allow(dead_code)]suppresses the warning but feels like a workaround- Functions are
pubonly so they can be tested fromtests/directory - Warning appears after refactoring removes callers but keeps the function
Common Causes
- Functions marked
pubfor 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.Suppress the warning for test-only functions:
- 2.```rust
- 3.// For functions only used in integration tests
- 4.#[allow(dead_code)]
- 5.pub fn calculate_metrics(data: &[f64]) -> Metrics {
- 6.// ...
- 7.}
- 8.
` - 9.**Use
cfgattribute for test-visible functions**: - 10.```rust
- 11.#[cfg_attr(test, pub)]
- 12.fn calculate_metrics(data: &[f64]) -> Metrics {
- 13.// In test builds: pub (visible to tests)
- 14.// In production builds: private (no dead_code warning)
- 15.}
- 16.
` - 17.Move testable code to a library crate:
- 18.```rust
- 19.// src/lib.rs - library crate, pub means visible to users and tests
- 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.Suppress at the crate level for CLI tools:
- 2.```rust
- 3.// At the top of main.rs
- 4.#![allow(dead_code)]
// Or selectively in Cargo.toml [lints.clippy] dead_code = "allow" ```
- 1.Wire up CLI subcommands to eliminate dead code:
- 2.```rust
- 3.// Instead of unused pub fn, wire it into CLI dispatch
- 4.fn main() {
- 5.let cli = Cli::parse();
- 6.match cli.command {
- 7.Commands::Metrics { input } => {
- 8.let data = read_data(&input);
- 9.let metrics = calculate_metrics(&data);
- 10.println!("{:?}", metrics);
- 11.}
- 12.}
- 13.}
- 14.
`
Prevention
- Follow the binary + library pattern: put logic in
lib.rs, call frommain.rs - Use
#[cfg(test)]for test-only helper functions - Run
cargo clippyregularly and address warnings before they accumulate - Consider
cargo deadlinksandcargo udepsfor additional dead code detection - Document why
#[allow(dead_code)]is used when it is necessary - Use feature flags to conditionally compile unused code paths