漫行记Wandering
Journal 2,244 字 8 分钟 Golang

Golang Variable Initialization

Just take a quick look at the go variable initialization, basically, there are four ways to initialize variables in Go.

1. Zero Value Initialization

Go provides zero values for all types as a safety feature - no uninitialized memory.

var i int        // 0
var s string     // ""
var b bool       // false
var p *int       // nil
var slice []int  // nil
var m map[string]int // nil
var ch chan int  // nil

2. Direct Expression Initialization

// Basic types
var i int = 42
var s string = "hello"
var b bool = true

// Composite types
var arr [3]int = [3]int{1, 2, 3}
var slice []int = []int{1, 2, 3}
var m map[string]int = map[string]int{"key": 1}

// Short declaration
i := 42
s := "hello"
slice := []int{1, 2, 3}

3. make() - For Reference Types Only

make() can only be used with slice, map, and channel. It allocates and initializes the underlying data structure. So the object returned by make() is ready to use and won’t be nil forever.

  • Returns a value, not a pointer.
  • Returns an initialized and usable object
  • Allocate memory internally and performs initialization.
// slice
slice1 := make([]int, 5)        // length 5, capacity 5, zero values
slice2 := make([]int, 3, 10)    // length 3, capacity 10, zero values

// map  
m1 := make(map[string]int)      // empty map, ready to use
m2 := make(map[string]int, 10)  // map with pre-allocated capacity

// channel
ch1 := make(chan int)           // unbuffered channel
ch2 := make(chan int, 5)        // buffered channel with capacity 5

Here are some pesudo implementations of make() for different types:


// make([]int, 3, 5)
func makeSlice(et *_type, len, cap int) slice {
    // calculate memory size
    mem := mallocgc(et.size * cap, et, true)
    // creative slice header
    return slice{
        array: mem,
        len:   len,
        cap:   cap,
    }
}

// make(map[string]int)
func makeMap(t *maptype, hint int) *hmap {
    h := new(hmap)
    h.hash0 = fastrand()
    if hint > 0 {
        h.buckets = mallocgc(...)
    }
    return h
}

// make(chan int, 3)
func makeChan(t *chantype, size int) *hchan {
    c := new(hchan)
    c.dataqsiz = uint(size)
    c.elemsize = uint16(t.elem.size)
    if size > 0 {
        c.buf = mallocgc(...)
    }
    return c
}

4. new() - For All Types

new(T) works with any type T,

  • Returns a pointer
  • Points to zero value
  • No additional initialization performed
// Basic types
pi := new(int)        // *int, pointer to zero value 0
ps := new(string)     // *string, pointer to zero value ""

// Composite types  
type Person struct {
    Name string
    Age  int
}

p1 := new(Person)     // *Person, pointer to zero value Person{Name: "", Age: 0}
p2 := new([]int)      // *[]int, pointer to nil slice
p3 := new(map[string]int) // *map[string]int, pointer to nil map

new() isn’t much useful for reference types like slices, maps, and channels since they need initialization to be usable. Take map as an example:

m1 := new(map[string]int) // *map[string]int, pointer to nil map
(*m1)["key"] = 1      // panic: assignment to entry in nil map
// if we want to use this make, we need to use make to initialize it first
*m1 = make(map[string]int) 
(*m1)["key"] = 1      // works fine now

new() is more commonly used for structs:

type Person struct {
    Name string
    Age  int
    Email string
}

func main() {
    p1 := new(Person)
    
    fmt.Printf("p1 type: %T\n", p1)        // *main.Person
    fmt.Printf("p1 address: %p\n", p1)        // 0x... (pointer address)
    fmt.Printf("p1 points to: %+v\n", *p1)   // {Name: Age:0 Email:}
    
    // we can use the p1 directly
    p1.Name = "Alice"    
    p1.Age = 25
    p1.Email = "alice@example.com"
   
}
← 随笔