Is clippy::only_used_in_recursion sane?

This is how I gather you can define and implement a trait over an existing type:

use std::collections::BTreeSet;

trait SetLike {
    fn contains(&self, v: &i32) -> bool;
}

impl SetLike for BTreeSet<i32> {
    fn contains(&self, v: &i32) -> bool {
        self.contains(v)
    }
}

fn main() {
    let s: &dyn SetLike = &BTreeSet::new();
    assert!(!s.contains(&0));
}

(Playground)

Works fine, but these days clippy complains:

warning: parameter is only used in recursion
 --> src/main.rs:8:24
  |
8 |     fn contains(&self, v: &i32) -> bool {
  |                        ^ help: if this is intentional, prefix with an underscore: `_v`
  |

Why? This is not recursion, just an implementation of a trait method.

I agree though it looks like recursion and an endless loop. Is there a neater way to write the example?

There's an issue for the false positive:

3 Likes

Maybe it'd be better to desugar the method call, to make it explicit that this is the inherent method and not the trait method?

impl SetLike for BTreeSet<i32> {
    fn contains(&self, v: &i32) -> bool {
        BTreeSet::contains(self, v)
    }
}
2 Likes

That does look neater, and it calms down clippy too.

That form avoids auto-deref, but it may still resolve to a trait method if there isn't one inherent.

2 Likes

True, though the compiler does tell you off with warning: function cannot return without recursing, unless the default #[warn(unconditional_recursion)] is disabled (and why would you?). And is there any alternative, apart from picking (somewhat awkward) unique names for the trait methods?

Unfortunately, I don't know any way to write a strictly inherent call. I could imagine this being something like <Self as Self>::contains or fully <BTreeSet<i32> as BTreeSet<i32>>::contains, but no:

error[E0576]: cannot find method or associated constant `contains` in `BTreeSet`

So that syntax is sort of accepted, but I'm guessing it's looking at that type like a trait.

1 Like