diff --git a/src/buffer.rs b/src/buffer.rs index 4ff2c1d97eb..c951f7dff6b 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -641,20 +641,38 @@ impl Drop for PyBuffer { /// The data cannot be modified through the reference, but other references may /// be modifying the data. #[repr(transparent)] -pub struct ReadOnlyCell(cell::UnsafeCell); - -impl ReadOnlyCell { - /// Returns a copy of the current value. - #[inline] - pub fn get(&self) -> T { - unsafe { *self.0.get() } - } +pub struct ReadOnlyCell(cell::UnsafeCell); +impl ReadOnlyCell { /// Returns a pointer to the current value. #[inline] pub fn as_ptr(&self) -> *const T { self.0.get() } + + /// Converts `&T` to `&ReadOnlyCell`. + #[inline] + pub fn from_ref(t: &T) -> &ReadOnlyCell { + // Safety: ReadOnlyCell is repr(transparent), and &ReadOnlyCell is strictly less capable + // than &T. + unsafe { &*(t as *const T as *const ReadOnlyCell) } + } + + /// Converts `&Cell` to `&ReadOnlyCell`. + #[inline] + pub fn from_cell(t: &cell::Cell) -> &ReadOnlyCell { + // Safety: Cell and ReadOnlyCell are both repr(transparent), and &ReadOnlyCell is + // strictly less capable than &Cell. + unsafe { &*(t as *const cell::Cell as *const ReadOnlyCell) } + } +} + +impl ReadOnlyCell { + /// Returns a copy of the current value. + #[inline] + pub fn get(&self) -> T { + unsafe { *self.0.get() } + } } macro_rules! impl_element( @@ -686,7 +704,7 @@ impl_element!(f64, Float); #[cfg(test)] mod tests { - use super::PyBuffer; + use super::{PyBuffer, ReadOnlyCell}; use crate::ffi; use crate::Python; @@ -942,4 +960,25 @@ mod tests { assert_eq!(buffer.to_fortran_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]); }); } + + #[test] + fn test_read_only_cell_conversions() { + let mut x = 42; + let x_ref_mut = &mut x; + + let x_ref = &*x_ref_mut; + let x_rocell1 = ReadOnlyCell::from_ref(x_ref); + let x_rocell2 = ReadOnlyCell::from_ref(x_ref); + assert_eq!(x_rocell1.get(), x_rocell2.get()); + assert_eq!(x_rocell1.get(), 42); + + let x_cell = std::cell::Cell::from_mut(x_ref_mut); + let x_rocell3 = ReadOnlyCell::from_cell(x_cell); + let x_rocell4 = ReadOnlyCell::from_cell(x_cell); + assert_eq!(x_rocell3.get(), x_rocell4.get()); + assert_eq!(x_rocell3.get(), 42); + + // Importantly, this doesn't compile, because x_ref and x_cell cannot coexist. + // _ = *x_ref; + } }