如何在保留原始结构的情况下引用 trait?

4

我的目标是拥有一个引用计数的结构体,在一个上下文中被称为特征(trait),在另一个上下文中被其具体类型所引用。最好通过代码来解释:

#![feature(box_syntax)]

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

trait Employee {
    fn be_managed(&mut self);
}

struct Human;

impl Human {
    fn be_human(&mut self) {
        println!("I'm just a human who needs a mutable self sometimes");
    }
}

impl Employee for Human {
    fn be_managed(&mut self) {
        println!("Off to the salt mines");
    }
}

struct Manager {
    my_employee: Rc<RefCell<Box<Employee + 'static>>>, //'
}

fn main() {
    let mut human1 = Rc::new(RefCell::new(box Human as Box<Employee>));

    let manager1 = Manager {
        my_employee: human1.clone(), // This works due to cast above
    };

    manager1.my_employee.borrow_mut().be_managed();

    human1.borrow_mut().be_human(); // But we can't be human anymore



    let mut human2 = Rc::new(RefCell::new(box Human));

    let manager2 = Manager {
        my_employee: human2.clone(), // This doesn't work
    };

    manager2.my_employee.borrow_mut().be_managed();

    human2.borrow_mut().be_human();
}

我希望Manager能够接受实现了Employee特质的任意结构体作为my_employee,但其他引用仍应能够调用原始对象上的其他方法,例如be_human
目前,我从上述代码中得到以下错误:
src/main.rs:37:25: 37:35 error: type `core::cell::RefMut<'_, Box<Employee>>` does not implement any method in scope named `be_human`
src/main.rs:37     human1.borrow_mut().be_human(); // But we can't be human anymore
                                       ^~~~~~~~~~
src/main.rs:44:22: 44:36 error: mismatched types:
 expected `alloc::rc::Rc<core::cell::RefCell<Box<Employee + 'static>>>`,
    found `alloc::rc::Rc<core::cell::RefCell<Box<Human>>>`
(expected trait Employee,
    found struct `Human`) [E0308]
src/main.rs:44         my_employee: human2.clone(), // This doesn't work
                                    ^~~~~~~~~~~~~~

在这种情况下,应该采取什么样的方法?


你能不能为RefCell<Box<Human>>实现Employee,然后使用Rc<Employee>呢?我相信这样的做法可能会让你达到想要的效果。 - oli_obk
@ker 我不确定 - 所以我在问 :) 你介意写一下如何作为答案的内容吗? - Guy Cook
哦,抱歉,我刚注意到这被https://github.com/rust-lang/rust/issues/18248屏蔽了。 - oli_obk
1个回答

3
声明:在这个答案中,我假设您有意不使用 enum,因为您希望 Employee 是开放的。

这个问题在使用动态多态的每一种语言中都会出现,传统的解决方案是 访问者模式

然而,这并不完全理想,因为它引入了依赖关系,所以如果必要的话,可以使用 无环访问者模式;但是我建议您先从一个裸骨访问者开始,然后再深入研究。

trait EmployeeVisitor {
    fn visit_employee(&self, e: &Employee);
    fn visit_human(&self, h: &Human);
}

trait Employee {
    fn accept(&self, v: &EmployeeVisitor) {
        v.visit_employee(self);
    }
}

impl Employee for Human {
    fn accept(&self, v: &EmployeeVisitor) {
        v.visit_human(self);
    }
 }

这是传统的“通过间接层解决所有问题”的方法,它带来了另一个间接层的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接