In what order are the resources my object holds closed?

← Error Handling and Exceptions · Ref: Q1842

In REVERSE declaration order - the last declared is closed first. That is reverse ACQUISITION order (last made, first closed) only when the constructor acquires its closeable properties in declaration order, so the compiler requires exactly that: a constructor that sets a closeable property while one declared after it already holds a resource is error E81064. Property initialisers run in declaration order before the constructor body, so a closeable property with an initialiser counts as acquired before anything the body sets, and one path through the constructor acquiring out of order is enough.

The order matters as soon as one resource depends on another: a writer that writes to a ledger must be closed - and flushed - before the ledger. Declare the foundation first and what depends on it after, and acquire them in that order.

The program below declares a ledger, then a writer that writes to it, and acquires them in that order. Each prints when it is opened and when it is closed: the writer, made last, is closed first.

Example

defines module qa.errorhandling.closeablepropertiesindeclarationorder

  defines class

    Channel
      name as String?

      default private Channel()

      Channel()
        -> text as String
        name: text
        stdout <- Stdout()
        stdout.println(`opened ${name}`)

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

      default operator ?

    <?- Declared, and acquired, foundation first: the writer is closed first, the ledger last -?>
    Report
      ledger as Channel?
      writer as Channel?

      Report()
        ledger: Channel("ledger")
        writer: Channel("writer")

      default operator close

      default operator ?

  defines program

    ReportWriter()
      stdout <- Stdout()

      try
        -> report <- Report()
        stdout.println("writing the report")

Common mistakes

E81064 — Properties are closed in reverse declaration order, so declared this way the ledger would be closed before the writer that writes to it. Declare them in the order the constructor acquires them. See ek9 -h E81064 for details.

Incorrect:

    Report
      writer as Channel?
      ledger as Channel?

Correct:

    Report
      ledger as Channel?
      writer as Channel?
Other ways to ask this
  • Why must I acquire closeable properties in the order they are declared?
  • Why is setting a closeable property in my constructor an E81064 error?
  • How do I make sure a writer is closed before the ledger it writes to?
  • What does E81064 mean?

Coming from another language?

C++: members are destroyed in reverse declaration order whatever the constructor's initialiser list says, and compilers only warn (-Wreorder) when the two disagree. Rust: fields are dropped in DECLARATION order, an acknowledged accident. Java and C#: you close fields by hand in whatever order you write. EK9: properties are closed in reverse declaration order and the constructor must acquire them in declaration order (E81064).

Keywords: acquisition order, close, operator close, E81064, closeable, order, constructor, property, declaration order