LEVIATHAN v962456e · 962456eee1

Types

Reference vs value semantics

Which types are shared when assigned or passed (class instances) and which are copied (primitives, structs, arrays).

since 0.1.0-alpha.1linuxwindowswasm

Description

A type is either a reference type or a value type, and that decides what an assignment or a function call does with it.

  • Instances of a class are references. Assigning one to another variable, or passing it to a function, shares the same object. A change made through either name is visible through the other.
  • Primitives, struct values and arrays are values. Assigning or passing one makes an independent copy. A change to the copy does not affect the original.

Sharing versus copying

class Counter {
    int n = 0;
}
struct Point {
    int x = 0;
    int y = 0;
}

void bumpClass(Counter c) { c.n = c.n + 1; }
void bumpInt(int n) { n = n + 1; }
void movePoint(Point p) { p.x = 99; }

Counter c1 = Counter();
Counter c2 = c1;
c2.n = 5;
bumpClass(c1);
console.writeln(c1.n);

int a = 1;
bumpInt(a);
console.writeln(a);

Point p1 = Point(1, 2);
Point p2 = p1;
p2.x = 10;
movePoint(p1);
console.writeln(p1.x);
6
1
1

Rules

  • A class instance is shared by every variable, field, parameter and collection slot that refers to it.
  • A primitive (int, float, bool, char, string and the sized numeric types) is copied on assignment and when passed.
  • A struct is copied on assignment, on passing as an argument and when read out of a collection. Mutating the copy leaves the original unchanged.
  • An array is a value: Array<int> ys = xs; makes ys an independent array, so ys[0] = 99; does not change xs. Methods such as add return a new array and leave the receiver unchanged.
  • A copy of a collection is shallow. An array of class instances copies the references, so both arrays still point at the same objects.
  • A closure captures an object by reference, so mutations through it are shared. It captures a primitive local by value: the closure works on its own copy, so the enclosing variable never changes.

Examples

Arrays copy; the objects inside them do not:

Arrays are values, their elements may not be

class Counter {
    int n = 0;
}

Array<int> xs = [1, 2, 3];
Array<int> ys = xs;
ys[0] = 99;
ys = ys.add(4);
console.writeln(xs[0]);
console.writeln(xs.length());
console.writeln(ys.length());
Array<Counter> list = [Counter()];
Array<Counter> list2 = list;
list2[0].n = 4;
console.writeln(list[0].n);
1
3
4
4

A closure captures an object live but a primitive by value:

What a closure captures

class Box {
    int v = 0;
}

void run() {
    int seen = 0;
    Box box = Box();
    () => void inc = () => {
        seen = seen + 1;
        box.v = box.v + 1;
    };
    inc();
    inc();
    console.writeln(seen);
    console.writeln(box.v);
}
run();
0
2

Notes

To share mutable state between a closure and its caller, put the state in a small class and capture the instance, not a local int.

See also