2
\$\begingroup\$

My scenario is the following: I'm modelling a Program which has many Modules. Each Module has Symbols, some of which pointing at dependencies, which are other Modules. So a Module can be pointed to by many other Modules as a dependency.

Now I want to be able to modify a Module (typically, running an optimiser on its contents, removing unused dependencies, etc), and then any other Module which has it as a dependency should point to the "new" version.

I've tried two approaches:

  • Having a ModuleId for each Module, and have Modules point to their dependencies by their ModuleId. Then I can update the Module associated to a ModuleId and all links are still valid. One drawback is that there's no compile-time guarantee that I won't try to update a non-existing Module.
  • Using Rc<RefCell<Content>> to store the content to be updated, in which case I can update Content without any of the dependency links breaking. I find it suspicious that I have to use such advanced tools for this, and they're not risk free as RefCell is checked at run time.

I'm wondering if I missed another approach, and which one would be the most idiomatic.

Here is a link to a simplified playground with both approaches, and my code below:

mod with_module_id {
    use std::collections::hash_map::{Entry, HashMap};

    type Content = usize;

    #[derive(Debug)]
    struct Module {
        content: Content,
        dependency: Option<ModuleId>,
    }

    type ModuleId = String;

    #[derive(Debug)]
    struct Prog {
        modules: HashMap<ModuleId, Module>,
    }

    fn update_module(p: Prog, id: ModuleId, c: Content) -> Prog {
        let mut p = p;
        match p.modules.entry(id.clone()) {
            Entry::Occupied(mut m) => m.get_mut().content = c,
            _ => panic!("module id not found: {}", id),
        };
        p
    }

    pub fn run() {
        let m0 = Module {
            content: 42,
            dependency: None,
        };

        let m1 = Module {
            content: 43,
            dependency: Some(String::from("m0")),
        };

        let m2 = Module {
            content: 44,
            dependency: Some(String::from("m0")),
        };

        let p = Prog {
            modules: vec![
                (String::from("m0"), m0),
                (String::from("m1"), m1),
                (String::from("m2"), m2),
            ]
            .into_iter()
            .collect(),
        };

        println!("We start with this program:\n{:#?}\n", p);

        let p = update_module(p, String::from("m0"), 151);

        println!("We update module m0 to contain 151:\n{:#?}\n", p);

        println!("We try to update an unexisting module and boom...");
        let _p = update_module(p, String::from("invalid :("), 404);
    }
}

mod with_ref_cell {
    use std::rc::Rc;
    use std::cell::RefCell;

    type Content = usize;

    #[derive(Debug)]
    struct Module {
        content: Rc<RefCell<Content>>,
        dependency: Option<Rc<Module>>,
    }

    fn update_module(m: Rc<RefCell<Content>>, c: Content) {
        *m.borrow_mut() = c;
    }

    pub fn run() {

        let content0 = Rc::new(RefCell::new(42));

        let m0 = Module {
            content: Rc::clone(&content0),
            dependency: None,
        };

        let content1 = Rc::new(RefCell::new(43));

        let m0 = Rc::new(m0);

        let m1 = Module {
            content: Rc::clone(&content1),
            dependency: Some(Rc::clone(&m0)),
        };

        let content2 = Rc::new(RefCell::new(44));

        let m2 = Module {
            content: Rc::clone(&content2),
            dependency: Some(Rc::clone(&m0)),
        };

        println!("We start with this program:\n{:#?}\n\n{:#?}\n", m1, m2);

        update_module(content0, 151);

        println!("We update module m0 to contain 151:\n{:#?}\n\n{:#?}\n", m1, m2);
    }
}

fn main() {
    with_ref_cell::run();
    with_module_id::run();
}

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.