Before deep diving into essentials of Go tooling, let’s mention some of these awesome features.
Compiled language: By using build command, go source files are compiled to machine code. After linking process, one single file (executable) can be generated from go packages.
Strongly-typed language: It’s not permitted to assign a type to variable with another type, literals and types should be in alignment.
Statically-typed language: Performs type checking at compile time, not during runtime/execution time.
Garbage Collection: Since we highly use dynamic memory allocation, some metrics are observed for releasing memory by cleaning unused objects in heap area.
Has Pointers: By default, has pointers -I heard the “Hell Yeah!” sounds of C++ developers, btw- but has no pointer arithmetic.
Concurrency Model: This is the most thrilling point. Go has its own concurrency model, which means having unique primitives for implementing it, such as goroutines and channels, which will be discussed in detail later on.
Favor Composition over Inheritance: There are no class-object architecture, friend class or friend function. We have interfaces and structs for building excellent programs!
In theoretically, escape analysis is a compiler optimization technique, but in Go, it is meant as one of the stages of the compilation phase. Basically, it proposes a decision mechanism for projecting which objects will be stored on heap, which objects will be stored on stack and which of them will be inlined by analyzing source code. Only thing to do is passing -race flag while building/running your go package.
As stated earlier, Go has its own built-in primitives which are ready-to-use for developing concurrent applications. Let’s have a look at concurrency primitives that are mentioned above.
Goroutines can be defined as lightweight threads. Go implements these goroutines as functions -as can be seen from this function- and maps these goroutines to operating system threads by using m:n threading model.
Channels are synchronization primitives and can be thought as pipes -Let’s say hello to our Unix folks!- between goroutines.
Go modules are the essential component which is designed for the source code distribution, we may think of a logical container of our source code which help us being ready-to-distribute/package/ship for our newly-developed components. We can remind “jar packages for the Java source code” here for an analogy for a better description of purpose of the usage of go modules.
Once we go deeper, go modules can contain one or more go packages which may indicate potential dependencies, it is highly encouraged you to have a look at this story for a better explanation. As a best practice, it is recommended that designing packages as atomic units while designing modules as an enveloping structure for the packages. For summing up, as per Go wiki, while each Go module contains one or more Go packages, each Go package contains one ore more source files within a directory.
As stated above, analogy between “jar packages” and “go modules” may help us for better understanding the purpose of usage. But the point is, while there is a need for a compilation stage for creating jar files, there are no such restriction for the go modules, if you have a bunch of files (go source files, go test files, configuration files, etc.) under your repository configured with a version control system(such as git), you may prepare a go module for distributing your repository with one or two simple steps! Go has decentralized approach once we compare to official registry/repository approach, such as PyPI(Python), npm, etc, please have a look at this article. Everything you committed/tagged to your upstream repository by obeying the semantic versioning rules are the actual versions of your software distribution.
The first step is, initializing your go module via go mod init command, we can make an analogy here with the git init command.
go mod init github.com/celikelozdinc/logger
Once we initialized our module, a file named as go.mod is created under the project directory. Once we review this file, it indicates the import path of our module, by using this statement, other modules can import our modules by using statement import github.com/celikelozdinc/logger
Besides this, go.mod also indicates the dependent modules of our logger module as indicated by require statement. For this example, logger module needs two different versions of formatter module.
module github.com/celikelozdinc/loggergo 1.15require (
github.com/celikelozdinc/formatter v1.1.0 github.com/celikelozdinc/formatter/v2 v2.0.0
So, our module is ready. After we tagged and committed our recent changes to upstream repository, one can use the command below for injecting the dependency to her/his repository and use it as well :
go get github.com/celikelozdinc/formatter