Defer has two advantages over try…finally: firstly, it doesn’t introduce a nesting level.
Secondly, if you write
foo
defer revert_foo
, when scanning the code, it’s easier to verify that you didn’t forget the revert_foo part than when there are many lines between foo and the finally block that calls revert_foo.A disadvantage is that defer breaks the “statements are logically executed in source code order” convention. I think that’s more than worth it, though.
https://jacobfilipp.com/DrDobbs/articles/CUJ/2000/cexp1812/a...
A similar macro later (2006) made its way into Boost as BOOST_SCOPE_EXIT:
https://www.boost.org/doc/libs/latest/libs/scope_exit/doc/ht...
I can't say for sure whether Go's creators took inspiration from these, but it wouldn't be surprising if they did.
busy = true
Task {
defer { busy = false }
// do async stuff, possibly throwing exceptions and whatnot
}Defer is more flexible/requires less boilerplate to add callsite specific handling. For an example, see https://news.ycombinator.com/item?id=46410610
func atomic_get_and_inc() -> Int {
sem.wait()
defer {
value += 1
sem.signal()
}
return value
} struct PrintOnDrop;
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("dropped");
}
}
fn main() {
let p = PrintOnDrop;
return println!("returning");
}
But the idea of altering the return value of a function from within a `defer` block after a `return` is evaluated is zany. Please never do that, in any language.It gets even better in swift, because you can put the return statement in the defer, creating a sort of named return value:
func getInt() -> Int {
let i: Int // declared but not
// defined yet!
defer { return i }
// all code paths must define i
// exactly once, or it’s a compiler
// error
if foo() {
i = 0
} else {
i = 1
}
doOtherStuff()
}The magical thing I was misremembering is that you can reference a not-yet-defined value in a defer, so long as all code paths define it once:
fn callFoo() -> FooResult {
let fooParam: Int // declared, not defined yet
defer {
// fooParam must get defined by the end of the function
foo(fooParam)
otherStuffAfterFoo() // …
}
// all code paths must assign fooParam
if cond {
fooParam = 0
} else {
fooParam = 1
return // early return!
}
doOtherStuff()
}
Blame it on it being years since I’ve coded in swift, my memory is fuzzy. #include <iostream>
#define RemParens_(VA) RemParens__(VA)
#define RemParens__(VA) RemParens___ VA
#define RemParens___(...) __VA_ARGS__
#define DoConcat_(A,B) DoConcat__(A,B)
#define DoConcat__(A,B) A##B
#define defer(BODY) struct DoConcat_(Defer,__LINE__) { ~DoConcat_(Defer,__LINE__)() { RemParens_(BODY) } } DoConcat_(_deferrer,__LINE__)
int main() {
{
defer(( std::cout << "Hello World" << std::endl; ));
std::cout << "This goes first" << std::endl;
}
}