How do I let other code use a resource my object owns without letting it close or keep it?
← Error Handling and Exceptions · Ref: Q1740
When an object owns a closeable in a property, other code must not receive it as a return value (E81057 - a closeable return must be a new instance) or store it in a trait or Any slot (E81059). Reverse the direction: LEND it. Give the owner a method that takes a function, and have the owner call that function with the resource it holds. The resource is passed as an ARGUMENT, so it is borrowed for that call only; the owner's generated close still closes it.
Declare the function type with a parameter whose type is NOT closeable (a trait without 'operator close', or StringInput for a file's lines), so the callee can read but never close. Then the owner:
lend()
-> user as PageUser
<- rtn as Integer: user(notebook)
This is the pattern to use for a file held as a CloseableStringInput property, because (unlike a TCP connection's input() and output(), which return a separate StringInput view) a file's closeable has no view to return. The loan is one of three ways to use a resource you do not own; the other two - a view the standard library provides, and a by-name capture inside the owning try - are in Q1900. The program below owns a closeable Notebook, lends it to a function that counts lines, and lets the try close it afterwards.
Example
defines module qa.errorhandling.loanpattern defines trait Pages next() as abstract <- rtn as String? hasNext() as abstract <- rtn as Boolean? defines class Notebook with trait of Pages lines as List of String? position as Integer: 0 default private Notebook() Notebook() -> first as String lines: List() of String lines += first lines += "second" lines += "third" override next() <- rtn as String: lines.getOrDefault(position, "") position++ override hasNext() <- rtn as Boolean: position < length lines operator close as pure stdout <- Stdout() stdout.println("closed notebook") default operator ? Owner notebook as Notebook? default private Owner() Owner() -> first as String notebook: Notebook(first) lend() -> user as PageUser <- rtn as Integer: user(notebook) default operator close default operator ? defines function PageUser() as abstract -> pages as Pages <- rtn as Integer? countPages() is PageUser -> pages as Pages <- rtn as Integer: 0 while pages.hasNext() if pages.next()? rtn++ defines program LoanPatternDemo() stdout <- Stdout() try -> owner <- Owner("first") stdout.println(`pages: ${owner.lend(countPages)}`) stdout.println("owner still holds the notebook")
Other ways to ask this
- What is the loan pattern for a closeable property?
- How do I lend a file I hold when a StringInput view is not available?
- Why does returning my CloseableStringInput property give E81057 or E81059?
- How do I let a caller read the lines of a file my class keeps open?
Coming from another language?
Java: a getter returns the held Reader and any caller can close it, or keep it past the owner's lifetime. Rust: a closure or function taking &Reader borrows it for the call only. Swift: a non-escaping closure parameter has the same lifetime limit. Python: a context manager that yields the handle lets the body close it. EK9: the owner calls a supplied function with the resource - an argument is a loan.
Keywords: lend, loan, StringInput, close, function parameter, E81057, E81059, ownership, TextFile, closeable, property, borrow