What is the meaning of '*' and '&'? - go

I am doing the http://tour.golang.org/. Could anyone explain this function to me lines 1,3,5 and 7, especially what '*' and '&' do? By mentioning them in a function declaration, what are they supposed/expected to do? A toy example:
1: func intial1(var1 int, var2 int, func1.newfunc[]) *callproperfunction {
2:
3: addition:= make ([] add1, var1)
4: for i:=1;i<var2;i++ {
5: var2 [i] = *addtother (randomstring(lengthofcurrent))
6: }
7: return &callproperfunction {var1 int, var2 int, func1.newfunc[], jackpot}
8: }
It seems that they are pointers like what we have in C++. But I cannot connect those concepts to what we have here. In other words, what '*' an '&' do when I use them in function declaration in Go.
I know what reference and dereference mean. I cannot understand how we can use a pointer to a function in Go? For example lines 1 and 7, what do these two lines do? The function named intial1 is declared that returns a pointer? And in line 7, we call it with arguments using the return function.

This is possibly one of the most confusing things in Go. There are basically 3 cases you need to understand:
The & Operator
& goes in front of a variable when you want to get that variable's memory address.
The * Operator
* goes in front of a variable that holds a memory address and resolves it (it is therefore the counterpart to the & operator). It goes and gets the thing that the pointer was pointing at, e.g. *myString.
myString := "Hi"
fmt.Println(*&myString) // prints "Hi"
or more usefully, something like
myStructPointer = &myStruct
// ...
(*myStructPointer).someAttribute = "New Value"
* in front of a Type
When * is put in front of a type, e.g. *string, it becomes part of the type declaration, so you can say "this variable holds a pointer to a string". For example:
var str_pointer *string
So the confusing thing is that the * really gets used for 2 separate (albeit related) things. The star can be an operator or part of a type.

