An impl block attaches members to a type. Anything declared inside one belongs to the type it names,
and is reached through that type rather than through the surrounding scope.
struct Vector2D {
x: int;
y: int;
}
impl Vector2D {
fn new(x: int, y: int): Self {
Vector2D { x, y }
}
fn magnitude_squared(self): int {
self.x * self.x + self.y * self.y
}
}An impl block may be written for any struct or enum, and a type may have more than one.
Self is a type: an alias for whatever type the block implements. Writing Self instead of the
type's name keeps a block independent of it.
self is a value: the instance an instance method was called on. It takes no annotation, because
its type is always Self.
A function whose first parameter is self is an instance method. It is called on a value with
..
A function without self is an associated function. It is called on the type itself, also with
..
impl Vector2D {
fn origin(): Self { // Associated function.
Vector2D { x: 0, y: 0 }
}
fn translate(self, dx: int, dy: int) { // Instance method.
self.x += dx;
self.y += dy;
}
}
let v = Vector2D.origin(); // Called on the type.
v.translate(3, 4); // Called on the value.An instance method reaches the receiver's fields through self, including private ones.
struct Scalar {
private inner: int;
}
impl Scalar {
fn get(self): int {
self.inner // Reachable here, and only here.
}
fn set(self, inner: int) {
self.inner = inner;
}
}An impl block may declare constants with const. Like other constants they are annotated with a
type and bound to a literal.
impl Scalar {
const DEFAULT_VALUE: int = 0;
const LIMIT: int = 100;
fn new(): Self {
Scalar { inner: Self.DEFAULT_VALUE } // Reached through `Self` inside the block.
}
}
let d = Scalar.DEFAULT_VALUE; // Reached through the type name outside it.An impl block may declare type aliases with type. An associated type is named through the type it
belongs to, which lets a signature refer to a type the implementation chooses.
impl Scalar {
type Inner = int;
fn widened(self): Self.Inner {
self.inner
}
}
let value: Scalar.Inner = 4; // The alias resolves to `int`.Inside the block an associated item is reached as Self.NAME; outside, as TypeName.NAME.
Associated functions, constants, and types are public unless marked private, matching struct fields.
A private member is reachable only from inside an implementation of the same type.
struct Counter {
private count: int;
}
impl Counter {
public const START: int = 0;
private const STEP: int = 1;
public fn new(): Self {
Counter { count: Self.START }
}
private fn step_size(): int {
Self.STEP // Private members are reachable from within.
}
public fn advance(self) {
self.count += Self.step_size();
}
}Visibility applies to the member, not to the block, so a single block may mix public and private members freely.
A type may be given several impl blocks, and the members of all of them are merged. This is useful
for grouping related members, or for keeping a type's declaration next to only part of its behavior.
struct Grid {
width: int;
height: int;
}
impl Grid {
fn square(side: int): Self { Grid { width: side, height: side } }
}
impl Grid {
fn area(self): int {
self.width * self.height
}
}
let g = Grid.square(4);
let a = g.area();Because the blocks are merged, a member name may be defined only once across all of a type's implementations. Two blocks that declare the same name are in conflict.
Everything above applies to enums as well as structs.
enum Cell {
X = -1,
O = 1,
Empty = 0
}
impl Cell {
const COUNT: int = 3;
fn to_symbol(self): str {
if (self as int == -1) { "X" }
else if (self as int == 1) { "O" }
else { "." }
}
}
let symbol = Cell.X.to_symbol();
let n = Cell.COUNT;