Skip to content

Instantly share code, notes, and snippets.

@asd142513
Last active June 10, 2025 05:19
Show Gist options
  • Save asd142513/ee494971fc93b46a4ed81935e2e4261a to your computer and use it in GitHub Desktop.
Save asd142513/ee494971fc93b46a4ed81935e2e4261a to your computer and use it in GitHub Desktop.
/*
Original post:
https://thephd.dev/c2y-the-defer-technical-specification-its-time-go-go-go
Inspired by for loop and Python’s `with` statement:
https://en.cppreference.com/w/c/language/for.html
for ( init-clause ; cond-expression ; iteration-expression ) loop-statement
https://docs.python.org/3/reference/compound_stmts.html#with
Syntax:
with ( init-clause ; cond-expression ; cleanup-expression ) context-statement
Comparison: `thephd.dev defer` vs `with`
1. Automatically creates a new scope.
2. Supports only expressions — no deferred statements.
3. `break` is allowed within the `with` block.
4. Nested `with` statements are not allowed within init-clause, cond-expression, or cleanup-expression.
However, the context-statement can include `with`, just like in loop bodies.
5. No mid-scope defers.
- In the original `defer`, deferred statements could appear anywhere in the scope.
- This is suboptimal, as developers must read the entire block to find all deferred actions.
- The `with` statement improves clarity by forcing all deferred behavior to be declared at the start of the block.
6. Unlike `defer`, which helps maintain a flat block structure `with` introduces an additional level of nesting.
This is even worse in common `malloc`–`init_object` patterns as it introduces two levels of additional nesting.
Comparison: `for` loop vs `with` block
1. Trivially, context-statement is executed only once; `continue` is not allowed.
2. Unlike a `for` loop's iteration-expression, the cleanup-expression is evaluated when exiting via `break` or `return`.
Jump to outside of block with `goto` skips the cleanup-expression.
*/
#include <threads.h>
extern int do_sync_work(int id, mtx_t *m);
int main(void)
{
with (mtx_t m = {}; mtx_init(&m, mtx_plain) == thrd_success; mtx_destroy(&m)) {
for (int i = 0; i < 12; ++i) {
with (; mtx_lock(&m) == thrd_success; mtx_unlock(&m)) {
if (do_sync_work(i, &m) == 0) {
return 1;
}
}
}
}
return 0;
}
@asd142513
Copy link
Author

One critical feature that the with statement lacks, compared to defer, is handling initialization failure. While a with-else could be introduced to handle failures in the condition expression, the lack of a variable to store the evaluated value of the condition expression could pose a serious problem. This issue is shared with for loops, but it's more critical in this context because we are performing initialization rather than just checking a condition.

Possible solution:

mtx_t m = {};
with (int ret = mtx_init(&m, mtx_plain); ret == thrd_success; mtx_destroy(&m)) {
	for (int i = 0; i < 12; ++i) {
		with (; mtx_lock(&m) == thrd_success; mtx_unlock(&m)) {
			if (do_sync_work(i, &m) == 0) {
				return 1;
			}
		}
	}
} else {
	if (ret == thrd_error) {
		// ...
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment