Memo for Golang learning

Pointer

Use var var_name *var-type to define pointer type, * marks this var is a pointer of a type.

var p *int
var fp *float32

The value &p will be a pointer’s memery address.

p := 42
fmt.Printf("Pointer memory address: %x\n", &p  )

To give pointer a memory value.

i := 42  
var p *int
p = &i
// var p *int = &i
fmt.Printf("Pointer memory address: %x\n", &p  )

Check out following example to better understand this:

func main() {
	i,l := 42,50
	fmt.Printf("l's memory address: %x\n", &l  )
	fmt.Printf("i's memory address: %x\n", &i  )
    var p *int = &i // set p to be i's memory address, it's same as define p:= &i
    fmt.Printf("print i's memory address: %x\n", p  )
    fmt.Printf("print i's memory address's memory address: %x\n", &p  )
	x := &p
    fmt.Printf("print x value: %x\n", x  ) // this should equals to &p
    fmt.Printf("print &p's memory address: %x\n", &x  )
    fmt.Printf("p's Actual value: %d\n", *p  )  // print this p's acutal value which is i's value
    *p = 21  // reset *p(i) to be 21
    fmt.Printf("p's Actual value: %d\n", *p  )  // The * operator denotes the pointer's underlying value, p is a pointer &i, changing *p is actually changing i's value
    fmt.Printf("i's Actual value: %d\n", i  )
}

output:

l's memory address: c000126008
i's memory address: c000126000
print i's memory address: c000126000
print i's memory address's memory address: c000120020
print x value: c000120020
print &p's memory address: c000120028
p's Actual value: 42
p's Actual value: 21
i's Actual value: 21

Receiver and Method

func (t test) TestValue() {} this type of func is called receiver method. (t test) is its value receiver. When use a pointer to be receiver, it’s called pointer receiver func (t *test) TestValue() {}. Regular value func use predefined var as input and use its copy to run, but leave the original var no changed. On the other hand, Pointer recevier func will change the orignal var as it acutally interact with vars.

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
    fmt.Println(v.X*v.X + v.Y*v.Y)
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	fmt.Println(v.X)
	v.Y = v.Y * f
	fmt.Println(v.Y)
}

func main() {
	v := Vertex{3, 4}
	v.Scale(10)
	fmt.Println(v.Abs())
}

output:

30
40
2500
50

if we change func (v *Vertex) Scale(f float64) to be func (v Vertex) Scale(f float64):

30
40
25
5

Compare the resulats we can see that with * it modify the original Vertex{3, 4} in Scale, or did nothing if we remove *. As a convenience, Go interprets the statement v.Scale(5) as (&v).Scale(5) since the Scale method has a pointer receiver.

This code can also be written in regualr func, and have the same result:

func Scale(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}
	Scale(&v, 10)
	fmt.Println(Abs(v))
}

Note that we have &v in Scale(&v, 10) here. In fact it says use Vertex memory address as Scale input var, and in the Scale func it says v is a pointer in type Vertex, any action inside this func against v performs based on its pointer value.

Following example shows how confusing this method can be:

func Scale(v *Vertex, f float64) {
    fmt.Println(v)    //&{3 4}
	fmt.Println(v.X)  //3
	v.X = v.X * f
	fmt.Println(v.X)
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.X)   //3
	fmt.Println(&v)    //&{3,4}
	fmt.Println(&v.X)  //0xc00010a000
	fmt.Println(&v.Y)  //0xc00010a008
	Scale(&v, 10)      //30
	fmt.Println(Abs(v))//50
}

Checking Var type

The only way to do it:

fmt.Println(reflect.TypeOf(a))
//or
fmt.Printf("Underlying Type of b: %T\n", b) //keyword T here means type

Map Comparison

Golang doesn’t have easy code(like python if map == map) to compare if two given maps having same content. To do that we need to use built-in lib reflect.

