我将仅介绍您的代码的工作简化版和更惯用的版本,然后解释我在那里所做的所有更改:
use std::rc::{Rc, Weak};
use std::any::{Any, AnyRefExt};
struct Shared {
example: int,
}
struct Widget {
parent: Option<Weak<Widget>>,
specific: Box<Any>,
shared: Shared,
}
impl Widget {
fn new(specific: Box<Any>, parent: Option<Rc<Widget>>) -> Widget {
let parent_option = match parent {
Some(parent) => Some(parent.downgrade()),
None => None,
};
let shared = Shared { example: 10 };
Widget{
parent: parent_option,
specific: specific,
shared: shared
}
}
fn get_specific<T: 'static>(&self) -> Option<&T> {
self.specific.downcast_ref::<T>()
}
}
struct Woo {
foo: int,
}
impl Woo {
fn new() -> Woo {
Woo { foo: 10 }
}
}
fn main() {
let widget = Widget::new(box Woo::new() as Box<Any>, None);
let specific = widget.get_specific::<Woo>().unwrap();
println!("{}", specific.foo);
}
首先,有一些不必要的RefCell
位于您的结构内部。RefCell
很少需要 - 仅当您需要仅使用来改变对象的内部状态时&
参考(而不是&mut
)。这是实现抽象的有用工具,但在应用程序代码中几乎不需要它。因为从你的代码中并不清楚你really需要它,我认为它被错误使用并且可以删除。
Next, Rc<Box<Something>>
when Something
是一个结构(就像你的情况一样Something
= Widget
)是多余的。将拥有的框放入引用计数框只是不必要的双重间接和分配。清楚的Rc<Widget>
是表达这件事的正确方式。当动态大小的类型落地时,特征对象也是如此。
最后,您应该尝试始终返回未装箱的值。返回Rc<Box<Widget>>
(甚至Rc<Widget>
) 对于代码的调用者来说是不必要的限制。你可以从Widget
to Rc<Widget>
很容易,但反之则不然。 Rust 自动优化按值回报;如果您的来电者需要Rc<Widget>
他们可以自己装箱返回值:
let rc_w = box(RC) Widget::new(...);
同样的事情也适用于Box<Any>
由返回Woo::new()
.
你可以看到,在没有RefCell
s your get_specific()
方法可以非常容易地实现。但是,你真的不能这样做RefCell
因为RefCell
使用 RAII 进行动态借用检查,因此您无法从方法返回对其内部的引用。你必须回来core::cell::Ref
s,并且您的呼叫者将需要downcast_ref()
他们自己。这只是使用的另一个原因RefCell
是有节制的。