Handle "Slice Struct" properly? (golang) - go

I have created a Slice Struct.
But why can't I append or output values?
package main
import "fmt"
type Slicestruct []struct {
num []int
emptynum []int
}
func main() {
slicestruct := &Slicestruct{
{[]int{1, 2, 3}, []int{}},
{[]int{4, 5, 6}, []int{}},
}
// is working:
fmt.Println(slicestruct)
// isn't working:
fmt.Println(slicestruct[0].num[0])
// isn't working:
slicestruct[0].emptynum = append(slicestruct[0].emptynum, 99)
}
The error message is: "invalid operation: slicestruct[0] (type *Slicestruct does not support indexing)"

You need to dereference the pointer before getting an element
(*slicestruct)[0]
Since it's the actual slice you're accessing an element from, not the pointer.
For pointers to arrays (not slices as you have here), this step would be done automatically.
Here's a related question about pointers to slices and arrays: Pointer to slice and array
Alternatively, you can remove the & when declaring your variable to make it not a pointer type. In the short sample we've seen here, there's nothing to necessitate a pointer. In general, legitimate uses of pointers to slices types are rare.

Related

Why can I not use an slice of a custom type as an empty interface slice when using append in Go? [duplicate]

This question already has answers here:
Type converting slices of interfaces
(9 answers)
Cannot convert []string to []interface {}
(7 answers)
Cannot use args (type []string) as type []interface {} [duplicate]
(1 answer)
slice of struct != slice of interface it implements?
(6 answers)
Closed 4 months ago.
I'm slightly confused about the behavior of the unpack/spread operator when trying to append into a slice of empty interfaces (i.e. []interface{}) from an slice of a custom type. I expected it to work since the interface{} can hold any type, but I'm currently receiving errors when trying to do this operation.
The following snippet illustrates the issue:
package main
import (
"fmt"
)
type CustomType struct {
value int
}
func main() {
data := []interface{}{}
values := []CustomType{
{value: 0},
{value: 1},
}
// This does not compile:
// data = append(data, values...)
// But this works fine
data = append(data, values[0])
for _, value := range data {
fmt.Printf("Value: %v\n", value)
}
}
Go playground with the snippet above
I expected to be able to unpack the values slice and append its elements into the data slice since it can hold any values, but the compiler does not like that and complains about the values array not being of []interface{} type. When I append the values one by one, then compiler is OK with the operation.
Why is unpacking the values slice not allowed in this situation?
Obviously, the code data = append(data, values...) could be compiled error cannot use values (variable of type []CustomType) as type []interface{} in argument to append
Per faq Can I convert a []T to an []interface{}?
Not directly. It is disallowed by the language specification because the two types do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
For your question
I expected to be able to unpack the values slice and append its elements into the data slice since it can hold any values, but the compiler does not like that and complains about the values array not being of []interface{} type.
The difference between the element type CustomType and interface
type CustomType represented in memory like value
type interface{} represented in memory
pointer to type CustomType
value
They have different representations in memory.
Besides that, converting a []CustomType to an []interface{} is O(n) time because each value of the slice must be converted to an interface{}. It could be one complex operation.

How []interface{} in Go is implemented?

In Go I can do something like this:
func main() {
var intSlice []interface{}
intSlice = append(intSlice, "hello world")
intSlice = append(intSlice, 1)
for _, v := range intSlice {
fmt.Println(v) // hello world
// 1
}
}
Since a slice is deep down an array, without given a specific type to that array, how can Go know the layout of this array's memory structure? If it's a []string then I know that for every iteration I have to add current address with 4 to get the next item's address, but for an interface{} how can Go knows what to do? I am confused. One possible explain for this is that interface{} is actually a pointer, so []interface{} stores pointers only, the value 1 or "hello world" is stored somewhere outside of the slice. Am I right about this?
An interface is two values: a pointer to the value, and a pointer to the type of the value. So a []interface{} containing all int values is simply an array of interfaces, where each element containing those two values, with each element of the array pointing to the int value, and to its type.

Strange behaviour when passing a struct property (slice) to a function that removes elements from it

