Methods & Associated Functions
Methods are functions with a self receiver in an impl block. Associated functions have no self - constructors like new are the familiar example.
Recipe
struct Rectangle { width: u32, height: u32 }
impl Rectangle {
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let r = Rectangle::new(10, 20);
println!("{}", r.area());
}When to reach for this: Grouping behavior with data, builder APIs, and encapsulating invariants on struct mutation.
Working Example
#[derive(Debug)]
struct Account {
balance: i64,
}
impl Account {
fn open(initial: i64) -> Self {
Self { balance: initial.max(0) }
}
fn deposit(&mut self, amount: i64) {
if amount > 0 { self.balance += amount; }
}
fn balance(&self) -> i64 {
self.balance
}
}
fn main() {
let mut a = Account::open(100);
a.deposit(50);
println!("{:?}", a.balance());
}What this demonstrates:
openis associated function - called asAccount::opendepositmutates through&mut selfbalancereads through&self- Invariants enforced inside methods
Deep Dive
Receiver Types
| Receiver | Meaning |
|---|---|
&self | Immutable borrow |
&mut self | Exclusive mutable borrow |
self | Takes ownership (consuming) |
&self with #[rustfmt::skip] patterns | Rare advanced cases |
Multiple impl Blocks
impl Rectangle { /* methods */ }
impl Rectangle { /* more methods - merges conceptually */ }Generic impl
impl<T: std::fmt::Display> Pair<T> {
fn show(&self) { println!("{}", self.0); }
}Traits vs Inherent Methods
Inherent methods are type-specific. Trait impls provide shared interfaces (Display, etc.).
Gotchas
- Public fields bypass encapsulation - Callers mutate directly. Fix: Make fields private, expose methods.
selfvs&selfchoice wrong - Consuming method when borrow suffices forces moves. Fix: Default to&self.- Method name conflicts with field -
self.x()vsself.x- different namespaces but confusing. Fix: Rename for clarity. - Forgot
Selfalias -Selfrefers to implementing type inimpl. Fix: UseSelfin constructors returning own type. - Async methods need
async fnin trait carefully - Native async traits stabilized in recent Rust. Fix: Follow edition docs for async in traits.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Free functions fn f(s: &S) | Behavior not central to type | Method expresses capability clearly |
| Trait impl | Polymorphic interface | Single concrete type only |
| Extension trait pattern | Add methods to foreign types | Orphan rule blocks direct impl |
| Builder struct | Many optional constructor fields | Simple new enough |
FAQs
Are methods the same as C++ member functions?
Similar, but no this pointer - explicit self parameter with ownership semantics.
Can enums have methods?
Yes. impl Enum { ... } works like structs.
What is `Self`?
Type alias for the type being implemented in the current impl block.
Associated constants?
impl Foo { const BAR: u32 = 1; } - called as Foo::BAR.
Method chaining?
Return &self or &mut self from setters for builder fluency (or consume self).
Privacy of methods?
pub fn on private struct is useless outside module. Struct must be pub for external use.
Default methods from traits?
Trait provides default; inherent method preferred when both exist - name resolution rules apply.
Static method naming?
Convention: new, default, from_* for constructors.
Can methods be generic?
Yes: fn convert<T>(&self) -> T where ...
Operator overloading?
Via traits like Add in ops - see Operator Overloading page.
Related
- Structs Basics - struct forms
- Traits Basics - trait methods
- Enums - methods on enums
- Deriving Traits - auto impls
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.