4 ms·
I don't see how: temp := someFunc() p := &temp is any less explicit than p := &someFunc() It seems that the `&` is still required to put something ont
by kevincox 6y ago
I don't see how:
temp := someFunc()
p := &temp
is any less explicit than
p := &someFunc()
It seems that the `&` is still required to put something onto the heap.
- zeeboo 6y agoWhat about `&m[x]` where m is some map? Does that heap allocate and create a copy, or is it a pointer to the actual storage slot? If the former, that's a hidden copy/allocation that didn't exist before, and if it's the latter, resizing the map invalidates the pointer, so it must be updated somehow.
- kevincox 6y ago`&` will "move" something to the heap if it isn't already on the heap. The simpler way to think about it is that in Golang everything is on the heap. However the optimizer will move things to the stack if they don't have their address taken. I think the point about explicitness is that if you don't use `&` then it will be able to be put on the stack. So `&` doesn't cause a heap allocation but lack of `&` (or new()) confirms that there isn't one. (I don't actually know if that is true but I can't think of any counterexamples)
- zeeboo 6y agoI think I didn't communicate my point clearly. Consider this hypothetical program: x := make(map[int]int) x[0] = 5 y := &x[0] *y = 10 print(x[0]) // 5 or 10? x[0] = 6 print(*y) // 6 or 10? // force the map to grow and reallocate the buckets for j := 1; j < 100; j++ { x[j] = j } *y = 11 print(x[0]) // 5, 6, 10, or 11? The crux of the problem is answering what y actually points at: the value in the map bucket, or some freshly allocated value? There are problems with whichever one you pick. edit: changed the second print to *y instead of x[0]. thanks masklinn for catching this error.
- masklinn 6y ago> print(x[0]) // 5, 6 or 10? Do you mean `print(*y)`? You just assigned to `x[0]` so its value should not be in question. Also > if it's the latter, resizing the map invalidates the pointer, so it must be updated somehow. It doesn't (have to) invalidate the pointer though. When resized the map's content get copied to a new backing buffer, the pointer can keep pointing to the old buffer. That's basically the same behaviour as slices: when a slice resizes, a new backing array is allocated, the contents get copied to the new array, and the slice is retargeted to the new array. There can be other slices pointing to the old array (it's of course a very bad idea to update slices to shared arrays, but Go will let you do it).
- zeeboo 6y ago> It doesn't (have to) invalidate the pointer though. When resized the map's content get copied to a new backing buffer, the pointer can keep pointing to the old buffer. That's true, but I don't think it's very comparable to slices. With slices, you have to explicitly reallocate either by creating a whole new slice or using append. Reslicing, indexing, or other operations do not reallocate. On the other hand, maps may end up resizing on any operation that involves them, or even theoretically in the background without any operations (during GC, for example). It would be unfortunate to lose that implementation flexibility, and keeping it means that you're essentially picking the "make a copy" option.
- masklinn 6y ago> That's true, but I don't think it's very comparable to slices It's exactly the same. > With slices, you have to explicitly reallocate either by creating a whole new slice or using append. That's a distinction without a difference. `append` does not "explicitly reallocate", it may or may not reallocate, you've no idea. Even if the backing array is full, it might be realloc'd in-place. > On the other hand, maps may end up resizing on any operation that involves them, or even theoretically in the background without any operations (during GC, for example). So? Also technically nothing prevents a GC from reallocating the slice. > It would be unfortunate to lose that implementation flexibility, and keeping it means that you're essentially picking the "make a copy" option. I've never heard of a hashmap implementation which would do otherwise. Trying to extend in-place and attempting to properly redistribute if that works sounds like absolute hell. Likewise trying to shrink in-place, though at least you've got some scratch space which you don't have in the other case: you'd have to segregate everything into one half of the map then insert them in the other half, before shrinking your allocation, which might give you a new allocation anyway, at which point you've moved all your values thrice whereas just creating a new allocation and reinserting your stuff there is a single move.
- masklinn 6y ago> So `&` doesn't cause a heap allocation but lack of `&` (or new()) confirms that there isn't one. (I don't actually know if that is true but I can't think of any counterexamples) I think assigning to a pointer would cause an escape. Just taking a reference wouldn't though, the reference still has to escape (of course you'd usually take a reference so that it can escape but that's not always the case, especially with inlining).
- kevincox 6y agoWhat do you mean by assigning to a pointer? You can only assign a pointer value to a pointer variable and you need to get that pointer from & IIUC.
- iovec 6y agoSince it doesn't seem like this was answered in the other discussion, the answer is that Go does not allow taking the address of a map value. You get a compile-time error: "cannot take the address of m[x]". https://play.golang.org/p/rX8A6ez9fVx https://play.golang.org/p/rX8A6ez9fVx
- zeeboo 6y agoIndeed. This is in a thread where the original comment was "I think the best approach would be making & work in basically any scenario." I'm trying to demonstrate the complications of making it work on map accesses.
- deleted 6y ago[deleted]