无法找出一个函数来返回存储在RefCell <box <any>&gt; </box <any>中的给定类型的引用

时间:2014-08-10 23:16:57

标签: rust

这大部分是样板文件,作为可编辑的示例提供。向下滚动。

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)。我尝试了几种编写功能的方法来完成所有工作,但参考和终生魔法的数量超出了我的范围。

可以吗? 我的示例代码底部的注释方法是否可以变为现实?

关于更好地完成这一切的方法的评论值得赞赏,但请将其放在评论部分(或创建一个新问题并将其链接)

1 个答案:

答案 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}}的另一个原因。