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
./main
go run main.go
2 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
:= 2
bleh
//Expanded
var bleh int
= 2 bleh
4 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}
== x2 //false
x
//SLICE
var z = make([]int,5) // [0,0,0,0,0]
var x = []int{1,2,3,4}
var y = [][]int
== y //ERROR
x == nil //true y
append
returns a new array or slice.
4.1 Slicing lists
- same as in Python BUT sublists SHARE THE SAME MEMORY
= []int{1,2,3,4}
x = x[1:3]
y = []int{9,9}
y //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.
= []int{1,2,3,4}
x = make([]int,2)
y 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"
[0] = "p" //ERROR - immutable
s
var e string = s[1:3] //Slice
[0] = "p" //ERROR - immutable e
- Go uses a sequence of bytes to represent a string
var b byte = s[2] // b = l
.Printf("%s\n", string(b)) // output: l
fmt
var bb []byte = []byte(s[0:3])
.Printf("%s\n", string(bb)) // output: Hel fmt
6 loops
6.1 for loop
for i:=0; i < 5; i++ {
+= i
sum }
6.2 iterator loops
for keyX, ValX := range arr {
.Println(keyX)
fmt.Println(ValX)
fmt}
6.3 object loops
:= []Person{{"a",4}, {"b",5}, {"c",7}, {"d",4}, {"e",3}, {"f",1}}
arr for index, obj := range arr {
.Println(obj.Name)
fmt.Println(obj.Age)
fmt}
6.4 While loops
while (i != 5)
var i int = 0
for ; i != 5 ; {
.Println(i)
fmt= i + 1
i }
for i := 0; i != 5 ; {
.Println(i)
fmt= i + 1
i }
7 Hashmap aka Dictionaries
var exMap1 map[string]int // value: nil
:= map[string][]int {
exMap2 "even": []int{0,2,4,6},
"odd": []int{1,3,5,7},
}
, ok := exMap2["none"] //ok = false, val = 0
val, ok := exMap2["even"]
val
:= make(map[string][]int,4) exMap3
8 Functions
func typicalfunction() string{
return "simple function"
}
var mylambda = func() string { return "this is lambda string func" }
.Println(mylambda()) fmt
8.1 Void function
- Function that returns no values
// simple right?
func add(x int) {
.Println(x)
fmt}
9 Struct and API
type person struct{
string
name int
age }
:= person{
bob : "Joe",
name: 2
age}
9.1 Anonymous Struct
:= struct {
blob string
name int
age }{
: "bob"
name: 4
age}
10 Scoping
func main(){
:= 5
x if x > 0 {
.Println(x) //OUT: 5
fmt:= 3
x .Println(x) //OUT: 3
fmt}
.Println(x) //OUT: 5
fmt}
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
go vet
11 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
.Size = 1 //this FAILS to modify the real thing
edge}
GOOD
for i := range mygraph.Edges{ //use slice index to modify the real thing
.Edges[i].Attributes.Size = 1 //GOOD
mygraph}
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
:= make(map[string]Node) //string -> COPY of Node
nodeHashMap for i := range mygraph.Nodes{
[mygraph.Nodes[i].Key] = mygraph.Nodes[i] //FAIL to modify the real mygraph
nodeHashMap}
GOOD
:= make(map[string]*Node) //string -> Pointer to Node
nodeHashMap for i := range mygraph.Nodes{
[mygraph.Nodes[i].Key] = &mygraph.Nodes[i] //GOOD
nodeHashMap}
12 Learn by doing
(name string) string {
gofunc Hellovar bleh string
var sentinel string
:= [3]int{1,2,3}
arr const heh int = 3;
var (
= 9
target = 3
curr )
const (
= 3.14
pi = 2.718
e )
if name == "bob" {
= "pie"
sentinel } else if name == "annie" {
= "cola"
sentinel }
switch sentinel {
case "bob":
= "bob likes pie"
bleh .Println(bleh)
fmt}
.Println(arr[0])
fmt.Println(heh)
fmtreturn "Hello World"
}
13 Switch statement
if i == 0 {
.Println("Zero")
fmt}else if i == 1 {
.Println("One")
fmt}else if i == 2{
.Println("Two")
fmt}else {
.Println("default")
fmt}
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 {
float64
Width float64
Height }
func (r *Rectangle) AreaX int {
return 5
}
func (r Rectangle) Area() int {
return 5
}
func main(){
:= Rectangle{12, 6}
rect := rect.Area()
outp := AreaX(&Rect)
outpX .Println(outp)
fmt//5
}
Just by passing in the Rectangle argument for a method ‘Area()’, the method automatically is attached to struct ‘Rectangle’
15 Interfaces
- Unlike
implements
in 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
Square
implements
and 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());
}
= new Square();
Square x printArea(x)
type IShape interface {
() float64
Area}
type Square struct {
float64
Length }
func (s Square) Area() float64{
return s.Length * s.Length
}
func printArea(s IShape){ //IShape defined as param in compiletime
.Println(s.Area())
fmt}
:= Square{3}
x (x) //Square passed as param in runtime printArea
Another example of interface
type PlayerStore interface{
(name string) int
GetPlayerStore}
type PlayerServer struct{
score PlayerStore}
type StubPlayerStore struct {
map[string]int
bleh }
func (s *StubPlayerStore) GetPlayerScore(name string) int {
:= s.bleh[name]
score 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){
.Println(x[i])
fmt}
return
}
17 Pointer
Just as in our “Quick C” ,
- think of passing pointers in args as passing addresses.
- The temporary
ptr *int
will bind to the passed address&x
.
func trans(ptr *int){
*ptr = 0
}
func main(){
:= 5
x (&x)
trans
}
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
:= "bob"
name := name + " ross"
name //GOOD
const fstname = "bob"
const lstname = " ross"
const name = fstname + lstname
//BAD
:= [3]int{7}
arr [1] = 8
arr[2] = 9
arr//GOOD
:= [3]int{7}
arr := append(arr,[2]int{8,9}) arr2
17.1 Lambda
:= func() interface{}{return 2}
f .Println(f())
fmt//output 2
.Println((func() interface{}{return 2})())
fmt//output 2
17.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{
int
data []tree //tree wrapped in List functor
left *tree //pointer to self
right }
:= tree{data:2,left: []tree{tree{data:4}}}
a .Println(a.left[0].data)
fmt:= tree{data:2,right: &tree{data:4}}
b .Println(b.right.data) fmt
18 Runes
//BAD
int main() {
= "Hello, 世界!";
string str
int len = str.length();
for(int i=0; i<len; i++){
<< i << " "; // Guess the output, now you can
cout }
<< endl;
cout
for(int i=0; i<len; i++){
<< str[i] << " "; // Umm, you can sense that something unexpected will happen
cout }
}
// OUTPUT:
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// H e l l o , � � � � � � !
//GOOD
int main() {
= "Hello, 世界!";
string str
for(auto x = str.begin(); x != str.end(); x++) {
<< *x;
cout }
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() {
:= "Hello, 世界!"
str
for i:=0; i<len(str); i++ {
.Printf("%v", str[i]) // This will print the decimal values
fmt}
for i:=0; i<len(str); i++ {
.Printf("%c", str[i]) // This will print the chars, but what happens to the chinese chars?
fmt}
}
// OUTPUT
// 72101108108111443222818415023114914033
// Hello, ä¸ç!
//GOOD
func main() {
:= "世界"
str
for i, v : range(str) {
.Println(i)
fmt.Println(v)
fmt}
}
// OUTPUT
// 0
// 19990 <--This is 世 represented as 3 bytes unicode
// 3 <--3 bytes
// 30028 <--This is 界 represented as 3 bytes unicode
19 Scenarios
19.1 Loops and mutation fail
for index, edge := range mygraph.Edges{
.Size = 1 //Fails to modify mygraph object
edge}
Why? edge.Size
is just a local copy.
20 Traps
20.1 Pointer receivers and interfaces
- server implements the
ServeHTTP
function to work
type MyServer struct {
}
func (p *MyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
}
func main(){
:= MyServer{} //FAILS to run server
server := &MyServer{} //GOOD
server .ListenAndServe(":5000", server)
http}
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) ServeHTTP
tells us ONLY A POINTER TOMyServer
has 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 {
struct {
Data string `json:"message"`
Message string `json:"messageType"`
MessageType } `json:"data"`
string `json:"datacontenttype"`
Datacontenttype ...}
.Handle("/something", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
router, _ := io.ReadAll(r.Body)
bodyBytesvar msg Message;
:= json.Unmarshal(bodyBytes,&msg)
err .Println(string(bodyBytes)) //output string form of json
log}))
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, "", " ")