How to defer a call if condition is met?

I’m having a hard time with “defer is executed at the end of the scope” thing.

I want to do conditional defer, i.e. “defer this call if the condition is met”. But I can’t do that with current defer behavior:

foo :: proc() {
    cond := true

    // This won't work, because defered call will be executed at the end of the `if` block 
    if cond {
        defer fmt.println("if cond { defer }")
    }

    // This will won't work because of the same reason
    if cond do defer {
        fmt.println("if cond do defer") 
    }

    // Edit: Same behaviour here
    if cond do defer fmt.println("if cond do defer") 

    // This won't work because `if cond` check will be executed at the end of the procedure
    // and at that moment cond will be modified to false
    defer if cond {
        fmt.println("defer if cond")
    }

    cond = false
    fmt.println("exit")
}

Is there a way to evaluate if outside of defer and at the same time defer the call on the procedure level (and not in if block level)?

I am completely new and clueless to Odin, but why not:

if cond do defer fmt.println("if cond do defer")

Untested, but it just might work :smile:

Edit: I read/saw somewhere that “braces or a do are always required”

Tested it now, it worked the same way as if cond do defer { … }

foo :: proc() {
    cond := true

    // This won't work, because defered call will be evalueted at the end of the if block 
    if cond {
        defer fmt.println("if cond { defer }")
    }

    // This will won't work because of the same reason
    if cond do defer {
        fmt.println("if cond do defer {}") 
    }

    // Same behaviour here
    if cond do defer fmt.println("if cond do defer") 

    // This won't work because if cond check will be evaluated at the end of the procedure
    // and at that momend cond will be modified to false
    defer if cond {
        fmt.println("defer if cond")
    }

    cond = false
    fmt.println("exit")
}
if cond { defer }
if cond do defer {}
if cond do defer
exit

I tested it by copying the cond to other_cond and set other_cond to false:

...
    other_cond: bool = cond
    other_cond = false
    fmt.println("exit")
}

The output:

if cond { defer }
if cond do defer {}
if cond do defer
exit
defer if cond

If it doesn’t work with the same boolean, why not capture it at the start of the procedure and use that for the defer?
I did it the other way around out of laziness :slight_smile:

Edit: I know that I am not really answering your question, but it’s better than no input, I hope!

Technically yes, it would be better to not reuse original boolean. But I can think of situations when that’s not the case. E.g. when using ok variable with multiple function calls

If any function call fails, and you are forced to exit/panic, then the defers will be called at the end of scope, so no need to use a bool to test if a defer should be set.
I wouldn’t use defer for program logic, just something that is guaranteed to be called if something goes awry. Like _at_exit() or whatsitcalled in C.

See → Idioms - “defer if” - Odin Docs

1 Like

I wonder if this is acceptable to you.

At the point where you would do if cond do defer, which is the point which you want to cement the value which the defer will respect, I instead set a constant. This constant is made especially for the defer block.

main :: proc() {
	// Use this for defers - const so it can't change before defer
	cond_DEFER : bool : true
	// Use a copy for logic that is mutable.
	cond : bool = cond_DEFER
	cond = false
	defer {
		if cond_DEFER do fmt.println("deferred and iffed")
		else          do fmt.println("deferred and elsed")
	}
	
}

Of course you could expand it to use a switch statement for more complex situations, and if your goal is some type of error handling I imagine the switch is what you would use.

1 Like
main :: proc() {
	@(deferred_in=check_later_guard)
	check_later :: proc(b: bool, pred: proc(bool)) -> bool {
		fmt.println("initial value", b)
		return b
	}
	check_later_guard :: proc(b: bool, pred: proc(bool)) {
		fmt.println("defer value", b)
		pred(b)
	}

	ok := check_later(false, proc(b: bool) {
		   if !b do fmt.eprintln("error...")
	})
	fmt.println("ok is =", ok)
	ok = true
	fmt.println("ok is =", ok)
}

Bit more cucumbersome but this will call the guard at the end of the function, and can be reused multiple times so you don’t have to have multiple magic variables declared. :slight_smile:

2 Likes

why not keep this simple and make a copy of the condition so that it is evaluated there and then?

this_cond := cond
defer if this_cond {
    fmt.println("defer if this_cond")
}
6 Likes