I'm trying to learn lifetimes so I tried to write the following code. It is just a simple family tree program, where there are person and relationship. I have created 3 structs Person Relationship and PersonRelationship. PersonRelationship is just a struct referring to a Person and a Relationship. You can see the structs and their implementations below
#Help with lifetimes and mutable reference
13 messages · Page 1 of 1 (latest)
use std::iter::Filter;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Person {
name: String,
relationships: Vec<PersonRelationship<'static>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PersonRelationship<'a> {
relationship: &'a Relationship,
with: &'a Person,
}
struct PersonRelationshipVisitor;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Relationship {
name: String,
}
impl Person {
pub fn new(name: String) -> Self {
Self {
name,
relationships: vec![],
}
}
pub fn get_relationships<F>(
&self,
callback: F,
) -> Filter<std::slice::Iter<'_, PersonRelationship<'_>>, F>
where
F: FnMut(&&PersonRelationship) -> bool,
{
self.relationships.iter().filter(callback)
}
pub fn count_relationships(&self, relationship: Relationship) -> usize {
self.get_relationships(|x| *x.relationship == relationship)
.count()
}
pub fn relate_with(
&mut self,
relationship: &'static Relationship,
person: &'static Person,
) -> anyhow::Result<()> {
let person_exists = self.get_relationships(|x| x.with == person).next();
if person_exists.is_some() {
return Err(anyhow::anyhow!("Person already exists"));
}
let relationship = PersonRelationship {
relationship: &relationship,
with: person,
};
self.relationships.push(relationship);
Ok(())
}
}
pub struct FamilyTree {
people: Vec<Person>,
relationships: Vec<Relationship>,
}
impl FamilyTree {
pub fn new() -> Self {
Self {
people: vec![],
relationships: vec![],
}
}
pub fn add_person(&mut self, name: String) {
let person = Person::new(name);
// check if person exists
let exists = self.people.iter().find(|x| x == &&person);
if exists.is_none() {
self.people.push(person);
} else {
println!("Person already exists");
}
}
pub fn add_relationship(&mut self, name: String) {
let relationship = Relationship { name };
// check if relationship exists
let exists = self
.relationships
.iter()
.find(|x| x.name == relationship.name);
if exists.is_none() {
self.relationships.push(relationship);
} else {
println!("Relationship already exists");
}
}
pub fn get_relationship(&self, name: String) -> Option<&Relationship> {
self.relationships.iter().find(|x| x.name == name)
}
pub fn get_person(&mut self, name: String) -> Option<&mut Person> {
let index = self.people.iter().position(|x| x.name == name)?;
self.people.get_mut(index)
}
pub fn get_person_by_ref(&self, name: String) -> Option<&Person> {
self.people.iter().find(|x| x.name == name)
}
}
now I'm trying to do this
fn main() {
let mut family_tree = FamilyTree::new();
family_tree.add_person("john".to_string());
family_tree.add_person("mary".to_string());
family_tree.add_relationship("wife".to_string());
let john = family_tree.get_person("john".to_string()).unwrap();
let mary = family_tree.get_person_by_ref("mary".to_string()).unwrap();
let relationship = family_tree.get_relationship("wife".to_string()).unwrap();
john.relate_with(relationship, mary);
}
I get the following error
rustc: cannot borrow `family_tree` as immutable because it is also borrowed as mutable
immutable borrow occurs here [E0502]
Is my approach to the problem wrong or is it with my implementation ?
the approach is going to cause you grief
references do not play nice with interlaced structures
you want to approach it with a more hierarchical frame of mind, what structure owns what substructures
The lifetime structure here is deeply suspictious
PersonRelationship<'static>
So, if a personrelationship borrows a person and a relationship, this means both of those have to live forever
It sounds like you're creating a graph here. Yes, it's a family tree, but if a tree has parent pointers then it's a graph, and I'm pretty sure you want children to have pointers to their parents.
Lifetimes and ownership will never work for that, no matter how abstruse you make them. The typical way to make graphs is by using indices:
pub struct FamilyTree {
people: Vec<Person>,
relationships: Vec<Person>,
}
pub struct Person {
name: String,
relationships: Vec<usize> //these are indices in the `people` vec
}
pub struct Relationship {
name: String,
}