Inconsistent dead code warnings?

I'm curious to understand the rules for cargo check and rust analyzer to report unused functions. It seems inconsistent to me, but probably I'm missing something. Here's what I see.

While learning Rust I copied some modules from a previous project to a new one and indeed some utility functions are not used in the new project. Here are two examples:

The Region::new function is never used except for unit tests, and I see no complaints about it being unused.

At the same time, the TouchPoints::get is also never used except for unit tests, but tools are complaining about it:

Here's complete output from cargo check:

$ cargo check
    Checking bevy-lyon-playground v0.1.0 (/home/tad/Projects/bevy-lyon-playground)
warning: associated function `get` is never used
  --> src/touch.rs:19:12
   |
19 |     pub fn get(&self, identifier: i64) -> Option<&TouchPoint> {
   |            ^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: `bevy-lyon-playground` (bin "bevy-lyon-playground") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s

The two functions are both public and implemented for a public struct. What makes cargo complain about one of them but not the other?

1 Like

Reduced the example to something like

mod foo {
    pub struct Region; // no warning

    impl Region {
        pub fn new() -> Self { // no warning
            Self
        }
    }

    impl From<Region> for () {
        fn from(_: Region) {
            ()
        }
    }
}

mod bar {
    pub struct Region; // struct `Region` is never constructed

    impl Region {
        pub fn new() -> Self { // associated function `new` is never used
            Self
        }
    }
}

Rust Playground

1 Like

Indeed, adding the following to my code eliminates the warning.

impl From<TouchPoints> for () {
    fn from(_: TouchPoints) -> Self {
        ()
    }
}

That is weird, right?

Hmm… so maybe, the compiler anticipates settings that don’t quite apply in this concrete situation, but in principle might be (conservatively) correctly analyzed to avoid false warnings… settings such as… imagine new was a method, i.e. with a self type, and imagine From<T> was some other trait with a fn blah(self) -> T method and no default implementations, and this was the only From<T> implementation for ().

The code could arguably call ().blah().new() from outside the crate, relying on type inference to choose Region for T in the From<T> implementation to be chosen (as it’s the only one in existence) and hence the function new was not entirely unused.


Some illustrative code

pub trait From<T> {
    fn from(self) -> T;
}

// this could be in a different crate, since everything mentioned is `pub`
fn usage_demo() {
    ().from().new(); // calls `new`
}

mod foo {
    use super::From;

    pub struct Region; // no warning

    impl Region {
        pub fn new(&self) -> Self { // no warning
            Self
        }
    }

    impl From<Region> for () {
        fn from(self) -> Region {
            Region
        }
    }
}
2 Likes

I’m not sure if this is or isn’t a “best practice”… but since this seems to be a binary crate, to get more reliable unused warnings, maybe simply change all usage of pub into pub(crate) instead?

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.