rcell/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![warn(rustdoc::missing_crate_level_docs)]
4//! Example:
5//! ```
6//! use rcell::*;
7//!
8//! // Our Type
9//! #[derive(Debug, PartialEq)]
10//! struct MyType<T>(T);
11//!
12//! let my_rcell = RCell::new(MyType(100u8));
13//! assert_eq!(*my_rcell.request().unwrap(), MyType(100));
14//! ```
15
16use std::mem;
17#[cfg(feature = "sync")]
18#[doc(hidden)]
19pub use std::sync::{Arc as Strong, Weak};
20
21#[cfg(not(feature = "sync"))]
22#[doc(hidden)]
23pub use std::rc::{Rc as Strong, Weak};
24
25/// A RCell holding either an `Strong<T>`, a `Weak<T>` or being `Empty`.
26#[derive(Debug)]
27pub enum RCell<T> {
28    /// Strong reference
29    Strong(Strong<T>),
30    /// Weak reference
31    Weak(Weak<T>),
32    /// Empty cell
33    Empty,
34}
35
36impl<T> RCell<T> {
37    /// Creates a new strong (Strong<T>) RCell from the supplied value.
38    pub fn new(value: T) -> Self {
39        RCell::Strong(Strong::new(value))
40    }
41
42    /// Returns 'true' when this RCell contains a `Strong<T>`.
43    pub fn retained(&self) -> bool {
44        matches!(*self, RCell::Strong(_))
45    }
46
47    /// Returns the number of strong references holding an object alive. The returned strong
48    /// count is informal only, the result may be approximate and has race conditions when
49    /// other threads modify the reference count concurrently.
50    pub fn refcount(&self) -> usize {
51        match self {
52            RCell::Strong(arc) => Strong::strong_count(arc),
53            RCell::Weak(weak) => weak.strong_count(),
54            RCell::Empty => 0,
55        }
56    }
57
58    /// Tries to upgrade this RCell from Weak<T> to Strong<T>. This means that as long the RCell
59    /// is not dropped the associated data won't be either. When successful it returns
60    /// Some<Strong<T>> containing the value, otherwise None is returned on failure.
61    pub fn retain(&mut self) -> Option<Strong<T>> {
62        match self {
63            RCell::Strong(strong) => Some(strong.clone()),
64            RCell::Weak(weak) => {
65                if let Some(strong) = weak.upgrade() {
66                    let _ = mem::replace(self, RCell::Strong(strong.clone()));
67                    Some(strong)
68                } else {
69                    None
70                }
71            }
72            RCell::Empty => None,
73        }
74    }
75
76    /// Downgrades the RCell, any associated value may become dropped when no other references
77    /// exist. When no strong reference left remaining this cell becomes Empty.
78    pub fn release(&mut self) {
79        if let Some(weak) = match self {
80            RCell::Strong(strong) => Some(Strong::downgrade(strong)),
81            RCell::Weak(weak) => Some(weak.clone()),
82            RCell::Empty => None,
83        } {
84            if weak.strong_count() > 0 {
85                let _ = mem::replace(self, RCell::Weak(weak));
86            } else {
87                let _ = mem::replace(self, RCell::Empty);
88            }
89        }
90    }
91
92    /// Removes the reference to the value. The rationale for this function is to release
93    /// *any* resource associated with a RCell (potentially member of a struct that lives
94    /// longer) in case one knows that it will never be upgraded again.
95    pub fn remove(&mut self) {
96        let _ = mem::replace(self, RCell::Empty);
97    }
98
99    /// Tries to get an `Strong<T>` from the RCell. This may fail if the RCell was Weak and all
100    /// other strong references became dropped.
101    pub fn request(&self) -> Option<Strong<T>> {
102        match self {
103            RCell::Strong(arc) => Some(arc.clone()),
104            RCell::Weak(weak) => weak.upgrade(),
105            RCell::Empty => None,
106        }
107    }
108}
109
110/// Helper Trait for replacing the content of a RCell with something new.
111pub trait Replace<T> {
112    /// Replaces the contained value in self with T.
113    fn replace(&mut self, new: T);
114}
115
116impl<T> Replace<Strong<T>> for RCell<T> {
117    /// Replaces the RCell with the supplied `Strong<T>`. The old entry becomes dropped.
118    fn replace(&mut self, strong: Strong<T>) {
119        let _ = mem::replace(self, RCell::Strong(strong));
120    }
121}
122
123impl<T> Replace<Weak<T>> for RCell<T> {
124    /// Replaces the RCell with the supplied `Weak<T>`. The old entry becomes dropped.
125    fn replace(&mut self, weak: Weak<T>) {
126        let _ = mem::replace(self, RCell::Weak(weak));
127    }
128}
129
130impl<T> From<Strong<T>> for RCell<T> {
131    /// Creates a new strong RCell with the supplied `Strong<T>`.
132    fn from(strong: Strong<T>) -> Self {
133        RCell::Strong(strong)
134    }
135}
136
137impl<T> From<Weak<T>> for RCell<T> {
138    /// Creates a new weak RCell with the supplied `Weak<T>`.
139    fn from(weak: Weak<T>) -> Self {
140        RCell::Weak(weak)
141    }
142}
143
144impl<T> Default for RCell<T> {
145    /// Creates an RCell that doesn't hold any reference.
146    fn default() -> Self {
147        RCell::Empty
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use crate::{RCell, Strong, Replace};
154
155    #[test]
156    fn smoke() {
157        let rcell = RCell::new("foobar");
158        assert!(rcell.retained());
159    }
160
161    #[test]
162    fn new() {
163        let mut rcell = RCell::new("foobar");
164        assert!(rcell.retained());
165        assert_eq!(*rcell.request().unwrap(), "foobar");
166        rcell.release();
167        assert_eq!(rcell.request(), None);
168    }
169
170    #[test]
171    fn default() {
172        let rcell = RCell::<i32>::default();
173        assert!(!rcell.retained());
174        assert_eq!(rcell.request(), None);
175    }
176
177    #[test]
178    fn from_strong() {
179        let strong = Strong::new("foobar");
180        let mut rcell = RCell::from(strong);
181        assert!(rcell.retained());
182        assert_eq!(*rcell.request().unwrap(), "foobar");
183        rcell.release();
184        assert_eq!(rcell.request(), None);
185    }
186
187    #[test]
188    fn from_weak_release() {
189        let strong = Strong::new("foobar");
190        let weak = Strong::downgrade(&strong);
191        let mut rcell = RCell::from(weak);
192        assert!(!rcell.retained());
193        assert_eq!(*rcell.request().unwrap(), "foobar");
194        rcell.release();
195        assert_eq!(*rcell.request().unwrap(), "foobar");
196        rcell.remove();
197        assert_eq!(rcell.request(), None);
198    }
199
200    #[test]
201    fn from_weak_drop_original() {
202        let strong = Strong::new("foobar");
203        let weak = Strong::downgrade(&strong);
204        let mut rcell = RCell::from(weak);
205        assert!(!rcell.retained());
206        assert_eq!(*rcell.request().unwrap(), "foobar");
207        drop(strong);
208        assert_eq!(rcell.request(), None);
209        rcell.remove();
210        assert_eq!(rcell.request(), None);
211    }
212
213    #[test]
214    fn replace_strong() {
215        let mut rcell = RCell::default();
216        assert!(!rcell.retained());
217        rcell.replace(Strong::new("foobar"));
218        assert_eq!(*rcell.request().unwrap(), "foobar");
219        rcell.remove();
220        assert_eq!(rcell.request(), None);
221    }
222
223    #[test]
224    fn replace_weak() {
225        let strong = Strong::new("foobar");
226        let mut rcell = RCell::default();
227        assert!(!rcell.retained());
228        rcell.replace(Strong::downgrade(&strong));
229        assert_eq!(*rcell.request().unwrap(), "foobar");
230        rcell.remove();
231        assert_eq!(rcell.request(), None);
232    }
233}