Your question doesn't match very well the example given but I'll try to be straightforward.
Let's suppose we have a variable named a which holds the integer 5 and another variable named p which is going to be a pointer.
This is where the * and & come into the game.
Printing variables with them can generate different output, so it all depends on the situation and how well you use.
The use of * and & can save you lines of code (that doesn't really matter in small projects) and make your code more beautiful/readable.
& returns the memory address of the following variable.
* returns the value of the following variable (which should hold the memory address of a variable, unless you want to get weird output and possibly problems because you're accessing your computer's RAM)
var a = 5
var p = &a // p holds variable a's memory address
fmt.Printf("Address of var a: %p\n", p)
fmt.Printf("Value of var a: %v\n", *p)
// Let's change a value (using the initial variable or the pointer)
*p = 3 // using pointer
a = 3 // using initial var
fmt.Printf("Address of var a: %p\n", p)
fmt.Printf("Value of var a: %v\n", *p)
All in all, when using * and & in remember that * is for setting the value of the variable you're pointing to and & is the address of the variable you're pointing to/want to point to.
Hope this answer helps.

Those are pointers like we have in C++.
The differences are:
Instead of -> to call a method on a pointer, you always use ., i.e. pointer.method().
There are no dangling pointers. It is perfectly valid to return a pointer to a local variable. Golang will ensure the lifetime of the object and garbage-collect it when it's no longer needed.
Pointers can be created with new() or by creating a object object{} and taking the address of it with &.
Golang does not allow pointer-arithmetic (arrays do not decay to pointers) and insecure casting. All downcasts will be checked using the runtime-type of the variable and either panic or return false as second return-value when the instance is of the wrong type, depending on whether you actually take the second return type or not.

This is by far the easiest way to understand all the three cases as explained in the #Everett answer
func zero(x int) {
x = 0
}
func main() {
x := 5
zero(x)
fmt.Println(x) // x is still 5
}
If you need a variable to be changed inside a function then pass the memory address as a parmeter and use the pointer of this memory address to change the variable permanently.
Observe the use of * in front of int in the example. Here it just represents the variable that is passed as a parameter is the address of type int.
func zero(xPtr *int) {
*xPtr = 0
}
func main() {
x := 5
zero(&x)
fmt.Println(x) // x is 0
}

Simple explanation.. its just like, you want to mutate the original value
func zero(num *int){ // add * to datatype
*num = 0 // can mutate the original number
}
i := 5
zero(&i) // passing variable with & will allows other function to mutate the current value of variable```

& Operator gets the memory address where as * Opeartor holds the memory address of particular variable.

Related

Object instantiation by reference

Here is a sample of Go code which I do not really understand:
type MyClass struct {
field1 string
field2 string
}
...
objectinstance1 := MyClass{field1:"Hello", field2:"World"}
objectinstance2 := &MyClass{field1:"Hello", field2:"World"}
I can do exactly the same thing with objectinstance1 and objectinstance2 (method call for example) with the same syntax.
So I do not understand the role of the & operator. What I understand is that objectinstance1 contains an object whereas objectinstance2 contains a pointer.
It is for me the same thing in C than the difference between char and char*.
But in this case I should use -> instead of . (dot)?
The & operator gives you a pointer to a struct, while not using it gives you the struct value.
The biggest place this is relevant is when you pass this struct over to another function - if you pass the pointer that you made using the & operator, the other function has access to the same struct. If that function changes it, you've got the changed struct as well.
If you pass the variable that you made without the & operator, the function that you pass it to has a copy of the struct. There is nothing that that function or any other function can possibly do to change what you see in your variable.
This effectively makes the value variable safe for use across multiple go routines with no race conditions - everyone has their own copy of the struct.
If you pass the pointer made with & to other go routines, all have access to the same struct, so you really want that to be intentional and considered.
Difference is not visible because it’s hidden in 2 things:
Operator := which assigns value and type for a variable simultaneously. So it looks like objectinstance1 and objectinstance2 are the same. But in fact first is a MyClass instance and second is a pointer to it. It will be more palpable if use long-form operator:
var objectinstace1 MyClass = MyClass{}
var objectinstance2 *MyClass = &MyClass{}
If you omit * or & variable and type become incompatible and assignment fails.
Implicit pointer indirection. Go does it automatically in statements like ptr1.Field1 to access a specific field in struct by pointer to it.
Only on rare cases when there’s ambiguity you have to use full form:
*ptr1.Value1
or sometimes even:
(*ptr1).Value1
UPDATE:
Explicit pointer usage for disambiguation:
type M map[int]int
func (m *M) Add1() {
// this doesn't work - invalid operation: m[1] (type *M does not support indexing)
// m[1] = 1
// the same - invalid operation: m[1] (type *M does not support indexing)
// *m[1] = 1
// this works
(*m)[1] = 1
}
https://play.golang.org/p/JcXd_oNIAw
But in this case i should use -> instead of . (dot) ?
No. Golang is not C is not Golang. In Golang there is no ->. You use dot (.) for pointers aswell. Golang is meant to be simple, there is no point in introducing another operator if the intention is clear (what else would . on a pointer mean than calling a method?)
In go, the & operator takes the address of its argument and returns a pointer to the type of the argument.
Pointer indirection happens automatically so there is no -> operator, the dot operator handles all field (member) operations and accesses for all types, whether a pointer or a struct.
package main
import (
"fmt"
"reflect"
)
func main() {
type Foo struct{ name string }
foo1 := Foo{"Alpha"} // A plain struct instance.
foo2 := &Foo{"Bravo"} // A pointer to a struct instance.
fmt.Printf("foo1=%v, name=%v\n", reflect.TypeOf(foo1), foo1.name)
fmt.Printf("foo2=%v, name=%v\n", reflect.TypeOf(foo2), foo2.name)
// foo1=main.Foo, name=Alpha
// foo2=*main.Foo, name=Bravo
}

Can I Use the Address of a returned value? [duplicate]

What's the cleanest way to handle a case such as this:
func a() string {
/* doesn't matter */
}
b *string = &a()
This generates the error:
cannot take the address of a()
My understanding is that Go automatically promotes a local variable to the heap if its address is taken. Here it's clear that the address of the return value is to be taken. What's an idiomatic way to handle this?
The address operator returns a pointer to something having a "home", e.g. a variable. The value of the expression in your code is "homeless". if you really need a *string, you'll have to do it in 2 steps:
tmp := a(); b := &tmp
Note that while there are completely valid use cases for *string, many times it's a mistake to use them. In Go string is a value type, but a cheap one to pass around (a pointer and an int). String's value is immutable, changing a *string changes where the "home" points to, not the string value, so in most cases *string is not needed at all.
See the relevant section of the Go language spec. & can only be used on:
Something that is addressable: variable, pointer indirection, slice indexing operation, field selector of an addressable struct, array indexing operation of an addressable array; OR
A composite literal
What you have is neither of those, so it doesn't work.
I'm not even sure what it would mean even if you could do it. Taking the address of the result of a function call? Usually, you pass a pointer of something to someone because you want them to be able to assign to the thing pointed to, and see the changes in the original variable. But the result of a function call is temporary; nobody else "sees" it unless you assign it to something first.
If the purpose of creating the pointer is to create something with a dynamic lifetime, similar to new() or taking the address of a composite literal, then you can assign the result of the function call to a variable and take the address of that.
In the end you are proposing that Go should allow you to take the address of any expression, for example:
i,j := 1,2
var p *int = &(i+j)
println(*p)
The current Go compiler prints the error: cannot take the address of i + j
In my opinion, allowing the programmer to take the address of any expression:
Doesn't seem to be very useful (that is: it seems to have very small probability of occurrence in actual Go programs).
It would complicate the compiler and the language spec.
It seems counterproductive to complicate the compiler and the spec for little gain.
I recently was tied up in knots about something similar.
First talking about strings in your example is a distraction, use a struct instead, re-writing it to something like:
func a() MyStruct {
/* doesn't matter */
}
var b *MyStruct = &a()
This won't compile because you can't take the address of a(). So do this:
func a() MyStruct {
/* doesn't matter */
}
tmpA := a()
var b *MyStruct = &tmpA
This will compile, but you've returned a MyStruct on the stack, allocated sufficient space on the heap to store a MyStruct, then copied the contents from the stack to the heap. If you want to avoid this, then write it like this:
func a2() *MyStruct {
/* doesn't matter as long as MyStruct is created on the heap (e.g. use 'new') */
}
var a *MyStruct = a2()
Copying is normally inexpensive, but those structs might be big. Even worse when you want to modify the struct and have it 'stick' you can't be copying then modifying the copies.
Anyway, it gets all the more fun when you're using a return type of interface{}. The interface{} can be the struct or a pointer to a struct. The same copying issue comes up.
You can't get the reference of the result directly when assigning to a new variable, but you have idiomatic way to do this without the use of a temporary variable (it's useless) by simply pre-declaring your "b" pointer - this is the real step you missed:
func a() string {
return "doesn't matter"
}
b := new(string) // b is a pointer to a blank string (the "zeroed" value)
*b = a() // b is now a pointer to the result of `a()`
*b is used to dereference the pointer and directly access the memory area which hold your data (on the heap, of course).
Play with the code: https://play.golang.org/p/VDhycPwRjK9
Yeah, it can be annoying when APIs require the use of *string inputs even though you’ll often want to pass literal strings to them.
For this I make a very tiny function:
// Return pointer version of string
func p(s string) *string {
return &s
}
and then instead of trying to call foo("hi") and getting the dreaded cannot use "hi" (type string) as type *string in argument to foo, I just wrap the argument in a call to to p():
foo(p("hi"))
a() doesn't point to a variable as it is on the stack. You can't point to the stack (why would you ?).
You can do that if you want
va := a()
b := &va
But what your really want to achieve is somewhat unclear.
At the time of writing this, none of the answers really explain the rationale for why this is the case.
Consider the following:
func main() {
m := map[int]int{}
val := 1
m[0] = val
v := &m[0] // won't compile, but let's assume it does
delete(m, 0)
fmt.Println(v)
}
If this code snippet actually compiled, what would v point to!? It's a dangling pointer since the underlying object has been deleted.
Given this, it seems like a reasonable restriction to disallow addressing temporaries
guess you need help from More effective Cpp ;-)
Temp obj and rvalue
“True temporary objects in C++ are invisible - they don't appear in your source code. They arise whenever a non-heap object is created but not named. Such unnamed objects usually arise in one of two situations: when implicit type conversions are applied to make function calls succeed and when functions return objects.”
And from Primer Plus
lvalue is a data object that can be referenced by address through user (named object). Non-lvalues include literal constants (aside from the quoted strings, which are represented by their addresses), expressions with multiple terms, such as (a + b).
In Go lang, string literal will be converted into StrucType object, which will be a non-addressable temp struct object. In this case, string literal cannot be referenced by address in Go.
Well, the last but not the least, one exception in go, you can take the address of the composite literal. OMG, what a mess.

