Created
February 8, 2025 12:36
-
-
Save douglasmakey/2c6be5a13a7eda83aae38afc8d151650 to your computer and use it in GitHub Desktop.
Custom cell for the note
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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