Skip to content

Instantly share code, notes, and snippets.

@douglasmakey
Created February 8, 2025 12:36
Show Gist options
  • Save douglasmakey/2c6be5a13a7eda83aae38afc8d151650 to your computer and use it in GitHub Desktop.
Save douglasmakey/2c6be5a13a7eda83aae38afc8d151650 to your computer and use it in GitHub Desktop.
Custom cell for the note
use std::cell::UnsafeCell;
struct MyCell<T> {
value: UnsafeCell<T>,
}
impl<T> MyCell<T> {
fn new(value: T) -> MyCell<T> {
Self {
value: UnsafeCell::new(value),
}
}
fn set_direct(&self, value: T) {
// SAFETY: MyCell is not Sync, so no other thread can access this value.
// Also, because we never expose references to the inner value, this direct
// mutation is safe.
unsafe {
// we get a raw pointer to the inner value
let ptr = self.value.get();
// we can dereference the pointer and change the value
*ptr = value;
}
}
fn set(&self, value: T) {
self.replace(value);
}
fn replace(&self, value: T) -> T {
// SAFETY: MyCell is not Sync, so no other thread can access this value.
// Also, because we never expose references to the inner value, this replacement is safe.
std::mem::replace(unsafe { &mut *self.value.get() }, value)
}
}
impl<T: Copy> MyCell<T> {
fn get(&self) -> T {
// SAFETY: MyCell is not Sync, so we can assume that no other thread is accessing the value
// and as we are using Copy types, we can safely return the value because it's a copy not a reference
unsafe { *self.value.get() }
}
}
fn main() {}
#[cfg(test)]
mod test {
use std::cell::UnsafeCell;
use crate::MyCell;
#[test]
fn std_cell_copy() {
use std::cell::Cell;
let x = Cell::new(42);
x.set(100); // Mutation allowed, even though `x` isn't declared as `mut`
assert_eq!(x.get(), 100);
}
#[test]
fn std_cell_copy_in_struct() {
use std::cell::Cell;
struct MyStruct {
x: i32,
y: Cell<i32>,
}
let s = MyStruct {
x: 42,
y: Cell::new(42),
};
s.y.set(100); // Mutation allowed, even though `s` isn't declared as `mut`
assert_eq!(s.y.get(), 100);
// s.x = 100; // Error: cannot assign to `s.x`, as `s` is not declared as `mut`
}
#[test]
fn std_cell_non_copy() {
use std::cell::Cell;
let cell = Cell::new(String::from("Hello"));
// As `String` is not `Copy`, we can't get the value out of the cell with `get`
// For non-copy types, we can use `replace` to swap the value with a new one and get the old value
let old = cell.replace(String::from("World"));
assert_eq!(old, "Hello");
// Or we can use `take` to get the value and replace it with the default value
let old = cell.take();
assert_eq!(old, "World");
// We can also use `into_inner` to get the value and consume the cell and as we used `take` before, the value should be the default value
assert_eq!(cell.into_inner(), "");
}
#[test]
fn my_cell() {
let x = MyCell::new(42);
x.set(100);
assert_eq!(x.get(), 100);
x.set_direct(200);
assert_eq!(x.get(), 200);
}
#[test]
fn my_cell_non_copy() {
let x = MyCell::new(String::from("Hello"));
let old = x.replace(String::from("World"));
assert_eq!(old, "Hello");
let old = x.replace(String::from("Rust"));
assert_eq!(old, "World");
}
#[test]
fn my_cell_in_struct() {
struct MyStruct {
x: i32,
y: MyCell<i32>,
}
let s = MyStruct {
x: 42,
y: MyCell::new(42),
};
s.y.set(100);
assert_eq!(s.y.get(), 100);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment