LEVIATHAN v962456e · 962456eee1

Declarations

weak fields

Hold a non-owning reference to an object that reads as None once the object is gone.

since 0.1.0-alpha.1linuxwindowswasm

Description

A weak field refers to an object without keeping it alive. It is meant for back-references such as a child pointing to its parent, where a normal reference would form a cycle that keeps both objects alive.

class Component {
    weak IComponent? parent = None;
    weak readonly IComponent? fixedParent = None;
}

The field's type must be an optional class or interface type, T?. Assigning a T, a T? or None to the field is allowed and does not keep the referent alive. Reading the field gives a T?: None once the last strong reference to the object has gone, and otherwise a normal reference to the object. Because every read checks whether the object is still alive, copy the read into a local variable before testing it for None and using it.

A weak field after its owner is released

class Parent { string name = "p"; }
class Child {
    weak Parent? parent = None;
}
Child c = Child();
void attach(Child child) {
    Parent temp = Parent();
    child.parent = temp;
    Parent? live = child.parent;
    if (live != None) {
        console.writeln("inside: " + live.name);
    }
}
attach(c);
console.writeln(c.parent == None);
inside: p
true

While attach runs, temp owns the parent, so reading child.parent finds it. When attach returns, temp is released, and the weak field reads as None.

Rules

  • weak applies to instance fields only, and the declared type must be T? where T is a class or interface. A struct, string, array, map, Block or function type is a compile error.
  • Storing into a weak field does not retain the referent.
  • A read of the field returns a fresh T?: None if the referent is gone, otherwise an ordinary reference. Once you hold that reference in a local variable, it keeps the object alive like any other.
  • The field itself cannot be narrowed. if (c.p != None) { c.p.n } is rejected; copy it first with Parent? p = c.p; and test p.
  • Weakness belongs to the field, not to the value. A weak read passed to a function, returned, or captured by a closure is an ordinary strong reference.
  • weak readonly is allowed; weak const is a compile error.

Examples

Storing the only reference to a temporary leaves the weak field empty at once:

A temporary that is not kept alive

class Parent { string name = "p"; }
class Child { weak Parent? parent = None; }
Child c = Child();
console.writeln(c.parent == None);
c.parent = Parent();
console.writeln(c.parent == None);
true
true

The field cannot be narrowed directly:

Read into a local first

class P { int n = 1; }
class C { weak P? p = None; }
C c = C();
P x = P();
c.p = x;
if (c.p != None) { console.writeln(c.p.n); }   // error: narrow the union before member access
P? local = c.p;
if (local != None) { console.writeln(local.n); }   // fine
not run — a compile error on purpose

Notes

A weak field becomes None when the last strong reference is released. When you need a predictable moment for that, keep the owner in a named variable and let it go out of scope; do not depend on the release of a temporary that is passed as an argument.

See also

  • Fields — Declare the data a class or struct holds, with optional initializers, and how a field without an initializer gets its default value.
  • readonly fields — Fix an instance field once, either in its initializer or in every constructor, and keep it read-only afterwards.
  • Classes — Declare reference types with fields, methods, constructors and accessors, with generics and inheritance from one or more base classes.