Quick Golang 1
- 1 Running or Building
- 2 typical go file
- 3 Some surprises
- 4 Use slice(list) instead of arrays
- 5 Strings
- 6 loops
- 7 Hashmap aka Dictionaries
- 8 Functions
- 9 Struct and API
- 10 Scoping
- 11 Hashmap and loop assign
- 12 Learn by doing
- 13 Switch statement
- 14 struct
- 15 Interfaces
- 16 Generics
- 17 Pointer
- 18 Runes
- 19 Scenarios
- 20 Traps
- 21 JSON
- Go func pass-by-value
1 Running or Building
go build main.go
./maingo run main.go2 typical go file
package main
import (
"fmt"
)
func main(){...}3 Some surprises
for loops and if statements conditions do not have parenthesis.
if(x == 2){..}\(\Rightarrow\)if x == 2 {..}for(int i=0;i<5;i++){..}\(\Rightarrow\)for i:=0;i<5;i++ {..}
3.1 := vs =
var bleh int = 3 equivalent bleh := 3
:= is shorthand to create new variable implicity then assign it.
//Shorthand
bleh := 2
//Expanded
var bleh int
bleh = 24 Use slice(list) instead of arrays
I will just call slice a list due to preference
- list is just an array without inputting the len arg
- Only use arrays if you know the exact length ahead of time
| Def | list | Array |
|---|---|---|
| init | var x = []int{4,5,6}var x = make([]int,3) |
var x = [3]int{4,5,6} |
comparison >= == |
Only with nil |
Yes |
//ARRAY
var x = [6]int{1,3:4,7,5:9} //4 at pos 3, 9 at pos 5
//[1,0,0,4,7,9]
var x2 = [...]int{6,4,3,3}
x == x2 //false
//SLICE
var z = make([]int,5) // [0,0,0,0,0]
var x = []int{1,2,3,4}
var y = [][]int
x == y //ERROR
y == nil //trueappend returns a new array or slice.
4.1 Slicing lists
- same as in Python BUT sublists SHARE THE SAME MEMORY
x = []int{1,2,3,4}
y = x[1:3]
y = []int{9,9}
//x is [1,9,9,4]Never use Append with sublists
How to do the Pythonic subslice? Use copy
Warning if len doesn’t fit, it will NOT autoexpand the list; it will get cut off.
x = []int{1,2,3,4}
y = make([]int,2)
copy(y,x[1:3])5 Strings
- A string is a sequence of UTF-8 code-points
- Go strings are immutable meaning when sliced, they automatically make copy; they behave like pythonic strings
var s string = "Hello"
s[0] = "p" //ERROR - immutable
var e string = s[1:3] //Slice
e[0] = "p" //ERROR - immutable - Go uses a sequence of bytes to represent a string
var b byte = s[2] // b = l
fmt.Printf("%s\n", string(b)) // output: l
var bb []byte = []byte(s[0:3])
fmt.Printf("%s\n", string(bb)) // output: Hel6 loops
6.1 for loop
for i:=0; i < 5; i++ {
sum += i
}6.2 iterator loops
for keyX, ValX := range arr {
fmt.Println(keyX)
fmt.Println(ValX)
}6.3 object loops
arr := []Person{{"a",4}, {"b",5}, {"c",7}, {"d",4}, {"e",3}, {"f",1}}
for index, obj := range arr {
fmt.Println(obj.Name)
fmt.Println(obj.Age)
}6.4 While loops
while (i != 5)
var i int = 0
for ; i != 5 ; {
fmt.Println(i)
i = i + 1
} for i := 0; i != 5 ; {
fmt.Println(i)
i = i + 1
}7 Hashmap aka Dictionaries
var exMap1 map[string]int // value: nil
exMap2 := map[string][]int {
"even": []int{0,2,4,6},
"odd": []int{1,3,5,7},
}
val, ok := exMap2["none"] //ok = false, val = 0
val, ok := exMap2["even"]
exMap3 := make(map[string][]int,4)8 Functions
func typicalfunction() string{
return "simple function"
}
var mylambda = func() string { return "this is lambda string func" }
fmt.Println(mylambda())8.1 Void function
- Function that returns no values
// simple right?
func add(x int) {
fmt.Println(x)
}9 Struct and API
type person struct{
name string
age int
}
bob := person{
name: "Joe",
age: 2
}9.1 Anonymous Struct
blob := struct {
name string
age int
}{
name: "bob"
age: 4
}10 Scoping
func main(){
x := 5
if x > 0 {
fmt.Println(x) //OUT: 5
x := 3
fmt.Println(x) //OUT: 3
}
fmt.Println(x) //OUT: 5
} go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
go vet11 Hashmap and loop assign
- Problem: Mutating list of items DOES NOT work when iterating objects like
for index,obj := range stuffList - Problem: Mutating the Value of a hashmap FAILS if we make a BAD Hashmap that FAILS to capture the real object like
make(map[string]SomeObject)
Both of these problems are similar to the problem of pass-by-value vs pass-by-reference.
11.1 modifying item list via loop
Below we are trying to modify the Size of the edges of a graph.
But since we can’t loop by-reference, we can use Indices.
BAD
for index, edge := range mygraph.Edges{ //edge is just a COPY
edge.Size = 1 //this FAILS to modify the real thing
}GOOD
for i := range mygraph.Edges{ //use slice index to modify the real thing
mygraph.Edges[i].Attributes.Size = 1 //GOOD
}11.2 modifying hashmap items
Below we are creating a hashmap from Node Keys : String to actual _ : Node struct
Similarly we can see that hashmap below is a map from string to a COPY of Node
BAD
nodeHashMap := make(map[string]Node) //string -> COPY of Node
for i := range mygraph.Nodes{
nodeHashMap[mygraph.Nodes[i].Key] = mygraph.Nodes[i] //FAIL to modify the real mygraph
}GOOD
nodeHashMap := make(map[string]*Node) //string -> Pointer to Node
for i := range mygraph.Nodes{
nodeHashMap[mygraph.Nodes[i].Key] = &mygraph.Nodes[i] //GOOD
}12 Learn by doing
gofunc Hello(name string) string {
var bleh string
var sentinel string
arr := [3]int{1,2,3}
const heh int = 3;
var (
target = 9
curr = 3
)
const (
pi = 3.14
e = 2.718
)
if name == "bob" {
sentinel = "pie"
} else if name == "annie" {
sentinel = "cola"
}
switch sentinel {
case "bob":
bleh = "bob likes pie"
fmt.Println(bleh)
}
fmt.Println(arr[0])
fmt.Println(heh)
return "Hello World"
}13 Switch statement
if i == 0 {
fmt.Println("Zero")
}else if i == 1 {
fmt.Println("One")
}else if i == 2{
fmt.Println("Two")
}else {
fmt.Println("default")
}switch i {
case 0: fmt.Println("Zero")
case 1: fmt.Println("One")
case 2: fmt.Println("Two")
default: fmt.Println("default")
}14 struct
type Rectangle struct {
Width float64
Height float64
}
func (r *Rectangle) AreaX int {
return 5
}
func (r Rectangle) Area() int {
return 5
}
func main(){
rect := Rectangle{12, 6}
outp := rect.Area()
outpX := AreaX(&Rect)
fmt.Println(outp)
//5
}Just by passing in the Rectangle argument for a method ‘Area()’, the method automatically is attached to struct ‘Rectangle’
15 Interfaces
- Unlike
implementsin Java, we keep it in our heads that a concrete struct implements an interface.- It is made more explicit when we pass it into a function that takes the interface
- Validation: Interface as Function arguments so that input keeps a certain shape
- Polymorphism: function that return an interface means a function can return different types
How to implement an Interface:
- Java: Explicit, concrete type
Squareimplementsand defining interface functionArea() - Go: In addition to defining
Area()for concrete type- Defining any function that takes interface as arg
printArea(x IShape) - Passing in the concrete type
printArea(x)withx::Square
- Defining any function that takes interface as arg
In Golang, the act of calling ANY function that uses the interface is how the interface gets implemented implicity
interface IShape{
float Area();
}
class Square implements IShape {
float Length = 3;
public float Area(){
this.length * this.length
};
}
public printArea(IShape s){
System.out.println(s.Area());
}
Square x = new Square();
printArea(x)type IShape interface {
Area() float64
}
type Square struct {
Length float64
}
func (s Square) Area() float64{
return s.Length * s.Length
}
func printArea(s IShape){ //IShape defined as param in compiletime
fmt.Println(s.Area())
}
x := Square{3}
printArea(x) //Square passed as param in runtimeAnother example of interface
type PlayerStore interface{
GetPlayerStore(name string) int
}
type PlayerServer struct{
score PlayerStore
}
type StubPlayerStore struct {
bleh map[string]int
}
func (s *StubPlayerStore) GetPlayerScore(name string) int {
score := s.bleh[name]
return score
}16 Generics
//ofc dont use this since fmt.Println can do this already
func PrintList[T any]( x []T ) {
for i,_ := range(x){
fmt.Println(x[i])
}
return
}17 Pointer
Just as in our “Quick C” ,
- think of passing pointers in args as passing addresses.
- The temporary
ptr *intwill bind to the passed address&x.
func trans(ptr *int){
*ptr = 0
}
func main(){
x := 5
trans(&x)
}| type | range |
|---|---|
| int8 | [-128,127] |
| int16 | [-32768,32767] |
| int32 | [-2147483648,2147483647] |
| int64 | [–9223372036854775808,9223372036854775807] |
| uint8 | [0,255] |
| uint16 | [0,65536] |
| uint32 | [0,4294967295] |
| uint64 | [0,18446744073709551615] |
Style
//BAD
name := "bob"
name := name + " ross"
//GOOD
const fstname = "bob"
const lstname = " ross"
const name = fstname + lstname//BAD
arr := [3]int{7}
arr[1] = 8
arr[2] = 9
//GOOD
arr := [3]int{7}
arr2 := append(arr,[2]int{8,9})17.1 Lambda
f := func() interface{}{return 2}
fmt.Println(f())
//output 2
fmt.Println((func() interface{}{return 2})())
//output 217.2 Tree
- Golang does not allow recursive data structs, 2 ways to solve this:
- Self-reference to self wrapped in a Functor
- Tree self-refers to ListFunctor(Tree)
- Use pointer to self
- Self-reference to self wrapped in a Functor
type tree struct{
data int
left []tree //tree wrapped in List functor
right *tree //pointer to self
}
a := tree{data:2,left: []tree{tree{data:4}}}
fmt.Println(a.left[0].data)
b := tree{data:2,right: &tree{data:4}}
fmt.Println(b.right.data)18 Runes
//BAD
int main() {
string str = "Hello, 世界!";
int len = str.length();
for(int i=0; i<len; i++){
cout << i << " "; // Guess the output, now you can
}
cout << endl;
for(int i=0; i<len; i++){
cout << str[i] << " "; // Umm, you can sense that something unexpected will happen
}
}
// OUTPUT:
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// H e l l o , � � � � � � !
//GOOD
int main() {
string str = "Hello, 世界!";
for(auto x = str.begin(); x != str.end(); x++) {
cout << *x;
}
return 0;
}
// OUTPUT
// Hello, 世界!typical loops will only loop byte by byte which cause errors when looping on non-standard characters like chinese runes.
Iterators will loop through runes.
Golang rune type is basically a better char
//BAD
func main() {
str := "Hello, 世界!"
for i:=0; i<len(str); i++ {
fmt.Printf("%v", str[i]) // This will print the decimal values
}
for i:=0; i<len(str); i++ {
fmt.Printf("%c", str[i]) // This will print the chars, but what happens to the chinese chars?
}
}
// OUTPUT
// 72101108108111443222818415023114914033
// Hello, ä¸ç!
//GOOD
func main() {
str := "世界"
for i, v : range(str) {
fmt.Println(i)
fmt.Println(v)
}
}
// OUTPUT
// 0
// 19990 <--This is 世 represented as 3 bytes unicode
// 3 <--3 bytes
// 30028 <--This is 界 represented as 3 bytes unicode19 Scenarios
19.1 Loops and mutation fail
for index, edge := range mygraph.Edges{
edge.Size = 1 //Fails to modify mygraph object
}Why? edge.Size is just a local copy.
20 Traps
20.1 Pointer receivers and interfaces
- server implements the
ServeHTTPfunction to work
type MyServer struct {
}
func (p *MyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
}
func main(){
server := MyServer{} //FAILS to run server
server := &MyServer{} //GOOD
http.ListenAndServe(":5000", server)
}| pointer receiver | server | Working? |
|---|---|---|
func (p *MyServer) ServeHTTP |
server := MyServer{} |
FAILS |
func (p *MyServer) ServeHTTP |
server := &MyServer{} |
WORKS |
func (p MyServer) ServeHTTP |
server := MyServer{} |
WORKS |
func (p MyServer) ServeHTTP |
server := &MyServer{} |
FAILS |
Why?
- The pointer receiver
func (p *MyServer) ServeHTTPtells us ONLY A POINTER TOMyServerhas the function ServeHTTP.server := MyServer{}is NOT a POINTER to MyServerserver := &MyServer{}IS a POINTER to MyServer
21 JSON
reading a JSON request as AOB. We can print as a string
type Message struct {
Data struct {
Message string `json:"message"`
MessageType string `json:"messageType"`
} `json:"data"`
Datacontenttype string `json:"datacontenttype"`
...}
router.Handle("/something", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bodyBytes, _ := io.ReadAll(r.Body)
var msg Message;
err := json.Unmarshal(bodyBytes,&msg)
log.Println(string(bodyBytes)) //output string form of json
}))JSON is []bytes in golang !!
- Response body something read as an JSON aka []bytes via (
io.readAll(r.Body)) - JSON aka []bytes can be printed nicely by casting to string
- JSON aka []bytes can be converted into struct with
json.Unmarshal(bodyBytes::[]bytes,&msg::struct)- Printing msg will only print Values, looks ugly
- We can convert struct to JSON aka []bytes via
myjsonAOB, err := json.MarshalIndent(msg, "", " ")