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

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: ownership, E81060, parameter, close, property, capture, double close, E01094, owner, operator close