Golang Memo
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)
}
}