SyntaxStudy
Sign Up
Rust Struct Visibility and Encapsulation
Rust Beginner 1 min read

Struct Visibility and Encapsulation

In Rust, items are private by default and must be explicitly marked `pub` to be accessible outside their module. Struct fields follow the same rule: even a `pub struct` has private fields unless each field is individually marked `pub`. This default privacy encourages encapsulation — callers must use the struct's public API rather than accessing internal data directly. Modules are the unit of encapsulation in Rust. A struct defined in a module can have private fields that are accessible to any code within the same module but invisible to code outside. This is a stricter default than many languages, where you have to opt into privacy rather than opt into visibility. The `pub(crate)` visibility modifier exposes an item to the entire crate but not to external dependents. Builder patterns are a common idiom in Rust when a struct has many optional fields. A separate builder struct collects configuration with method chaining and then a `build()` method constructs the final value, often returning `Result` if validation can fail. This pattern avoids large constructors with many optional parameters and keeps the core struct's fields read-only after construction.
Example
mod config {
    #[derive(Debug)]
    pub struct ServerConfig {
        host: String,           // private
        pub port: u16,          // public
        max_connections: usize, // private
        timeout_secs: u64,      // private
    }

    impl ServerConfig {
        // Only way to construct from outside the module
        pub fn new(host: &str, port: u16) -> Self {
            ServerConfig {
                host: host.to_string(),
                port,
                max_connections: 100,
                timeout_secs: 30,
            }
        }

        pub fn host(&self) -> &str { &self.host }
        pub fn max_connections(&self) -> usize { self.max_connections }

        pub fn with_max_connections(mut self, n: usize) -> Self {
            self.max_connections = n;
            self
        }

        pub fn with_timeout(mut self, secs: u64) -> Self {
            self.timeout_secs = secs;
            self
        }
    }
}

fn main() {
    // Builder-style construction using method chaining
    let cfg = config::ServerConfig::new("127.0.0.1", 8080)
        .with_max_connections(500)
        .with_timeout(60);

    println!("host = {}", cfg.host());
    println!("port = {}", cfg.port);     // pub field — direct access ok
    println!("max connections = {}", cfg.max_connections());
    // cfg.host = "x".to_string(); // compile error: field is private
}