Last active
May 30, 2017 20:06
-
-
Save mmstick/3dcece7ff31d739cdb48f0b1f333f3cb to your computer and use it in GitHub Desktop.
An example implementation of a friends list without shared references.
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
use std::collections::{HashSet, HashMap}; | |
struct Person { | |
/// The unique name of this person that no other person may have. | |
name: String, | |
/// Use of a `HashSet` ensures that a friend is never listed twice. | |
friends: HashSet<usize>, | |
} | |
#[derive(Debug)] | |
enum PeopleErr { | |
PersonDoesNotExist | |
} | |
struct People { | |
/// Positions are unique IDs for each respective person. | |
/// When a person is removed, replace their value with None. | |
people: Vec<Option<Person>>, | |
/// In the event that you do not know the ID, perform a lookup | |
/// based on the name of that person. | |
lookup: HashMap<String, usize> | |
} | |
impl People { | |
/// If an empty position is available, add the person in that position. | |
/// Otherwise, add them at the end of the list. | |
fn add_person(&mut self, name: String) -> usize { | |
let new_person = Person { | |
name: name, | |
friends: HashSet::new() | |
}; | |
let mut pos = 0; | |
for person in &mut self.people { | |
if let Some(_) = *person { | |
pos += 1; | |
continue | |
} | |
self.lookup.insert(new_person.name.clone(), pos); | |
*person = Some(new_person); | |
return pos; | |
} | |
self.lookup.insert(new_person.name.clone(), pos); | |
self.people.push(Some(new_person)); | |
pos | |
} | |
/// Given two people, adds them as friends to each other. | |
fn add_friends(&mut self, a_id: usize, b_id: usize) -> Result<(), PeopleErr> { | |
// Check if person A exists. | |
match self.people.get(a_id) { | |
Some(&Some(_)) => (), | |
_ => return Err(PeopleErr::PersonDoesNotExist) | |
} | |
// Check if person B exists. | |
match self.people.get(b_id) { | |
Some(&Some(_)) => (), | |
_ => return Err(PeopleErr::PersonDoesNotExist) | |
} | |
{ // Add person B to person A's friends list. | |
let person_a = self.get_person_mut(a_id).unwrap(); | |
person_a.friends.insert(b_id); | |
} | |
{ // Add person A to person B's friends list. | |
let person_b = self.get_person_mut(b_id).unwrap(); | |
person_b.friends.insert(a_id); | |
} | |
Ok(()) | |
} | |
/// Obtain the ID of the associated person's name, if they exist. | |
fn get_id(&self, name: &str) -> Option<usize> { | |
self.lookup.get(name).cloned() | |
} | |
/// Get an immutable reference to a person by their ID. | |
fn get_person<'p>(&'p self, id: usize) -> Option<&'p Person> { | |
match self.people.get(id) { | |
Some(people) => people.as_ref(), | |
None => None | |
} | |
} | |
/// Get a mutable reference to a person by their ID. | |
fn get_person_mut<'p>(&'p mut self, id: usize) -> Option<&'p mut Person> { | |
match self.people.get_mut(id) { | |
Some(people) => people.as_mut(), | |
None => None | |
} | |
} | |
fn new() -> People { | |
People { people: Vec::new(), lookup: HashMap::new() } | |
} | |
} | |
fn main() { | |
// Initialize the list of people. | |
let mut people = People::new(); | |
let spongebob = people.add_person("Spongebob".into()); | |
let patrick = people.add_person("Patrick".into()); | |
let sandy = people.add_person("Sandy".into()); | |
// Make them friends of each other. | |
people.add_friends(spongebob, patrick).unwrap(); | |
people.add_friends(spongebob, sandy).unwrap(); | |
// Exchange ID's for temporary references. This one is immutable. | |
if let Some(ref person) = people.get_person(spongebob) { | |
println!("Friends of {}:", person.name); | |
for friend in person.friends.iter().flat_map(|&x| people.get_person(x)) { | |
println!(" {}", friend.name); | |
} | |
} | |
// Same as above | |
if let Some(ref person) = people.get_person(patrick) { | |
println!("Friends of {}:", person.name); | |
for friend in person.friends.iter().flat_map(|&x| people.get_person(x)) { | |
println!(" {}", friend.name); | |
} | |
} | |
println!("ID of Sandy is {:?}", people.get_id(&String::from("Sandy"))); | |
// Change the name of a person through a mutable borrow | |
if let Some(ref mut person) = people.get_person_mut(sandy) { | |
person.name = String::from("Mr. Krabs"); | |
} | |
// Now verify that Sandy's name has changed. | |
if let Some(ref person) = people.get_person(spongebob) { | |
println!("Friends of {}:", person.name); | |
for friend in person.friends.iter().flat_map(|&x| people.get_person(x)) { | |
println!(" {}", friend.name); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment