Return an Arc::clone (trait) of a field (struct) without any warning

While trying to make clippy happy I found some problem I don’t understand entirely:

#![allow(dead_code)]

use std::sync::Arc;
use std::fmt::Debug;

struct Struct {
    field: Arc<()>
}

impl Struct {

    // error: expected trait std::fmt::Debug, found ()
    //pub fn clone_some_a(&self) -> Arc<dyn Debug> {
    //    Arc::clone(&self.field)
    //}

    // warn: clippy::let_and_return
    pub fn clone_some_b(&self) -> Arc<dyn Debug> {
        let result = Arc::clone(&self.field);
        result
    }

    // warn: trivial_casts
    pub fn clone_some_c(&self) -> Arc<dyn Debug> {
        Arc::clone(&self.field) as Arc<dyn Debug>
    }

    // error: cannot move
    //pub fn clone_some_d(&self) -> Arc<dyn Debug> {
    //    Arc::clone(&(self.field as Arc<dyn Debug>))
    //}

    // error: non-primitive cast
    //pub fn clone_some_e(&self) -> Arc<dyn Debug> {
    //    Arc::clone(&self.field as &Arc<dyn Debug>)
    //}

    // warn: clippy::clone_on_ref_ptr
    pub fn clone_some_f(&self) -> Arc<dyn Debug> {
        self.field.clone()
    }

    // works without a warning but looks crappy
    pub fn clone_some_g(&self) -> Arc<dyn Debug> {
        (Arc::clone(&self.field), ()).0
    }
}

Why does version a fail, but b and g are ok?
Why is a cast necessary (version c vs. a), but on the other hand it’s marked as trivial?

Qualifying the generic is general way to make compiler happy.
The compiler tries; <Arc<dyn Debug> as Clone>::clone(&self.field) and fails.
Can make it work by switching to; <Arc<()> as Clone>::clone(&self.field). The compiler adds the “trivial” cast.
Step further also works; Arc::<()>::clone(&self.field)

Fail also if you try; Clone::clone(&self.field)

2 Likes

Hey, thank you!

I tried qualifying the generic but used the result type rather than the source type (as you did). That didn’t make any difference to the outcome so I omitted them from my list.

For completeness: I still don’t understand why clone_some_b works. Maybe the binding implies some restriction (sizedness?) that leaves the compiler with a single option?

clone_some_b works because you have two operations, 1 is Arc::clone, the other is coercing the Arc<()> to Arc<dyn Debug>.

clone_some_a doesn’t work because the coercion doesn’t kick in, so you have a type error

clone_some_d doesn’t work because you can’t temporarily move out of a value that you don’t own.

clone_some_e doesn’t work because you can’t cast the Arc behind a reference, you must own the Arc to cast it.

2 Likes

Thank you

I’ve got to dig deeper into coercions - sometimes they’re still too magic for me.