Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to properly initialize a struct in Rust, with good enough encapsulations?

How to properly initialize a struct in Rust, with good enough encapsulations?

Or more naively:
how to leverage object/instance methods in the initialization/constructing process of structs?

For example, as the initialization block in Kotlin:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

private class BinaryIndexedTree(nums: IntArray) {
    private val nNums = nums.size
    private val fenwick = IntArray(nNums + 1) { 0 }

    // where to put this block in Rust?
    init {
        for (idx in nums.indices) {
            update(idx, nums[idx])
        }
    }

    fun update(index: Int, value: Int) {
        var idx = index + 1
        while (idx <= nNums) {
            fenwick[idx] += value
            idx += (idx and -idx)
        }
    }

    fun query(index: Int): Int {
        var sum = 0

        var idx = index + 1
        while (idx > 0) {
            sum += fenwick[idx]
                idx -= (idx and -idx)
        }

        return sum
    }
}

According to Rust Design Patterns, there is no regular constructors as other languages, the convention is to use an associated function.

Correspondingly, in Rust:

struct BinaryIndexedTree{
    len_ns: isize,
    fenwick: Vec<i32>,
}

impl BinaryIndexedTree{
    pub fn new(nums: &Vec<i32>) -> Self{
        let len_ns: usize = nums.len();
        let fenwick: Vec<i32> = vec![0; len_ns + 1];
        for (idx, num) in nums.iter().enumerate(){
            // how to leverage `update()` for initialization
            // update(idx as isize, num);
            // or even earlier: where/how to put the initialization logic?
        }
        Self{
            len_ns: len_ns as isize,
            fenwick,
        }
    }
    pub fn update(&mut self, index: isize, value: i32){
        let mut idx = index + 1;
        while idx <= self.len_ns{
            self.fenwick[idx as usize] += value;
            idx += (idx & -idx);
        }
    }
    pub fn query(&self, index: isize) -> i32{
        let mut sum: i32 = 0;
        let mut idx = index + 1;
        while idx > 0{
            sum += self.fenwick[idx as usize];
            idx -= (idx & -idx);
        }
        sum
    }
}

Is there any way to properly leverage the update method?
As a rule of thumbs, how to properly handle the initialization work after the creation of (all the fields of) the struct?

The builder pattern is a way to go, which introduces much more code just for initialization.

>Solution :

Yes, you can construct the struct then call a function on it before returning it. There is nothing special about the new function name or how the struct is constructed at the end of the function.

pub fn new(nums: &Vec<i32>) -> Self {
    let len_ns: usize = nums.len();
    let fenwick: Vec<i32> = vec![0; len_ns + 1];

    // Construct an incomplete version of the struct.
    let mut new_self = Self {
        len_ns: len_ns as isize,
        fenwick,
    };

    // Do stuff with the struct
    for (idx, num) in nums.iter().enumerate(){
        new_self.update(idx as isize, num);
    }

    // Return it
    new_self
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading