Written for a friend.
sudo apt install golang-go
Make a file called main.go
package main
func main() {
}
Compile it using go build main.go
. Produces a binary, which you can run by typing ./main
.
Go is compiled, not interpreted, so it is very fast. But it is garbage collected, so it includes a small runtime within the binary.
Benefits of compiled Go binaries over Java JARs:
- Completely self contained: don't need any JRE installed, literally don't need anything installed
- Single binary contains everything it needs, so it makes it easy to share and deploy
- Fast
There is only one way to format code
Run go fmt
And it will automatically format your code the one true way.
Every go file needs a package. A package is just a namespace. We've called the package the same name as the file.
Here is an import example. I've deliberately imported a module called "os" but I'm not using it.
package main
import (
"fmt"
"os"
)
func main() {
}
Go will tell you off if you import something you don't use. This forces you to only have code that you need and keeps binary sizes smaller.
When assigning a value to a variable you use =
as usual.
When declaring a new variable and assigning a value to it at the same time, you use :=
.
You don't need to declare the type of the variable when declaring because this is established the first time you assign something to it. It then retains the same type for its lifetime, you can't change the type. So it is still a strongly typed system.
func main() {
shell := os.Getenv("MY_SHELL_VARIABLE") // this declares a variable called shell and assigns it
shell = "FOOBAR" // i can change the value like this
}
Go tells you off if you declare or assign variables that you don't use. This forces you to keep your code clean at all points in time.
If you really want to bypass this, set the variable name to _
which is you telling the compiler you don't care.
In the terminal go mod
manages your dependencies a bit like Maven except without the bullshit/nightmares.
go mod init myapplication
This just creates a file in our project called go.mod
that will list our dependencies.
Now we can add import as much as we like. Here is an example program where we make a simple webserver. Notice the third party library mux
that we use.
import (
"fmt"
"github.com/gorilla/mux"
)
func rootPage(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte(`<html>Test page</html>`))
}
func main() {
fmt.Println("Hello Yuri")
router := mux.NewRouter()
router.HandleFunc("/", rootPage).Methods("GET")
err := http.ListenAndServe("80", router)
}
To make sure that go.mod is updated just run go mod tidy
and it will automatically scan through our program to find any third party libraries and add them to our dependency list.
To fetch and build everything run go build
without any arguments. It will automatically download dependencies and compile everything.
As you may have noticed above, one thing that is different about Go is that the type always comes after the variable name. This is just a small but annoying difference to get used to.
As you may have noticed above, the keyword func is used where we usually put return type in Java. In Goland return type is put after parameters. This example has string as a return type:
func GetSerialNumber(orderID int) (string) {
// body implementation
return "FOOBAR"
}
In Golang you can also return two things instead of one, if you want to. Here we return a string and a return code that can be used for error handling.
func GetSerialNumber(orderID int) (string, err) {
// body implementation
return "FOOBAR", 0
}
This above example is typically useful if you want to keep return values completely separate from errors.
Go does not have generics. They may come in Go version 2. You can work around this by using interfaces, which work just like how interfaces do in Java.
Golang is not fully OOP because it does not support inheritance. If you want to mimic inheritance you have to use interfaces. However, inheritence is a bit overrated, so you might not miss it.
To make a simple object called Student
with the method Print
see below
package main
import "fmt"
type Student struct {
Name string
Roll int
}
func (s Student) Print() {
fmt.Println(s)
}
func main() {
jack := Student{"Jack", 123}
jack.Print() // {Jack 123}
}
As you can see above, adding func (s Student)
before the method name just goes to show that methods are simply just functions restricted to a particular namespace and variable scope.
Arraylists are called "slices" in Go. They are part of the language.
Example here: https://play.golang.org/p/HnQ30wOftb
You can also slice them up like in Python.
Go has in built language support for maps. No imports needed.
employeeSalary := make(map[string]int)
employeeSalary["Tom"] = 2000 //Adding a key value
val, exists := employeeSalary["Tom"]
if exists {
fmt.Printf("Val: %d\n", val)
}
By default Go is pass-by-value, just like Java. But Go also does support pass-by-reference, which is obviously wanted when you need good performance (by using pointers, no copies are made by the compiler).
func passByValue(item string) {
item = "Foobar"
}
func passByReference(item *string) {
*item = "Foobar"
}
You don't have to worry about "freeing" references like in C/C++ because this is a garbage collected language.
In Java to have any form of concurrency you need to use threads. These can be quite bulky to work with due to the amount of boilerplate.
Go has a language concept for lightweight threads known as co-routines. These are called goroutines. You don't need any imports. To kick off a function call to run in the background, precede it with the keyword go
package main
import "fmt"
import "time"
func backgroundTask() {
for i := 0; i < 3; i++ {
fmt.Println("Hello from background task")
time.Sleep(1 * time.Second)
}
}
func main() {
go backgroundTask()
for i := 0; i < 3; i++ {
fmt.Println("Hello from foreground task")
time.Sleep(1 * time.Second)
}
}
package main
import "fmt"
type Channel struct {
name string
}
func main() {
var channels []Channel // an empty list
channels = append(channels, Channel{name:"some channel name"})
channels = append(channels, Channel{name:"some other name"})
// "%+v" prints fields names of structs along with values.
fmt.Printf("%+v\n", channels)
// or, create a list of a pre-determined length:
ten_channels := make([]Channel, 10)
for i := 0; i < 10; i++ {
ten_channels[i].name = fmt.Sprintf("chan %d", i)
}
fmt.Println(ten_channels)
}