Clone Arc<T> into Arc<dyn U> where T implements U
Asked Answered
F

2

5

I find pretty weird that

use std::sync::Arc;

trait Fruit {}
struct Pear {}
impl Fruit for Pear {}

fn main() {
    let pear = Arc::new(Pear {});
    let cloned = Arc::clone(&pear);
    let cloned_casted: Arc<dyn Fruit> = cloned;
}

compiles, but

use std::sync::Arc;

trait Fruit {}
struct Pear {}
impl Fruit for Pear {}

fn main() {
    let pear = Arc::new(Pear {});
    let cloned_casted: Arc<dyn Fruit> = Arc::clone(&pear);
}

errors out with

error[E0308]: mismatched types
 --> main.rs:9:52
  |
9 |     let cloned_casted: Arc<dyn Fruit> = Arc::clone(&pear);
  |                                                    ^^^^^ expected trait object `dyn Fruit`, found struct `Pear`
  |
  = note: expected reference `&Arc<dyn Fruit>`
             found reference `&Arc<Pear>`

Why is so, and is there a shorter variant to clone Arc<dyn Fruit> from Arc<Pear>?

Fatty answered 10/5, 2021 at 9:50 Comment(1)
implicit cast, play.rust-lang.org/… also remember trait in rust is not interface.Bedroom
B
9

This is a result of how type inference works in Rust. The method Arc::clone() accepts an &Arc<T>. The compiler needs to determine what T is.

In the first code snippet, the compiler doesn't have any constraints for the return type of Arc::clone(&pear), so it infers T based on the argument that is passed in. The argument has type &Arc<Pear>, so the compiler infers that T = Pear. The last line in the code snippet implicitly performs an unsized coercion from Arc<Pear> to Arc<dyn Fruit>.

In the second code snippet, the compiler already knows the desired return type of Arc::clone(&pear), since the target variable includes a type annotation. Based on that, the compiler infers T = dyn Fruit and expects an argument of type &Arc<dyn Fruit>. However, it finds an &Arc<Pear> instead, which can't be coerced into the desired type, so the compiler errors out.

The details how type inference works in Rust are not fully specified at this time. Whenever the compiler does not infer the correct type, you should add an explicit type hint:

let pear = Arc::new(Pear {});
let cloned_casted: Arc<dyn Fruit> = Arc::<Pear>::clone(&pear);

This way, the compiler doesn't need to infer what T is, and the code works as expected.

Sometimes you can also nudge the compiler to infer the correct type. This works as well:

let cloned_casted: Arc<dyn Fruit> = Arc::clone(&pear) as _;

The compiler can infer _ to mean Arc<dyn Fruit>, but it doesn't know anymore what type you are casting from, so it needs to infer that type the same way it had to in the first code snippet.

See also:

Brose answered 10/5, 2021 at 12:5 Comment(0)
F
1

In addition to Sven Marnach's variants, we can also do just

    let cloned_casted: Arc<dyn Fruit> = pear.clone();
Fatty answered 10/5, 2021 at 13:18 Comment(1)
The only problem with this form is that it's not clear whether you meant to clone the Arc, or clone the Pear. That's why it's common to write Arc::clone()-- it lets the reader know what you meant, and tells the compiler it should be an error for you to put anything there that isn't an Arc.Priapitis

© 2022 - 2024 — McMap. All rights reserved.