为什么在把参数传给某个方法时,不能直接使用Rc::clone()?
所以,我刚刚迈出Rust的第一步……当然,我已经深受多年来在Java等语言上进行面向对象编程的影响。
我有两个结构体。我们把它们称作 Struct1 和 Struct2。Struct1 实现了一个名为 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>>。
强制转换之间的差异是微妙的。带引用的情况之所以不可行,是因为它不能引用原始的 Rc。Rc 本身也需要被强制转换。
有没有办法直接在对
add_child()的调用中把它搞定?
在这种情况下,你可以像下面这样引入一个 as 类型转换:
struct2.add_child(Rc::clone(&struct1) as Rc<_>);
这将阻止类型推断冒泡到到 Rc::clone。