diff --git a/docs/.custom_wordlist.txt b/docs/.custom_wordlist.txt index 2b973ae..44709fb 100644 --- a/docs/.custom_wordlist.txt +++ b/docs/.custom_wordlist.txt @@ -1,24 +1,28 @@ -analyzer -gdb +GoLand Golang -https +I/O IDE IDEs Integrations +JetBrains +Precompiled +RustRover +SDK +SDKs +VSCode +WSL +WebAssembly +analyzer +gdb +gopls +https io -I/O javac -JetBrains lldb +microservices mingw -RustRover rustup -SDK -SDKs snapcraft toolchain toolchains -VSCode wasm -WebAssembly -WSL diff --git a/docs/.sphinx/requirements.txt b/docs/.sphinx/requirements.txt index cb4b960..ad3835d 100644 --- a/docs/.sphinx/requirements.txt +++ b/docs/.sphinx/requirements.txt @@ -1,2 +1,2 @@ -canonical-sphinx +canonical-sphinx[full] sphinx-autobuild diff --git a/docs/.sphinx/spellingcheck.yaml b/docs/.sphinx/spellingcheck.yaml index 467676d..cf45db4 100644 --- a/docs/.sphinx/spellingcheck.yaml +++ b/docs/.sphinx/spellingcheck.yaml @@ -3,6 +3,7 @@ matrix: aspell: lang: en d: en_GB + add-extra-dicts: en_US dictionary: wordlists: - .wordlist.txt diff --git a/docs/conf.py b/docs/conf.py index a5a81b0..0c9085f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -99,7 +99,7 @@ # Change to the folder that contains the documentation # (usually "/" or "/docs/") - 'github_folder': '/docs', + 'github_folder': '/docs/', # Change to an empty value if your GitHub repo doesn't have issues enabled. # This will disable the feedback button and the issue link in the footer. diff --git a/docs/howto/go-setup.md b/docs/howto/go-setup.md index 10c1362..62df730 100644 --- a/docs/howto/go-setup.md +++ b/docs/howto/go-setup.md @@ -1,90 +1,90 @@ # How to set up development environment for Go on Ubuntu -Go is a popular language for backend web development, -microservices and CLI tools. This how-to guide outlines -how to install a Go distribution and set up a development -environment on Ubuntu. +Go is a popular language for back-end web development, microservices and CLI tools. This how-to guide outlines how to install a Go distribution and set up a development environment on Ubuntu. + ## Installing and setting up Go -Go includes the `go` command, a compiler and other tools. -You can install Go as a Debian package or as a snap. -Snap provides a larger number of Go versions but — unlike -a Debian package — these cannot be installed in a Docker container. +Go includes the `go` command, a compiler and other tools. You can install Go as a Debian package or as a snap. Snap provides a larger number of Go versions but — unlike a Debian package — these cannot be installed in a Docker container. :::{note} -See [Precompiled Go binaries](#precompiled-go-binaries) for instructions -on how to install Go without a package manager. +See [Precompiled Go binaries](#precompiled-go-binaries) for instructions on how to install Go without a package manager. ::: + ### Debian package 1. Update the list of available packages: -```none -sudo apt update -``` + ```none + sudo apt update + ``` 2. Install the latest version of Go: -```none -sudo apt install golang-go -``` + ```none + sudo apt install golang-go + ``` 3. Confirm successful installation with: -```none -go version -``` + ```none + go version + ``` :::{note} -If you need to install a specific version of Go use: +To install a specific version of Go use: - sudo apt install golang- +```none +sudo apt install golang- +``` ::: + ### Snap package 1. Install the latest Go snap: -```none -sudo snap install go --classic -``` + ```none + sudo snap install go --classic + ``` 2. Confirm successful installation: -```none -go version -``` + ```none + go version + ``` :::{note} To install a specific version of Go, specify the channel; for example: - sudo snap install go --channel=1.22/stable --classic +```none +sudo snap install go --channel=1.22/stable --classic +``` ::: -### Adding Go binary to path -If you are not using a package manager to install Go, you need -to add the Go binary to your `$PATH`. +### Adding the Go binary to `$PATH` -1. To temporarily add Go to your `$PATH`, run: +When not using a package manager to install Go, you need to add the Go binary to your `$PATH` environment variable. -```none -export PATH=$PATH:/path/to/your/go/bin -``` +* To temporarily add `go` to your `$PATH`, run: -2. To persist the change and make go available in new terminal sessions and across reboots, -append the above line to `$HOME/.profile` (or `/etc/profile`) and source the file: + ```none + export PATH=$PATH:/path/to/your/go/bin + ``` -```none -source $HOME/.profile -``` +* To persist the change and make `go` available in new terminal sessions and across reboots, append the above line to `$HOME/.profile` (or `/etc/profile`) and source the file: + + ```none + source $HOME/.profile + ``` :::{note} -The files to modify and commands to use when modifying the `$PATH` may vary -depending on your shell environment. +The files to modify and commands to use when modifying the `$PATH` may vary depending on your shell environment. ::: + + (precompiled-go-binaries)= ### Precompiled Go binaries @@ -92,47 +92,52 @@ Precompiled Go binaries are available in a compressed format on the [release pag 1. Fetch a specific Go version with `wget`: -```none -wget https://go.dev/dl/go.linux-amd64.tar.gz -``` + ```none + wget https://go.dev/dl/go.linux-amd64.tar.gz + ``` 2. Extract the files into an appropriate directory: -```none -sudo tar -C /usr/local -xzf go.linux-amd64.tar.gz -``` + ```none + sudo tar -C /usr/local -xzf go.linux-amd64.tar.gz + ``` 3. Add the `go` binary to your `$PATH` environment variable: -```none -export PATH=$PATH:/usr/local/go/bin -``` + ```none + export PATH=$PATH:/usr/local/go/bin + ``` ### Downloading and using multiple Go versions -Sometimes it may be necessary to run multiple Go versions -on the same machine. +Sometimes it may be necessary to run multiple Go versions on the same machine. -1. To install Go versions `1.21` and `1.23`, run: +1. To install Go versions 1.21 and 1.23, run: -```none -sudo apt install golang-1.21 golang-1.23 -``` + ```none + sudo apt install golang-1.21 golang-1.23 + ``` -Each `go` binary will be installed in `/usr/lib/go-/bin/` + Each `go` binary is installed in `/usr/lib/go-/bin/` 2. Test the `go` binaries by calling them with their full paths: -```none -/usr/lib/go-1.21/bin/go version -/usr/lib/go-1.23/bin/go version -``` + ```none + /usr/lib/go-1.21/bin/go version + /usr/lib/go-1.23/bin/go version + ``` + ## Editing and debugging +While it is possible to write and edit Go code using any plain-text editor, various Integrated Development Environments (IDEs) offer features to simplify the development process. + +Advanced text editors can also be extended using Language Server Protocol plugins to enhance the user experience. + + ### Integrated Development Environments and editors -Some of the most common Integrated Development Environments (IDEs) used for Go are: +Some of the most common IDEs used for Go are: - [Visual Studio Code](https://code.visualstudio.com/): a free editor with a dedicated [Go extension](https://marketplace.visualstudio.com/items?itemName=golang.Go) maintained by the Go Team - [GoLand](https://www.jetbrains.com/go/): a paid editor developed specifically for the Go programming language @@ -160,38 +165,36 @@ sudo apt install delve An overview of basic Delve usage is included in our [how to develop with Go](./go-use.md) guide. +A Go language server, [gopls](https://pkg.go.dev/golang.org/x/tools/gopls), is actively maintained, which has helped ensure that Go is widely supported across many editors, including Emacs, (Neo)Vim, and others. -A Go language server [gopls](https://pkg.go.dev/golang.org/x/tools/gopls) is actively maintained, which has helped ensure that Go is widely supported across many editors, including Emacs, (Neo)Vim and others. - -You can install gopls from the Ubuntu archive: +To install gopls from the Ubuntu archive: ```none sudo apt install gopls ``` :::{note} -If you have Go installed and `go` on your `$PATH` then -the latest version of many Go tools like gopls and Delve can be installed with: +If you have Go installed and `go` in your `$PATH`, then the latest version of many Go tools like `gopls` and Delve can be installed with: - go install url/of/tool/@latest +```none +go install url/of/tool/@latest +``` -This can be useful if you need a version of a tool that is not yet available -through the package manager. +This is useful when you need a version of a tool that is not yet available through the package manager. ::: + ## Cross-compilation -Go has excellent cross-platform build capabilities. -To build a program called `hello.go`, containing valid Go code, run: +Go has excellent cross-platform build capabilities. To build a program called `hello.go`, containing valid Go code, run: ```none go build hello.go ``` -This builds a binary that can be run on your Ubuntu system. -Different target systems can be set for the compiler. +This builds a binary that can be run on your Ubuntu system.Different target systems can be set for the compiler. -To set the environment for a Windows amd64 build, run: +To set the environment for a Windows AMD64 build, run: ```none export GOOS=windows GOARCH=amd64 @@ -206,10 +209,10 @@ To test the (Linux) binary, execute it by running: ``` :::{note} -For the basics of writing and testing a Hello World program in Go -see our [how to develop with Go](./go-use.md) guide. +For the basics of writing and testing a Hello World program in Go,see our [how to develop with Go](./go-use.md) guide. ::: + ### Building for multiple targets For a full list of targets, run: @@ -224,9 +227,7 @@ For this example, filter the output to Windows and Linux on `amd`: go tool dist list | grep 'amd' | grep -E 'windows|linux' ``` -Create a makefile that automatically sets -the build environment and creates executable binaries for -both Windows and Linux platforms: +Create a Makefile that automatically sets the build environment and creates executable binaries for both Windows and Linux platforms: ```make EXE=heygo @@ -241,13 +242,13 @@ windows: $(WINDOWS) linux: $(LINUX) $(WINDOWS): - env GOOS=windows GOARCH=amd64 go build -v -o $(WINDOWS) -ldflags="-s -w" ./heygo.go + env GOOS=windows GOARCH=amd64 go build -v -o $(WINDOWS) -ldflags="-s -w" ./heygo.go $(LINUX): - env GOOS=linux GOARCH=amd64 go build -v -o $(LINUX) -ldflags="-s -w" ./heygo.go + env GOOS=linux GOARCH=amd64 go build -v -o $(LINUX) -ldflags="-s -w" ./heygo.go clean: - rm -f $(WINDOWS) $(LINUX) + rm -f $(WINDOWS) $(LINUX) ``` Generate the builds and test the Linux build: @@ -256,4 +257,3 @@ Generate the builds and test the Linux build: make all ./hello_linux_amd64 ``` - diff --git a/docs/howto/go-use.md b/docs/howto/go-use.md index 7965f44..45b88ed 100644 --- a/docs/howto/go-use.md +++ b/docs/howto/go-use.md @@ -1,342 +1,323 @@ # How to develop using Go on Ubuntu -This guide shows how to build, run and debug Go programs on Ubuntu. -For instructions on how to install Go and the Delve debugger, refer to -our dedicated [install and set up Go](./go-setup.md) guide. +This guide shows how to build, run, and debug Go programs on Ubuntu. For instructions on how to install Go and the Delve debugger, refer to our dedicated [install and set up Go](./go-setup.md) guide. + ## Creating a Go project 1. Create a project directory and change into it: -```none -mkdir heygo && cd heygo -``` + ```none + mkdir heygo && cd heygo + ``` + +2. Initialize the project as a Go module. When initializing a module, provide the URL of a repository for hosting the source. For testing purposes, use a generic URL: -2. Initialise the project as a Go module. -When initiliasing a module, provide the url of a repository for hosting the source. -For testing purposes, you can use a generic URL: + ```none + go mod init youruser.github.com/heygo + ``` -```none -go mod init youruser.github.com/heygo -``` -The `go mod init` command creates a `go.mod` file in the project root that tracks the -version of Go used for the program and any external dependencies. + The `go mod init` command creates a `go.mod` file in the project root that tracks the version of Go used for the program and any external dependencies. -:::{note} -If you `go get ` to fetch a specific package or `go mod tidy` -to scan your code for references to external packages, the `go.mod` file -is updated automatically. -::: + :::{note} + When you use `go get ` to fetch a specific package or `go mod tidy` to scan your code for references to external packages, the `go.mod` file is updated automatically. + ::: 3. Create a `heygo.go` file with the following content: -```{code-block} go -:caption: `heygo.go` -package main + ```{code-block} go + :caption: `heygo.go` + package main + + import "fmt" -import "fmt" + func main() { + fmt.Println("Hey Go!") + } + ``` -func main() { - fmt.Println("Hey Go!") -} -``` + Here, the name of the package, `main`, is identified, and the `fmt` package is imported from the Go standard library. A `main()` function is defined and a method is called from `fmt` to print a line of text that is passed as an argument. -Here, the name of the package — `main` — is identified, and the `fmt` -package is imported from the Go standard library. -A `main()` function is defined and a method is called from `fmt` -to print a line of text that is passed as an argument. +4. To compile and run your program, use the `go run` command. If you pass the current directory as an argument (`.`), the Go compiler automatically finds the main package and main function during the build: -4. To compile and run your program, use the `go run` command. -If you pass the current directory as an argument ("."), the Go compiler -automatically finds the main package and main function during -the build: + ```none + go run . + ``` -```none -go run . -``` + This outputs: -This outputs: + ```none + Hey Go! + ``` -```none -Hey Go! -``` ## Improving Go code with the help of tooling -Go's built-in tooling, including `go vet` and `gofmt`, can be used to debug and format code. -Delve is recommended for advanced debugging. +Tooling built in Go, including `go vet` and `gofmt`, can be used to debug and format code. Delve is recommended for advanced debugging. -### Go vet and gofmt -1. In the same directory where you initialised the module, -delete `heygo.go` and replace it with a new file, `heygoV2.go`: +### Go vet and `gofmt` -```{code-block} go -:caption: `heygoV2.go` -:linenos: -package main; +1. In the same directory where you initialized the module, delete `heygo.go` and replace it with a new file, `heygoV2.go`: -import "fmt"; + ```{code-block} go + :caption: `heygoV2.go` + :linenos: + package main; -func main() { -fmt.Println(greeting); -} -``` + import "fmt"; -This code contains a bug and is poorly formatted. + func main() { + fmt.Println(greeting); + } + ``` -2. Run `go vet heygoV2.go` + This code contains a bug and is poorly formatted. -This outputs the following error: +2. Run `go vet` on the file: -```none -vet: ./heygoV2.go:6:14: undefined: greeting -``` + ```none + got vet heygoV2.go + vet: ./heygoV2.go:6:14: undefined: greeting + ``` 3. Fix the error by defining the `greeting` variable: -```{code-block} diff -:caption: `heygoV2.go` -:linenos: -package main + ```{code-block} diff + :caption: `heygoV2.go` + :linenos: + package main -import "fmt"; + import "fmt"; -+var greeting="Hey Go!" + +var greeting="Hey Go!" -func main() { -fmt.Println(greeting) -} -``` + func main() { + fmt.Println(greeting) + } + ``` -4. Running `gofmt -w heygoV2.go` identifies formatting -issues and writes necessary changes to the file: +4. Running `gofmt` with the `-w` parameter on the file identifies formatting issues and writes necessary changes to the file: -```{code-block} diff -:caption: `heygoV2.go` -:linenos: -package main + ```none + gofmt -w heygoV2.go + ``` -- import "fmt"; -+ import "fmt" + In this case, unneeded semicolons are removed from the `import` line, and the call to the print method in the `main` function is indented correctly: -var greeting="Hey Go!" + ```{code-block} diff + :caption: `heygoV2.go` + :linenos: + package main -func main() { --fmt.Println(greeting) -+ fmt.Println(greeting) -} -``` + - import "fmt"; + + import "fmt" -In this case, unneeded semicolons are removed -from the `import` line, and the call to the print -method in the `main` function is indented correctly. + var greeting="Hey Go!" + + func main() { + -fmt.Println(greeting) + + fmt.Println(greeting) + } + ``` -### Debugging with Delve -[Delve](https://github.com/go-delve/delve) is a popular debugger for Go code. -Many editors, including VSCode and GoLang, support Delve. -In this guide, Delve is used as a command-line debugging tool. +### Debugging with Delve -1. Create a file to debug called `main.go` in a new folder where you have initialised a Go module. +[Delve](https://github.com/go-delve/delve) is a popular debugger for Go code. Many editors, including VSCode and GoLand, support Delve. In this guide, Delve is used as a command-line debugging tool. -This program is intended to calculate the average value from an array of integers. -However, there is a bug in the `for` loop that needs to be investigated: +1. Create a file to debug called `main.go` in a new folder where you have initialized a Go module. -```{code-block} go -:caption: `main.go` -:linenos: -:emphasize-lines: 7,8,9 -package main + This program is intended to calculate the average value from an array of integers. However, there is a bug in the `for` loop that needs to be investigated: -import "fmt" + ```{code-block} go + :caption: `main.go` + :linenos: + :emphasize-lines: 7,8,9 + package main -func calculateAverage(numbers []int) float64 { - sum := 0 - for i := 0; i <= len(numbers); i++ { - sum += numbers[i] - } - return float64(sum) / float64(len(numbers)) -} + import "fmt" -func main() { - numbers := []int{10, 45, 30} - average := calculateAverage(numbers) + func calculateAverage(numbers []int) float64 { + sum := 0 + for i := 0; i <= len(numbers); i++ { + sum += numbers[i] + } + return float64(sum) / float64(len(numbers)) + } - fmt.Printf("Average value of is: %.2f\n", average) -} + func main() { + numbers := []int{10, 45, 30} + average := calculateAverage(numbers) -``` + fmt.Printf("Average value of is: %.2f\n", average) + } + ``` 2. Initiate a debugging session with Delve by running `dlv debug` on the file: -```none -dlv debug main.go -``` + ```none + dlv debug main.go + ``` -This puts you in an interactive debugging session. -You can interact with the debugger by entering commands after the `(dlv)` prompt: + This puts you in an interactive debugging session. You can interact with the debugger by entering commands after the `(dlv)` prompt: -```none -Type 'help' for list of commands. -(dlv) -``` + ```none + Type 'help' for list of commands. + (dlv) + ``` -To exit at any time: + To exit at any time: -```none -(dlv) exit -``` + ```none + (dlv) exit + ``` -Delve is used in this example to debug the `calculateAverage` function. -You need to be in a debugging session, indicated by the `(dlv)` prompt. + Delve is used in this example to debug the `calculateAverage` function. You need to be in a debugging session, indicated by the `(dlv)` prompt. 3. Set a break point at line 6: -```none -(dlv) break main.go:6 -``` + ```none + (dlv) break main.go:6 + ``` -If the break point is set successfully, you get the following message: + If the break point is set successfully, you get the following message: -```none -Breakpoint 1 set at 0x49cee9 for main.calculateAverage() ./main.go:6 -``` + ```none + Breakpoint 1 set at 0x49cee9 for main.calculateAverage() ./main.go:6 + ``` 4. Continue to the `for` loop: -```none -(dlv) continue -``` - -Delve shows visually where you are in the code with "=>"; in this case, at the start of the `for` loop: - -```none -> [Breakpoint 1] main.calculateAverage() ./main.go:7 (hits goroutine(1):1 total:1) (PC: 0x49cee9) - 2: - 3: import "fmt" - 4: - 5: func calculateAverage(numbers []int) float64 { - 6: sum := 0 -=> 7: for i := 0; i <= len(numbers); i++ { - 8: sum += numbers[i] - 9: } - 10: return float64(sum) / float64(len(numbers)) - 11: } - 12: -``` + ```none + (dlv) continue + ``` + + Delve shows visually where you are in the code with `=>`; in this case, at the start of the `for` loop: + + ```none + > [Breakpoint 1] main.calculateAverage() ./main.go:7 (hits goroutine(1):1 total:1) (PC: 0x49cee9) + 2: + 3: import "fmt" + 4: + 5: func calculateAverage(numbers []int) float64 { + 6: sum := 0 + => 7: for i := 0; i <= len(numbers); i++ { + 8: sum += numbers[i] + 9: } + 10: return float64(sum) / float64(len(numbers)) + 11: } + 12: + ``` 5. Check the value of `sum` with the `print` command: -```none -(dlv) print sum -``` + ```none + (dlv) print sum + ``` -As expected, `sum` has been initialised to `0`. + As expected, `sum` has been initialized to `0`. 6. Step through the `for` loop with: -```none -(dlv) step -``` - -Again, your position in the code is shown: - -```none -> main.calculateAverage() ./main.go:8 (PC: 0x49cf09) - 3: import "fmt" - 4: - 5: func calculateAverage(numbers []int) float64 { - 6: sum := 0 - 7: for i := 0; i <= len(numbers); i++ { -=> 8: sum += numbers[i] - 9: } - 10: return float64(sum) / float64(len(numbers)) - 11: } - 12: - 13: func main() { -``` - -:::{note} -This output showing the code position is truncated for the remainder of this guide. -::: + ```none + (dlv) step + ``` + + Again, your position in the code is shown: + + ```none + > main.calculateAverage() ./main.go:8 (PC: 0x49cf09) + 3: import "fmt" + 4: + 5: func calculateAverage(numbers []int) float64 { + 6: sum := 0 + 7: for i := 0; i <= len(numbers); i++ { + => 8: sum += numbers[i] + 9: } + 10: return float64(sum) / float64(len(numbers)) + 11: } + 12: + 13: func main() { + ``` + + :::{note} + This output showing the code position is truncated for the remainder of this guide. + ::: 7. Check the value of the index: -```none -(dlv) print i -``` + ```none + (dlv) print i + ``` -This outputs `0`. + This outputs `0`. -8. Step again to confirm that the `sum` value has been incremented -with the first element in the `numbers` array: +8. Step again to confirm that the `sum` value has been incremented with the first element in the `numbers` array: -```none -(dlv) step -... -... -(dlv) print sum -10 -(dlv) print numbers -[]int len: 3, cap: 3, [10,45,30] -``` + ```none + (dlv) step + ... + ... + (dlv) print sum + 10 + (dlv) print numbers + []int len: 3, cap: 3, [10,45,30] + ``` -So far so good, the sum is equal to the first element of the list. + So far so good; the sum is equal to the first element of the list. 9. Keep stepping through the code until you find the bug: -```none -(dlv) step -... -... -(dlv) print i -1 -(dlv) step -... -... -(dlv) print sum -55 -(dlv) step -... -... -(dlv) print i -2 -(dlv) step -... -... -(dlv) print sum -85 -(dlv) step -... -... -(dlv) print i -3 -(dlv) step - -``` - -The last step causes a panic: - -```none -> [unrecovered-panic] runtime.fatalpanic() /usr/local/go/src/runtime/panic.go:1217 (hits goroutine(1):1 total:1) (PC: 0x43a604) -Warning: debugging optimized function - runtime.curg._panic.arg: interface {}(string) "runtime error: index out of range [3] with length 3" -``` - -The array has a length of `3` but the index is initialised at `0`. -This means the loop attempts to run four times on three values. -There is an off-by-one error. + ```none + (dlv) step + ... + ... + (dlv) print i + 1 + (dlv) step + ... + ... + (dlv) print sum + 55 + (dlv) step + ... + ... + (dlv) print i + 2 + (dlv) step + ... + ... + (dlv) print sum + 85 + (dlv) step + ... + ... + (dlv) print i + 3 + (dlv) step + + ``` + + The last step causes a panic: + + ```none + > [unrecovered-panic] runtime.fatalpanic() /usr/local/go/src/runtime/panic.go:1217 (hits goroutine(1):1 total:1) (PC: 0x43a604) + Warning: debugging optimized function + runtime.curg._panic.arg: interface {}(string) "runtime error: index out of range [3] with length 3" + ``` + + The array has a length of `3`, but the index is initialized at `0`. This means the loop attempts to run four times on three values. There is an off-by-one error. 10. Change the code as follows to fix the error: -```{code-block} diff -func calculateAverage(numbers []int) float64 { - sum := 0 -- for i := 0; i <= len(numbers); i++ { -+ for i := 0; i < len(numbers); i++ { - sum += numbers[i] - } - return float64(sum) / float64(len(numbers)) -} -``` + ```diff + func calculateAverage(numbers []int) float64 { + sum := 0 + - for i := 0; i <= len(numbers); i++ { + + for i := 0; i < len(numbers); i++ { + sum += numbers[i] + } + return float64(sum) / float64(len(numbers)) + } + ```