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}