tock_cells/
optional_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//! `OptionalCell` convenience type
6
7use core::cell::Cell;
8
9/// `OptionalCell` is a `Cell` that wraps an `Option`. This is helper type
10/// that makes keeping types that can be `None` a little cleaner.
11pub struct OptionalCell<T> {
12    value: Cell<Option<T>>,
13}
14
15impl<T> OptionalCell<T> {
16    /// Create a new OptionalCell.
17    pub const fn new(val: T) -> OptionalCell<T> {
18        OptionalCell {
19            value: Cell::new(Some(val)),
20        }
21    }
22
23    /// Create an empty `OptionalCell` (contains just `None`).
24    pub const fn empty() -> OptionalCell<T> {
25        OptionalCell {
26            value: Cell::new(None),
27        }
28    }
29
30    /// Update the stored value.
31    pub fn set(&self, val: T) {
32        self.value.set(Some(val));
33    }
34
35    /// Insert the value of the supplied `Option`, or `None` if the supplied
36    /// `Option` is `None`.
37    pub fn insert(&self, opt: Option<T>) {
38        self.value.set(opt);
39    }
40
41    /// Replace the contents with the supplied value.
42    /// If the cell was not empty, the previous value is returned, otherwise
43    /// `None` is returned.
44    pub fn replace(&self, val: T) -> Option<T> {
45        let prev = self.take();
46        self.set(val);
47        prev
48    }
49
50    /// Reset the stored value to `None`.
51    pub fn clear(&self) {
52        self.value.set(None);
53    }
54
55    /// Check if the cell contains something.
56    pub fn is_some(&self) -> bool {
57        let value = self.value.take();
58        let out = value.is_some();
59        self.value.set(value);
60        out
61    }
62
63    /// Check if the cell is None.
64    pub fn is_none(&self) -> bool {
65        let value = self.value.take();
66        let out = value.is_none();
67        self.value.set(value);
68        out
69    }
70
71    /// Returns true if the option is a Some value containing the given value.
72    pub fn contains(&self, x: &T) -> bool
73    where
74        T: PartialEq,
75    {
76        let value = self.value.take();
77        let out = match &value {
78            Some(y) => y == x,
79            None => false,
80        };
81        self.value.set(value);
82        out
83    }
84
85    /// Transforms the contained `Option<T>` into a `Result<T, E>`, mapping
86    /// `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
87    ///
88    /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing
89    /// the result of a function call, it is recommended to use `ok_or_else`,
90    /// which is lazily evaluated.
91    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
92        self.value.into_inner().ok_or(err)
93    }
94
95    /// Transforms the contained `Option<T>` into a `Result<T, E>`, mapping
96    /// `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
97    pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E>
98    where
99        F: FnOnce() -> E,
100    {
101        self.value.into_inner().ok_or_else(err)
102    }
103
104    /// Returns `None` if the option is `None`, otherwise returns `optb`.
105    pub fn and<U>(self, optb: Option<U>) -> Option<U> {
106        self.value.into_inner().and(optb)
107    }
108
109    /// Returns `None` if the option is `None`, otherwise calls `predicate` with
110    /// the wrapped value and returns:
111    ///
112    /// - `Some(t)` if `predicate` returns `true` (where `t` is the wrapped value), and
113    /// - `None` if `predicate` returns `false`.
114    pub fn filter<P>(self, predicate: P) -> Option<T>
115    where
116        P: FnOnce(&T) -> bool,
117    {
118        self.value.into_inner().filter(predicate)
119    }
120
121    /// Returns the option if it contains a value, otherwise returns `optb`.
122    ///
123    /// Arguments passed to or are eagerly evaluated; if you are passing the
124    /// result of a function call, it is recommended to use `or_else`, which
125    /// is lazily evaluated.
126    pub fn or(self, optb: Option<T>) -> Option<T> {
127        self.value.into_inner().or(optb)
128    }
129
130    /// Returns the option if it contains a value, otherwise calls `f` and
131    /// returns the result.
132    pub fn or_else<F>(self, f: F) -> Option<T>
133    where
134        F: FnOnce() -> Option<T>,
135    {
136        self.value.into_inner().or_else(f)
137    }
138
139    /// Return the contained value and replace it with None.
140    pub fn take(&self) -> Option<T> {
141        self.value.take()
142    }
143
144    /// Returns the contained value or a default
145    ///
146    /// Consumes the `self` argument then, if `Some`, returns the contained
147    /// value, otherwise if `None`, returns the default value for that type.
148    pub fn unwrap_or_default(self) -> T
149    where
150        T: Default,
151    {
152        self.value.into_inner().unwrap_or_default()
153    }
154}
155
156impl<T: Copy> OptionalCell<T> {
157    /// Returns a copy of the contained [`Option`].
158    //
159    // This was originally introduced in PR #2531 [1], then renamed to `extract`
160    // in PR #2533 [2], and finally renamed back in PR #3536 [3].
161    //
162    // The rationale for including a `get` method is to allow developers to
163    // treat an `OptionalCell<T>` as what it is underneath: a `Cell<Option<T>>`.
164    // This is useful to be interoperable with APIs that take an `Option<T>`, or
165    // to use an *if-let* or *match* expression to perform case-analysis on the
166    // `OptionalCell`'s state: this avoids using a closure and can thus allow
167    // Rust to deduce that only a single branch will ever be entered (either the
168    // `Some(_)` or `None`) branch, avoiding lifetime & move restrictions.
169    //
170    // However, there was pushback for that name, as an `OptionalCell`'s `get`
171    // method might indicate that it should directly return a `T` -- given that
172    // `OptionalCell<T>` presents itself as to be a wrapper around
173    // `T`. Furthermore, adding `.get()` might have developers use
174    // `.get().map(...)` instead, which defeats the purpose of having the
175    // `OptionalCell` convenience wrapper in the first place. For these reasons,
176    // `get` was renamed to `extract`.
177    //
178    // Unfortunately, `extract` turned out to be a confusing name, as it is not
179    // an idiomatic method name as found on Rust's standard library types, and
180    // further suggests that it actually removes a value from the `OptionalCell`
181    // (as the `take` method does). Thus, it has been renamed back to `get`.
182    //
183    // [1]: https://github.com/tock/tock/pull/2531
184    // [2]: https://github.com/tock/tock/pull/2533
185    // [3]: https://github.com/tock/tock/pull/3536
186    pub fn get(&self) -> Option<T> {
187        self.value.get()
188    }
189
190    /// Returns the contained value or panics if contents is `None`.
191    /// We do not use the traditional name for this function -- `unwrap()`
192    /// -- because the Tock kernel discourages panicking, and this name
193    /// is intended to discourage users from casually adding calls to
194    /// `unwrap()` without careful consideration.
195    #[track_caller]
196    pub fn unwrap_or_panic(&self) -> T {
197        self.value.get().unwrap()
198    }
199
200    /// Returns the contained value or a default.
201    pub fn unwrap_or(&self, default: T) -> T {
202        self.value.get().unwrap_or(default)
203    }
204
205    /// Returns the contained value or computes a default.
206    pub fn unwrap_or_else<F>(&self, default: F) -> T
207    where
208        F: FnOnce() -> T,
209    {
210        self.value.get().unwrap_or_else(default)
211    }
212
213    /// Call a closure on the value if the value exists.
214    pub fn map<F, R>(&self, closure: F) -> Option<R>
215    where
216        F: FnOnce(T) -> R,
217    {
218        self.value.get().map(|val| closure(val))
219    }
220
221    /// Call a closure on the value if the value exists, or return the
222    /// default if the value is `None`.
223    pub fn map_or<F, R>(&self, default: R, closure: F) -> R
224    where
225        F: FnOnce(T) -> R,
226    {
227        self.value.get().map_or(default, |val| closure(val))
228    }
229
230    /// If the cell contains a value, call a closure supplied with the
231    /// value of the cell. If the cell contains `None`, call the other
232    /// closure to return a default value.
233    pub fn map_or_else<U, D, F>(&self, default: D, closure: F) -> U
234    where
235        D: FnOnce() -> U,
236        F: FnOnce(T) -> U,
237    {
238        self.value.get().map_or_else(default, |val| closure(val))
239    }
240
241    /// If the cell is empty, return `None`. Otherwise, call a closure
242    /// with the value of the cell and return the result.
243    pub fn and_then<U, F: FnOnce(T) -> Option<U>>(&self, f: F) -> Option<U> {
244        self.value.get().and_then(f)
245    }
246}
247
248// Manual implementation of the [`Default`] trait, as
249// `#[derive(Default)]` incorrectly constraints `T: Default`.
250impl<T> Default for OptionalCell<T> {
251    /// Returns an empty [`OptionalCell`].
252    fn default() -> Self {
253        OptionalCell::empty()
254    }
255}