简体   繁体   English

使用选项的双向引用问题<Rc<RefCell<T> &gt;&gt;

[英]Trouble with bidirectional references using Option<Rc<RefCell<T>>>

I am building a NES emulator to learn Rust.我正在构建一个 NES 模拟器来学习 Rust。 I have difficulty organizing components.我很难组织组件。

My emulator uses the structure Bus to communicate with CPU and PPU.我的模拟器使用结构 Bus 与 CPU 和 PPU 通信。 CPU and PPU also need to communicate with bus. CPU 和 PPU 也需要与总线通信。

I figured that it is a good idea to create a shared pointer for Bus so that PPU and CPU have the same pointer reference to a bus.我认为为 Bus 创建一个共享指针是个好主意,这样 PPU 和 CPU 对总线具有相同的指针引用。 This is what I tried: playground .这就是我尝试过的: 操场 Unfortunately it didn't work.不幸的是,它没有用。

error[E0308]: mismatched types
  --> src/main.rs:33:9
   |
32 |     fn bus(&mut self) -> &mut Bus {
   |                          -------- expected `&mut Bus` because of return type
33 |         self.bus_helper().borrow_mut()
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         expected `&mut Bus`, found struct `RefMut`
   |         help: consider mutably borrowing here: `&mut self.bus_helper().borrow_mut()`
   |
   = note: expected mutable reference `&mut Bus`
                         found struct `RefMut<'_, Bus>`

How to get my code to compile?如何让我的代码编译? Also, I wonder whether it is the best way to create bidirectional references?另外,我想知道这是否是创建双向引用的最佳方式? Are there any alternatives?有没有其他选择?

borrow_mut() returns a RefMut , which is a wrapper that maintains the borrow count of the cell so that the cell can panic if Rust's aliasing rules are violated at runtime (eg if a mutable reference and any other reference try to exist at the same time). borrow_mut()返回一个RefMut ,它是一个包装器,用于维护单元格的借用计数,因此如果在运行时违反了 Rust 的别名规则(例如,如果可变引用和任何其他引用试图同时存在,则单元格可以恐慌) )。

You can fix this by changing the return type to RefMut<'_, Bus> .您可以通过将返回类型更改为RefMut<'_, Bus>来解决此问题。

Alternatively, to make the RefCell implementation detail hidden from the caller, you can instead return impl DerefMut<Target=Bus> + '_ which says "this method returns something that can deref as a mutable Bus and which captures the lifetime of self ."或者,要对调用者隐藏RefCell实现细节,您可以改为返回impl DerefMut<Target=Bus> + '_表示“此方法返回可以作为可变Bus取消引用并捕获self生命周期的东西。”

Also, I wonder whether it is the best way to create bidirectional references?另外,我想知道这是否是创建双向引用的最佳方式?

Generally for bidirectional references, you want all of the references away from the "primary value" (eg the root of a tree) to be Rc and references in the other direction to be Weak .通常对于双向引用,您希望远离“主值”(例如树的根)的所有引用为Rc ,而另一个方向的引用为Weak This allows you to drop the primary value and have the whole network of structures be automatically destroyed.这使您可以删除主要值并自动销毁整个结构网络。 If you have two values with Rc s to each other then you have a reference cycle that must be explicitly broken to avoid a memory leak when you release your "top-level" reference to the network of structures.如果您有两个彼此具有Rc的值,那么当您释放对结构网络的“顶级”引用时,您必须明确打破引用循环以避免内存泄漏。

However, if you find yourself wanting bidirectional references in this kind of case where you have a fixed set of values that want to refer to each other, you likely want a different pattern altogether.但是,如果您发现自己在这种情况下需要双向引用,即您有一组固定的值想要相互引用,那么您可能需要完全不同的模式。 Based on your playground link, the CPU and PPU have strong shared ownership of the bus, and the bus has weak ownership back to the CPU and PPU.根据您的操场链接,CPU 和 PPU 对总线具有很强的共享所有权,而总线对 CPU 和 PPU 的所有权较弱。 (In your model, you should express that with Weak and not raw pointers.) (在你的模型中,你应该用Weak指针而不是原始指针来表达。)

What this hints at is that a single entity should own all of the components, and that entity should be responsible for their communication.这暗示了一个实体应该拥有所有组件,并且该实体应该负责它们的通信。 There's a few ways you could model this.有几种方法可以对此进行建模。

  • A System value owns all of the components, and this value would provide a communication mechanism. System值拥有所有组件,并且该值将提供一种通信机制。 This would replace Bus .这将取代Bus The circular reference can be avoided by requiring a reference to System be given to the component values any time they need to do anything.可以通过要求在组件值需要执行任何操作时对System进行引用来避免循环引用。
  • Channels could be used for communication between components, though this makes synchronous messaging between components trickier.通道可用于组件之间的通信,尽管这使得组件之间的同步消息传递更加棘手。
  • If the performance implications of channels are prohibitive and the desired API can't work by passing references to System around with every call, you could have references from the components back to System if you pin System so that it can't move, and therefore the references can't be invalidated.如果通道的性能影响令人望而却步,并且所需的 API 无法通过在每次调用时传递对System的引用来工作,那么如果您固定System使其无法移动,则可以将组件的引用返回给System ,因此引用不能失效。 This is a bit of an advanced topic;这是一个有点高级的话题; you may want to read the relevant documentation .您可能需要阅读相关文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM