Append a slice from a map value does not affect the map - go

mp := map[int][]int{}
slice := make([]int, 0, 1)
fmt.Printf("slice address:%p\n", slice)
mp[0] = slice
slice = append(slice, 1)
fmt.Println("after append")
fmt.Printf("slice address:%p\n", slice)
fmt.Println("slice:", slice)
fmt.Println("mp[0]:", mp[0])
fmt.Printf("mp[0] address:%p\n", mp[0])
output:
slice address:0xc042008f78
after append
slice address:0xc042008f78
slice: [1]
mp[0]: []
mp[0] address:0xc042008f78
The address of the slice does not change as its cap is large enough during append. So why the map value does not take effect?

As explained in Go Slices: usage and internals, two slices may point to the same memory location, but may have different len and cap attributes.

In Golang it is mentioned in blog on Go Slices: usage and internals
Slicing does not copy the slice's data. It creates a new slice value
that points to the original array. This makes slice operations as
efficient as manipulating array indices. Therefore, modifying the
elements (not the slice itself) of a re-slice modifies the elements of
the original slice:
slice = append(slice, 1)
So in the above case it is creating a new slice with pointing to the same original underlying array. That is the reason it is showing the same address.
To get the data of underlying array pointed by slice use reflect with unsafe:
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
data := *(*[1]int)(unsafe.Pointer(hdr.Data))
Working code on Playground

This is caused by the fact, that multiple slices can be backed by the same data but use different "sections" of the data. This means, that yes, an element is added to the data backing mp[0], but the length of the slice in mp is not changed. You can do that manually:
fmt.Println(mp[0][:1])
which does print [1].
You can grow any slice to it's capacity without changing the underlying data by using slice[:cap(slice)]. slice[:n] will panic if cap(slice) < n.
slice[n] on the other hand will panic when len(slice) <= n. I assume that the former is possible to allow the growing of slices without changing the underlying data (as far as that is possible). The latter, I would say, is "normal" behavior.
This also explains why mp[0][:2] panics, as cap(mp[0]) is 1.
For more details you might want to read this official blog post, as suggested by Flimzy.

Related

What happens when I range over an uninitialized pointer to array in golang

I have this code
var j *[33]byte
for i := range j {
fmt.Println(j[i])
}
Now when I run this code I get nil pointer dereference error when I try access values in j. I'm not sure why I was even able to enter the loop in the first place considering my pointer is uninitialized.
I know an uninitialized array has all its values set to their zero value. That is
var a [5]int
Will have a default value of [0, 0, 0, 0, 0].
But I don't understand what golang does when you don't initialize a pointer to an array. Why is range able to range over it even though its nil?
From the Go spec Range Clause:
... For an array, pointer to array, or slice value a, the index
iteration values are produced in increasing order...
so as a convenience the Go language is dereferencing the pointer with the intent to iterating over its elements. The fact that the pointer is nil is a simple programming error. If this can occur, one should have a runtime check in place to guard against it.
Static analysis may be able to detect this type of bug ahead of time - but what if the variable j is accessible from another goroutine - how would the compiler know for sure that another goroutine may update it to a non-nil value right before the range loop is reached?
Go has a zero value defined for each type when you initialize a variable with var keyword (this may change when using :=, ideally used when need copies of values or specific values). In the case of the pointer the zero value is nil (also maps, interfaces, channels, slices, and functions) in case of array of type int the zero value is 0.
So, to answer your question, Go is able to iterate because you have 33 valid spaces idependently of what value is inside of that position. You can check the diference between slices and arrays on the Golang documentation to have more insights on why is that.

Confused about why hashsum, encode, print is different then write, hashum, encode, print in Go?

