One caution on use of blocks is that at least for C++, you can end up having a large amount of stack usage. While destructors follow strict lexical scoping, stack allocations are only guaranteed to be released at the end of a (non-inline) function. The compiler can reuse memory allocated on the stack for multiple variables assuming the lifetimes don't overlap, but this isn't guaranteed. For example of when reuse doesn't help:
void someProc() {
char buffer1[256];
{
char buffer2[256];
// string ops
}
// we still have 512 bytes on the stack whereas it would only be
// 256 if we used a non-inline function instead of a block
someDeepCall();
}
I'm not sure what Rust or Go do in cases like this.