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.