Optionally reference parent struct

64 Views Asked by At

I'm trying to implement a "scope" in Rust in which it can contain values. It can optionally have a parent scope which it can look up if the parent contains the value but it doesn't.

The scope needs to be passed around to many other functions. Say I have the following code:

use std::collections::HashMap;

struct Value {
    value: String,
}

struct Scope {
    values: HashMap<String, Value>,
    parent: Option</* ??? */>,
}

impl Scope {
    fn new() -> Scope {
        Scope {
            values: HashMap::new(),
            parent: None,
        }
    }

    fn new_with_parent(scope: /* ??? */) -> Scope {
        Scope {
            values: HashMap::new(),
            parent: Some(scope),
        }
    }

    pub fn add_value(&mut self, name: String, val: Value) -> () {
        self.values.insert(name, val);
    }

    pub fn get_parent_value(&mut self, name: String) -> Value {
        match self.parent {
            None => Value {
                value: "None".to_string(),
            },
            Some(parent) => panic!()
        }
    }
}

fn main() {
    let mut scope = Scope::new();
    scope.add_value(
        String::from("test"),
        Value {
            value: "test".to_string(),
        },
    );

  test(&mut scope);
}

fn test(scope: &mut Scope) -> Value {
  let mut child = Scope::new_with_parent(&scope);
  child.get_parent_value(String::from("test"))
}

This is a minimal example of what I'm trying to do, I just can't figure out which type to use and how to use it for this.

I'm not sure if this is the correct way of doing such a thing in Rust, but I can't think of anything else as I'm quite new to this.

I've tried using Pins using Box::pin, using *const, RefCell but I couldn't figure out a way to make any of them work as I usually would get a "shared reference" error

1

There are 1 best solutions below

0
trojanfoe On

You need to specify the life time of the reference to the parent. Something like this:

use std::collections::HashMap;

#[derive(Debug)]
struct Value {
    _value: String,
}

impl Value {
    fn new(value: &str) -> Self {
        Self { _value: value.to_string() }
    }
}

struct Scope<'a> {
    values: HashMap<String, Value>,
    parent: Option<&'a Scope<'a>>,
}

impl<'a> Scope<'a> {
    fn new(parent: Option<&'a Scope>) -> Scope<'a> {
        Scope {
            values: HashMap::new(),
            parent,
        }
    }

    pub fn add_value(&mut self, name: &str, val: Value) {
        self.values.insert(name.to_string(), val);
    }

    pub fn get_value(&self, name: &str) -> Option<&Value> {
        self.values.get(name)
    }


    pub fn get_parent_value(&self, name: &str) -> Option<&Value> {
        match self.parent {
            Some(scope) => scope.get_value(name),
            None => None
        }
    }
}

fn main() {
    let mut parent = Scope::new(None);
    parent.add_value("parent_name", Value::new("parent_value"));

    let mut child = Scope::new(Some(&parent));
    child.add_value("child_name", Value::new("child_value"));
    
    println!("child value: {:?}", child.get_value("child_name"));
    println!("parent value: {:?}", child.get_parent_value("parent_name"));

}

Output:

child value: Some(Value { _value: "child_value" })
parent value: Some(Value { _value: "parent_value" })

Rust Playground

Note: better semantics for get_value() would probably be to see if the value exists in self.values and if not try the parent; this would continue until no more parents exist.