Go passing parameters of pointers or copies to functions

I am contemplating on the Go pointers, passing variables as parameters to functions by value or by reference. In a book I have encountered a good example, which is the first code snippet below, on passing a pointer.
The first version is working as expected, in function that takes parameter of a pointer makes changes to the variable itself, not on a copy of it. But the second example below I am tinkering with works on a copy of it. I have thought they should behave equivalently, and second one to work on the variable passed as parameter, not on copy of it.
Essentially, what these two versions of the function is behaving different?
version in the book, passing parameters by reference:
package main
import (
"fmt"
)
// simple function to add 1 to a
func add1(a *int) int {
*a = *a+1 // we changed value of a
return *a // return new value of a
}
func main() {
x := 3
fmt.Println("x = ", x) // should print "x = 3"
x1 := add1(&x) // call add1(&x) pass memory address of x
fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
fmt.Println("x = ", x) // should print "x = 4"
}
my alternative tinkering version, passing pointer parameter:
package main
import (
"fmt"
)
// simple function to add 1 to a
func add1(a int) int {
p := &a
*p = *p+1 // we changed value of a
return *p // return new value of a
}
func main(){
fmt.Println("this is my go playground.")
x := 3
fmt.Println("x = ", x) // should print "x = 3"
x1 := add1(x) // call add1(&x) pass memory address of x
fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
fmt.Println("x = ", x) // should print "x = 4"
}
Why should they behave equivalently? In the first example you pass a pointer (to int), in the second example you pass an int value.
What happens in the 2nd example is that you pass an int value. Parameters in functions work like local variables. A local variable called a of type int will be created, and will be initialized with the value you passed (3 which is the value of x). This local variable is addressable like any other variables.
You take its address (p := &a) and you increment the value pointed by this pointer (which is variable a itself). And you return the value pointed by it which is the incremented value of a.
Outside from where you called this add1() function, the value of x is not modified because only the local copy a was modified.
Everything in golang is pass by value, which means the function always gets a copy of the thing being passed.
In your second example, the value of x is actually sent to the function add, so any modification you made to the copy wont affect the original x.
See pass by value

