4 ms·
In Go, you can take the address of a local variable and return it. It will be allocated on the heap automatically. This is weird for C++ programmers that think
by uluyol 3y ago
In Go, you can take the address of a local variable and return it. It will be allocated on the heap automatically.
This is weird for C++ programmers that think about the stack vs heap, but perfectly natural for Go. In Go, stack allocation for variables is a compiler optimization, not part of the language semantics. Go also only uses scope for visibility, not RAII.
- woodruffw 3y agoI think the weirdness there is not with returning a local variable (C++ allows you to do that too, via RVO and NRVO), but the idea that you'd return a local variable by address. That's fundamentally very weird from any systems background, and suggests a language design error: if people are returning pointers to "locals" because returning by value does an implicit copy, then maybe it shouldn't have been specified to do that.
- uluyol 3y agoI don't agree and my job requires that I program in C++. It's different, yes, but it's only strange if you learn about C or C++ first. Whether you agree or not, I definitely think that promoting loop variables to be heap allocated is consistent with the rest of Go.
- woodruffw 3y agoI don't disagree that it's consistent! Consistency and weirdness are separate dimensions; it's possible to be consistently weird, and IMO Go's pattern of returning local-looking-but-actually-heap addresses is pretty weird, especially when you consider that they could have made return-by-value a transparently optimized move instead.
- xyzzy_plugh 3y agoIt's only weird because you are expecting a local variable to not be on the heap. If local variables were always on the heap, you wouldn't notice. That's precisely what is happening here.
- amscanne 3y agoThere is such thing as "local-looking" in Go. That's the point -- the compiler and runtime make the decisions as needed and the programmer doesn't care. (Obviously there are some consequences to this, wherein you may find yourself with heap-based allocation in hot paths that you didn't expect. But in terms of productivity, I think that not caring and having no distinction certainly reduces one thing that the programmer has to think about.) I think there would be analogous design decisions that remove various low-level concepts in many languages. For example, Python ints are arbitrary precision, which removes a whole set of concerns from the programmer. Rather than being "weird", I think this is just optimizing for a particular kind of programmer productivity.
- tsimionescu 3y agoYou are coming from a C++ background too much. In the semantics of Go (and many other GC-based languages) there is no such thing as a local object. All objects in general are allocated in the same place, and you can always safely share a reference to an object (from a use-after-free perspective, not parallelism etc). So there is nothing weird, at all, about returning the address of an object you created locally in a function. That the runtime can allocate objects on the stack instead is an optimization, not a part of the language semantics. Letting the reference escape the enclosing function does not "automatically promote the object to the heap", it simply prevents the optimization above. Also, none of this has anything to do with the problem of taking the address of loop variables. The confusing thing there is not the lifetime of the loop variable per se. It is the fact that the range-based loop variable is mutated to contain a copy of the next element in the range at the next iteration. It doesn't work like the C++ range-based loop, where the range_declaration is considered part of the loop body, for example.
- amluto 3y ago> All objects in general are allocated in the same place, and you can always safely share a reference to an object (from a use-after-free perspective, not parallelism etc). Every other language I can think of that does this spells it in a materially different way, kind of like this (intentionally not matching any particular language): x = func(); y = x; Now x and y are references to the same thing, each one is sufficient to keep the referent alive, and they are on equal footing. This includes every GC language I’m familiar with except Go. It also includes C++ when using any sort of pointer (including smart pointers) and even plain references (with the obvious caveats about returning a reference to a local variable being nonsense in C++). It includes Swift with ARC. And it includes functional languages, pure and otherwise. If you return a (correct) reference to an object that would otherwise be local, you are returning the same type as the original local variable. I think go is unusual in that it looks more like this: x = func(); y = &x: Here x has type int (or whatever). y has type int. Yet x keeps that integer alive, and so does y. x and y refer to the same thing. &x and &y are not equivalent. So I think woodruffw is reasonable to find returning the address of a local variable weird coming from pretty much any background.