这大部分是样板文件,作为可编辑的示例提供。向下滚动。
use std::rc::{Rc, Weak};
use std::cell::RefCell;
use std::any::{Any, AnyRefExt};
struct Shared {
example: int,
}
struct Widget {
parent: Option<Weak<Box<Widget>>>,
specific: RefCell<Box<Any>>,
shared: RefCell<Shared>,
}
impl Widget {
fn new(specific: Box<Any>,
parent: Option<Rc<Box<Widget>>>) -> Rc<Box<Widget>> {
let parent_option = match parent {
Some(parent) => Some(parent.downgrade()),
None => None,
};
let shared = Shared{pos: 10};
Rc::new(box Widget{
parent: parent_option,
specific: RefCell::new(specific),
shared: RefCell::new(shared)})
}
}
struct Woo {
foo: int,
}
impl Woo {
fn new() -> Box<Any> {
box Woo{foo: 10} as Box<Any>
}
}
fn main() {
let widget = Widget::new(Woo::new(), None);
{
// This is a lot of work...
let cell_borrow = widget.specific.borrow();
let woo = cell_borrow.downcast_ref::<Woo>().unwrap();
println!("{}", woo.foo);
}
// Can the above be made into a function?
// let woo = widget.get_specific::<Woo>();
}
我正在学习Rust并试图找出一些可行的方法来构建一个小部件层次结构。以上基本上做了我需要的,但有点麻烦。特别令人烦恼的是,我必须使用两个语句来转换内部窗口小部件(specific
成员Widget
)。我尝试了几种编写功能的方法来完成所有工作,但参考和终生魔法的数量超出了我的范围。
可以吗? 我的示例代码底部的注释方法是否可以变为现实?
关于更好地完成这一切的方法的评论值得赞赏,但请将其放在评论部分(或创建一个新问题并将其链接)
答案 0 :(得分:7)
我将提供一个简单且更加惯用的代码版本,然后解释我在那里所做的所有更改:
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
。这是实现抽象的有用工具,但在应用程序代码中几乎不需要它。因为您的代码中并不清楚真的需要它,所以我认为它被错误地使用并且可以删除。
接下来,Rc<Box<Something>>
Something
是一个结构(就像你的情况Something
= Widget
)是多余的。Rc<Widget>
将拥有的框放入引用计数框只是一个不必要的双重间接和分配。简单Rc<Box<Widget>>
是表达这件事的正确方法。当动态大小的类型着陆时,对于特征对象也是如此。
最后,您应该尝试始终返回未装箱的值。返回Rc<Widget>
(或甚至Widget
)不必限制代码的调用者。您可以轻松地从Rc<Widget>
转到Rc<Widget>
,但不能相反。 Rust自动优化按值返回;如果您的呼叫者需要let rc_w = box(RC) Widget::new(...);
,他们可以自行设置返回值:
Box<Any>
Woo::new()
返回的RefCell
同样如此。
您可以看到,在没有get_specific()
的情况下,您的RefCell
方法可以非常轻松地实施。但是,您实际上无法使用RefCell
执行此操作,因为core::cell::Ref
使用RAII进行动态借用检查,因此您无法从方法返回对其内部的引用。您必须返回downcast_ref()
,并且您的来电者需要自己RefCell
。这只是谨慎使用{{1}}的另一个原因。