LEVIATHAN v962456e · 962456eee1

Standard Library

interface IDisposable

The interface of an object that must be cleaned up when its work is finished.

since 0.1.0-alpha.1linuxwindowswasm

Overview

A class that implements IDisposable can be declared with using. The close method then runs exactly once when the enclosing block is left, however it is left: by reaching the end, by return, by break or continue, or by a thrown error. When a block has several using declarations, they are closed in the reverse of the order they were declared. File implements it.

Description

interface IDisposable { void close(); }

IDisposable is the contract that a using declaration requires. A type that implements it promises that close() releases whatever the object holds, and a using declaration calls close() for you on every way out of the block that declared it: reaching the end, return, an uncaught throw, break and continue. The library types that implement it are File, TaskGroup and InStream<T>. Your own classes opt in the same way, by naming IDisposable in their declaration and defining close().

Rules

  • close() runs exactly once per using declaration, on every exit from the block.
  • Several using declarations in one block close in reverse order: the last one declared is closed first.
  • using needs a reference type (a class, not a struct) that implements IDisposable. A type that only happens to have a close() method does not qualify. TcpStream is an example: call its close() yourself.
  • The using binding cannot be reassigned.
  • close() is looked up on the object's real class, so a using whose declared type is IDisposable still runs the concrete class's close().
  • If close() throws while another exception is already unwinding, the new exception replaces it.
  • Make close() safe to call twice where you can. The library's stream and socket types are idempotent; File.close() is not, and throws if the file is already closed.

Examples

A class that implements IDisposable, used three ways. The block ends normally, throws, and returns early; close() runs each time, and two resources in one block close in reverse order:

using closes resources on every exit

class Resource : IDisposable {
    string name;
    new Resource(string n) { name = n; console.writeln("open ${n}"); }
    void close() { console.writeln("close ${name}"); }
}

void useBoth() {
    using Resource first = Resource("first");
    using Resource second = Resource("second");
    console.writeln("working");
}
useBoth();

void failing() {
    using Resource guarded = Resource("guarded");
    throw RuntimeException("boom");
}
try {
    failing();
} catch (RuntimeException e) {
    console.writeln("caught ${e.message}");
}

int firstEven(Array<int> items) {
    using Resource scan = Resource("scan");
    for (int n in items) {
        if (n % 2 == 0) { return n; }
    }
    return -1;
}
console.writeln(firstEven([1, 3, 4, 5]));
open first
open second
working
close second
close first
open guarded
close guarded
caught boom
open scan
close scan
4

Notes

Namespace-level constants of the standard library, such as std::read, are initialized before the program runs. std::read, std::write, std::append and std::binary are const: they are facts, not variables, and assigning to one is a compile error (cannot assign to const 'read').

Examples

class Resource : IDisposable {
    string name;
    new Resource(string n) {
        name = n;
        console.writeln("open ${n}");
    }
    void close() {
        console.writeln("close ${name}");
    }
}
void work() {
    using Resource a = Resource("a");
    using Resource b = Resource("b");
    console.writeln("working");
}
work();
open a
open b
working
close b
close a

Methods

close

close() -> void

Releases what the object holds.

A using declaration calls it for you. Implement it so that it is safe to run once; do not call it yourself on an object that a using declaration owns, or it will run twice.

Examples

class Lock : IDisposable {
    bool held = true;
    void close() {
        held = false;
    }
}
Lock lock = Lock();
console.writeln(lock.held);
lock.close();
console.writeln(lock.held);
true
false

See also

  • File — An open file.
  • TaskGroup — A set of tasks that live and end together.
  • InStream — The reading end of a stream: a queue of values of type T that something else produces.
  • using — deterministic cleanup — A using declaration owns a resource and closes it exactly once on every way out of its block, in reverse order of declaration.