master
Ondřej Hruška 4 years ago
parent f0cb49e436
commit c11b65d6d0
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 4
      .gitignore
  2. 4
      Cargo.lock
  3. 7
      Cargo.toml
  4. 254
      src/lib.rs

4
.gitignore vendored

@ -0,0 +1,4 @@
/target
**/*.rs.bk
.idea/
*.iml

4
Cargo.lock generated

@ -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…
Cancel
Save