depot/third_party/tvl/fun/defer_rs/README.md
Default email c4fb0432ae Project import generated by Copybara.
GitOrigin-RevId: 3fc1143a04da49a92c3663813c6a0c1e8ccd477f
2020-09-29 23:42:59 -04:00

2.4 KiB

defer in Rust

After a Hacker News discussion about implementing Go's defer keyword in C++, I stumbled upon this comment and more specifically this response to it by "Occivink":

There's plenty of one-time cases where you don't want to declare an entire class but still enjoy scope-based functions.

Specificall the "don't want to declare an entire class" suggests that languages like C++ have high friction for explaining your desired invariant (cleanup is run when $thing is destroyed) to the compiler.

It seems like most languages either hand-wave this away (cough Java cough) or use what seems like a workaround (defer).

Rust has the so-called Drop trait, which is a typeclass that contains a single method with no return value that is run when a variable is dropped (i.e. goes out of scope).

This works fine for most general cases - i.e. closing file handlers - but can get complicated if other use-cases of defer are considered:

  • returning an error-value by mutating a reference in the enclosing scope (oh boy)
  • deferring a decision about when/whether to run cleanup to the caller

While thinking about how to do this with the Drop trait I realised that defer can actually be trivially implemented in Rust, using Drop.

A simple implementation of defer can be seen in defer.rs, an implementation using shared mutable state for error returns is in the file defer-with-error.rs and an implementation that allows cleanup to be cancelled (don't actually do this, it leaks a pointer) is in undefer.rs.

Whether any of this is actually useful is not up to me to decide. I haven't actually had a real-life need for this.

You can run the examples with cargo run --example defer, etc.

Notes

  • Drop is not guaranteed to run in case of panics or program aborts, if you need support for that check out scopeguard
  • undefer could be implemented safely by, for example, carrying a boolean that by default causes execution to happen but can be flipped to disable it

Further reading: