Rust Structs

Defining Structs

Named fields, tuple structs, unit structs
// Named fields
struct Host {
    name: String,
    ip: String,
    port: u16,
    active: bool,
}

// Tuple struct — fields accessed by index
struct Point(f64, f64);
let p = Point(1.0, 2.0);
println!("{}", p.0);

// Unit struct — no fields, used as marker
struct Marker;

Creating Instances

Literal, shorthand, and update syntax
let h = Host {
    name: String::from("sw01"),
    ip: String::from("10.50.1.10"),
    port: 22,
    active: true,
};

// Field init shorthand — variable name matches field name
let name = String::from("sw02");
let port = 443;
let h2 = Host { name, port, ..h };  // remaining fields from h
// NOTE: h.ip was moved to h2 — h.ip is no longer valid

The struct update syntax ..h copies or moves remaining fields. If any moved field is a heap type, the original is partially invalidated.

Methods with impl

impl blocks define methods
impl Host {
    // Constructor (convention, not a language feature)
    fn new(name: &str, ip: &str, port: u16) -> Self {
        Host {
            name: name.to_string(),
            ip: ip.to_string(),
            port,
            active: true,
        }
    }

    // Method — borrows self
    fn address(&self) -> String {
        format!("{}:{}", self.ip, self.port)
    }

    // Mutable method — can modify self
    fn deactivate(&mut self) {
        self.active = false;
    }

    // Consuming method — takes ownership of self
    fn into_parts(self) -> (String, String, u16) {
        (self.name, self.ip, self.port)
    }
}

let mut h = Host::new("sw01", "10.50.1.10", 22);
println!("{}", h.address());
h.deactivate();
let (name, ip, port) = h.into_parts();
// h is consumed — no longer valid

&self borrows, &mut self borrows mutably, self consumes. Choose based on what the method needs to do.

Deriving Traits

#[derive] generates trait implementations
#[derive(Debug, Clone, PartialEq)]
struct Config {
    host: String,
    port: u16,
    verbose: bool,
}

let c = Config {
    host: "localhost".into(),
    port: 8080,
    verbose: true,
};

println!("{:?}", c);       // Debug
println!("{:#?}", c);      // Pretty Debug
let c2 = c.clone();        // Clone
assert_eq!(c, c2);         // PartialEq

Common derives: Debug (printing), Clone (deep copy), PartialEq (comparison), Hash (use as map key), Default (zero-value construction), Serialize/Deserialize (serde).

Visibility

pub controls field access
pub struct Connection {
    pub host: String,     // public
    pub port: u16,        // public
    token: String,        // private — only accessible in this module
}

impl Connection {
    pub fn new(host: &str, port: u16, token: &str) -> Self {
        Connection {
            host: host.into(),
            port,
            token: token.into(),
        }
    }
}
// Outside this module: Connection { host, port, .. } won't compile
// Must use Connection::new() — enforces the constructor

Private fields force callers through a constructor, which can validate inputs. This is how Rust enforces invariants without exceptions.