parent
9986e02566
commit
0ff10f5c45
@ -0,0 +1,3 @@ |
|||||||
|
/target |
||||||
|
**/*.rs.bk |
||||||
|
.idea |
@ -0,0 +1,4 @@ |
|||||||
|
[[package]] |
||||||
|
name = "animals" |
||||||
|
version = "0.1.0" |
||||||
|
|
@ -0,0 +1,7 @@ |
|||||||
|
[package] |
||||||
|
name = "animals" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
[dependencies] |
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<module type="RUST_MODULE" version="4"> |
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true"> |
||||||
|
<exclude-output /> |
||||||
|
<content url="file://$MODULE_DIR$"> |
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> |
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" /> |
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" /> |
||||||
|
<sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" /> |
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" /> |
||||||
|
</content> |
||||||
|
<orderEntry type="inheritedJdk" /> |
||||||
|
<orderEntry type="sourceFolder" forTests="false" /> |
||||||
|
</component> |
||||||
|
</module> |
@ -0,0 +1,183 @@ |
|||||||
|
use std::cell::Cell; |
||||||
|
use std::cell::Ref; |
||||||
|
use std::cell::RefCell; |
||||||
|
use std::cell::RefMut; |
||||||
|
|
||||||
|
use crate::slot::Slot; |
||||||
|
use std::rc::Rc; |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
enum NodeType { |
||||||
|
Question, |
||||||
|
Animal, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
struct NodeStruct { |
||||||
|
kind: NodeType, |
||||||
|
text: String, |
||||||
|
branch_true: Slot<NodeStruct>, |
||||||
|
branch_false: Slot<NodeStruct>, |
||||||
|
} |
||||||
|
|
||||||
|
pub struct AnimalDB { |
||||||
|
root: Slot<NodeStruct>, |
||||||
|
enable_debug: bool, |
||||||
|
} |
||||||
|
|
||||||
|
pub trait UserAPI { |
||||||
|
fn notify_new_game(&self); |
||||||
|
fn notify_game_ended(&self); |
||||||
|
fn notify_new_animal(&self, animal: &str); |
||||||
|
fn notify_victory(&self); |
||||||
|
fn answer_yes_no(&self, question: &str) -> bool; |
||||||
|
fn is_it_a(&self, animal: &str) -> bool; |
||||||
|
fn what_is_it(&self) -> String; |
||||||
|
fn how_to_tell_apart(&self, other: &str) -> (String, bool); |
||||||
|
} |
||||||
|
|
||||||
|
impl AnimalDB { |
||||||
|
pub fn new() -> AnimalDB { |
||||||
|
AnimalDB { |
||||||
|
root: Slot::new(), |
||||||
|
enable_debug: false, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn enable_debug(&mut self, yes: bool) { |
||||||
|
self.enable_debug = yes; |
||||||
|
} |
||||||
|
|
||||||
|
fn debug(&self, s: &str) { |
||||||
|
if self.enable_debug { |
||||||
|
println!("{}", s); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn insert_new_animal( |
||||||
|
&self, |
||||||
|
user: &dyn UserAPI, |
||||||
|
following: Rc<NodeStruct>, |
||||||
|
updated_branch: &Slot<NodeStruct>, |
||||||
|
) { |
||||||
|
let new_name = user.what_is_it(); |
||||||
|
let (question, answer_for_new) = user.how_to_tell_apart(&following.text); |
||||||
|
|
||||||
|
self.debug(&format!( |
||||||
|
"Updating DB with: ({}, y:{}, n:{})", |
||||||
|
question, |
||||||
|
if answer_for_new { |
||||||
|
&new_name |
||||||
|
} else { |
||||||
|
&following.text |
||||||
|
}, |
||||||
|
if answer_for_new { |
||||||
|
&following.text |
||||||
|
} else { |
||||||
|
&new_name |
||||||
|
} |
||||||
|
)); |
||||||
|
|
||||||
|
// we have to insert a new Question node between the parent node and the
|
||||||
|
// Animal node we just found
|
||||||
|
|
||||||
|
drop(following); // must drop this lease, or .take() will panic
|
||||||
|
let old_animal = Rc::try_unwrap(updated_branch.take()).unwrap(); |
||||||
|
|
||||||
|
let mut new_q = NodeStruct { |
||||||
|
kind: NodeType::Question, |
||||||
|
text: question, |
||||||
|
branch_true: Slot::new(), |
||||||
|
branch_false: Slot::new(), |
||||||
|
}; |
||||||
|
|
||||||
|
let (new_animal_branch, old_animal_branch) = match answer_for_new { |
||||||
|
true => (&new_q.branch_true, &new_q.branch_false), |
||||||
|
false => (&new_q.branch_false, &new_q.branch_true), |
||||||
|
}; |
||||||
|
|
||||||
|
user.notify_new_animal(&new_name); |
||||||
|
|
||||||
|
let mut new_animal = NodeStruct { |
||||||
|
kind: NodeType::Animal, |
||||||
|
text: new_name, |
||||||
|
branch_true: Slot::new(), |
||||||
|
branch_false: Slot::new(), |
||||||
|
}; |
||||||
|
|
||||||
|
new_animal_branch.put(new_animal); |
||||||
|
old_animal_branch.put(old_animal); |
||||||
|
|
||||||
|
assert!(updated_branch.is_empty()); |
||||||
|
updated_branch.put(new_q); |
||||||
|
} |
||||||
|
|
||||||
|
fn play_game(&self, user: &dyn UserAPI) { |
||||||
|
// question node for the upcoming iteration of the loop
|
||||||
|
let mut next = self.root.lease(); |
||||||
|
|
||||||
|
loop { |
||||||
|
let user_answer; |
||||||
|
let updated_branch; |
||||||
|
let following; |
||||||
|
|
||||||
|
if let NodeType::Animal = &next.kind { |
||||||
|
// this is a leaf node (happens only when a leaf is in root).
|
||||||
|
// root is used as a parent reference for inserting a new
|
||||||
|
// in-between question if needed.
|
||||||
|
updated_branch = &self.root; |
||||||
|
|
||||||
|
// move from next instead of another lease
|
||||||
|
// we can do this here, because updated_branch is borrowed
|
||||||
|
// from root directly, so there would be no orphaned reference
|
||||||
|
// like in the 'else' branch
|
||||||
|
following = next; |
||||||
|
} else { |
||||||
|
user_answer = user.answer_yes_no(&next.text); |
||||||
|
// find which branch will be updated if this is a new animal
|
||||||
|
updated_branch = match user_answer { |
||||||
|
true => &next.branch_true, |
||||||
|
false => &next.branch_false, |
||||||
|
}; |
||||||
|
// cannot overwrite next and use it in the rest of the function,
|
||||||
|
// because updated_branch is borrowed from it and the references
|
||||||
|
// would become invalid
|
||||||
|
following = updated_branch.lease(); |
||||||
|
} |
||||||
|
|
||||||
|
if let NodeType::Animal = &following.kind { |
||||||
|
if user.is_it_a(&following.text) { |
||||||
|
user.notify_victory(); |
||||||
|
} else { |
||||||
|
self.insert_new_animal(user, following, updated_branch); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} else { |
||||||
|
// we found another Question node, proceed with questions.
|
||||||
|
next = following; // move lease
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//noinspection RsBorrowChecker
|
||||||
|
pub fn start_game(&self, user: &dyn UserAPI) { |
||||||
|
user.notify_new_game(); |
||||||
|
|
||||||
|
if self.root.is_empty() { |
||||||
|
let name = user.what_is_it(); |
||||||
|
self.debug(&format!("Initializing empty DB with: ({})", name)); |
||||||
|
|
||||||
|
self.root.put(NodeStruct { |
||||||
|
kind: NodeType::Animal, |
||||||
|
text: name, |
||||||
|
branch_true: Slot::new(), |
||||||
|
branch_false: Slot::new(), |
||||||
|
}); |
||||||
|
} else { |
||||||
|
self.play_game(user); |
||||||
|
} |
||||||
|
|
||||||
|
user.notify_game_ended(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,221 @@ |
|||||||
|
use std::collections::HashMap; |
||||||
|
|
||||||
|
mod animals; |
||||||
|
mod slot; |
||||||
|
|
||||||
|
struct AnswerMachine<'a> { |
||||||
|
answer: String, |
||||||
|
yes_no: HashMap<&'a str, bool>, |
||||||
|
distinguish: HashMap<&'a str, (&'a str, bool)>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> AnswerMachine<'a> { |
||||||
|
fn abort(&self, msg: String) -> ! { |
||||||
|
println!("{}", msg); |
||||||
|
std::process::exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> animals::UserAPI for AnswerMachine<'a> { |
||||||
|
fn notify_new_game(&self) { |
||||||
|
println!("----- NEW GAME -----"); |
||||||
|
println!("Secret animal is: {}", self.answer); |
||||||
|
} |
||||||
|
|
||||||
|
fn notify_game_ended(&self) { |
||||||
|
println!("Game ended.\n"); |
||||||
|
} |
||||||
|
|
||||||
|
fn notify_new_animal(&self, animal: &str) { |
||||||
|
println!("Learned a new animal: {}", animal); |
||||||
|
} |
||||||
|
|
||||||
|
fn notify_victory(&self) { |
||||||
|
println!("Yay, found an answer!"); |
||||||
|
} |
||||||
|
|
||||||
|
fn answer_yes_no(&self, question: &str) -> bool { |
||||||
|
println!("{}", question); |
||||||
|
|
||||||
|
let answer = match self.yes_no.get(question) { |
||||||
|
Some(a) => *a, |
||||||
|
None => { |
||||||
|
self.abort(format!("* Missing answer for {}! *", &self.answer)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
println!("> {}", match answer { true => "yes", false => "no" }); |
||||||
|
answer |
||||||
|
} |
||||||
|
|
||||||
|
fn is_it_a(&self, animal: &str) -> bool { |
||||||
|
println!("Is it a {}?", animal); |
||||||
|
|
||||||
|
let answer = animal == &self.answer; |
||||||
|
println!("> {}", match answer { true => "yes", false => "no" }); |
||||||
|
|
||||||
|
answer |
||||||
|
} |
||||||
|
|
||||||
|
fn what_is_it(&self) -> String { |
||||||
|
println!("What animal is it?"); |
||||||
|
println!("> {}", self.answer); |
||||||
|
|
||||||
|
self.answer.clone() |
||||||
|
} |
||||||
|
|
||||||
|
fn how_to_tell_apart(&self, other: &str) -> (String, bool) { |
||||||
|
println!("How to tell apart {} and {}?", self.answer, other); |
||||||
|
|
||||||
|
let (s, b) = self.distinguish.get(other).unwrap_or_else(|| { |
||||||
|
self.abort(format!("* I don't know how to tell apart {} and {}! *", self.answer, other)); |
||||||
|
}); |
||||||
|
|
||||||
|
println!("> {}", s); |
||||||
|
println!("What is the answer for {}?", self.answer); |
||||||
|
println!( |
||||||
|
"> {}", |
||||||
|
match *b { true => "yes", false => "no" } |
||||||
|
); |
||||||
|
|
||||||
|
(s.to_string(), *b) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// a little hack for map initializer literals
|
||||||
|
macro_rules! map( |
||||||
|
{ $($key:expr => $value:expr),* } => { |
||||||
|
{ |
||||||
|
let mut m = std::collections::HashMap::new(); |
||||||
|
$( m.insert($key, $value); )* |
||||||
|
m |
||||||
|
} |
||||||
|
}; |
||||||
|
); |
||||||
|
|
||||||
|
fn main() { |
||||||
|
let mut db = animals::AnimalDB::new(); |
||||||
|
|
||||||
|
let duck = AnswerMachine { |
||||||
|
answer: "duck".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => true |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"cow" => ("Does it moo?", false) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&duck); |
||||||
|
|
||||||
|
let dog = AnswerMachine { |
||||||
|
answer: "dog".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => false, |
||||||
|
"Does it woof?" => true, |
||||||
|
"Is it wild?" => false |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"duck" => ("Does it quack?", false), |
||||||
|
"wolf" => ("Is it wild?", false) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&dog); |
||||||
|
|
||||||
|
let wolf = AnswerMachine { |
||||||
|
answer: "wolf".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => false, |
||||||
|
"Is it wild?" => true, |
||||||
|
"Does it fly?" => false, |
||||||
|
"Does it eat acorns?" => false |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"dog" => ("Is it wild?", true) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&wolf); |
||||||
|
|
||||||
|
let cow = AnswerMachine { |
||||||
|
answer: "cow".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => false, |
||||||
|
"Is it wild?" => false, |
||||||
|
"Does it woof?" => false, |
||||||
|
"Does it give milk?" => true, |
||||||
|
"Is it grown for meat?" => true |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"dog" => ("Does it woof?", false) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&cow); |
||||||
|
|
||||||
|
let pig = AnswerMachine { |
||||||
|
answer: "pig".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => false, |
||||||
|
"Is it wild?" => false, |
||||||
|
"Does it woof?" => false, |
||||||
|
"Does it give milk?" => false |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"cow" => ("Does it give milk?", false) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&pig); |
||||||
|
|
||||||
|
let goat = AnswerMachine { |
||||||
|
answer: "goat".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => false, |
||||||
|
"Is it wild?" => false, |
||||||
|
"Does it woof?" => false, |
||||||
|
"Does it give milk?" => true, |
||||||
|
"Is it grown for meat?" => false |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"cow" => ("Is it grown for meat?", false) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&goat); |
||||||
|
|
||||||
|
let raven = AnswerMachine { |
||||||
|
answer: "raven".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => false, |
||||||
|
"Is it wild?" => true, |
||||||
|
"Does it woof?" => false, |
||||||
|
"Does it fly?" => true |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"wolf" => ("Does it fly?", true) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&raven); |
||||||
|
|
||||||
|
let boar = AnswerMachine { |
||||||
|
answer: "boar".to_string(), |
||||||
|
yes_no: map! { |
||||||
|
"Does it quack?" => false, |
||||||
|
"Is it wild?" => true, |
||||||
|
"Does it woof?" => false, |
||||||
|
"Does it fly?" => false |
||||||
|
}, |
||||||
|
distinguish: map! { |
||||||
|
"wolf" => ("Does it eat acorns?", true) |
||||||
|
}, |
||||||
|
}; |
||||||
|
db.start_game(&boar); |
||||||
|
|
||||||
|
|
||||||
|
// try how the DB works with the learned animals
|
||||||
|
println!("===== Testing known animals ====="); |
||||||
|
|
||||||
|
db.start_game(&cow); |
||||||
|
db.start_game(&dog); |
||||||
|
db.start_game(&wolf); |
||||||
|
db.start_game(&duck); |
||||||
|
db.start_game(&pig); |
||||||
|
db.start_game(&goat); |
||||||
|
db.start_game(&raven); |
||||||
|
} |
@ -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::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