parent
f0cb49e436
commit
c11b65d6d0
@ -0,0 +1,4 @@ |
|||||||
|
/target |
||||||
|
**/*.rs.bk |
||||||
|
.idea/ |
||||||
|
*.iml |
@ -0,0 +1,4 @@ |
|||||||
|
[[package]] |
||||||
|
name = "slot" |
||||||
|
version = "0.1.0" |
||||||
|
|
@ -0,0 +1,7 @@ |
|||||||
|
[package] |
||||||
|
name = "slot" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
[dependencies] |
@ -0,0 +1,254 @@ |
|||||||
|
use std::cell::{Ref, RefCell}; |
||||||
|
use std::fmt; |
||||||
|
use std::rc::Rc; |
||||||
|
|
||||||
|
/// Smart cell that wraps an optional reference-counted value,
|
||||||
|
/// which can be leased, temporarily borrowed, removed, or replaced.
|
||||||
|
pub struct Slot<T> { |
||||||
|
v: RefCell<Option<Rc<T>>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> Slot<T> { |
||||||
|
/// Create a slot
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use slot::Slot;
|
||||||
|
/// let s : Slot<String> = Slot::new();
|
||||||
|
/// assert_eq!(true, s.is_empty());
|
||||||
|
/// ```
|
||||||
|
pub fn new() -> Slot<T> { |
||||||
|
Slot { |
||||||
|
v: RefCell::new(None), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a slot with an initial value
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use slot::Slot;
|
||||||
|
/// let x = String::from("lorem");
|
||||||
|
/// let s : Slot<String> = Slot::new_with(x);
|
||||||
|
///
|
||||||
|
/// assert_eq!("lorem", s.lease().as_str());
|
||||||
|
/// assert_eq!("lorem", s.take().as_str());
|
||||||
|
/// ```
|
||||||
|
pub fn new_with(value: T) -> Slot<T> { |
||||||
|
Slot { |
||||||
|
v: RefCell::new(Some(Rc::new(value))), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Take a hold of the inner value via an `Rc`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if empty
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use slot::Slot;
|
||||||
|
/// let s : Slot<String> = Slot::new_with(String::from("lorem"));
|
||||||
|
///
|
||||||
|
/// let lease1 = s.lease();
|
||||||
|
/// let lease2 = s.lease();
|
||||||
|
///
|
||||||
|
/// assert_eq!("lorem", lease1.as_str());
|
||||||
|
/// assert_eq!("lorem", lease2.as_str());
|
||||||
|
///
|
||||||
|
/// s.put(String::from("ipsum")); // here we replace the value, but the old Rc's remain valid
|
||||||
|
///
|
||||||
|
/// assert_eq!("lorem", lease1.as_str());
|
||||||
|
/// assert_eq!("lorem", lease2.as_str());
|
||||||
|
///
|
||||||
|
/// let lease3 = s.lease();
|
||||||
|
/// assert_eq!("ipsum", lease3.as_str());
|
||||||
|
/// ```
|
||||||
|
pub fn lease(&self) -> Rc<T> { |
||||||
|
Rc::clone(self.v.borrow().as_ref().unwrap()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Check if the container is empty
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use slot::Slot;
|
||||||
|
/// let s : Slot<String> = Slot::new();
|
||||||
|
/// assert!(s.is_empty());
|
||||||
|
///
|
||||||
|
/// let s = Slot::new_with(132);
|
||||||
|
/// assert!(! s.is_empty());
|
||||||
|
/// ```
|
||||||
|
pub fn is_empty(&self) -> bool { |
||||||
|
self.v.borrow().is_none() |
||||||
|
} |
||||||
|
|
||||||
|
/// Put a value into the slot. Returns the old value, if any.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently borrowed.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use slot::Slot;
|
||||||
|
/// use std::rc::Rc;
|
||||||
|
///
|
||||||
|
/// let s : Slot<i32> = Slot::new();
|
||||||
|
/// s.put(123);
|
||||||
|
///
|
||||||
|
/// assert!(! s.is_empty());
|
||||||
|
/// assert_eq!(123, Rc::try_unwrap(s.take()).unwrap());
|
||||||
|
/// ```
|
||||||
|
pub fn put(&self, value: T) -> Option<Rc<T>> { |
||||||
|
self.v.replace(Some(Rc::new(value))) |
||||||
|
} |
||||||
|
|
||||||
|
/// Take a value out of the slot, setting it to `None`.
|
||||||
|
/// If there are any leases, they remain valid, as they point
|
||||||
|
/// to clones of the same `Rc`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if empty
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use slot::Slot;
|
||||||
|
/// use std::rc::Rc;
|
||||||
|
///
|
||||||
|
/// let s : Slot<i32> = Slot::new();
|
||||||
|
/// s.put(789);
|
||||||
|
///
|
||||||
|
/// assert!(! s.is_empty());
|
||||||
|
/// let r = s.take();
|
||||||
|
/// assert!(s.is_empty());
|
||||||
|
///
|
||||||
|
/// assert_eq!(789, Rc::try_unwrap(r).unwrap());
|
||||||
|
/// ```
|
||||||
|
pub fn take(&self) -> Rc<T> { |
||||||
|
let x = self.v.replace(None); |
||||||
|
x.unwrap() |
||||||
|
} |
||||||
|
|
||||||
|
/// Empty the slot. This is equivalent to `take()`, except the old value is discarded, if any.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use slot::Slot;
|
||||||
|
///
|
||||||
|
/// let s : Slot<i32> = Slot::new();
|
||||||
|
/// s.put(123);
|
||||||
|
///
|
||||||
|
/// assert!(! s.is_empty());
|
||||||
|
/// s.clear();
|
||||||
|
/// assert!(s.is_empty());
|
||||||
|
///
|
||||||
|
/// s.clear(); // this is allowed - no-op if empty
|
||||||
|
/// ```
|
||||||
|
pub fn clear(&self) { |
||||||
|
self.v.replace(None); |
||||||
|
} |
||||||
|
|
||||||
|
/// Borrow. you must call `.as_ref().unwrap()` to get a reference to the inner value,
|
||||||
|
/// but this reference lives only as long as the `Ref` it came from, so it can't be
|
||||||
|
/// done inside the function.
|
||||||
|
///
|
||||||
|
/// It is possible to get `Ref(None)` if the container is currently empty.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use slot::Slot;
|
||||||
|
/// use std::rc::Rc;
|
||||||
|
///
|
||||||
|
/// let s : Slot<i32> = Slot::new();
|
||||||
|
/// s.borrow(); // no panic
|
||||||
|
///
|
||||||
|
/// let s = Slot::new_with("aaa".to_string());
|
||||||
|
///
|
||||||
|
/// assert_eq!("aaa", format!("{}", s.borrow().as_ref().unwrap()));
|
||||||
|
/// ```
|
||||||
|
pub fn borrow(&self) -> Ref<Option<Rc<T>>> { |
||||||
|
self.v.borrow() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> fmt::Display for Slot<T> |
||||||
|
where |
||||||
|
T: fmt::Display, |
||||||
|
{ |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
match self.is_empty() { |
||||||
|
true => write!(f, "nil"), |
||||||
|
false => write!(f, "{}", self.borrow().as_ref().unwrap()), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> fmt::Debug for Slot<T> |
||||||
|
where |
||||||
|
T: fmt::Debug, |
||||||
|
{ |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
let b = self.borrow(); |
||||||
|
match b.is_none() { |
||||||
|
true => write!(f, "Slot(None)"), |
||||||
|
false => { |
||||||
|
let unwrapped = b.as_ref().unwrap(); |
||||||
|
if f.alternate() { |
||||||
|
write!( |
||||||
|
f, |
||||||
|
"Slot(Rc({}+{}) -> {:#?})", |
||||||
|
Rc::strong_count(unwrapped), |
||||||
|
Rc::weak_count(unwrapped), |
||||||
|
unwrapped |
||||||
|
) |
||||||
|
} else { |
||||||
|
write!( |
||||||
|
f, |
||||||
|
"Slot(Rc({}+{}) -> {:?})", |
||||||
|
Rc::strong_count(unwrapped), |
||||||
|
Rc::weak_count(unwrapped), |
||||||
|
unwrapped |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use crate::Slot; |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[should_panic] |
||||||
|
fn take_from_empty_panics() { |
||||||
|
let s: Slot<i32> = Slot::new(); |
||||||
|
let _s = s.take(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[should_panic] |
||||||
|
fn take_from_borrowed_panics() { |
||||||
|
let s: Slot<i32> = Slot::new(); |
||||||
|
let _a = s.borrow(); |
||||||
|
let _s = s.take(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[should_panic] |
||||||
|
fn lease_from_empty_panics() { |
||||||
|
let s: Slot<i32> = Slot::new(); |
||||||
|
let _a = s.lease(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue