Statements
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.
since 0.1.0-alpha.1linuxwindowswasm
Description
using Type name = expr; declares a variable that owns a resource. Type must be a reference type (a class, not a struct) that implements IDisposable:
interface IDisposable {
void close();
}
When the block that contains the declaration is left, the compiler calls name.close(). This happens exactly once and on every way out of the block: reaching its end, a return, an exception that propagates out of it, or a break or continue that leaves it. Several using declarations in one block close in reverse order of declaration, so the last resource opened is the first one closed.
File implements IDisposable, so using File f = File(path, std::read); closes the file however the block ends.
The close order on every exit
class Res : IDisposable {
string name;
new Res(string n) {
name = n;
console.writeln("open ${n}");
}
void close() {
console.writeln("close ${name}");
}
}
void both() {
using Res a = Res("a");
using Res b = Res("b");
console.writeln("body");
}
int early(int n) {
using Res r = Res("early");
if (n > 0) return n;
return 0;
}
void thrower() {
using Res t = Res("thrower");
throw RuntimeException("boom");
}
void loop() {
for (int i = 0; i < 3; i = i + 1) {
using Res x = Res("loop${i}");
if (i == 0) continue;
if (i == 1) break;
}
}
both();
console.writeln("--");
console.writeln(early(1));
console.writeln("--");
try {
thrower();
} catch (RuntimeException e) {
console.writeln("caught ${e.message}");
}
console.writeln("--");
loop();
open a
open b
body
close b
close a
--
open early
close early
1
--
open thrower
close thrower
caught boom
--
open loop0
close loop0
open loop1
close loop1
Rules
Typemust be a reference type that implementsIDisposable. Astruct, or a class that does not implement the interface, is a compile error ('using' requires a reference type implementing IDisposable).- The variable is implicitly constant: assigning to it is a compile error. Writing
using const File f = ...;is allowed and means the same thing. - A
usingdeclaration must be a direct statement of a block. It cannot be the one-statement body of a loop (while (c) using R r = R();), a field, a global, or the binding of afor-in. close()runs exactly once per declaration, whichever exit is taken.- Resources in one block are closed in reverse order of declaration.
close()is dispatched on the actual class of the object. With an interface-typed declaration,using IDisposable d = make();, the concrete class'sclose()runs.- If
close()throws while another exception is already unwinding, the new exception replaces the one in flight; the two are not chained. If the block exits normally andclose()throws, that exception propagates like any other. - A
break,continueor labeled exit that leaves several blocks closes the resources of every block it leaves. Seelang.labeled-loops.
Examples
Interface-typed using, and a close that throws
class Res : IDisposable {
string name;
new Res(string n) { name = n; }
void close() { console.writeln("close ${name}"); }
}
class Bad : IDisposable {
void close() { throw RuntimeException("close failed"); }
}
IDisposable make() => Res("made");
void viaInterface() {
using IDisposable d = make();
console.writeln("using interface");
}
void failing() {
using Bad b = Bad();
throw RuntimeException("body failed");
}
void quiet() {
using Bad b = Bad();
console.writeln("body ok");
}
viaInterface();
try { failing(); } catch (RuntimeException e) { console.writeln(e.message); }
try { quiet(); } catch (RuntimeException e) { console.writeln(e.message); }
using interface
close made
close failed
body ok
close failed
struct S : IDisposable { void close() { } }
void f() { using S s = S(); }
// error: 'using' requires a reference type implementing IDisposable (got 'S')
class R : IDisposable { void close() { } }
void g() { using R r = R(); r = R(); }
// error: cannot assign to const 'r'
void h() { while (true) using R r = R(); }
// error: a 'using' declaration must be a direct statement of a block
Notes
Leviathan has no finally. A using declaration is the way to guarantee cleanup, in the same way C#'s using and Python's context managers are used.
See also
- IDisposable — The interface of an object that must be cleaned up when its work is finished.