Method Syntax in Rust
Rust methods define behavior associated with a type such as a struct or enum. They are usually declared inside an impl block and are called on a value with dot syntax.
What Is a Method in Rust?
A method is a function whose first parameter is some form of self, which represents the value the method is called on. Methods keep behavior close to the type it belongs to and often make APIs easier to read.
Define Methods with impl
struct Circle {
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn circumference(&self) -> f64 {
2.0 * std::f64::consts::PI * self.radius
}
}
fn main() {
let circle = Circle { radius: 5.0 };
println!("Area: {:.2}", circle.area());
println!("Circumference: {:.2}", circle.circumference());
}impl Circledefines behavior forCircle.&selfborrows the current instance immutably.- Methods are called with dot syntax, such as
circle.area().
Understand self, &self, and &mut self
self: consume the value
struct Container {
items: Vec<i32>,
}
impl Container {
fn consume(self) -> Vec<i32> {
self.items
}
}
fn main() {
let container = Container { items: vec![1, 2, 3] };
let items = container.consume();
println!("{:?}", items);
}A method taking self by value normally consumes the instance.
&self: read-only borrowing
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}Use &self when the method only needs to inspect the value.
&mut self: mutable borrowing
struct Counter {
value: i32,
}
impl Counter {
fn increment(&mut self) {
self.value += 1;
}
}
fn main() {
let mut counter = Counter { value: 0 };
counter.increment();
println!("Counter value: {}", counter.value);
}Use &mut self when the method needs to modify the instance in place.
Method Chaining
Methods can return self, &self, or &mut self to support fluent call chains:
struct Builder {
name: String,
count: u32,
}
impl Builder {
fn new(name: &str) -> Self {
Self {
name: name.to_string(),
count: 0,
}
}
fn increment(&mut self) -> &mut Self {
self.count += 1;
self
}
fn set_name(&mut self, name: &str) -> &mut Self {
self.name = name.to_string();
self
}
fn build(&self) {
println!("Builder: {}, Count: {}", self.name, self.count);
}
}
fn main() {
Builder::new("Initial")
.increment()
.increment()
.set_name("Updated")
.build();
}Returning &mut Self is useful for builder-like APIs that mutate one value through a sequence of calls.
Associated Functions
Functions inside an impl block that do not take self are called associated functions:
struct Calculator;
impl Calculator {
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn new() -> Self {
Calculator
}
}
fn main() {
let _calculator = Calculator::new();
let sum = Calculator::add(10, 20);
println!("Sum: {}", sum);
}Associated functions are called with Type::function() syntax. Constructors named new are a convention rather than a special language feature.
Best Practices
- Use
&selfwhen a method only reads state. - Use
&mut selffor in-place mutation. - Take
selfwhen consuming the value is part of the API contract. - Use associated functions for constructors and operations that belong to the type but do not require an instance.
- Support chaining only when it makes the API clearer rather than merely shorter.
- Keep methods focused and choose names that communicate the operation’s effect.
Conclusion
Rust method syntax combines familiar dot notation with explicit ownership choices. Deciding whether a method takes self, &self, or &mut self makes its ownership and mutation behavior visible in the signature, which is a major part of designing clear Rust APIs.