Why the pointer is losing its value in this Go program

I don't understand why the pointer s is nil even after the input() method initialised it. Any idea?
package main
import "fmt"
type ps string
func(s *ps)input(){
x := ps("a")
s = &x
}
func(s *ps)output(){
}
func main() {
var v *ps
v.input()
if v == nil{
fmt.Println("v shouldn't be nil")
}
}
Playground http://play.golang.org/p/jU2hoMP7TS
You need two things--main needs to allocate space for a ps that input can write into, which you can do by replacing var v *ps with v := new(ps). The string will be "", but it doesn't matter what it is, just that there's space set aside in memory for a string header that input can write to. As Momer said, otherwise the pointer's nil and your program panics trying to dereference it.
And in order to assign through a pointer, input needs to use *s = x. Since *s is, informally, "get what s points to", you can read that as "change what s points to to x". Usually the automatic ref/deref behavior around the dot operator and method calls saves you from that, but when you assign through a pointer type or do other operations (arithmetic, indexing, etc.) the dereference needs to be there in the code.
v value (0) is passed into v.input. Passed value is stored in a local variable s. s value is modified. No one is saving new s value back into v.
If you want something modified in your function, you must pass pointer to the value. (or reference for slices, maps and so on).
If you want to change pointer value, you should pass pointer to your pointer.
Alex

Go, is this everything you need to know for assignments and initialization?

