Split code into modules

Completed

Besides helping you to better organize your code, modules also provide privacy guarantees to your values, types, and methods.

Take a look at this example, in which we model a simplified authentication API:

mod authentication {
    pub struct User {
        username: String,
        password_hash: u64,
    }

    impl User {
        pub fn new(username: &str, password: &str) -> User {
            User {
                username: username.to_string(),
                password_hash: hash_password(password),
            }
        }    
    }
    fn hash_password(input: &str) -> u64 { /*...*/ }
}

fn main() {

    let user = authentication::User::new("jeremy", "super-secret");

    println!("The username is: {}", user.username);
    println!("The password is: {}", user.password_hash);

}

In the preceding code, we can see that the authentication module provides the User struct with the User::new method, since they're both public. Notice the pub keyword.

This code fails to compile because it tries to access the username and password_hash fields from the User struct, but they're private. Running the code results in the following error:

    error[E0616]: field `username` of struct `User` is private
      --> src/main.rs:28:42
       |
    28 |     println!("The username is: {}", user.username);
       |                                          ^^^^^^^^ private field

    error[E0616]: field `password_hash` of struct `User` is private
      --> src/main.rs:29:42
       |
    29 |     println!("The password is: {}", user.password_hash);
       |                                          ^^^^^^^^^^^^^ private field

    error: aborting due to 2 previous errors

This error information is useful when we want to control which parts of our program can access each part of a given module. If we wanted to give read access to the username field and write access to the password field while keeping them private, we could use getter and setter methods:

mod authentication {

    // ...

    impl User {

    // ...

        pub fn get_username(&self) -> &String {
            &self.username
        }
    
        pub fn set_password(&mut self, new_password: &str) {
            self.password_hash = hash_password(new_password)
        }
    }
}

Now every read and write attempt will be controlled by the authentication module.

To view the code for this unit, visit this Rust Playground link.