https://github.com/meleu/learning-golang
Learning Go with tests, from https://quii.gitbook.io/learn-go-with-tests
https://github.com/meleu/learning-golang
Last synced: 9 months ago
JSON representation
Learning Go with tests, from https://quii.gitbook.io/learn-go-with-tests
- Host: GitHub
- URL: https://github.com/meleu/learning-golang
- Owner: meleu
- Created: 2022-11-23T01:01:27.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2024-06-02T21:55:05.000Z (over 1 year ago)
- Last Synced: 2024-12-31T02:13:20.142Z (11 months ago)
- Language: Go
- Homepage:
- Size: 18.6 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Learning Golang
Following the [Learn Go with tests](https://quii.gitbook.io/learn-go-with-tests/) gitbook.
Here I list some things I learned in each exercise.
## Hello, World
-
### What I learned
#### Golang tooling
Start a new project as a module with:
```bash
mkdir hello
cd hello
go mod init hello
# check if the "go.mod" file was created
```
Recommended linter: [GolangCI-Lint](https://golangci-lint.run/). It can be installed with Homebrew or `asdf` (I used `asdf`).
Useful config to be used in VSCode:
```json
{
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"],
"go.coverOnSave": true,
"go.coverageDecorator": {
"type": "gutter",
"coveredHighlightColor": "rgba(64,128,128,0.5)",
"uncoveredHighlightColor": "rgba(128,64,64,0.25)",
"coveredGutterStyle": "blockgreen",
"uncoveredGutterStyle": "slashred"
}
}
```
Awesome tool to rerun tests on file change: [watchexec](https://github.com/watchexec/watchexec). Example of usage:
```bash
# run 'go test' when a change happens on a file ending with .go
watchexec -e go -- go test -v
```
Local documentation:
```bash
# installing local documentation
go install golang.org/x/tools/cmd/godoc@latest
# NOTE: sometimes godoc won't be in your path.
# in my case it went to `~/.asdf/` structure and I created an alias:
alias godoc=$(find $HOME/.asdf/installs/golang -type f -path '*packages/bin/godoc')
# now you can launch the local documentation server
godoc -http :8000
```
#### Golang basics
- a program have a `main` package defined with a `main` func inside.
- `func` defines a function with a name and a body (aka block)
- blocks are defined with `{`curly braces`}`
- `import "fmt"` is necessary to use `fmt.Println`
- `if` works like other programming languages, without `(`parenthesis`)`
- variables are ~~assigned~~ declared like this: `varName := value`
- I [researched](https://stackoverflow.com/a/36513229/6354514) and realized that
- `:=` for [short variable declarations](https://go.dev/ref/spec#Short_variable_declarations) (with type inference)
- `=` for [variable declarations](https://go.dev/ref/spec#Variable_declarations) and [assignments](https://go.dev/ref/spec#Assignment_statements).
- constants are defined like `const myConst = "My String"`
- `PublicFunctions` start with a capital letter and `privateFunctions` start with a lowercase.
- `func greetingPrefix(language string) (prefix string)` creates a **named return value**
- creates a variable called `prefix` in the function
- it will be assigned the "zero" value. In this case (`string`): `""`
- example (also showing a `switch` statement):
```go
func greetingPrefix(language string) (prefix string) {
switch language {
case spanish:
prefix = spanishHelloPrefix
case french:
prefix = frenchHelloPrefix
case portuguese:
prefix = portugueseHelloPrefix
default:
prefix = englishHelloPrefix
}
return
}
```
- example of grouping constants:
```go
const (
spanish = "Spanish"
french = "French"
portuguese = "Portuguese"
englishHelloPrefix = "Hello, "
spanishHelloPrefix = "Hola, "
frenchHelloPrefix = "Bonjour, "
portugueseHelloPrefix = "Olรก, "
)
```
#### Golang testing
- file name must be `${something}_test.go`
- `import "testing"`
- the test function must start with `Test`
- test function takes only one argument `t *testing.T` (it's your "hook" into the testing framework)
- `t.Errorf` prints a message when a test fails.
- `%q` means "string surrounded with double quotes", in the string format context
- subtests go in `t.Run("test name", testFunction)`. Example:
```go
func TestHello(t *testing.T) {
// ๐ t.Run(testName, testFunction)
t.Run("say hello to people", func(t *testing.T) {
actual := Hello("Chris")
expected := "Hello, Chris!"
assertCorrectMessage(t, actual, expected)
})
// ๐ t.Run(testName, testFunction)
t.Run("say 'Hello, World!' when passing empty string", func(t *testing.T) {
actual := Hello("")
expected := "Hello, World!"
assertCorrectMessage(t, actual, expected)
})
}
// comments about this helper function right after this codeblock
func assertCorrectMessage(t testing.TB, actual, expected string) {
t.Helper() // <-- pra que isso?
if actual != expected {
t.Errorf("expected: %q; actual: %q", expected, actual)
}
}
```
- For helper functions, accept `testing.TB` is a good idea.
- `t.Helper` is needed to report the caller line number when the test fails
(not the line number in the helper function)
## Integers
### Testable Examples
[Official article](https://go.dev/blog/examples).
Here's an example:
```go
func ExampleAdd() {
sum := Add(1, 5)
fmt.Println(sum)
// Output: 6
}
```
The special comment `// Output: 6` makes the example to be executed.
This example also goes to the documentation of your package. You can check by
running `godoc -http :8000` and looking for the `Integers` package.
## Iteration
### Golang
In Go you iterate using `for`. There are **no** `while`, `do`, `until` keywords.
It's usually used like other C-like languages:
```go
for i := 0; i < 5; i++ {
repeated += character
}
```
Other ways of using `for` are listed here:
### Benchmarking
Example:
```go
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("a")
}
}
```
- `testing.B` gives you access to the (cryptic) `b.N`.
- the benchmark code is executed `b.N` times and measures how long it takes.
- the amount of times shouldn't matter, the framework determine what is a "good" value.
- run the benchmark with `go test -bench=.`
- the results show how many times the code was executed and how many nanoseconds it took to run.
## Arrays
### Golang
Arrays can be initialized in two ways:
- `[N]type{value1, value2, ..., valueN}`
- example: `numbers := [5]int{1, 2, 3, 4, 5}`
- `[...]type{value1, value2, ..., valueN}`
- example: `numbers := [...]int{1, 2, 3, 4, 5}`
The `%v` placeholder print the variable in the "default" format (in this case
an array).
Let's check the `range` instruction:
```go
func Sum(numbers [5]int) int {
sum := 0
// numbers is the array given as argument
for _, number := range numbers {
sum += number
}
return sum
}
```
- `range` let you iterate over an array
- on each iteration it returns two values: the index and the value
- in the example we are choosing to ignore the index by using the
`_` [blank identifier](https://go.dev/doc/effective_go#blank)
## Slices
### Golang
The [slice type](https://go.dev/doc/effective_go#slices) allows us to have
collections of any size. The syntax is very similar to arrays, just omit
the size.
Example: `mySlice := []int{1, 2, 3}`
Checking equality of slices:
```go
import "reflect"
reflect.DeepEqual(slice1, slice2)
```
Adding elements to a slice:
```go
// append() creates a new slice, therefore you need to assign the variable again
mySlice = append(mySlice, newElement)
// you can use append() to merge two slices:
mySlice = append(mySlice, anotherSlice...)
```
### Golang testing
Check the coverage with
```bash
go test -cover
```
## Structs, Methods and Interfaces
### Golang
```go
// declaring a struct
type Rectangle struct {
Width float64
Height float64
}
// declaring a method for a struct
func (r Rectangle) Area() float64 {
return r.Height * r.Width
}
```
An interesting thing about interfaces in Go (which makes me remember Duck Typing):
**interface resolution is implicit**.
Here's an example of an interface:
```go
type Shape interface {
Area() float64
}
```
Once this ๐ is declared **any** struct witha method called `Area()` that returns
a `float64` is automatically considered a `Shape`.
We don't need to explicitly say "My type Foo implements interface Bar".
### Golang testing
I learned about Table Driven Tests. It's useful but not that easy to read
(without some training).
Here's an example:
```go
func TestArea(t *testing.T) {
// using Table Drive Tests
areaTests := []struct {
name string
shape Shape
hasArea float64
}{
{
name: "Rectangle",
shape: Rectangle{Width: 12, Height: 6},
hasArea: 72.0,
},
{
name: "Circle",
shape: Circle{Radius: 10},
hasArea: 314.1592653589793,
},
{
name: "Triangle",
shape: Triangle{Base: 12, Height: 6},
hasArea: 36.0,
},
}
for _, tt := range areaTests {
// using tt.name to use it as the `t.Run` test name
t.Run(tt.name, func(t *testing.T) {
got := tt.shape.Area()
if got != tt.hasArea {
// the `%#v` format string prints the struct with values in its fields
t.Errorf("%#v got %g; want %g", tt.shape, got, tt.hasArea)
}
})
}
}
```