Who closes a closeable value, and can I close one myself?

← Error Handling and Exceptions · Ref: Q1600

No - you never close anything yourself. Every closeable value has exactly ONE owner, decided by where it lives, and the owner's close is generated by the compiler:
- a LOCAL is a try-with-resources resource, and the try closes it when it ends;
- a value that is never NAMED - a call result, a constructed value - is closed by the statement that made it;
- a PROPERTY is owned by its object: the type's close - 'default operator close', or an 'operator close' you write for extra work - closes it after its body, last acquired first, then calls its super class's close;
- a PARAMETER belongs to the caller, which lends it for the call;
- a CAPTURE of a named value belongs to the scope that made the dynamic function or class;
- the RETURNING variable is handed to the caller, who becomes its owner;
- 'this' is owned by whoever made the object.

So there is no 'close' statement (E01094) and the close operator is never called as a method (E81060). A close written by a non-owner is a double close, or a close while the owner is still using the value - Java allows both, EK9 cannot express them. A type owns its properties simply by declaring its close:

  default operator close

A slot that will close its value - a closeable property or a try resource - therefore takes only a NEW value (E81062).

In the program below, Session owns its Connection and its generated close closes it; report() only uses the connection it is given and leaves the close to the try that owns it.

Example

#!ek9
defines module qa.errorhandling.onlytheownercloses

  defines class

    Connection
      name as String?

      default private Connection()

      Connection()
        -> label as String
        name: label

      describe() as pure
        <- rtn as String: `connection ${name}`

      operator close as pure
        stdout <- Stdout()
        stdout.println(`closed ${name}`)

      default operator ?

    Session
      connection as Connection?

      default private Session()

      Session()
        -> label as String
        connection: Connection(label)

      describe() as pure
        <- rtn as String: connection.describe()

      default operator close

      default operator ?

  defines function

    report()
      -> connection as Connection
      stdout <- Stdout()
      stdout.println(connection.describe())

  defines program

    CloseOwnershipDemo()
      stdout <- Stdout()

      try
        -> session <- Session("owned")
        stdout.println(session.describe())

      try
        -> connection <- Connection("borrowed")
        report(connection)

Common mistakes

E01094 — 'close' is not a statement: a parameter belongs to the caller, whose try closes it, so a close here would close it twice. Every close is generated by the owner. See ek9 -h E01094 for details.

Incorrect:

      stdout.println(connection.describe())
      close connection

Correct:

      stdout.println(connection.describe())

E81060 — The close operator is never called as a method: the caller's try closes the connection it lent, so this call would close it twice. See ek9 -h E81060 for details.

Incorrect:

      stdout.println(connection.describe())
      connection.close()

Correct:

      stdout.println(connection.describe())
Other ways to ask this
  • Can a function close a resource it was passed as a parameter?
  • Why is 'close conn' error E01094?
  • Can I write 'close conn' for a connection held in a property?
  • Who owns a closeable value in EK9?
  • Can a dynamic function close a variable it captured?

Coming from another language?

Java: any code holding an AutoCloseable may call close() on it - a method that was only passed a stream often closes it, and the caller's try-with-resources closes it again; a class may close a field from any method. Rust: a borrower (&T) cannot drop what it borrowed; only the owner drops it. C#: CA2000/CA2213 are warnings. EK9: only the owner closes, and every close is generated - there is no close statement (E01094) and close() is never called as a method (E81060).

Keywords: E01094, E81060, capture, close, double close, operator close, owner, ownership, parameter, property