Why does EK9 reject manual close of local resources?
← Error Handling and Exceptions · Ref: Q802
EK9 has no 'close' statement: 'close conn' is E01094, rejected as early as 'break' or 'return'. Every close is generated, because a close written by hand is unsafe in two ways. If an exception happens before it, the resource leaks:
conn <- DatabaseConn("test-db") doWork() //an exception here leaks conn
Research shows ~32% of resource leak bugs are caused by exceptions occurring before manual close() calls. And if the value has another owner - the caller that passed it, the try that made it - a close by hand closes it twice, or under an owner still using it.
CORRECT: TRY-WITH-RESOURCES
A local closeable is declared in the try header, and the try closes it when it ends, normally or by exception:
try -> conn <- DatabaseConn("test-db") stdout.println(conn.query("SELECT 1"))
WHERE THE OTHER CLOSES COME FROM
- A closeable value you never name is closed by the statement that made it.
- A type's own closeable properties are closed by its 'operator close' - 'default operator close', or the chain after a close you write for extra work.
- A parameter, a capture or a returned value is closed by its owner, never by you.
The close operator is never called as a method either: 'conn.close()' is E81060.
See Q137 for try-with-resources syntax. See Q803 for abandoned resource detection. See Q1600 for who owns a closeable.
Example
defines module qa.errorhandling.manualclose defines class DatabaseConn connName <- String() DatabaseConn() -> connectionName as String connName: connectionName query() -> queryText as String <- queryResult as String: `${queryText} from ${connName}` operator close as pure require true override operator ? as pure <- rtn <- true defines function safeResourceUsage() stdout <- Stdout() try -> conn <- DatabaseConn("test-db") queryResult <- conn.query("SELECT 1") stdout.println(queryResult)
Common mistakes
E01094 — 'close' is not a statement in EK9: a close written by hand never runs if an exception comes first, and closes twice a value somebody else owns. Declare the local in the try header and the try closes it. See ek9 -h E01094 for details.
Incorrect:
conn <- DatabaseConn("test-db") queryResult <- conn.query("SELECT 1") stdout.println(queryResult) close conn
Correct:
try -> conn <- DatabaseConn("test-db") queryResult <- conn.query("SELECT 1") stdout.println(queryResult)
Other ways to ask this
- What does E01094 'close' is not a statement mean?
- Why must I use try-with-resources for local variables?
- How do I properly close resources in EK9?
- Can I write 'close conn' when I have finished with a connection?
Coming from another language?
Java: allows manual close() but try-with-resources recommended since Java 7. Python: 'with' statement recommended but manual close allowed. Rust: RAII with Drop trait, automatic cleanup. Go: defer close() pattern, easy to forget. C#: using statement for IDisposable. EK9: there is no close statement (E01094) and close() is never called as a method (E81060) - every close is generated.
Keywords: E01094, with, try, leak, close, resources, resource, local, manual