What happens when a closeable resource is never closed?
← Error Handling and Exceptions · Ref: Q803
EK9 raises E81056 when a local variable holds a closeable value (its type has an 'operator close') outside a try-with-resources declaration. Nothing would ever close it, so it is a guaranteed resource leak.
THE RULE
A closeable value you never NAME is owned by the statement that created it and is closed when that statement finishes. A closeable value you NAME must be declared as a try-with-resources resource, and is closed when the try ends.
NOT A LOCAL
- RETURNED: the returning variable hands it to the caller (a factory)
- A PROPERTY: the object owns it
- A PARAMETER: the caller owns it
Passing a named local on to a function or constructor does NOT transfer it - the local still holds it.
FIX OPTIONS
1. try-with-resources: closed when the try ends (recommended)
2. Do not name it: FileConn("log.txt").readAll() is closed when its statement finishes
3. Return it: transfer ownership to the caller
Research shows ~30% of resource leak bugs are caused by developers forgetting to close resources entirely.
See Q1520 for the whole ownership rule. See Q802 for manual close rejection. See Q137 for try-with-resources syntax.
Example
defines module qa.errorhandling.resourceneverclosed defines class FileConn fileName <- String() FileConn() -> name as String fileName: name readAll() <- content as String: "contents of " + fileName operator close as pure require true override operator ? as pure <- rtn <- true defines function safeFileRead() stdout <- Stdout() try -> fileConn <- FileConn("log.txt") content <- fileConn.readAll() stdout.println(content)
Common mistakes
E81056 — A closeable value held in a named local variable must be a try-with-resources resource, or nothing would ever close it. Declare it in the try. See ek9 -h E81056 for details.
Incorrect:
fileConn <- FileConn("log.txt") content <- fileConn.readAll() stdout.println(content)
Correct:
try -> fileConn <- FileConn("log.txt") content <- fileConn.readAll() stdout.println(content)
Other ways to ask this
- What triggers E81056 closeable held in a named local?
- How does EK9 detect abandoned resources?
- Why must a named closeable be a try-with-resources resource?
Coming from another language?
Java: unclosed resources only detected by FindBugs/SpotBugs, not the compiler. Python: no detection, relies on linters. Rust: Drop trait guarantees cleanup. Go: no detection (defer is voluntary). C#: Roslyn CA2000 warns but code still compiles. EK9: an unnamed closeable is closed by its statement and a named one must be a try-with-resources resource (compile-time error E81056), so abandoned resources cannot exist.
Keywords: E81056, transfer, try, leak, close, try-with-resources, resource, abandoned, ownership