Skip to content

Latest commit

 

History

History
194 lines (144 loc) · 4.31 KB

File metadata and controls

194 lines (144 loc) · 4.31 KB

05: Implementations

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 and self

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.

Associated Functions and Instance Methods

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;
    }
}

Associated Constants

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.

Associated Types

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.

Member Visibility

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.

Multiple Implementation Blocks

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.

Implementations on Enums

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;