reflect.DeepEqual(a, b) can tell if two maps are identical:

func main() {
	a := map[int]int{1: 2}
	b := map[int]int{1: 2}
	if reflect.DeepEqual(a, b) {
		fmt.Println("yes")
	}
}

Format Conversion

Format in golang is as important as in C, this is a huge difference from Python. You need to think about every single var/const defined in your code. In most cases, you need to use standard lib strconv, it has func to convert string, int, float but not slice. There’s no simple one line code to convet a slice/array, you need to loop over entire slice(become byte or rune) then strings.Join again to get it converted.

In a lot of cases if you loop through a given string, you’d get element shown as reflect.TypeOf()=uint8, this is acutally an alias of byte.

string -> int

Yes, it’s Atoi, not Stoi…

func main() {
	v := "10"
	if s, err := strconv.Atoi(v); err == nil {
		fmt.Printf("%T, %v", s, s)
	}
}

int -> string

func main() {
	i := 10
	s := strconv.Itoa(i)
	fmt.Printf("%T, %v\n", s, s)
}

int -> float,float -> int

Finally a simple one, like what Python does, simple as that:

func main() {
	var x float64 = 5.7
	y := int(x)
	z := float64(y)
	fmt.Printf("x=%v,y=%v,z=%v\n", x, y, z)
	fmt.Printf("x=%T,y=%T,z=%T\n", x, y, z)
}

output

x=5.7,y=5,z=5
x=float64,y=int,z=float64

int -> []int

convert int into slice is much complex than expected. Using recursive func:

func IntToSlice(n int, sequence []int) []int {
    if n != 0 {
        i := n % 10
        // sequence = append(sequence, i) // reverse order output
        sequence = append([]int{i}, sequence...)
        return IntToSlice(n/10, sequence)
    }
    return sequence //only when recurisve touches last digit of n, then return final result.
}

var changes during each loop call:

n:=123
sequence value each loop:
[3]
[2 3]
[1 2 3]
[1 2 3]

[]int -> int

reverse engineering of what have be done during int -> []int:

func SliceToInt(sequence []int, n int) int {
	if len(sequence) != 0 {
		n = n + sequence[0]*int(math.Pow10(len(sequence)-1))
		sequence = sequence[1:]
		return SliceToInt(sequence, n)
	}
	return n
}

var changes during each loop call:

n:=[1 2 3]
sequence value each loop:
[2 3]
[3]
[]
123

[]int -> string

plain method
func main() {
    
    // The int slice we are converting to a string.
    values := []int{10, 200, 3000}
    valuesText := []string{}
    
    // Create a string slice using strconv.Itoa.
    // ... Append strings to it.
    for i := range values {
        valuesText = append(valuesText, strconv.Itoa(values[i]))
    }
    
    // Join our string slice.
    fmt.Println(strings.Join(valuesText, ""))
}

outout

102003000
json middleman
func main() {
	data := []int{1, 2, 3}
	s, _ := json.Marshal(data) // it is same as if you loop entire slice data, convert int to string, and then save each in s []byte, so elements become type `byte`
	fmt.Println(s) // same as doing fmt.Println([]byte(s))
	fmt.Println(string(s)) //'byte' type slice can be stringized easily.
}

outptut:

[91 49 44 50 44 51 93]
[1,2,3]

string -> []string -> []byte

If you’d like to do a pointer by pointer compare between slices, better to use feature 'A' == byte(65) or 'A' == 65('A' is rune, which is type int32), so that you can say if n in a []byte equals A then do something, no need to stick with byte numbers, save some time.

Convert String to its slice.

func main() {
	s := strings.Split("abc", "")
}	

Convert String to its slice in byte is easy:

func main() {
	a := "abc"
	fmt.Println([]byte(a))
}

if you loop through a string, then element becomes type rune:

func main() {
	a := "abc"
	var b []rune
	for _, v := range a {
		b = append(b, v)
	}
}