nautilus_core/shared.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Efficient and ergonomic wrappers around frequently-used `Rc<RefCell<T>>` / `Weak<RefCell<T>>` pairs.
17//!
18//! The NautilusTrader codebase heavily relies on shared, interior-mutable ownership for many
19//! engine components (`Rc<RefCell<T>>`). Repeating that verbose type across many APIs—alongside
20//! its weak counterpart—clutters code and increases the likelihood of accidentally storing a
21//! strong reference where only a weak reference is required (leading to reference cycles).
22//!
23//! `SharedCell<T>` and `WeakCell<T>` are zero-cost new-types that make the intent explicit and
24//! offer convenience helpers (`downgrade`, `upgrade`, `borrow`, `borrow_mut`). Because the
25//! wrappers are `#[repr(transparent)]`, they have the exact same memory layout as the wrapped
26//! `Rc` / `Weak` and introduce no runtime overhead.
27
28//! ## Choosing between `SharedCell` and `WeakCell`
29//!
30//! * Use **`SharedCell<T>`** when the current owner genuinely *owns* (or co-owns) the value –
31//! just as you would normally store an `Rc<RefCell<T>>`.
32//! * Use **`WeakCell<T>`** for back-references that could otherwise form a reference cycle.
33//! The back-pointer does **not** keep the value alive, and every access must first
34//! `upgrade()` to a strong `SharedCell`. This pattern is how we break circular ownership such
35//! as *Exchange ↔ `ExecutionClient`*: the exchange keeps a `SharedCell` to the client, while the
36//! client holds only a `WeakCell` back to the exchange.
37
38use std::{
39 cell::{Ref, RefCell, RefMut},
40 rc::{Rc, Weak},
41};
42
43/// Strong, shared ownership of `T` with interior mutability.
44#[repr(transparent)]
45#[derive(Debug)]
46pub struct SharedCell<T>(Rc<RefCell<T>>);
47
48impl<T> Clone for SharedCell<T> {
49 fn clone(&self) -> Self {
50 Self(self.0.clone())
51 }
52}
53
54impl<T> SharedCell<T> {
55 /// Wraps a value inside `Rc<RefCell<..>>`.
56 #[inline]
57 pub fn new(value: T) -> Self {
58 Self(Rc::new(RefCell::new(value)))
59 }
60
61 /// Creates a [`WeakCell`] pointing to the same allocation.
62 #[inline]
63 #[must_use]
64 pub fn downgrade(&self) -> WeakCell<T> {
65 WeakCell(Rc::downgrade(&self.0))
66 }
67
68 /// Immutable borrow of the inner value.
69 #[inline]
70 #[must_use]
71 pub fn borrow(&self) -> Ref<'_, T> {
72 self.0.borrow()
73 }
74
75 /// Mutable borrow of the inner value.
76 #[inline]
77 #[must_use]
78 pub fn borrow_mut(&self) -> RefMut<'_, T> {
79 self.0.borrow_mut()
80 }
81
82 /// Number of active strong references.
83 #[inline]
84 #[must_use]
85 pub fn strong_count(&self) -> usize {
86 Rc::strong_count(&self.0)
87 }
88
89 /// Number of active weak references.
90 #[inline]
91 #[must_use]
92 pub fn weak_count(&self) -> usize {
93 Rc::weak_count(&self.0)
94 }
95}
96
97impl<T> From<Rc<RefCell<T>>> for SharedCell<T> {
98 fn from(inner: Rc<RefCell<T>>) -> Self {
99 Self(inner)
100 }
101}
102
103impl<T> From<SharedCell<T>> for Rc<RefCell<T>> {
104 fn from(shared: SharedCell<T>) -> Self {
105 shared.0
106 }
107}
108
109impl<T> std::ops::Deref for SharedCell<T> {
110 type Target = Rc<RefCell<T>>;
111
112 fn deref(&self) -> &Self::Target {
113 &self.0
114 }
115}
116
117/// Weak counterpart to [`SharedCell`].
118#[repr(transparent)]
119#[derive(Debug)]
120pub struct WeakCell<T>(Weak<RefCell<T>>);
121
122impl<T> Clone for WeakCell<T> {
123 fn clone(&self) -> Self {
124 Self(self.0.clone())
125 }
126}
127
128impl<T> WeakCell<T> {
129 /// Attempts to upgrade the weak reference to a strong [`SharedCell`].
130 #[inline]
131 pub fn upgrade(&self) -> Option<SharedCell<T>> {
132 self.0.upgrade().map(SharedCell)
133 }
134
135 /// Returns `true` if the pointed-to value has been dropped.
136 #[inline]
137 #[must_use]
138 pub fn is_dropped(&self) -> bool {
139 self.0.strong_count() == 0
140 }
141}
142
143impl<T> From<Weak<RefCell<T>>> for WeakCell<T> {
144 fn from(inner: Weak<RefCell<T>>) -> Self {
145 Self(inner)
146 }
147}
148
149impl<T> From<WeakCell<T>> for Weak<RefCell<T>> {
150 fn from(cell: WeakCell<T>) -> Self {
151 cell.0
152 }
153}