Go
Starting places
- A Tour of Go
- An Introduction to Programming in Go
- How to Write Go Code
- Effective Go
- Tutorials
- Documentation
- Style Guide
Project organisation
Workspaces
Tutorial: Getting started with multi-module workspaces
go work init ./hello
Modules
A module’s name is set in the root of the project directory in the go.mod
file, eg:
./go.mod
module myapp
go 1.19
This file is generated as follows:
mkdir myapp
cd myapp
go mod init myapp
go: creating new go.mod: module myapp
...
The project root directory name doesn’t have to be myapp, but it’s probably a good convention.
myapp
├── go.mod
├── main.go
└── mypackage
├── function1.go
├── function1_test.go
├── function2.go
└── function2_test.go
Packages
Rather than put everything in the main.go
file, myapp can be split into packages
which in turn can be split into any number of files.
It’s easiest to give each package its own subdirectory. All go files in a given subdirectory would start with the line
package mypackage
Clients of a package (eg main.go) import it as "myapp/mypackage"
main.go
package main
import (
"fmt"
"myapp/mypackage"
)
func main() {
fmt.Println(mypackage.ReverseRunes("!oG ,olleH"))
}
Note that functions etc declared in packages need to be upercassed to be exported:
Note shared functions and variables must start with capital letters to be seen by other files, though modules sharing the same package name only have to be in the same directory, they don’t need to import each other.
A moduleN.go
file looks like this:
// Package mypackages implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package mypackage
// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
go test
Example driven development
go doc
godoc -http=:6060
https://dev.to/billylkc/parse-json-api-response-in-go-10ng
https://blog.alexellis.io/golang-json-api-client/
https://pkg.go.dev/github.com/jackc/pgx/v4
https://pkg.go.dev/github.com/jackc/pgx
https://www.alexedwards.net/blog/introduction-to-using-sql-databases-in-go
https://www.alexedwards.net/blog/using-postgresql-jsonb
Q: What’s the advantage of “If with a short statement”? stackoverflow
if err := json.NewEncoder(w).Encode(todos); err != nil {
panic(err)
}
A: Scoping. In the above example, err’s scope is limited to inside the if statement.
goroutines
channels
https://www.honeybadger.io/blog/go-web-services/
https://blog.logrocket.com/how-to-make-http-post-request-with-json-body-in-go/
Go commands
go get github.com/gin-gonic/gin
install packages.
go doc encoding/json
shows documentation for a given package.
go fmt foo.go
converts foo.go into tab (4 spaces) indented and other style changes.
Modules
A module is a collection of Go packages stored in a file tree with a go.mod file at its root. The go.mod file defines the module’s module path, which is also the import path used for the root directory, and its dependency requirements, which are the other modules needed for a successful build. Each dependency requirement is written as a module path and a specific semantic version.
$ go mod init example/hello
go: creating new go.mod: module example/hello
$ go run .
$ go help
$ go mod tidy
go: finding module for package rsc.io/quote
go: found rsc.io/quote in rsc.io/quote v1.5.2
Return and handle an error
Testing
Standard library
Command-line flags
One or two minus signs may be used; they are equivalent.
Flag parsing stops just before the first non-flag argument ("-" is a non-flag argument) or after the terminator “–”.
server --port=3030 --pidfile=http.pid --workers=4
package main
import (
"flag"
"fmt"
)
func main() {
portPtr := flag.Int("port", 8080, "port number")
userPtr := flag.String("user", "nginx", "user")
flag.Parse()
fmt.Println("port:", *portPtr)
fmt.Println("user:", *userPtr)
}
os
func WriteFile(name string, data []byte, perm FileMode) error
type Page struct {
Title string
Body []byte
}
func (p *Page) save() error {
filename := p.Title + ".txt"
return os.WriteFile(filename, p.Body, 0600)
}
func main() {
p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
p1.save()
}
func ReadFile(name string) ([]byte, error)
func loadPage(title string) (*Page, error) {
filename := title + ".txt"
body, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}
func main() {
// p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
// p1.save()
p2, _ := loadPage("TestPage")
fmt.Println(string(p2.Body))
}
net/http
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func ListenAndServe(addr string, handler Handler) error
func main() {
...
log.Fatal(http.ListenAndServe(":8080", nil))
}
type Request struct {
Method string
URL *url.URL
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header Header
Body io.ReadCloser
GetBody func() (io.ReadCloser, error)
ContentLength int64
TransferEncoding []string
Close bool
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
Trailer Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Response *Response
}