Arc::clone(&s) vs s.clone() and trait object dyn SomeTrait

Hello!

I recently came across something i don't understand while using Arc<dyn SomeTrait> in my code.

Here is a minimal piece of code that reproduce the exact same conditions, with the error given by the compiler in comment:

use std::sync::Arc;

trait A {
    fn a(&self) -> bool;
}

#[derive(Debug)]
struct S<T> {
    t: T,
}

impl<T> S<T> {
    pub fn new(t: T) -> Self {
        Self { t: t }
    }
}

impl<T> A for S<T> {
    fn a(&self) -> bool {
        false
    }
}

type AArc = Arc<dyn A + Send + Sync>;

struct Coll {
    c: Vec<AArc>,
}

impl Coll {
    pub fn new() -> Self {
        Self { c: vec![] }
    }
    pub fn add(&mut self, c: AArc) {
        self.c.push(c);
    }
}

fn main() {
    let mut c = Coll::new();
    let s = Arc::new(S::new(0));

    /*
    error[E0308]: mismatched types
      --> demo.rs:36:26
       |
    36 |         c.add(Arc::clone(&s));
       |                          ^^ expected trait object `dyn A`, found struct `S`
       |
       = note: expected reference `&Arc<dyn A + Send + Sync>`
                  found reference `&Arc<S<{integer}>>`

    error: aborting due to previous error

    For more information about this error, try `rustc --explain E0308`.
    */
    c.add(Arc::clone(&s));

    // this is ok
    c.add(s.clone());

    println!("{:?}", s);
}

The error happen when using Arc::clone(&s) but not when using s.clone().

The question is: why is that? :sweat_smile:

Thanks!

I think this is some interaction between method call resolution, trait impl resolution, and coercion. In this version:

    c.add(s.clone());

I believe the compiler is going to go figure out the method call first, and decide it must be Arc<S<_>>::clone, and the result of that is an Arc<S<_>>. Then it coerces that into the Arc<dyn ...>.

But in this version:

    c.add(Arc::clone(&s));

There is no method call resolution. And my guess is that

  • Since the type of the argument is known (Arc<dyn ...>)
  • This influences the trait impl resolution for Clone to find Arc::<dyn ...> as Clone
  • And it can't coerce behind the & in the call to clone
  • And hence the error
    • The message finds one way that this could have worked: s was a dyn ...

These both compile:

    // No pre-ascribed type to influence which `Clone` impl to use
    let t = Arc::clone(&s);
    c.add(t);
    // Just be explicit about which `Clone` impl to use
    c.add(Arc::<S<_>>::clone(&s));

I don't know how feasible it would be to improve the error reporting to point out either of these work-arounds.

4 Likes

It also works with an inferred type cast:

    c.add(Arc::clone(&s) as _);
2 Likes

Ah, nice. I guess the as _ "throws out" the argument context, and everything proceeds as in the temporary variable case.

See also.

1 Like

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.