Methods
Methods are functions defined inside a struct block that can be called on instances of that struct.
struct_def = [ directives ] [ "pub" ] "struct" IDENT "{" [ field_list ] [ method_list ] "}" ;
field_list = field_def { "," field_def } [ "," ] ;
method_list = method_def { method_def } ;
method_def = [ directives ] "fn" IDENT "(" [ method_params ] ")" [ "->" type ] block ;
method_params = method_param { "," method_param } [ "," ] ;
method_param = receiver | ( [ "inout" | "borrow" ] IDENT ":" type ) ;
receiver = [ "inout" | "borrow" | "mut" ] "self" ;
Method Definition
A method is a function defined inside a struct block that takes self as its first parameter.
The self parameter represents the receiver value and has the type of the enclosing struct.
struct Point {
x: i32,
y: i32,
fn get_x(self) -> i32 {
self.x
}
}
fn main() -> i32 {
let p = Point { x: 42, y: 10 };
p.get_x() // Returns 42
}
Method Calls
Methods are called using dot notation: receiver.method(args).
A method call receiver.method(args) is desugared to a function call with the receiver as the first argument.
Methods MAY have additional parameters after self.
struct Point {
x: i32,
y: i32,
fn add(self, dx: i32, dy: i32) -> Point {
Point { x: self.x + dx, y: self.y + dy }
}
}
fn main() -> i32 {
let p = Point { x: 10, y: 20 };
let p2 = p.add(32, 0);
p2.x // Returns 42
}
Method Chaining
When a method returns the same struct type, method calls MAY be chained.
struct Counter {
value: i32,
fn inc(self) -> Counter {
Counter { value: self.value + 1 }
}
}
fn main() -> i32 {
let c = Counter { value: 39 };
c.inc().inc().inc().value // Returns 42
}
Associated Functions
A function in a struct block that does not take self as its first parameter is an associated function.
Associated functions are called using dot notation: Type.function(args).
struct Point {
x: i32,
y: i32,
fn origin() -> Point {
Point { x: 0, y: 0 }
}
}
fn main() -> i32 {
let p = Point.origin();
p.x // Returns 0
}
The Self Type
Within a struct block, the type keyword Self denotes the enclosing struct type. It MAY be used wherever a type is expected — in a method's parameter types, in its return type, and in Self { ... } struct-literal expressions in the body — and is equivalent to writing the struct's name.
struct Point {
x: i32,
y: i32,
fn origin() -> Self {
Self { x: 0, y: 0 }
}
fn translate(self, other: Self) -> Self {
Self { x: self.x + other.x, y: self.y + other.y }
}
}
fn main() -> i32 {
let p = Point.origin().translate(Point { x: 42, y: 0 });
p.x // Returns 42
}
Multiple Methods
A struct may have multiple methods defined in its block.
Method names MUST be unique within a struct definition.
struct Point {
x: i32,
y: i32,
fn get_x(self) -> i32 { self.x }
fn get_y(self) -> i32 { self.y }
}
fn main() -> i32 {
let p = Point { x: 42, y: 10 };
p.get_x() // Returns 42
}
Fields and Methods Are Separate Name Spaces
Field names (6.2:3) and method names (6.4:16) occupy separate name spaces within a struct: a struct MAY declare a field and a method with the same name. The two are disambiguated by the form of the access. A postfix access without an argument list, receiver.name, is a field access (4.12) and denotes the field. An access with an argument list, receiver.name(args), is a method call (6.4:6) and invokes the method. The two forms never conflict, so the shared name is unambiguous at every use site.
struct P {
x: i32,
fn x(self) -> i32 { 99 }
}
fn main() -> i32 {
let p = P { x: 5 };
p.x + p.x() // 5 (field) + 99 (method) = 104
}
Error Conditions
Calling a method on a non-struct type is a compile-time error.
Calling an undefined method is a compile-time error.
Calling an associated function with method call syntax (receiver.function()) is a compile-time error.
Calling a method with associated function syntax (Type.method()) is a compile-time error.
Receiver Modes
A receiver MAY be declared borrow self or inout self, mirroring the borrow/inout parameter modes. A bare self receiver is passed by value (copied if the struct is Copy, otherwise moved). A borrow self receiver grants read-only access to the receiver without taking ownership. An inout self receiver grants exclusive mutable access without taking ownership, and mutations to self are observed by the caller after the call returns. A by-value receiver MAY additionally be declared mut self, making the binding mutable within the method body (6.4:35).
The receiver mode is a property of the method signature; it is NOT written at the call site. A call receiver.method(args) accesses the receiver in the method's declared mode automatically (autoref): by value for self, by immutable reference for borrow self, and by exclusive mutable reference for inout self.
Calling an inout self method requires the receiver to be a mutable place (for example a let mut binding, or a field or element reachable from one). A call whose receiver is not mutable is a compile-time error.
A borrow self or inout self method call whose receiver is not a place (for example the result of a function or method call) is a compile-time error: the receiver has no caller-visible storage to reference.
The law of exclusivity extends to receivers: the access implied by a borrow self or inout self receiver participates in exclusivity checking together with the call's borrow/inout arguments. Passing the receiver's own place as an inout (or conflicting borrow) argument to the same call is a compile-time error. An argument that only reads the receiver (for example v.push(v.len())) is permitted, because its read completes before the receiver access begins.
A borrow self or inout self method call does not consume the receiver: the receiver remains usable after the call. In particular, a borrow self getter MAY be called repeatedly, and an inout self mutator mutates the receiver in place rather than requiring the caller to rebuild it.
struct Counter {
n: i32,
fn get(borrow self) -> i32 {
self.n
}
fn bump(inout self) {
self.n = self.n + 1;
}
}
fn main() -> i32 {
let mut c = Counter { n: 0 };
c.bump();
c.bump();
let a = c.get(); // borrow does not consume `c`
c.bump();
a + c.get() // 2 + 3 = 5
}
Mutable By-Value Receivers
A by-value receiver MAY be declared mut self. The receiver is still passed by value exactly as a bare self receiver is — copied if the struct is Copy, otherwise moved — but the binding is mutable within the method body, like a let mut local (5.1:5): the body MAY assign to self, its fields, and its elements, and MAY call inout self methods on it. Mutations affect only the method's own value; they are never observed by the caller (caller write-back is inout self, 6.4:24). mut is not part of the method's signature: call sites are unaffected, and the caller's receiver expression is subject to the same rules as for a bare self receiver.
Assigning to self, a field of self, or an element of self, or calling an inout self method on self, inside a method whose receiver is a bare (non-mut, non-inout) self is a compile-time error. The mut modifier is only valid on a by-value receiver: mut cannot be combined with borrow (which grants no mutable access) or inout (which is already mutable).
struct Counter {
n: i32,
// Consumes the receiver, mutates its own copy, returns the result.
fn finish(mut self, bonus: i32) -> i32 {
self.n = self.n + bonus;
self.n
}
}
fn main() -> i32 {
let c = Counter { n: 40 };
c.finish(2) // 42; `c` was moved into the call
}