/* * Copyright 2022 taylor.fish * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #![no_std] #![forbid(unsafe_code)] //! This crate provides a [`Cell`] type (like the standard library’s //! [`Cell`][std-cell]) with methods for safely mutating and inspecting the //! inner value by reference ([`with`] and [`with_mut`]). //! //! For [`Copy`] types, this is implemented with [`get`][std-get] and //! [`set`][std-set], but [through an extension trait][cell-ext], this crate //! provides those same operations for types that are [`Default`] but not //! [`Copy`]. A [`get`] method is also available for types that are both //! [`Default`] and [`Clone`]. //! //! This crate depends only on [`core`], so it can be used inside `no_std` //! environments. //! //! Example //! ------- //! //! ```rust //! use cell_ref::{Cell, CellExt}; //! //! let c1 = Cell::new(2_u8); //! c1.with_mut(|x| *x += 3); //! assert!(c1.get() == 5); //! //! let c2 = Cell::new(vec![1, 2, 3]); //! c2.with_mut(|v| v.push(4)); // Works even though `Vec` isn't `Copy` //! assert_eq!(c2.with(Vec::len), 4); //! let v = c2.get(); // Clones the vector //! ``` //! //! [std-cell]: StdCell //! [cell-ext]: CellExt //! [`with`]: Cell::with //! [`with_mut`]: Cell::with_mut //! [std-get]: StdCell::get //! [std-set]: StdCell::set //! [`get`]: Cell::get use core::cell::Cell as StdCell; use core::cmp::Ordering; use core::fmt; use core::ops::{Deref, DerefMut}; #[cfg(test)] mod tests; /// A `Cell` type with methods for by-reference mutation and inspection. #[derive(Default)] pub struct Cell(StdCell); impl Cell { /// Creates a new [`Cell`] with the given value. pub fn new(value: T) -> Self { Self(StdCell::new(value)) } } impl Deref for Cell { type Target = StdCell; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Cell { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl From for Cell { fn from(value: T) -> Self { Self::new(value) } } impl From> for Cell { fn from(cell: StdCell) -> Self { Self(cell) } } impl From> for StdCell { fn from(cell: Cell) -> Self { cell.0 } } impl Cell { /// Gets the value held by the cell. pub fn get(&self) -> T { self.0.get() } /// Calls `f` with a reference to the contents of the cell. pub fn with(&self, f: F) -> R where F: FnOnce(&T) -> R, { f(&self.get()) } /// Calls `f` with a mutable reference to the contents of the cell. pub fn with_mut(&self, f: F) -> R where F: FnOnce(&mut T) -> R, { let mut value = self.get(); let result = f(&mut value); self.set(value); result } } mod sealed { pub trait Sealed {} } /// Provides additional methods for non-[`Copy`] types. pub trait CellExt: sealed::Sealed { /// Gets the value held by the cell. fn get(&self) -> T where T: Clone + Default; /// Calls `f` with a reference to the contents of the cell. fn with(&self, f: F) -> R where T: Default, F: FnOnce(&T) -> R; /// Calls `f` with a mutable reference to the contents of the cell. fn with_mut(&self, f: F) -> R where T: Default, F: FnOnce(&mut T) -> R; } impl sealed::Sealed for Cell {} impl CellExt for Cell { fn get(&self) -> T where T: Clone + Default, { self.with(T::clone) } fn with(&self, f: F) -> R where T: Default, F: FnOnce(&T) -> R, { let value = self.take(); let result = f(&value); self.set(value); result } fn with_mut(&self, f: F) -> R where T: Default, F: FnOnce(&mut T) -> R, { let mut value = self.take(); let result = f(&mut value); self.set(value); result } } impl Clone for Cell { fn clone(&self) -> Self { Self(self.0.clone()) } } impl fmt::Debug for Cell { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(fmt) } } impl Ord for Cell { fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(other) } } impl PartialOrd for Cell { fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(other) } } impl PartialEq for Cell { fn eq(&self, other: &Self) -> bool { self.0.eq(other) } } impl Eq for Cell {}