Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error when passing Rc<dyn Trait> as a function argument

Tags:

rust

I'm facing some strange behavior when passing an Rc<dyn Trait> as a function argument. The following code demonstrates the problem.

use std::rc::Rc;

trait Trait {}

struct Struct {}

impl Trait for Struct {}

fn function(_t: Rc<dyn Trait>) {}

fn main() {
    let n = Rc::new(Struct {});

    // ok
    let r = Rc::clone(&n);
    function(r);

    // error, why?
    // function(Rc::clone(&n));
}

If I store the Rc in a temporary variable, everything works fine. But if I try to call Rc::clone directly within the function call, I get the following error.

   |
19 |     function(Rc::clone(&n));
   |                        ^^ expected trait object `dyn Trait`, found struct `Struct`
   |
   = note: expected reference `&std::rc::Rc<dyn Trait>`
              found reference `&std::rc::Rc<Struct>`

Struct implements Trait. Why do I get this error?

like image 601
user14278279 Avatar asked Feb 11 '26 23:02

user14278279


1 Answers

This is basically just a wrinkle in the type inference rules. In order to call Rc::clone(&n), the compiler has to know what the type parameter of Rc is.

let r = Rc::clone(&n);
function(r);

At the first line, the compiler sees that Rc::clone is called with an argument of type &Rc<Struct>. It can freely pick a type for r, so it infers that the function being called should be Rc::<Struct>::clone and r is also Rc<Struct>. Moving to the next line, calling function(r) just coerces r to Rc<dyn Trait>.

function(Rc::clone(&n));

Here the compiler again has to pick a type parameter for Rc, but it doesn't have total freedom: it has to pick something that can be passed to function. So it assumes the type parameter is dyn Trait, since Rc<dyn Trait> is what function expects.

However, you cannot call Rc::<dyn Trait>::clone(&n), because while Rc<Struct> can be coerced to Rc<dyn Trait>, coercions are not transitive through referencing (you can't coerce a &Rc<Struct> to &Rc<dyn Trait>). So coercion cannot take place inside the Rc::clone(...) call.

You can make the one-line version compile by specifying the type argument as Struct using a turbofish:

function(Rc::<Struct>::clone(&n));

or by hinting to the compiler that it should not infer the type from its position as the argument of function, by adding an explicit cast:

function(Rc::clone(&n) as _);

n.clone() also works because the method resolution procedure for . leaves no room for doubt about the type parameter: it will always find Rc::<Struct>::clone because auto-dereferencing only looks at the receiver type (the type of n) and does not care about the expression's context.

Related

  • How does the mechanism behind the creation of boxed traits work?
like image 122
trent Avatar answered Feb 16 '26 15:02

trent