为什么在把参数传给某个方法时,不能直接使用Rc::clone()?

编程语言 2026-07-12

所以,我刚刚迈出Rust的第一步……当然,我已经深受多年来在Java等语言上进行面向对象编程的影响。

我有两个结构体。我们把它们称作 Struct1Struct2Struct1 实现了一个名为 Trait1 的trait。然后 Struct2 的子项被保存在一个由 Trait1 实例组成的向量中。

所以……大概是这样的:

use std::cell::RefCell;
use std::rc::Rc;

trait Trait1 {
    fn some_method(&mut self);
}

struct Struct1 {}

impl Struct1 {
    fn new() -> Struct1 {
        Struct1 {}
    }
}

impl Trait1 for Struct1 {
    fn some_method(&mut self) {
        println!("Calling Struct1::some_method()");
    }
}

struct Struct2 {
    children: Vec<Rc<RefCell<dyn Trait1>>>,
}

impl Struct2 {
    fn new() -> Struct2 {
        Struct2 { children: Vec::new() }
    }

    fn add_child(&mut self, child: Rc<RefCell<dyn Trait1>>) {
        self.children.push(Rc::clone(&child));
    }

    fn nag_children(&mut self) {
        for child in &mut self.children {
            child.borrow_mut().some_method();
        }
    }
}

好吧……现在,我将创建一个 Struct2 的实例和一个 Struct1 的实例。接着我想把 Struct1 的实例作为 Struct2 的一个子项加入(前提是 Struct1 实现了 Trait1),并且在把它注册为 Struct2 的子对象之后,仍然希望可以继续对 Struct1 的实例进行操作。

fn main() {
    let struct1 = Rc::new(RefCell::new(Struct1::new()));
    let mut struct2 = Struct2::new();
    struct2.add_child(Rc::clone(&struct1));
    println!("Bothering struct2's children");
    struct2.nag_children();
    println!("Bothering struct2's children");
    struct2.nag_children();
    println!("Bothering struct1 directly");
    struct1.borrow_mut().some_method();
}

而编译器对我注册子对象的这一行不太友好:

error[E0308]: mismatched types
   --> src/main.rs:45:33
    |
 45 |     struct2.add_child(Rc::clone(&struct1));
    |                       --------- ^^^^^^^^ expected `&Rc<RefCell<dyn Trait1>>`, found `&Rc<RefCell<Struct1>>`
    |                       |
    |                       arguments to this function are incorrect
    |
    = note: expected reference `&Rc<RefCell<(dyn Trait1 + 'static)>>`
               found reference `&Rc<RefCell<Struct1>>`
    = help: `Struct1` implements `Trait1` so you could box the found value and coerce it to the trait object `Box<dyn Trait1>`, you will have to change the expected type as well
note: method defined here
   --> /home/antoranz/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/clone.rs:236:8
    |
236 |     fn clone(&self) -> Self;
    |        ^^^^^

不过,我可以在另一行里简单地对它进行克隆来修复:

fn main() {
    let struct1 = Rc::new(RefCell::new(Struct1::new()));
    let mut struct2 = Struct2::new();
    let clone = Rc::clone(&struct1);  // cloning separately
    struct2.add_child(clone);
    println!("Bothering struct2's children");
    struct2.nag_children();
    println!("Bothering struct2's children");
    struct2.nag_children();
    println!("Bothering struct1 directly");
    struct1.borrow_mut().some_method();
}

在我这个没什么经验的眼里,这两者看起来是一样的,因此这里一定有我需要学习的地方。那么……为什么单独进行克隆就能工作,而在对 add_child() 进行调用时直接调用它却会失败?

解决方案

这是类型推断(type inference)与类型强制转换(type coercion)之间的相互作用。

在工作正常的情况下,

let clone = Rc::clone(&struct1);
struct2.add_child(clone);

Rc::clone 产出 Rc<RefCell<Struct1>>。它不受下面的 add_child 调用的影响,因为没有需要推断的东西——&struct1 的类型是明确的。然后在把它传递给 add_child 时,Rc<RefCell<Struct1>> 会被隐式转换成 Rc<RefCell<dyn Trait1>>。这是一种有效的转换。

在不工作/不可工作的情形下,

struct2.add_child(Rc::clone(&struct1));

Rc::clone 在一个期望为 Rc<RefCell<dyn Trait1>> 的上下文中对其进行求值。因此 &struct1 必须是 &Rc<RefCell<dyn Trait1>>。这是类型推断向上冒泡的结果。然而,&struct1 是一个 &Rc<RefCell<Struct1>>,它 不能 被转换为 &Rc<RefCell<dyn Trait1>>

强制转换之间的差异是微妙的。带引用的情况之所以不可行,是因为它不能引用原始的 RcRc 本身也需要被强制转换。


有没有办法直接在对 add_child() 的调用中把它搞定?

在这种情况下,你可以像下面这样引入一个 as 类型转换:

struct2.add_child(Rc::clone(&struct1) as Rc<_>);

这将阻止类型推断冒泡到到 Rc::clone

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章