我刚刚学习Rust,在选项中向上转换trait ref时遇到了一些问题。我想将其作为ref传递以减少开销。
其基本思想如下:我有些东西(children)在代码中的其他地方定义,它们是ChildT类型。我需要master能够访问它们的属性并操作它们。还有对象,也需要访问孩子-无论是直接访问还是通过master.get_child。当来自C++时,我想在结构体中存储一个指针。这样可以避免复制开销,并且引用沿着所有想要访问各自子对象的对象保持不变
当实现可变性时,它变得更加复杂,我试图用这种方式克服它:
#![allow(unused)]
use num_traits::Num;
use std::{any::Any, rc::Rc};
pub trait AsBase{
fn as_base(&self) -> &dyn BaseT;
}
impl <T: BaseT + Sized> AsBase for T{
fn as_base(&self) -> &dyn BaseT{
self
}
}
trait BaseT: AsBase{
fn get_child(&self) -> Option<Rc<&dyn BaseT>>;
fn get_child_mut(&mut self) -> Option<&mut Rc<&dyn BaseT>>;
}
trait ChildT: BaseT{
fn child_t_fn(&self);
}
struct C1{
child: Option<Rc<dyn ChildT>>,
}
impl BaseT for C1{
fn get_child(&self) -> Option<Rc<&dyn BaseT>> {
match &self.child{
Some(x) => return Some(Rc::new(x.as_base()) ),
None => return None
}
}
fn get_child_mut(&mut self) -> Option<&mut Rc<&dyn BaseT>> {
match &self.child{
Some(x) => return Some(&mut Rc::new(x.as_base()) ),
None => return None
}
}
}
impl ChildT for C1{
fn child_t_fn(&self) {
}
}
// same as C2 code-wise, for the sake of simplicity
struct C2{
child: Option<Rc<dyn ChildT>>,
}
impl BaseT for C2{
fn get_child(&self) -> Option<Rc<&dyn BaseT>> {
match &self.child{
Some(x) => return Some(Rc::new(x.as_base()) ),
None => return None
}
}
fn get_child_mut(&mut self) -> Option<&mut Rc<&dyn BaseT>> {
match &self.child{
Some(x) => return Some(&mut Rc::new(x.as_base()) ), // <- cannot pass temp. variable
None => return None
}
}
}
impl ChildT for C2{
fn child_t_fn(&self) {
}
}
// receive two structs, change one child
fn test(c1: &mut Rc<&dyn ChildT>, c2: &mut Rc<&dyn ChildT>){
let old_child = c2.get_child_mut();
let mut new_val: Rc<&dyn BaseT> = Rc::new(c1.as_base());
old_child = Some(new_val); // <- doesnt work. cannot make it mutable (I think because it is temp and would pass to another struct losing scope. how to overcome that?)
}
fn main(){
// Example use of the upper code
let c1 = C1{
child: None
};
let mut c2 = C2{
child: Some(Rc::new(c1))
};
let mut c3 = C2{
child: Some(Rc::new(c2))
};
let mut c4 = C1{
child: Some(Rc::new(c3))
};
// i just skipped the function .as_child(), it is basically the same as to_base
test(Rc::new(c4.as_child()),Rc::new(c2.as_child()));
}
字符串
1条答案
按热度按时间r1wp621o1#
首先,一个小小的免责声明:Rust的工作原理与其他语言完全不同。Rust声称没有未定义的行为,这导致了许多编译器强制执行的规则,而这些规则在C++中根本不存在。因此,许多在其他语言中工作的编程范例将无法转移到Rust,需要重新思考架构。特别是对于那些在其他语言中已经相当强大的人来说,这可能是一个挑战。
也就是说,通常有非常好的替代方案,最终甚至会使代码更干净,但可能需要一段时间才能理解问题及其潜在的解决方案。
在你的情况下,有几件事需要返工:
Rc<&>
(引用计数引用)没有意义,因为您现在得到了Rc
和&
的缺点,而没有任何额外的好处。(这是Rc
试图避免的主要事情),所以在所有情况下,&
比Rc<&>
更高级上级。所以我假设所有返回Rc<&>
的函数实际上都应该返回&
。Rc
内部的所有内容都是不可变的-您需要运行时检查以恢复可变性。这可以通过RefCell
,Cell
,Mutex
或Atomic
s来实现,具体取决于用例。在您的情况下,它是一个完整的对象,所以RefCell
或Mutex
是唯一有效的选项,而Mutex
(线程安全)只有在与Arc
而不是Rc
配对时才有意义,所以我假设在您的情况下正确的选择是Rc<RefCell<>>
。Rc<RefCell<>>
不再允许as获取对我们对象的纯&
引用。对RefCell
内部的每次访问都需要在运行时计数-这就是为什么refcell的borrow
函数返回一个执行运行时引用计数的令牌对象,当你使用&
引用时,这个对象需要保持活动状态。这意味着你不能再从你的函数返回&
引用。因此,有多种方法可以继续进行:
Rc
转换为Box
。Box
与Rc
相同,但没有引用计数器--它只是将一个对象放在堆上,这允许您存储dyn
对象。事实上,它现在不允许共享,这允许直接从其中获取&
引用,甚至&mut
引用;尽管你的main
(特别是c2
)表明共享对你来说是必要的。Rc
转换为Rc<RefCell<>>
并处理其后果。在您的情况下,这似乎是最有可能的。我们需要解决的下一个问题是如何在test
函数中获得修改节点所需的&mut dyn BaseT
。可悲的是,这不是一个简单的问题。所以我很遗憾地认为答案是:你的代码真的需要做这些事情吗?有没有其他方法可以更好地工作?恐怕重新思考你的架构可能是最好的解决方案。很遗憾,我现在无法帮助你,因为真的不清楚你试图用你的代码解决什么实际问题。