CSE224 - Go Basis
How to create a go project
-
mkdir myapp,cd myapp -
create a module:
go mod init github.com/yourname/myapp, then go will generate ago.modfile inside. -
create
main.go:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, Module!")
}execute go run main.go to run it.
- add dependencies:
package main
import (
"fmt"
"[github.com/google/uuid](https://github.com/google/uuid)"
)
func main() {
id := uuid.New()
fmt.Println("Generated UUID:", id)
}after I execute go run main.go, go will download github.com/google/uuid and add require github.com/google/uuid v1.3.1 to go.mod config file.
Packages
myproject/
│
├── go.mod -> mod file
├── main.go -> package main
├── utils/
│ └── math.go -> package utils
├── service/
│ └── handler.go -> package service// main.go
package main
import "myapp/utils"
func main() {
println(utils.SayHi())
}// utils/helper.go
package utils
func SayHi() string {
return "Hi"
}- Each
.gofile must havepackage xxx - A module usually have many packages.
- Each folder must have one package.
- Can import between packages.
- Functions can freely call each other as long as they’re defined in the same package (even if they are in different files).
- In a Go project, the entry point is always the main package, specifically the main() function inside it. When I run
go build, Go will look forpackage mainandfunc main(), and then compiles all dependencies recursively together into a single executable. - A module can have multiple main packages, each in a different subdirectory. Each main.go here is its own entry point, and Go builds them into separate executables.
mytool/ ← module
├── go.mod
├── cmd/
│ ├── server/
│ │ └── main.go ← package main (binary 1)
│ └── cli/
│ └── main.go ← package main (binary 2)
├── internal/
│ └── logic.go ← package internal (shared code)Value Type
Assigning it to a new variable, or passing it to a function, creates a copy of the value.
- Basic types: int, float64, bool, string
- Arrays: [3]int, [5]byte
- Structs: type Point struct {x, y int}
- Custom types: type MyInt int
type Point struct {
X, Y int
}
p1 := Point{X: 1, Y: 2}
p2 := p1 // Copy of p1
p2.X = 100
fmt.Println(p1.X) // 1 — unchanged
fmt.Println(p2.X) // 100 — only p2 changedPointer Type
The following are pointer types:
- slice
a := []int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a[0]) // 99- map
- channel
- function
- interface
Basic Syntax
var x int = 42
z := 3.14
func add(a int, b int) int {
return a + b
}
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
fmt.Println(p.Name)Array and Slices
Array:
Arrays are value types, so [3]int is not compatible with [4]int.
var arr [3]int // array of 3 integers, all initialized to 0
b := [3]int{1, 2, 3} // initialized with values
c := [...]int{4, 5, 6, 7} // size inferred to be 4
a[0] = 42 // mutationSlices
A slice is a flexible, powerful view into an underlying array. It lets you work with sequences that can grow, shrink, or share memory
A slice is a struct with three fields:
- A pointer to the underlying array
- A length (number of visible elements)
- A capacity (maximum number of elements before reallocation) (cap(t) = number of elements from (start of the slice) to end of the backing array.)
Slice creation:
// 1. From an array
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // slice of [2, 3, 4]
// 2. Using a literal
s := []int{10, 20, 30}
// 3. Using make
s := make([]int, 3) // length 3, capacity 3
s := make([]int, 2, 5) // length 2, capacity 5Slice internals:
s := []int{1, 2, 3, 4, 5}
t := s[1:4] // t = [2, 3, 4], length = 3, capacity = 4Modification
s := []int{1, 2, 3, 4, 5}
t := s[1:4] // [2 3 4]
fmt.Println(len(t)) // 3
fmt.Println(cap(t)) // 4
// t and s points to the same array
t = append(t, 99) // still fits in capacity, modifies s[4]!
fmt.Println(s) // [1 2 3 4 99]Append:
If there’s enough capacity, append extends the existing array. Otherwise, it allocates a new backing array.
s := []int{1, 2}
s = append(s, 3, 4) // s = [1, 2, 3, 4]Pointers
var x int = 10
var px *int = &x // px holds the address of x
*px = 20 // sets x to 20 through the pointer
func modify(n *int) {
*n = 42
}
func main() {
a := 10
modify(&a) // pass address of a
fmt.Println(a) // 42
}Loop
There is no while keyword in Go.
for i := 0; i < 5; i++ {
fmt.Println(i)
}
i := 0
for i < 5 {
fmt.Println(i)
i++
}
// Infinite loop
for {
fmt.Println("This will run forever unless you break it")
}
// Looping over collections
// range returns two stuffs: index and value. index and value are declared and initialized automatically by the Go compiler as part of the loop syntax.
nums := []int{10, 20, 30}
for index, value := range nums {
fmt.Println("Index:", index, "Value:", value)
}
for _, value := range nums {
fmt.Println(Value:", value)
}Map
Create a map:
ages := map[string]int{
"Alice": 25,
"Bob": 30,
}
colors := make(map[string]string)
colors["red"] = "#FF0000"
colors["green"] = "#00FF00"// Iterate a map:
for name, age := range ages {
fmt.Printf("%s %d\n", name, age)
}
// Check if exists:
// ok is a boolean indicating whether the key exists in the map
age, ok := ages["Alice"]
if ok {
fmt.Println("Found:", age)
} else {
fmt.Println("Not found")
}
// delete a key
delete(ages, "Bob")Function as parameters
func apply(fn func(int) int, val int) int {
return fn(val)
}