Why golang slice is empty after initialization? - go

My question here is why the slice is empty in another func when the slice is global to the file?
Here's a piece of code:
package main
import "fmt"
type Vec3 struct {
x float32
y float32
z float32
}
var a []Vec3
func main() {
a := make([]Vec3, 0)
a = append(a, Vec3{2.0, 3.0, 4.0})
a = append(a, Vec3{3.4, 5.6, 5.4})
a = append(a, Vec3{6.7, 4.5, 7.8})
fmt.Printf("%+v\n", a)
doSomethingWithA();
}
func doSomethingWithA() {
fmt.Printf("%+v\n", a)
}
Output:
[{x:2 y:3 z:4} {x:3.4 y:5.6 z:5.4} {x:6.7 y:4.5 z:7.8}]
[]
This is a repl.it link too, if you want to take a look.
Thanks for your kind help.

You have redefined it here:
a := make([]Vec3, 0)
To use the same variable you should assign a value with = but not declare a new variable with :=
a = make([]Vec3, 0)
Short variable declarations
Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type.

You are re declaring a, so actually you not initializing the global var, try:
a = make([]Vec3, 0)

Related

How to add new elements of a slice automatically to function parameter as slice grows

Is there a way to doing this automatically ?
package main
import "fmt"
func main() {
var a []string
a = append(a, "this", "this2", "this3")
increaseArguments(a)
a = append(a, "this4")
increaseArguments(a)
}
func increaseArguments(b []string) {
// I want, when i add new element to slice i want this function act as this
// fmt.Println(b[0],b[1], b[2], b[3])
fmt.Println(b[0], b[1], b[2])
}
Instead of adding b[3] as argument to fmt.Println is there a way to add it automatically ?
Note that if b would be of type []any, you could pass it as the value of the variadic parameter of fmt.Println():
fmt.Println(b...)
But since b is of type []string, you can't.
But if you transform b into a []any slice, you can. You can use this helper function to do it:
func convert[T any](x []T) []any {
r := make([]any, len(x))
for i, v := range x {
r[i] = v
}
return r
}
And then:
func increaseArguments(b []string) {
fmt.Println(convert(b)...)
}
This will output (try it on the Go Playground):
this this2 this3
this this2 this3 this4
Note: creating a new slice in convert() will not make this solution any slower, because passing values explicitly (like fmt.Println(b[0], b[1], b[2])) also implicitly creates a slice.
See related question: How to pass multiple return values to a variadic function?

Pointing map to a new map inside a method looses effect after leaving method

This code, visible on the Go PlayGround:
package main
import "fmt"
type MyType map[int]int
func (x MyType) updateIt() {
newItem := make(MyType)
for i := 0; i < 3; i++ {
newItem[i] = i
}
x = newItem
fmt.Println(x)
}
func main() {
x := make(MyType)
x.updateIt()
fmt.Println(x)
}
Produces:
map[0:0 1:1 2:2]
map[]
While I expect:
map[0:0 1:1 2:2]
map[0:0 1:1 2:2]
Why?
My understanding is that: x.updateIt() takes x as an argument, then creates newItem and changes the pointer of x so that it points to newItem. So x points to the data of newItem. This seems to be indeed the case inside updateIt, as visible from the first print line. But when x is print after the method, the change is lost.
I'm new to Go and this is unexpected behaviour for me, could you please explain?
You should be using a pointer receiver:
func (x *MyType) updateIt() {
newItem := make(MyType)
for i := 0; i < 3; i++ {
newItem[i] = i
}
*x = newItem
fmt.Println(x)
}
Playground: https://play.golang.org/p/K82TTjHdDgg
Explanation
Arguments "in front" of a function (so-called receivers) behave more or less like ordinary arguments.
Imagine a function like this:
func updateIt(x MyType) {
newItem := make(MyType)
for i := 0; i < 3; i++ {
newItem[i] = i
}
x = newItem
fmt.Println(x)
}
Here, you pass x by value, not by reference (or as a pointer).
What happens is that x gets replaced inside the function, but keeps its original value outside of it. By passing a pointer you can replace the value to which x is pointing, thereby changing the value of x outside of the function.
See also "A Tour of Go, Pointers": https://tour.golang.org/moretypes/1
Inside the main function you create a new map and assign the pointer of that new map to x.
Then the value of that pointer is passed to the updateIt function.
Then inside updateIt you replace it with pointer to another map you create inside updateIt. Then it updates this new map instead of updating the original map created inside main
The most important thing to understand here is that your program creates two different maps.
So avoid creating two maps and update the same map passed to your updateIt function.
func (x MyType) updateIt() {
for i := 0; i < 3; i++ {
x[i] = i
}
fmt.Println(x)
}
https://play.golang.org/p/-MemfTv1uJV

