Home > Enterprise >  declare a field of a struct as a function of others in Rust
declare a field of a struct as a function of others in Rust

Time:11-21

I have a struct A in Rust that has 3 fields, a, b and c

struct A {
  pub a: typeA,
  pub b: typeB,
  pub c: typeC
};

typeA, typeB and typeC are three different types of structs themselves. typeB and typeC have typeA as depdendency. Like for example

struct typeB {
    pub bb: typeA,
};

struct typeC {
    pub cc: typeA,
};

And typeA is defined below as follows:

struct typeA {
    pub aa: String,
};

I can instantiate A like this:

let instance_a = A {
    a1 : typeA {aa: "a".to_string()},
    b1 : typeB {bb: typeA {aa: "a".to_string()}},
    c1 : typeC {cc: typeA {aa: "a".to_string()}},
};

As you can see b1 and c1 both depend on a1 here. My question is, is there a cleaner way to make the field b1 depend on a1 directly at compile time without me having to declare them separately in each case for b1 and c1 as shown in instance_a?

The long term goal is to automatically update b1 and c1 as a1 gets modified. In other words, if I want to decide to update the value of a1 to this a1: typeA {aa : "b".to_string()} then b1 and c1 should automatically get updated.

I have tried approaching this problem in the following manner. I have deduced typeA, typeB and typeC as cloneable.

impl A {
    pub fn create(a: &str) -> Self {
        let old_instance = typeA {aa : a.to_string()};
        A {
            a1: old_instance.clone(),
            b1: typeB {bb: old_instance.clone()},
            c1: typeC {cc: old_instance.clone()},
        }
    }
}

So whenever I want to update whatever is happening within A, I just call A::create("hello_world"). The issue with this is I am having to clone multiple times which I would like to avoid.

CodePudding user response:

Since you want to automatically update b and c when a is modified then you either want to hold references to a or you want to have mutual ownership with Arc. Arc is read only so if you want to modify stuff you need to do it with interior mutability. There are a few ways to do all that. Here is one (if you don't need mutiple threads replace Arc with Rc)

use std::cell::RefCell;
use std::sync::Arc;

#[derive(Debug)]
struct A {
    pub a: Arc<typeA>,
    pub b: typeB,
    pub c: typeC
}

#[derive(Debug)]
struct typeB {
    pub bb: Arc<typeA>
}

#[derive(Debug)]
struct typeC {
    pub cc: Arc<typeA>
}


#[derive(Debug)]
struct typeA {
    pub aa: RefCell<String>
}

fn main() {
    let mut aa = Arc::new(typeA { aa: RefCell::new(String::from("test1")) });
    let instance_a = A {
        a : aa.clone(),
        b : typeB {bb: aa.clone()},
        c : typeC {cc: aa.clone()},
    };
    println!("{:?}", instance_a);
    // modify aa
    *aa.aa.borrow_mut() = String::from("test2");
    println!("{:?}", instance_a);
}

Output:

A { a: typeA { aa: RefCell { value: "test1" } }, b: typeB { bb: typeA { aa: RefCell { value: "test1" } } }, c: typeC { cc: typeA { aa: RefCell { value: "test1" } } } }
A { a: typeA { aa: RefCell { value: "test2" } }, b: typeB { bb: typeA { aa: RefCell { value: "test2" } } }, c: typeC { cc: typeA { aa: RefCell { value: "test2" } } } }
  • Related