It is just mind boggling how many different ways Go has for variable initialization. May be either i don't understand this completely or Go is a big after thought. Lot of things don't feel natural and looks like they added features as they found them missing. Initialization is one of them.
Here is running app on Go playground showing different ways of initialization
Here is what i understand
There are values and pointers. Values are initiated using var = or :=.
:= only works inside the methods
To Create value and reference you use new or &. And they only work on composite types.
There are whole new ways of creating maps and slices
Create slice and maps using either make or var x []int. Noticing there is no = or :=
Are there any easy way to understand all this for newbies? Reading specs gives all this in bits and pieces everywhere.
First, to mention some incorrect statements:
To Create value and reference you use new or &. And they only work on composite types.
new() works on everything. & doesn't
Create slice and maps using either make or var x []int. Noticing there is no = or :=
You can actually use := with x := []int{} or even x := []int(nil). However, the first is only used when you want to make a slice of len 0 that is not nil and the second is never used because var x []int is nicer.
Lets start from the beginning. You initialize variables with var name T. Type inference was added because you can type less and avoid worrying about the name of a type a function returns. So you are able to do var name = f().
If you want to allocate a pointer, you would need to first create the variable being pointed to and then get its pointer:
var x int
var px = &x
Requiring two statements can be bothersome so they introduced a built-in function called new(). This allows you to do it in one statement: var px = new(int)
However, this method of making new pointers still has an issue. What if you are building a massive literal that has structs that expect pointers? This is a very common thing to do. So they added a way (only for composite literals) to handle this.
var x = []*T{
&T{x: 1},
&T{x: 2},
}
In this case, we want to assign different values to fields of the struct the pointers reference. Handling this with new() would be awful so it is allowed.
Whenever a variable is initialized, it is always initialized to its zero value. There are no constructors. This is a problem for some of the builtin types. Specifically slices, maps, and channels. These are complicated structures. They require parameters and initialization beyond memory being set to zero. Users of Go who need to do this simply write initialization functions. So, that is what they did, they wrote make(). neW([]x) returns a pointer to a slice of x while make([]x, 5) returns a slice of x backed by an array of length 5. New returns a pointer while make returns a value. This is an important distinction which is why it is separate.
What about :=? That is a big clusterfuck. They did that to save on typing, but it led to some odd behaviors and the tacked on different rules until it became somewhat usable.
At first, it was a short form of var x = y. However, it was used very very often with multiple returns. And most of those multiple returns were errors. So we very often had:
x, err := f()
But multiple errors show up in your standard function so people started naming things like:
x, err1 := f()
y, err2 := g()
This was ridiculous so they made the rule that := only re-declares if it is not already declared in that scope. But that defeats the point of := so they also tacked on the rule that at least one must be newly declared.
Interestingly enough, this solves 95% of its problems and made it usable. Although the biggest problem I run into with it is that all the variables on the lefthand side must be identifiers. In other words, the following would be invalid because x.y is not an identifier:
x.y, z := f()
This is a leftover of its relation to var which can't declare anything other than an identifier.
As for why := doesn't work outside a function's scope? That was done to make writing the compiler easier. Outside a function, every part starts with a keyword (var, func, package, import). := would mean that declaration would be the only one that doesn't start with a keyword.
So, that is my little rant on the subject. The bottom line is that different forms of declaration are useful in different areas.
Yeah, this was one of those things that I found confusing early as well. I've come up with my own rules of thumb which may or may not be best practices, but they've served me well. Tweaked after comment from Stephen about zero values
Use := as much as possible, let Go infer types
If you just need an empty slice or map (and don't need to set an initial capacity), use {} syntax
s := []string{}
m := map[string]string{}
The only reason to use var is to initialize something to a zero value (pointed out by Stephen in comments) (and yeah, outside of functions you'll need var or const as well):
var ptr *MyStruct // this initializes a nil pointer
var t time.Time // this initializes a zero-valued time.Time
I think your confusion is coming from mixing up the type system with declaration and initialization.
In Go, variables can be declared in two ways: using var, or using :=. var only declares the variable, while := also assigns an initial value to it. For example:
var i int
i = 1
is equivalent to
i := 1
The reason this is possible is that := simply assumes that the type of i is the same as the type of the expression it's being initialized to. So, since the expression 1 has type int, Go knows that i is being declared as an integer.
Note that you can also explicitly declare and initialize a variable with the var keyword as well:
var i int = 1
Those are the only two delcaration/initialization constructs in Go. You're right that using := in the global scope is not allowed. Other than that, the two are interchangable, as long as Go is able to guess what type you're using on the right-hand-side of the := (which is most of the time).
These constructs work with any types. The type system is completely independent from the declaration/initialization syntax. What I mean by that is that there are no special rules about which types you can use with the declaration/initialization syntax - if it's a type, you can use it.
With that in mind, let's walk through your example and explain everything.
Example 1
// Value Type assignments [string, bool, numbers]
// = & := Assignment
// Won't work on pointers?
var value = "Str1"
value2 := "Str2"
This will work with pointers. The key is that you have to be setting these equal to expressions whose types are pointers. Here are some expressions with pointer types:
Any call to new() (for example, new(int) has type *int)
Referencing an existing value (if i has type int, then &i has type *int)
So, to make your example work with pointers:
tmp := "Str1"
var value = &tmp // value has type *int
value2 := new(string)
*value2 = "Str2"
Example 2
// struct assignments
var ref1 = refType{"AGoodName"}
ref2 := refType{"AGoodName2"}
ref3 := &refType{"AGoodName2"}
ref4 := new(refType)
The syntax that you use here, refType{"AGoodName"}, is used to create and initialize a struct in a single expression. The type of this expression is refType.
Note that there is one funky thing here. Normally, you can't take the address of a literal value (for example, &3 is illegal in Go). However, you can take the address of a literal struct value, like you do above with ref3 := &refType{"AGoodName2"}. This seems pretty confusing (and certainly confused me at first). The reason it's allowed is that it's actually just a short-hand syntax for calling ref3 := new(refType) and then initializing it by doing *ref3 = refType{"AGoodName2"}.
Example 3
// arrays, totally new way of assignment now, = or := now won't work now
var array1 [5]int
This syntax is actually equivalent to var i int, except that instead of the type being int, the type is [5]int (an array of five ints). If we wanted to, we could now set each of the values in the array separately:
var array1 [5]int
array1[0] = 0
array1[1] = 1
array1[2] = 2
array1[3] = 3
array1[4] = 4
However, this is pretty tedious. Just like with integers and strings and so on, arrays can have literal values. For example, 1 is a literal value with the type int. The way you create a literal array value in Go is by naming the type explicitly, and then giving the values in brackets (similar to a struct literal) like this:
[5]int{0, 1, 2, 3, 4}
This is a literal value just like any other, so we can use it as an initializer just the same:
var array1 [5]int = [5]int{0, 1, 2, 3, 4}
var array2 = [5]int{0, 1, 2, 3, 4}
array3 := [5]int{0, 1, 2, 3, 4}
Example 4
// slices, some more ways
var slice1 []int
Slices are very similar to arrays in how they are initialized. The only difference is that they aren't fixed at a particular length, so you don't have to give a length parameter when you name the type. Thus, []int{1, 2, 3} is a slice of integers whose length is initially 3 (although could be changed later). So, just like above, we can do:
var slice1 []int = []int{1, 2, 3}
var slice2 = []int{1, 2, 3}
slice3 := []int{1, 2, 3}
Example 5
var slice2 = new([]int)
slice3 := new([]int)
Reasoning about types can get tricky when the types get complex. As I mentioned above, new(T) returns a pointer, which has type *T. This is pretty straightforward when the type is an int (ie, new(int) has type *int). However, it can get confusing when the type itself is also complex, like a slice type. In your example, slice2 and slice3 both have type *[]int. For example:
slice3 := new([]int)
*slice3 = []int{1, 2, 3}
fmt.Println((*slice3)[0]) // Prints 1
You may be confusing new with make. make is for types that need some sort of initialization. For slices, make creates a slice of the given size. For example, make([]int, 5) creates a slice of integers of length 5. make(T) has type T, while new(T) has type *T. This can certainly get confusing. Here are some examples to help sort it out:
a := make([]int, 5) // s is now a slice of 5 integers
b := new([]int) // b points to a slice, but it's not initialized yet
*b = make([]int, 3) // now b points to a slice of 5 integers
Example 6
// maps
var map1 map[int]string
var map2 = new(map[int]string)
Maps, just like slices, need some initialization to work properly. That's why neither map1 nor map2 in the above example are quite ready to be used. You need to use make first:
var map1 map[int]string // Not ready to be used
map1 = make(map[int]string) // Now it can be used
var map2 = new(map[int]string) // Has type *map[int]string; not ready to be used
*map2 = make(map[int]string) // Now *map2 can be used
Extra Notes
There wasn't a really good place to put this above, so I'll just stick it here.
One thing to note in Go is that if you declare a variable without initializing it, it isn't actually uninitialized. Instead, it has a "zero value." This is distinct from C, where uninitialized variables can contain junk data.
Each type has a zero value. The zero values of most types are pretty reasonable. For example, the basic types:
int has zero value 0
bool has zero value false
string has zero value ""
(other numeric values like int8, uint16, float32, etc, all have zero values of 0 or 0.0)
For composite types like structs and arrays, the zero values are recursive. That is, the zero value of an array is an array with all of its entries set to their respective zero values (ie, the zero value of [3]int is [3]int{0, 0, 0} since 0 is the zero value of int).
Another thing to watch out for is that when using the := syntax, certain expressions' types cannot be inferred. The main one to watch out for is nil. So, i := nil will produce a compiler error. The reason for this is that nil is used for all of the pointer types (and a few other types as well), so there's no way for the compiler to know if you mean a nil int pointer, or a nil bool pointer, etc.

Resources