How do I pass Rc<RefCell<Box<MyStruct>>> to a function accepting Rc<RefCell<Box<MyTrait>>>?

匿名 (未验证) 提交于 2019-12-03 01:21:01

问题:

I have originally asked this question here, but it was marked as duplicate, although it duplicates only a part of it in my opinion, so I have created a more specific one:

Consider the following code:

use std::rc::Rc;  trait MyTrait {     fn trait_func(&self); }  struct MyStruct1; impl MyStruct1 {     fn my_fn(&self) {         // do something     } }  impl MyTrait for MyStruct1 {     fn trait_func(&self) {         // do something     } }  fn my_trait_fn(t: Rc<MyTrait>) {     t.trait_func(); }  fn main() {     let my_str: Rc<MyStruct1> = Rc::new(MyStruct1);     my_trait_fn(my_str.clone());     my_str.my_fn(); }

This code works fine. Now I want to change the definition of trait_func to accept a &mut self, but it won't work as Rc works with immutable data only. The solution I use is to wrap MyTrait into RefCell:

use std::cell::RefCell;  fn my_trait_fn(t: Rc<RefCell<Box<MyTrait>>>) {     t.borrow_mut().trait_func(); }  fn main() {     let my_str: Rc<RefCell<Box<MyStruct1>>> = Rc::new(RefCell::new(Box::new(MyStruct1)));     my_trait_fn(my_str.clone());     my_str.my_fn(); }

When I compile it I get an error:

error[E0308]: mismatched types   --> src/main.rs:27:17    | 27 |     my_trait_fn(my_str.clone());    |                 ^^^^^^^^^^^^^^ expected trait MyTrait, found struct `MyStruct1`    |    = note: expected type `std::rc::Rc<std::cell::RefCell<std::boxed::Box<MyTrait + 'static>>>`               found type `std::rc::Rc<std::cell::RefCell<std::boxed::Box<MyStruct1>>>`    = help: here are some functions which might fulfill your needs:            - .into_inner()

What's the best way to go around this problem?

回答1:

(An older revision of this answer essentially advised to clone the underlying struct and put it in a new Rc<RefCell<Box<MyTrait>> object; this was necessary at the time on stable Rust, but since not long after that time, Rc<RefCell<MyStruct>> will coerce to Rc<RefCell<MyTrait>> without trouble.)

Drop the Box<> wrapping, and you can coerce Rc<RefCell<MyStruct>> to Rc<RefCell<MyTrait>> freely and easily. Recalling that cloning an Rc<T> just produces another Rc<T>, increasing the refcount by one, you can do something like this:

use std::rc::Rc; use std::cell::RefCell;  trait MyTrait {     fn trait_func(&self); }  #[derive(Clone)] struct MyStruct1; impl MyStruct1 {     fn my_fn(&self) {         // do something     } }  impl MyTrait for MyStruct1 {     fn trait_func(&self) {         // do something     } }  fn my_trait_fn(t: Rc<RefCell<MyTrait>>) {     t.borrow_mut().trait_func(); }  fn main() {     // (The type annotation is not necessary here, but helps explain it.     // If the `my_str.borrow().my_fn()` line was missing, it would actually     // be of type Rc<RefCell<MyTrait>> instead of Rc<RefCell<MyStruct1>>,     // essentially doing the coercion one step earlier.)     let my_str: Rc<RefCell<MyStruct1>> = Rc::new(RefCell::new(MyStruct1));     my_trait_fn(my_str.clone());     my_str.borrow().my_fn(); }

As a general rule, see if you can make things take the contained value by reference, ideally even generically―fn my_trait_fn<T: MyTrait>(t: &T) and similar, which can typically be called as my_str.borrow() with automatic referencing and dereferencing taking care of the rest―rather than the whole Rc<RefCell<MyTrait>> thing.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!