nautilus_core/shared.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 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::{BorrowError, BorrowMutError, 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 /// Attempts to immutably borrow the inner value.
83 ///
84 /// Returns `Err` if the value is currently mutably borrowed.
85 #[inline]
86 pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
87 self.0.try_borrow()
88 }
89
90 /// Attempts to mutably borrow the inner value.
91 ///
92 /// Returns `Err` if the value is currently borrowed (mutably or immutably).
93 #[inline]
94 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
95 self.0.try_borrow_mut()
96 }
97
98 /// Number of active strong references.
99 #[inline]
100 #[must_use]
101 pub fn strong_count(&self) -> usize {
102 Rc::strong_count(&self.0)
103 }
104
105 /// Number of active weak references.
106 #[inline]
107 #[must_use]
108 pub fn weak_count(&self) -> usize {
109 Rc::weak_count(&self.0)
110 }
111}
112
113impl<T> From<Rc<RefCell<T>>> for SharedCell<T> {
114 fn from(inner: Rc<RefCell<T>>) -> Self {
115 Self(inner)
116 }
117}
118
119impl<T> From<SharedCell<T>> for Rc<RefCell<T>> {
120 fn from(shared: SharedCell<T>) -> Self {
121 shared.0
122 }
123}
124
125impl<T> std::ops::Deref for SharedCell<T> {
126 type Target = Rc<RefCell<T>>;
127
128 fn deref(&self) -> &Self::Target {
129 &self.0
130 }
131}
132
133/// Weak counterpart to [`SharedCell`].
134#[repr(transparent)]
135#[derive(Debug)]
136pub struct WeakCell<T>(Weak<RefCell<T>>);
137
138impl<T> Clone for WeakCell<T> {
139 fn clone(&self) -> Self {
140 Self(self.0.clone())
141 }
142}
143
144impl<T> WeakCell<T> {
145 /// Attempts to upgrade the weak reference to a strong [`SharedCell`].
146 #[inline]
147 pub fn upgrade(&self) -> Option<SharedCell<T>> {
148 self.0.upgrade().map(SharedCell)
149 }
150
151 /// Returns `true` if the pointed-to value has been dropped.
152 #[inline]
153 #[must_use]
154 pub fn is_dropped(&self) -> bool {
155 self.0.strong_count() == 0
156 }
157}
158
159impl<T> From<Weak<RefCell<T>>> for WeakCell<T> {
160 fn from(inner: Weak<RefCell<T>>) -> Self {
161 Self(inner)
162 }
163}
164
165impl<T> From<WeakCell<T>> for Weak<RefCell<T>> {
166 fn from(cell: WeakCell<T>) -> Self {
167 cell.0
168 }
169}