How to declare variable types for loop variables in Go?

See this code.
package main
import (
"fmt"
)
func main() {
var arr [4]string = [4]string{"foo", "bar", "baz", "qux"}
for a, b := range arr {
fmt.Println(a, b)
}
// How can I fix this code?
/*
for x int, y string = range arr {
fmt.Println(a, b)
}
*/
}
The first for loop uses the := operator to automatically deduce the types of a and b. But what if I want to explicitly specify the types of the loop variables? My attempt to do this is in the second block of commented code which of course failed with the following error.
# command-line-arguments
./foo.go:15: syntax error: unexpected name, expecting {
./foo.go:18: syntax error: unexpected }
Can you help me fix the second block of code such that I can specify the types of x and y explicitly?
Unfortunately the language specification doesn't allow you to declare the variable type in the for loop. The closest you could get is this:
var a int
var b string
for a, b = range arr {
fmt.Println(a, b)
}
But normally if you give your variable meaningful names, their type would be clear as well:
for index, element := range arr {
fmt.Println(index, element)
}
You need to declare first the vars.
var x int
var y string ...// now it should be ok.
for x,y = range arr {
fmt.Println(x, y) // it should be x and y instead of a,b
}
Check the fiddle
First of all your code is not a valid Go code. The for range loop returns the index and the value of an array, slice, string, or map, so there is no reason the explicitly specify the type of the value and the index.
You are specifying the type of the values at the variable initialization, and the language will deduce the type on the range iteration.
One special case is when you are using interface{} as variable type. In this case, you if you need to know the type of the value you can use the reflect package to deduce the type of the value.
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
s := reflect.ValueOf(t)
for i := 0; i < s.Len(); i++ {
fmt.Println(s.Index(i))
}
}
It's not possible as you are trying to declare two different types of data in same line, if you want explicitly declare variables, then you need to declare them before itself like above answers, but if you want them to be of other type then you need to covert them as for your needs,
package main
import (
"fmt"
)
func main() {
var arr = [4]string{"foo", "bar", "baz", "qux"}
var x int64
var b []byte
for x = 0; x < int64(len(arr)); x++ {
b = []byte(arr[x])
fmt.Println(x, b)
}
}

Modify array of interface{} golang

This type assertion, def-referencing has been driving me crazy. So I have a nested structure of Key string / Value interface{} pairs. Stored in the Value is an []interface which I want to modify each of the values. Below is an example of creating an array of Bar and passing it into the ModifyAndPrint function which should modify the top level structure. The problem that I come accross is as written it doesn't actually modify the contents of z, and I can't do a q := z.([]interface{})[i].(Bar) or & thereof.
Is there a way to do this? If so, what combination did I miss?
package main
import "fmt"
type Bar struct {
Name string
Value int
}
func ModifyAndPrint(z interface{}){
fmt.Printf("z before: %v\n", z)
for i, _ := range(z.([]interface{})) {
q := z.([]interface{})[i]
b := (q).(Bar)
b.Value = 42
fmt.Printf("Changed to: %v\n", b)
}
fmt.Printf("z after: %v\n", z)
}
func main() {
bars := make([]interface{}, 2)
bars[0] = Bar{"a",1}
bars[1] = Bar{"b",2}
ModifyAndPrint(bars)
}
https://play.golang.org/p/vh4QXS51tq
The program is modifying a copy of the value in the interface{}. One way to achieve your goal is to assign the modified value back to the slice:
for i, _ := range(z.([]interface{})) {
q := z.([]interface{})[i]
b := (q).(Bar)
b.Value = 42
z.([]interface{})[i] = b
fmt.Printf("Changed to: %v\n", b)
}
playground example

Shortest way / Shorthand to declare variable in go

We can use the following syntax for go variable declaration
var num int
var str string
but is there any shorthand in go for doing the same thing?
for example we can do so in python simply saying:
num = 13
strings = "Hello World"
or even
num, strings = 13,"Hello World"
The variable declaration can initialize multiple variables:
var x, y float32 = -1, -2
Or (short variable declaration with :=)
i, j := 0, 10
So this would work: play.golang.org
package main
import "fmt"
func main() {
a, b := 1, "e"
fmt.Printf("Hello, playground %v %v", b, a)
}
Output:
Hello, playground e 1
The := syntax is shorthand for declaring and initializing a Go variable .
For example:
to declare e string var we can simple use
str := "Hello world"
Short variable declarations
The := operator is the short variable declaration operator. This operator is used to both declare and initialize a variable.
Example:
package main
import "fmt"
func main() {
firstName := "Joey"
fmt.Println(firstName)
}
The variable type is not vital because the Go compiler is able to derive the type based on the value you have assigned. Since we are assigning a string to firstName, firstName is allocated as a variable type as string.

Resources