Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why must callers use a constructor instead of creating a struct directly?

Tags:

rust

Consider the following Rust snippet from The Rust Programming Language, second edition:

pub struct Guess {
    value: u32,
}

impl Guess {
    pub fn new(value: u32) -> Guess {
        if value < 1 || value > 100 {
            panic!("Guess value must be between 1 and 100, got {}.", value);
        }

        Guess {
            value
        }
    }

    pub fn value(&self) -> u32 {
        self.value
    }
}

and commentary from the corresponding tutorial, emphasis mine:

Next, we implement a method named value that borrows self, doesn’t have any other parameters, and returns a u32. This is a kind of method sometimes called a getter, since its purpose is to get some data from its fields and return it. This public method is necessary because the value field of the Guess struct is private. It’s important that the value field is private so that code using the Guess struct is not allowed to set value directly: callers outside the module must use the Guess::new function to create an instance of Guess, which ensures there’s no way for a Guess to have a value that hasn’t been checked by the conditions in the Guess::new function.

Why must callers use the new function? Couldn't they get around the requirement that Guess.value be between 1 and 100 by doing something like:

let g = Guess { value: 200 };
like image 367
George Avatar asked Dec 19 '22 03:12

George


1 Answers

This applies only when the Guess struct is defined in a different module than the code using it; the struct itself is public but its value field is not, so you can't access it directly.

You can verify it with the following example (playground link):

use self::guess::Guess;

fn main() {
    let guess1 = Guess::new(20); // works
    let guess2 = Guess::new(200); // panic: 'Guess value must be between 1 and 100, got 200.'
    let guess3 = Guess { value: 20 }; // error: field `value` of struct `guess::Guess` is private
    let guess4 = Guess { value: 200 }; // error: field `value` of struct `guess::Guess` is private
}

mod guess {
    pub struct Guess {
        value: u32,
    }

    impl Guess {
        pub fn new(value: u32) -> Guess {
            if value < 1 || value > 100 {
                panic!("Guess value must be between 1 and 100, got {}.", value);
            }

            Guess {
                value
            }
        }

        pub fn value(&self) -> u32 {
            self.value
        }
    }
}

The Book explains the rationale behind keeping a struct's contents private pretty well.

like image 171
ljedrz Avatar answered Jun 05 '23 18:06

ljedrz