#Help with lifetimes and mutable reference

13 messages · Page 1 of 1 (latest)

plush jolt
#

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

#
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 ?

wild grove
#

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

stiff kraken
#

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,
}