Rust MethodsownershipRustStructs

Article & Tutorial

Rust Structs and Methods: Fields, impl, Associated Functions and Ownership

Learn Rust structs and methods with named structs, tuple structs, unit structs, impl blocks, associated functions, ownership, borrowing and practical examples.

Rust Structs and Methods: Fields, impl, Associated Functions and Ownership article illustration
Rust Structs and Methods: Fields, impl, Associated Functions and Ownership article illustration
Contents

Structs let you create meaningful custom data types by grouping related values together. Instead of passing separate variables such as a server name, port, TLS flag and retry count, you can represent the entire configuration as one type.

Define and instantiate a struct

bash
struct Server { name: String, port: u16, tls: bool, } fn main() { let server = Server { name: String::from("api-01"), port: 8080, tls: true, }; println!("{}:{}", server.name, server.port); }

Each field has a name and type. An instance must provide values for all required fields.

Mutable struct instances

bash
let mut server = Server { name: String::from("api-01"), port: 8080, tls: false, }; server.tls = true;

The complete instance is mutable; Rust does not let you mark only a single field as mut in the struct definition.

Field init shorthand

bash
fn build_server(name: String, port: u16) -> Server { Server { name, port, tls: true, } }

When parameter/variable names match field names, you can omit name: name.

Struct update syntax

bash
let primary = Server { name: String::from("primary"), port: 443, tls: true, }; let backup = Server { name: String::from("backup"), ..primary };

Be careful with ownership: fields that do not implement Copy can be moved from the source instance by update syntax. Here the explicitly replaced name changes how much of primary is moved, while copied scalar fields remain simple values.

Tuple structs

bash
struct Color(u8, u8, u8); struct Point(i32, i32); let orange = Color(255, 120, 0); println!("red={}", orange.0);

Tuple structs are useful when a distinct type matters but field names add little value.

Unit-like structs

bash
struct HealthCheck; let check = HealthCheck;

Unit-like structs have no fields. They can still implement traits and methods and are useful as marker or behavior types.

Methods with impl

bash
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect = Rectangle { width: 30, height: 20, }; println!("area={}", rect.area()); }

A method is defined inside an impl block. Its first parameter is commonly one of &self, &mut self or self.

&self vs &mut self vs self

ReceiverMeaningTypical use
&selfImmutable borrowRead instance state
&mut selfMutable borrowModify instance state
selfTake ownershipConsume/transform instance

Mutable method

bash
impl Server { fn enable_tls(&mut self) { self.tls = true; } }

Consuming method

bash
struct Job { id: u64, } impl Job { fn finish(self) { println!("job {} finished", self.id); } }

After calling a consuming method, the original instance is no longer available unless the method returns it or returns another owned value.

Associated functions

Functions defined in an impl block that do not take self are associated functions. Constructors commonly use this style:

bash
impl Server { fn new(name: impl Into<String>, port: u16) -> Self { Self { name: name.into(), port, tls: false, } } } let server = Server::new("api", 8080);

new is a convention, not a special Rust keyword.

Multiple impl blocks

bash
impl Server { fn new(name: String, port: u16) -> Self { Self { name, port, tls: false } } } impl Server { fn endpoint(&self) -> String { let scheme = if self.tls { "https" } else { "http" }; format!("{scheme}://{}:{}", self.name, self.port) } }

Multiple impl blocks are valid and can help organize implementations.

Owned vs borrowed fields

bash
struct User { username: String, }

An owned String means the struct owns the username and can live independently of the text used to construct it. Storing references inside structs is possible, but usually requires lifetime relationships. We cover lifetimes later in the learning path.

Derived traits

bash
#[derive(Debug, Clone)] struct Server { name: String, port: u16, tls: bool, } println!("{:?}", server);

derive can ask the compiler to generate implementations of supported traits such as Debug, Clone, PartialEq and others when their requirements are met.

Practical example: application configuration

bash
#[derive(Debug)] struct AppConfig { host: String, port: u16, workers: usize, production: bool, } impl AppConfig { fn development() -> Self { Self { host: String::from("127.0.0.1"), port: 8080, workers: 2, production: false, } } fn endpoint(&self) -> String { format!("{}:{}", self.host, self.port) } fn scale(&mut self, workers: usize) { self.workers = workers; } } fn main() { let mut config = AppConfig::development(); config.scale(4); println!("{:?}", config); println!("endpoint={}", config.endpoint()); }

Structs vs tuples

Prefer a struct when field names improve meaning. Compare:

bash
let server = ("api", 443, true); // What does server.2 mean? struct Server { name: String, port: u16, tls: bool, } // server.tls is self-explanatory

Common errors

  • Trying to modify a field when the struct variable is not mutable.
  • Moving a String field out and then trying to use the original instance as though it were complete.
  • Taking self in a method when &self would be sufficient.
  • Cloning fields unnecessarily instead of borrowing them.
  • Using references as struct fields without understanding the lifetime relationship.

Hands-on mini lab: service configuration

bash
cargo new structs_lab cd structs_lab
  1. Create a Service struct with name , port , enabled and retries .
  2. Add a new associated function.
  3. Add an endpoint(&self) method.
  4. Add enable(&mut self) .
  5. Derive Debug and print the complete value.
  6. Create a second instance using struct update syntax and observe which fields move or copy.
bash
cargo fmt cargo clippy cargo test cargo run

Key points

  • Structs group related values into a custom type.
  • impl blocks attach behavior to a type.
  • &self reads, &mut self modifies, and self consumes.
  • Associated functions are called with Type::function() .
  • Ownership rules still apply to fields and struct update syntax.
  • Use named fields when they make code easier to understand.

Next chapter

Structs model data with one fixed shape. Rust enums let one type represent multiple valid states. Continue with Chapter 10: Enums, Option and Pattern Matching.

Official reference

See the official Rust Book chapter Using Structs to Structure Related Data.

Did this help?

Yes No

Join the discussion

0 Comments

Ask questions, add context, or share what worked.

Sign in to join the discussion

No comments yet. Start the discussion.

More from Melochip Editorial

Related articles

Stay in the loop. Get new Melochip articles

Receive Melochip article updates when new articles are available. Explicit opt-in only.

No pre-checked boxes. Unsubscribe support is part of the subscriber foundation.