How to deal with clippy::borrowed_box

Hello !

For the following code :

use std::fmt::Debug;

trait Foo: Debug {
    fn bar(&self) -> String;
}

#[derive(Debug)]
struct Zoo;
impl Foo for Zoo {
    fn bar(&self) -> String {
        "Hello world".to_string()
    }
}

fn my_func(a_foo: &Box<dyn Foo>) {
    println!("{:?}", a_foo);
}

fn main() {
    let zoo: Box<dyn Foo> = Box::new(Zoo);
    my_func(&zoo);
}

Clippy say : warning: you seem to be trying to use &Box<T>. Consider using just &T. But, if I change my_func to :

fn my_func(a_foo: &dyn Foo) {
    println!("{:?}", a_foo);
}

I got the error :

21 |     my_func(&zoo);
     |             ^^^^ the trait `Foo` is not implemented for `Box<dyn Foo>`

Considering I can't change the type of zoo, how to cast from &Box<dyn Foo> to &dyn Foo ?

Thanks !

my_func(&*zoo)

Normally the deref happens automatically, but I guess trait object coercion happens before that.

1 Like

Sorta funny: If you implemented Foo for Box<dyn Foo + '_>, then foo(&zoo) would work with the &dyn Foo parameter, but you'd have even more indirection than Clippy was trying to get rid of.[1] And no (default) Clippy lint catches it at the call site.


  1. Same amount of pointer indirection, but double vtable indirection. ↩ī¸Ž

Clippy can't catch it, because it can't look into the impl and see whether it does something non-trivial.

I guess it could do that in principle, but simply writing the impl in a slightly different style would be enough to throw off that analysis.

I did think of that -- it's a semantic change due to calling Box<_>'s implementation which might do more than forward it on -- but so is changing the signature of a function, which borrowed_box does already suggest :person_shrugging:. It can even be the same difference (Boxed vs non-Boxed implmenetation).

(Additionally with Box<dyn Trait>, you're also changing the (elided) trait object lifetime.)

There's an existing clippy issue about the OP.

2 Likes

Okay, thanks for these discussions !