I've started learning Go these days and got stuck in trying to pass a struct property's value (a slice) to a function. Apparently it's being passed as a reference (or it holds a pointer to its slice) and changes made inside the function affect it.
Here is my code, in which testFunction is supposed to receive a slice, remove its first 3 elements and print the updated values, but without affecting it externally:
package main
import (
"fmt"
)
type testStruct struct {
testArray []float64
}
var test = testStruct {
testArray: []float64{10,20,30,40,50},
}
func main() {
fmt.Println(test.testArray)
testFunction(test.testArray)
fmt.Println(test.testArray)
}
func testFunction(array []float64) {
for i:=0; i<3; i++ {
array = removeFrom(array, 0)
}
fmt.Println(array)
}
func removeFrom(array []float64, index int) []float64 {
return append(array[:index], array[index+1:]...)
}
That outputs:
[10 20 30 40 50]
[40 50]
[40 50 50 50 50]
My question is: what is causing the third fmt.Println to print this strange result?
Playground: https://play.golang.org/p/G8W3H085In
p.s.: This code is only an example. It's not my goal to remove the first elements of something. I just wanna know what is causing this strange behaviour.
Usually we don't know whether a given call to append will cause a reallocation, so we can't assume that the original slice refers to the same array as the resulting slice, nor that it refers to a different one.
To use slices correctly, it's important to remember that although the elements of the underlying array are indirect, the slice's pointer, length and capacity are not.
As a result, it's usual to assign the result of a call to append to the same slice variable:
array = append(array, ...)
So to sum up, to receive the desired result always remember to assign the append function to a new or the same slice variable.
Here is the corrected and working code:
package main
import (
"fmt"
)
type testStruct struct {
testArray []float64
}
var test = testStruct {
testArray: []float64{10,20,30,40,50},
}
func main() {
fmt.Println(test.testArray)
a := testFunction(test.testArray)
fmt.Println(a)
}
func testFunction(array []float64)[]float64 {
for i:=0; i<3; i++ {
array = removeFrom(array, 0)
}
fmt.Println(array)
return array
}
func removeFrom(array []float64, index int) []float64 {
return append(array[:index], array[index+1:]...)
}
Check it the working code on Go Playground.
Another solution is to pass the array argument via pointer reference:
func testFunction(array *[]float64) {
for i:=0; i<3; i++ {
*array = removeFrom(*array, 0)
}
fmt.Println(*array)
}
Go Playground
The slice is a composite type. It has a pointer to the data, the length and the capacity. When you pass it as an argument you're passing those values, the pointer, the length and the capacity; they are copies, always.
In your case you modify the data within the slice when you call removeFrom(), which you can do because you've copied the value of a pointer to the original data into the func, but the length and capacity remain unchanged outside the scope of that function as those are not pointers.
So, when you print it again from main() you see the altered values but it still uses the original length and capacity as any changes made to those within the scope of the other funcs were actually on copies of those values.
Here is a useful blog post about slices https://blog.golang.org/slices. It states this in particular.
It's important to understand that even though a slice contains a
pointer, it is itself a value. Under the covers, it is a struct value
holding a pointer and a length. It is not a pointer to a struct.
The reason you see [40 50 50 50 50] is because you changed the values in the slice, but you did not alter the slice itself(it's cap and len)

What's happening with these pointers?

I wrote some odd code, but I'm not sure why it works and what I can learn from it. I have a slice type build from another struct. I made a function on the slice type to modify itself. To do this, I seem to have to throw around *'s a little much.
I'm trying to learn about pointers in Go and would like a little help. Here's an example (http://play.golang.org/p/roU3MEeT3q):
var ClientNames = []string {"Client A", "Client B", "ClientC"}
type InvoiceSummaries []InvoiceSummary
type InvoiceSummary struct {
Client string
Amt int
}
func (summaries *InvoiceSummaries) BuildFromAbove() {
for _, name := range ClientNames {
*summaries = append(*summaries, InvoiceSummary{name, 100})
}
}
My question is: What is the purpose for each of these * and why am I not using any &?
What is the purpose for each of these * ?
By making the method receiver as pointer, you could easily change the property of the object. I think that's one of the benefit. This example below will prove it.
package main
import "fmt"
type someStruct struct {
someVar int
}
func (s someStruct) changeVal1(newVal int) {
s.someVar = newVal
}
func (s *someStruct) changeVal2(newVal int) {
s.someVar = newVal
}
func main() {
s := someStruct{0}
fmt.Println(s) // {0}
s.changeVal1(3)
fmt.Println(s) // {0}
s.changeVal2(4)
fmt.Println(s) // {4}
(&s).changeVal2(5)
fmt.Println(s) // {5}
}
and why am I not using any &?
Pointer method receiver is quite special, it can also be called from non-pointer struct object. Both of s.changeVal2(4) and (&s).changeVal2(5) are valid & will affect the value of someVar.
Example http://play.golang.org/p/sxCnCD2D6d
You have to use a pointer for the receiver - (summaries *InvoiceSummaries) - because otherwise the argument is passed by value, having a pointer means you pass a reference to the value instead. If not for that, then you couldn't modify the collection at all.
Inside of the methods body you have use * because it is the dereferncing operator and returns the value at the address. Ampersand (&) is the opposite, it gives the address of a value.
Nothing wrong with your code but normally addresses to slices aren't used. A slice is a small struct that gophers are normally happy to pass by value. If a method or function is creating a new slice, the gopher is happy to return the new slice, by value again, as the return value.
Of course passing a slice by value doesn't guarantee anything about the backing store remaining unchanged when the method/function returns. So it can't be used as a way of guaranteeing the data elements of the slice haven't mutated.

Slice storage reference in Go

In the Go library source you often see that a slice is passed by creating a new slice storage reference like so
method(s[:])
What's the benefit of this, compared to just passing the original slice?
method(s)
The s[:] construct is normally used only to create a new slice referencing an existing array, not for "passing the original slice".
If s[:] is really used somewhere in the stdlib and s is a slice than it could be e.g. a refactoring leftover. Please report such place if known to you on the Go issue tracker.
The only case where you would see code like this is when s is an array, and you want to pass as a parameter to a function that takes a slice as its input. Take the following code.
package main
func main() {
x := [...]int{1, 2, 3, 4, 5}
someFunction(x) // type mismatch error : expecting [] int, passed [5] int
someFunction(x[:])// no error
}
func someFunction(input []int){
// use input
}
The thing to note here is that [] int and [5] int are entirely different types.

Resources