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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With