LEVIATHAN v962456e · 962456eee1

Declarations

Accessors

Replace how a field is read or written with get and set, and define computed values and indexers.

since 0.1.0-alpha.1linuxwindowswasm

Description

An accessor changes what reading or writing a name does. get defines a read, set a write:

get value() => value;                    // read view over the 'value' slot
set value(int v) value = v * 2;          // write view
get ([])(int i) => cells[i];             // indexer (computed accessor)
set ([])(int i, int v) cells[i] = v;     // the value being stored is the last parameter

A parameterless accessor is a view over a field of the same name. Inside the accessor body the name refers to the raw field, so get value() => value + 100 reads the stored number and does not call itself. A get can also be declared with no field of that name, which makes it a computed, read-only property.

An accessor with parameters is a computed accessor and needs no backing field. The index accessor ([]) is the common case: a[i] calls the get ([]) accessor of a's class and a[i] = v calls the set ([]) accessor, which mutates the object in place.

Views, computed properties and an indexer

class Gauge {
    int value = 1;
    get value() => value + 100;
    set value(int v) value = v * 2;
    get doubled() => value * 2;
}
Gauge g = Gauge();
console.writeln(g.value);
g.value = 5;
console.writeln(g.value);
console.writeln(g.doubled);
class Grid {
    Array<int> cells;
    new Grid() { cells = [0, 0, 0, 0]; }
    get ([])(int i) => cells[i];
    set ([])(int i, int v) cells[i] = v;
}
Grid grid = Grid();
grid[2] = 7;
grid[0] += 5;
console.writeln(grid[2]);
console.writeln(grid[0]);
console.writeln(grid[1]);
101
110
220
7
5
0

Setting g.value = 5 stores 10 in the field, because the set accessor doubles it; reading it back adds 100. g.doubled is computed from value through its accessor, so it is (10 + 100) * 2.

Rules

  • get name() => expr and set name(T v) statement define the read and the write of name.
  • Inside an accessor for name, the bare name name is the raw field: the accessor does not call itself. Other names go through their own accessors.
  • A parameterized accessor is computed and needs no backing field. The value being assigned is the last parameter of a set.
  • The index accessors are written get ([])(params) and set ([])(params, value); the selector has no spaces inside the parentheses. a[i] += v reads through get and writes through set.
  • If a name has only a get accessor, writes go to the field directly (the stored value is changed but later reads still pass through get). If a get has no field behind it, a write to the name is silently ignored.
  • If a name has only a set accessor, reads return the field directly.
  • An accessor is bound to the field of its name in the class that declares it: in a combined object, a same-named field that another base contributes (a distinct one, say) is read and written directly, never through this accessor.
  • A set accessor in a struct is mutating without being marked. A set accessor may not be declared over a const or readonly field.

Examples

A write to a name that has only a computed get is ignored:

A get without a field

class C {
    int stored = 4;
    get twice() => stored * 2;
}
C c = C();
c.twice = 3;
console.writeln(c.twice);
c.stored = 10;
console.writeln(c.twice);
8
20

An accessor on a struct:

A set accessor on a struct

struct Temp {
    int raw;
    get celsius() => raw;
    set celsius(int v) raw = v * 2;
}
Temp t = Temp(1);
t.celsius = 5;
console.writeln(t.raw);
10

See also

  • Operator declarations — Define operators on a class or struct by declaring a member with a symbolic selector such as (+), (==) or ([]).
  • Fields — Declare the data a class or struct holds, with optional initializers, and how a field without an initializer gets its default value.
  • Value structs — Declare value types that are copied on every assignment, pass and return, with derived equality and explicit mutating methods.