tock_cells/take_cell.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Tock specific `TakeCell` type for sharing references.
6
7use core::cell::Cell;
8
9/// A shared reference to a mutable reference.
10///
11/// A `TakeCell` wraps potential reference to mutable memory that may be
12/// available at a given point. Rather than enforcing borrow rules at
13/// compile-time, `TakeCell` enables multiple clients to hold references to it,
14/// but ensures that only one referrer has access to the underlying mutable
15/// reference at a time. Clients either move the memory out of the `TakeCell` or
16/// operate on a borrow within a closure. Attempts to take the value from inside
17/// a `TakeCell` may fail by returning `None`.
18pub struct TakeCell<'a, T: 'a + ?Sized> {
19 val: Cell<Option<&'a mut T>>,
20}
21
22impl<'a, T: ?Sized> TakeCell<'a, T> {
23 pub const fn empty() -> TakeCell<'a, T> {
24 Self {
25 val: Cell::new(None),
26 }
27 }
28
29 /// Creates a new `TakeCell` containing `value`
30 pub fn new(value: &'a mut T) -> TakeCell<'a, T> {
31 TakeCell {
32 val: Cell::new(Some(value)),
33 }
34 }
35
36 pub fn is_none(&self) -> bool {
37 let inner = self.take();
38 let return_val = inner.is_none();
39 self.val.set(inner);
40 return_val
41 }
42
43 pub fn is_some(&self) -> bool {
44 !self.is_none()
45 }
46
47 /// Takes the mutable reference out of the `TakeCell` leaving a `None` in
48 /// it's place. If the value has already been taken elsewhere (and not
49 /// `replace`ed), the returned `Option` will be empty.
50 ///
51 /// # Examples
52 ///
53 /// ```
54 /// extern crate tock_cells;
55 /// use tock_cells::take_cell::TakeCell;
56 ///
57 /// let mut value = 1234;
58 /// let cell = TakeCell::new(&mut value);
59 /// let x = &cell;
60 /// let y = &cell;
61 ///
62 /// x.take();
63 /// assert_eq!(y.take(), None);
64 /// ```
65 pub fn take(&self) -> Option<&'a mut T> {
66 self.val.replace(None)
67 }
68
69 /// Stores `val` in the `TakeCell`
70 pub fn put(&self, val: Option<&'a mut T>) {
71 self.val.replace(val);
72 }
73
74 /// Replaces the contents of the `TakeCell` with `val`. If the cell was not
75 /// empty, the previous value is returned, otherwise `None` is returned.
76 pub fn replace(&self, val: &'a mut T) -> Option<&'a mut T> {
77 self.val.replace(Some(val))
78 }
79
80 /// Retrieves a mutable reference to the inner value that only lives as long
81 /// as the reference to this does.
82 ///
83 /// This escapes the "take" aspect of TakeCell in a way which is guaranteed
84 /// safe due to the returned reference sharing the lifetime of `&mut self`.
85 pub fn get_mut(&mut self) -> Option<&mut T> {
86 self.val.get_mut().as_mut().map(|v| &mut **v)
87 }
88
89 /// Allows `closure` to borrow the contents of the `TakeCell` if-and-only-if
90 /// it is not `take`n already. The state of the `TakeCell` is unchanged
91 /// after the closure completes.
92 ///
93 /// # Examples
94 ///
95 /// ```
96 /// extern crate tock_cells;
97 /// use tock_cells::take_cell::TakeCell;
98 ///
99 /// let mut value = 1234;
100 /// let cell = TakeCell::new(&mut value);
101 /// let x = &cell;
102 /// let y = &cell;
103 ///
104 /// x.map(|value| {
105 /// // We have mutable access to the value while in the closure
106 /// *value += 1;
107 /// });
108 ///
109 /// // After the closure completes, the mutable memory is still in the cell,
110 /// // but potentially changed.
111 /// assert_eq!(y.take(), Some(&mut 1235));
112 /// ```
113 pub fn map<F, R>(&self, closure: F) -> Option<R>
114 where
115 F: FnOnce(&mut T) -> R,
116 {
117 let maybe_val = self.take();
118 maybe_val.map(|val| {
119 let res = closure(val);
120 self.replace(val);
121 res
122 })
123 }
124
125 /// Performs a `map` or returns a default value if the `TakeCell` is empty
126 pub fn map_or<F, R>(&self, default: R, closure: F) -> R
127 where
128 F: FnOnce(&mut T) -> R,
129 {
130 let maybe_val = self.take();
131 maybe_val.map_or(default, |val| {
132 let res = closure(val);
133 self.replace(val);
134 res
135 })
136 }
137
138 /// Performs a `map` or generates a value with the default
139 /// closure if the `TakeCell` is empty
140 pub fn map_or_else<U, D, F>(&self, default: D, f: F) -> U
141 where
142 D: FnOnce() -> U,
143 F: FnOnce(&mut T) -> U,
144 {
145 let maybe_val = self.take();
146 maybe_val.map_or_else(default, |val| {
147 let res = f(val);
148 self.replace(val);
149 res
150 })
151 }
152
153 /// Behaves the same as `map`, except the closure is allowed to return
154 /// an `Option`.
155 pub fn and_then<F, R>(&self, closure: F) -> Option<R>
156 where
157 F: FnOnce(&mut T) -> Option<R>,
158 {
159 let maybe_val = self.take();
160 maybe_val.and_then(|val| {
161 let res = closure(val);
162 self.replace(val);
163 res
164 })
165 }
166
167 /// Uses the first closure (`modify`) to modify the value in the `TakeCell`
168 /// if it is present, otherwise, fills the `TakeCell` with the result of
169 /// `mkval`.
170 pub fn modify_or_replace<F, G>(&self, modify: F, mkval: G)
171 where
172 F: FnOnce(&mut T),
173 G: FnOnce() -> &'a mut T,
174 {
175 let val = match self.take() {
176 Some(val) => {
177 modify(val);
178 val
179 }
180 None => mkval(),
181 };
182 self.replace(val);
183 }
184}