ThePhD
@ThePhD

Copying by-value for defer is bad design in-general; anything that exists in the same scope and cannot escape that scope (such as statement expressions, defer statements, and otherwise) should always refer to existing variables through their name and at the same location (address or whatever you want to call it in ${LANG}).

At the inverse of this, if your transportable function type/syntax refers to variables by-reference instead of copying them, I fucking hate you and your language/extension/compiler, and I hope your toolchain burns in hell.

(This is influenced by GC existence/general variable behavior, and similar shenanigans.)


shieldfoss
@shieldfoss

My most-recent case of weird failure (during tests, thank God) was exactly this, a view that took a lambda that took a local by-reference[1], and then I passed the view out of the function that built the view because, hey, lazy evaluation right? Don't need the result yet, don't calculate it yet!

[1] Because that's always how I use lambdas (with <algorithm>) and I completely failed to consider how views change that up

(Mistake that is not caught by any flag I've found so far on gcc/clang, nor by /W4)


You must log in to comment.

in reply to @ThePhD's post:

go is even more fucked up than you are thinking though

func main() {
  i := 1
  defer func(x int) {
    println("captured i is", i, "and argument received is", x)
  }(i)
  i = 2
}

this prints captured i is 2 and argument received is 1, because the arguments of the call are eagerly evaluated (execution stops right before entering the function, not before evaluating the deferred statement)

did you know empty slices in go are different than nil slices? yeah every slice type shares a sentinel value for the data pointer that is used for any slice that has zero capacity but isn't nil. you can see this for yourself by printing the pointer that unsafe.SliceData gives you

also if you think that's a crime you should check out doubleDeferSandwich, or the method i devised to overlay one object's methods with another's (struct types that inherit their methods from multiple anonymous base fields will not have any methods with ambiguous (colliding) names in their interface, which you can exploit to do arbitrary set operations and still satisfy the union of two interfaces without writing a single pass-through method)