Sorry about the title. I couldn't come up with a better way of phrasing my question and will change it happily if somebody else can.
Hasher is defined with
hasher := md5.New()
Anyway, I am curious why this:
fmt.Println(hex.EncodeToString(hasher.Sum([]byte(input))))
gives me 6869d41d8cd98f00b204e9800998ecf8427e, while this:
hasher.Write([]byte(input))
fmt.Println(hex.EncodeToString(hasher.Sum(nil))
gives me 49f68a5c8493ec2c0bf489821c21fc3b
and this:
fmt.Printf("%x\n", md5.Sum([]byte(input)))
gives me 49f68a5c8493ec2c0bf489821c21fc3b.
Generally hasher.Sum() does not hash the passed slice. The passed slice is used as the destination: it appends the current hash to it and does not change the underlying hash state. hasher.Write() obviously includes the passed slice in the hash calculation. The 2 examples are fundamentally different, the different results are nothing but expected.
Always read the documentation. hash.Hash.Sum():
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
Sum(b []byte) []byte
So when you first call hasher.Sum(), whatever you pass to it, it doesn't matter in terms of the result hash. If you haven't written anything into hasher previously, you'll see the initial hash.
When you next call hasher.Write([]byte(input)), you'll write the bytes of input into the hasher, so when you call hasher.Sum(nil) next, you'll see the calculated hash of input. Since you pass nil, a new slice will be allocated to accommodate the result.
When you again call hasher.Write([]byte(input)), as previously written: this won't change the hash state, the passed slice is not used as input, but only as the destination for "returning" the result, the current hash value. So you will get the same hash value as you got from the previous hasher.Sum(nil) call. Obviously, if the passed slice does not have enough capacity to store the result, a new one will be allocated / used.
See this complete, runnable example which reproduces your output:
input := "hi"
hasher := md5.New()
fmt.Println(hex.EncodeToString(hasher.Sum([]byte(input))))
hasher.Write([]byte(input))
fmt.Println(hex.EncodeToString(hasher.Sum(nil)))
fmt.Printf("%x\n", md5.Sum([]byte(input)))
Try it on the Go Playground.

Go error: non-constant array bound

I'm trying to calculate the necessary length for an array in a merge sort implementation I'm writing in go. It looks like this:
func merge(array []int, start, middle, end int) {
leftLength := middle - start + 1
rightLength := end - middle
var left [leftLength]int
var right [rightLength]int
//...
}
I then get this complaint when running go test:
./mergesort.go:6: non-constant array bound leftLength
./mergesort.go:7: non-constant array bound rightLength
I assume go does not enjoy users instantiating an Array's length with a calculated value. It only accepts constants. Should I just give up and use a slice instead? I expect a slice is a dynamic array meaning it's either a linked list or copies into a larger array when it gets full.
You can't instantiate an array like that with a value calculated at runtime. Instead use make to initialize a slice with the desired length. It would look like this;
left := make([]int, leftLength)

Why don't Go slices just switch the underlying array on reallocation?

A slice contains three components: length, capacity and a pointer to the underlying array.
When we try to append to a slice that is full (len(s) == cap(s)), a larger array will be allocated.
I read in a book that we have to assign the return value of append back to the slice, because a different slice may be returned due to reallocation of the underlying array.
runes = append(runes, r)
But I don't know why this is necessary. Can't we just reallocate a new array and update the pointer of the original slice instance?
All function arguments are passed by value in Go. A function cannot change the caller's value.
The slice (length, capacity, pointer) is passed by value to the append function. Because append cannot change the caller's slice, the append function returns the new slice.
The append function could be written to take a pointer to a slice, but that would make append awkward to use in the many situations where slice values are not addressable.

How to remove the last element from a slice?

I've seen people say just create a new slice by appending the old one
*slc = append(*slc[:item], *slc[item+1:]...)
but what if you want to remove the last element in the slice?
If you try to replace i (the last element) with i+1, it returns an out of bounds error since there is no i+1.
You can use len() to find the length and re-slice using the index before the last element:
if len(slice) > 0 {
slice = slice[:len(slice)-1]
}
Click here to see it in the playground
TL;DR:
myslice = myslice[:len(myslice) - 1]
This will fail if myslice is zero sized.
Longer answer:
Slices are data structures that point to an underlying array and operations like slicing a slice use the same underlying array.
That means that if you slice a slice, the new slice will still be pointing to the same data as the original slice.
By doing the above, the last element will still be in the array, but you won't be able to reference it anymore.
If you reslice the slice to its original length you'll be able to reference the last object
If you have a really big slice and you want to also prune the underlying array to save memory, you probably wanna use "copy" to create a new slice with a smaller underlying array and let the old big slice get garbage collected.

Resources