Last active
October 3, 2016 18:42
-
-
Save randomPoison/ce2f80532c107ba3e9b955abdbda1e0e to your computer and use it in GitHub Desktop.
Additional cell types for all sorts of irresponsible usage.
This file contains 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
//! More shareable, mutable containers. | |
//! | |
//! [`Cell<T>`][cell] and [`RefCell<T>`][refcell] are a good start, but they | |
//! don't represent all the forms of interior mutability that developers might | |
//! need. This crate provides a more comprehensive suite of cell types to cover | |
//! the cases not solved by the standard library. For more information on cells | |
//! and interior mutability in general please see [the std::cell module's | |
//! documentation][std::cell]. | |
//! | |
//! # When Should You Use Which Cell? | |
//! | |
//! Here's a quick breakdown of each cell type and when it would be helpful: | |
//! | |
//! ### Use an `InitCell<T>` when: | |
//! | |
//! - You have a non-optional value that can't be initialized when the object | |
//! is created. | |
//! - You want to ensure that the value is never accessed in an unitialized | |
//! state. | |
//! - You don't need the dynamically checked borrow rules of a | |
//! [`RefCell<T>`][refcell] | |
//! - You have a thread-local static that needs to be lazily initialzed at | |
//! startup, but you want to access it without checking if it's initialized. | |
//! | |
//! ### Use an `AtomicInitCell<T>` when: | |
//! | |
//! - You have a static that needs to be lazily initialized, but you want to be | |
//! able to access the data without checking if it's initialized. | |
//! - You want to use a thread-safe `InitCell<T>`. | |
//! | |
//! ### Use an `AtomicCell<T>` when: | |
//! | |
//! - You want to use a thread-safe [`Cell<T>`][cell]. | |
//! | |
//! ### Use an `AtomicRefCell<T>` when: | |
//! | |
//! - You want to use a [`RefCell<T>`][refcell] but need to to be thread-safe. | |
//! - You want an [`RwLock<T>`][rwlock] that panics instead of blocking. | |
//! | |
//! [std::cell]: https://doc.rust-lang.org/std/cell/index.html | |
//! [cell]: https://doc.rust-lang.org/std/cell/struct.Cell.html | |
//! [refcell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html | |
//! [rwlock]: https://doc.rust-lang.org/std/sync/struct.RwLock.html | |
#![feature(const_fn)] | |
use std::cell::UnsafeCell; | |
use std::ops::{Deref, DerefMut}; | |
/// Cell that allows a value to be lazily initialized once. | |
/// | |
/// | |
pub struct InitCell<T>(UnsafeCell<Option<T>>); | |
impl<T> InitCell<T> { | |
/// Create a new, uninitialized `InitCell<T>`. | |
pub const fn new() -> InitCell<T> { | |
InitCell(UnsafeCell::new(None)) | |
} | |
/// Initialize the cell with the specified value. | |
/// | |
/// # Panics | |
/// | |
/// - If the cell has already been initialized. | |
pub fn init(&self, value: T) { | |
// It's safe to take a mutable reference to the data in here because | |
// reasons: | |
// | |
// - If the data hasn't been initialized, then attempts to take a | |
// reference to the data would have panicked, so we can assume there | |
// are no references to the data. | |
// - If the data has been initialized, then we're about to panic anyway | |
// so whatever. | |
let data = unsafe { &mut *self.0.get() }; | |
assert!(data.is_none(), "Cannot initialize InitCell more than once"); | |
*data = Some(value); | |
} | |
/// Get a reference to the data if the cell has been initialized. | |
pub fn get(&self) -> Option<&T> { | |
let data = unsafe { &*self.0.get() }; | |
data.as_ref() | |
} | |
/// Get a mutable reference to the data if the cell has been initialized. | |
pub fn get_mut(&mut self) -> Option<&mut T> { | |
let data = unsafe { &mut *self.0.get() }; | |
data.as_mut() | |
} | |
} | |
impl<T> Deref for InitCell<T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
self.get().expect("Cannot deref `InitCell` if it hasn't been initialized") | |
} | |
} | |
impl<T> DerefMut for InitCell<T> { | |
fn deref_mut(&mut self) -> &mut T { | |
self.get_mut().expect("Cannot deref `InitCell` if it hasn't been initialized") | |
} | |
} | |
unsafe impl<T: Send> Send for InitCell<T> {} | |
fn main() { | |
thread_local! { | |
static STATIC_DATA: InitCell<String> = InitCell::new(); | |
} | |
// Normal usage of `InitCell<T>`. | |
let cell = InitCell::<usize>::new(); | |
println!("cell before init: {:?}", cell.get()); | |
cell.init(10); | |
println!("cell after init: {:?}", cell.get()); | |
// Use `InitCell<T>` in a static. | |
STATIC_DATA.with(|static_data| static_data.init("foo".into())); | |
// STATIC_DATA.with(|static_data| println!("STATIC_DATA: {:?}", static_data)); | |
let empty_cell = InitCell::<f32>::new(); | |
println!("empty cell: {:?}", &*empty_cell); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment