8 comments

[ 0.18 ms ] story [ 5.9 ms ] thread
Article describes try/finally as a hack to get the effect of defer, but it looks to me like it's the other way around? Try/finally is more traditional, in one form or another.
A (lexically-scoped) defer is a more general than a finally block. You can express finally-block semantics using defer.

It's also more exception-safe when you have more than one throwing call in the try block.

Author here. I find try/finally verbose and vulnerable to mistakes. For me, it adds "function noise".
Would call stack/source map reasonably work with this?
Reasonably? Yes. Could probably be made to work well.

  $ bat 2.ts
  ───────────────
     1 │ function one() {
     2 │   let x = 1;
     3 │   defer (() => { throw new Error("thrown from one!"); })()
     4 │   x = 2;
     5 │ }
     6 │ one();
  ─────┴─────────

  $ node 2.js
  ~/healeycodes-typescript-go/2.js:29
            throw _errors_1[0];
            ^

  Error: thrown from one!
      at _callee_1 (/Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:9:46)
      at /Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:10:33
      at one (/Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:22:31)
      at Object.<anonymous> (/Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:34:1)
      at Module._compile (node:internal/modules/cjs/loader:1830:14)
      at Object..js (node:internal/modules/cjs/loader:1961:10)
      at Module.load (node:internal/modules/cjs/loader:1553:32)
      at Module._load (node:internal/modules/cjs/loader:1355:12)
      at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
      at Module.executeUserEntryPoint [as runMain (node:internal/modules/run_main:154:5)

  Node.js v24.15.0
Is "defer" a good pattern? Aren't constructors/destructor pairs, like in C++/Rust, or "with" blocks in Python, better, because you cannot forget to call the destructor. I the first code example in the article, it is easy to omit "defer" and the compiler won't notice.