Can a method return a closeable it already holds, or must it return a new one?
← Error Handling and Exceptions · Ref: Q1620
Returning a closeable value hands it to the caller, and the caller closes it - as a try-with-resources resource, or at the end of the statement when it is never named. So a method, operator, function or dynamic function whose declared return type is closeable must return a NEW instance (E81057 otherwise). That is a constructor call, or a call of another callable that itself declares a closeable return type, because that callable is held to the same rule:
openLog()
<- rtn as CloseableStringInput: TextFile("log.txt").input()
Anything somebody else already owns is an error: 'this', one of its own properties or a view of one (the object's generated close closes those), a parameter (the caller owns it), a captured variable, a try-with-resources resource (the try closes it as it ends, so the caller would get a closed object), or a call whose result may be held elsewhere. Every way the returning variable gets a value is checked - its initialiser, each assignment (an 'if rtn := ... then rtn?' head included) and each arm of a switch used as its value:
passBack()
-> input as CloseableStringInput
<- rtn as CloseableStringInput: input // ERROR E81057
A resource you hold is never handed out - not by returning it, and not wrapped in a 'view' type of your own, because a type that keeps the resource in a property owns it and would close it too (E81062, E81067). To let other code USE it, LEND it: the LOAN pattern (QA1740) keeps the resource in its owner and gives the owner a method that takes a function and calls it with the resource, so the callee borrows it for that call only. The other two borrower forms - a view the standard library provides, such as a TCP connection's input() and output(), and a dynamic class inside the owning try that captures the resource by name - are in Q1900. Never acquire with a fluent open() that returns 'this' (QA1720). The program below has a factory, a factory that delegates to it, and an owner that hands out a fresh closeable while keeping its own.
Example
defines module qa.errorhandling.closeablereturns defines class Tracker name as String? default private Tracker() Tracker() -> label as String name: label describe() as pure <- rtn as String: `tracker ${name}` operator close as pure stdout <- Stdout() stdout.println(`closed ${name}`) default operator ? Owner tracker as Tracker? default private Owner() Owner() -> label as String tracker: Tracker(label) spare() <- rtn as Tracker: Tracker("spare") describe() as pure <- rtn as String: tracker.describe() default operator close default operator ? defines function makeTracker() -> label as String <- rtn as Tracker: Tracker(label) delegatingFactory() -> label as String <- rtn as Tracker: makeTracker(label) defines program CloseableReturnDemo() stdout <- Stdout() try -> made <- delegatingFactory("made") stdout.println(made.describe()) try -> owner <- Owner("owned") stdout.println(owner.describe()) try -> spare <- owner.spare() stdout.println(spare.describe())
Common mistakes
E81057 — spare() declares a closeable return type, so it must return a NEW instance; returning the property 'tracker' would let the caller close the object Owner still owns and closes itself. See ek9 -h E81057 for details.
Incorrect:
spare()
<- rtn as Tracker: tracker
Correct:
spare()
<- rtn as Tracker: Tracker("spare")
Other ways to ask this
- Why is a getter for my connection property error E81057?
- How do I lend a resource I own without the caller closing it?
- Can a function return its parameter when the type has operator close?
- Why can't my operator + on a closeable class return this?
Coming from another language?
Java: a getter may return a field that is an AutoCloseable, and nothing says who closes it - the caller's try-with-resources then closes the object's own connection under it. C#: the same with IDisposable; CA2000/CA2213 are advisory. Rust: returning a borrowed &T cannot be dropped by the caller, and returning an owned value moves it out. EK9: a declared closeable return must be a new instance (E81057); lend a held resource as an argument instead (the loan pattern).
Keywords: factory, lend, return, E81057, ownership, close, getter, loan, operator close, closeable