[OOP] How to write properly encapsulation?


#1

By TRPL - 17 - 01, I’m trying to write the Object-Oriented encapsulation for a struct and use in difftrent mods.

For expamle, I have a file named structs.rs which is same as the TRPL provided:

pub struct AveragedCollection {
    list: Vec<i32>,
    average: f64,
}


impl AveragedCollection {
    pub fn add(&mut self, value: i32) {
        self.list.push(value);
        self.update_average();
    }

    pub fn remove(&mut self) -> Option<i32> {
      // some code here
    }

    pub fn average(&self) -> f64 {
        self.average
    }

    fn update_average(&mut self) {
        let total: i32 = self.list.iter().sum();
        self.average = total as f64 / self.list.len() as f64;
    }
}

Then, My question is how to use AveragedCollection in another file (mod)?
For example, I have another file named use_it.rs:

mod structs;
use structs::AveragedCollection;

fn main() {
    let mut foobar = AveragedCollection {
         a: aaa,
         b: bbbb,
   };
}

The compiler will tell me that can’t bind foobar because fields are private. But I don’t want fields be public as TRPL said.
How to write it?

Thank you

Solution:

To solve this, just create a Associated function, which return struct its self:

impl foo {
       fn new() -> Self {
           Self {
            // fields
           }
       }
}

And then bind the new variable by let foobar = foo::new();


#2

There’s a convention to create constructor-like functions. The usual name is “new”:

//inside structs.rs and inside the impl block for AveragedCollections

pub fn new() -> Self {
   Self {
      list: vec![],
      average: 0.0, // maybe use Option<f64> initialized to None
   }
}

Then use this new function in other modules.


#3

So, how to bind foobar?
in fact, I have tried it before, But i have no idea about how to bind it.


#4
let mut foobar = AveragedCollection::new();

If you want to pass arguments to the constructor, make the new function accept them (or create similar functions with more appropriate name).


#5

Oh, Thank you!
I wrote it before as let mut foobar = AveragedCollection.new(); :sweat_smile:


#6

Yeah, note new is not a method (ie doesn’t take self in any manner). These types of functions are called associated functions (ie associated with the type but not an instance of it, hence not a method callable with a dot).


#7

But, I’m confusing about it because both of them are in impl blocks. So, what the different?


#8

There are 3 styles of functions in Rust: free function, associated function, and methods.

Free functions aren’t associated with a type (not inside any impl block) - they’re inside some module (maybe the crate root) but have no association with a type.

Associated function is a function inside some impl block but doesn’t take self. If you’re familiar with C# or Java, these are like static methods.

Methods are inside an impl block too but take self, &self, or &mut self - they have a receiver instance of the type, on which the method is called. This is what you call with a dot operator.