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

Advertisements

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:

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
}

Leave a ReplyCancel reply