Table of Contents
- Introduction
- Go Hello, World!
- Comments
- Semicolons
- Functions
- Variables
- Built-in Types
- User-Defined Types
- Arrays
- Slices
- Constants
- Pointers
- Statements
- Anonymous Functions
- Methods
- References
- History
The Go is a general-purpose programming language created at Google. It’s an open source project to make programmers more productive. Go is very expressive and clean. Its concurrent programming capability helps programmers to write programs that need to get the most out of multicore and networked machines. Go is statically typed and a compiled programming language. Go compiles programs quickly into machine code. It also has the facility of garbage collection. The garbage collection makes concurrent code far easier to write. And beside all these, the Go has a very rich standard library.
Let’s have a look at the following ‘Hello, World!’ program written in Go:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
From this hello world program, we’ve got the following:
-
A package name called main. But what is it actually?
Well, every Go program is consists of packages. We either write a library or an executable program. An executable program belongs to the package main. This means executable programs start running in the package main. So at the beginning of a Go program, the package name is needed to be specified in the following way:
package main
This will tell the compiler that this program is an executable program. Not a library.
But if we write a library, it means we are writing a new package and we give a name to the package. Suppose our new library/package name will be ‘simplelib’
. If so, then at the beginning of every source file of our new library/package, the following line must be present:
package simplelib
This is how we tell the compiler that this source file belongs to the package ‘simplelib’.
-
We found an import statement that is importing something called "fmt".
An import statement is used to import external Go packages into another Go package/program. Quite similar to C’s include preprocessor directive.
The import statement is used in the following way:
import "package_name"
Or (This is recommended syntax when importing more than one package):
import (
"package_name"
"package_name2"
"package_name3"
"package_nameN"
)
In the hello world example, a package called fmt is imported. The package fmt implements formatted I/O with functions analogous to C's printf
and scanf
. The format 'verbs' are derived from C's but are simpler. So this package is necessary when interacting with console window.
-
A user-defined function named main
.
This function is analogous to C’s entry point main. Go program’s code execution begins from this function.
-
Inside the main
function, another function named Println
, is being called to print the "Hello, World!" message.
The Println
function is used to print something on the console window. This function belongs to the package fmt and that is why this package is imported at the very beginning of the hello world program.
Go's Println
very much useful. We can use this function to print values like strings or ints. Even, we can print more complex things like array. We don't need any loop to print elements of an array. The Println
function automatically inserts a newline after printing all the values.
Now, we will go through some features of the Go programming language.
We programmers use comments to describe a piece of code. That makes the code easier to understand and read. There are mainly two forms of comment in Go:
- Single line comment
- Multiline comment
Here is an example of the single line comment:
And the following is an example of the multi line comment:
Now we can see that doing comments in Go is analogous to C++, C#, Java etc.
Unlike C, C++, C#, Java etc. programming language, there is no need of using semicolons in Go to indicate statement’s termination. Go’s lexer uses a simple rule to deduce a statement’s end.
Idiomatic Go programs have semicolons only in places such as for loop clauses, to separate the initializer, condition, and continuation elements. They are also necessary to separate multiple statements on a line.
In Go, function creations have the following syntax:
func function_name(input_parameter_list) (return_type_list) {
function_stataments
}
We can see that function creation begins with a func keyword. Then we have to specify the function’s name. Function’s name should be any valid Go identifier. After that, we put all the input parameters. If a function has more than one parameter in the parameter list, they are needed to be separated via comma. And also, parameters are must be bounded by braces ( ). Then we specify the return type of the function. But if our function doesn’t require returning any value, then we can omit the return type.
For example:
func sing() {
}
Another example of a function that does addition between two numbers and returns the result as an integer value:
func add(x int, y int) int {
return x + y
}
Unlike C, functions can have multiple return values in Go, and it’s a built-in feature. In this case, it will require braces ( ) around the return types and all the return types are must be separated via comma operators.
For example:
func getall() (int, string) {
return 10, "that's all!"
}
And when we call the function, multiple return values are acquired in this way (this example code uses the short variable declaration form. We will discuss about it in the ‘short variable declarations’ section):
i, s := getall()
Now variable i
contains the first return value (which is 10) and the variable s
contains the second return value ( which is a string and is “that's all!").
In Go, this Multiple Return Values feature is usually used to return both result and error values from a function.
A global function that is named as main
is recognized as the program's entry point:
func main() {
}
Note that Go’s main function does not have parameters (like argc
, argv
in C) to access command-line arguments. However, a Go package called os gives a solution to that problem. The Args
variable of the os package provides access to raw command-line arguments. For example:
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println(len(os.Args), os.Args)
}
Variable declarations in Go have the following syntaxes:
- var identifier_list type
- var identifier_list = initializer_expression_list
- var identifier_list type = initializer_expression_list
Example:
var i int
var a, b, c int
var k = 0
var x, y int = 12, 9
We can see that variable declaration in Go begin with a var keyword. And also, it isn't always require specifying the type explicitly. We can let the compiler to deduce variable’s type from the initializer expression. This is just like we do in C++11 using an auto keyword.
A short variable declaration has the following syntax which doesn’t require the var keyword at the beginning of declaration and deduces variable’s type automatically:
identifier_list := initializer_expression_list
Example:
i := 80
Or:
a, b, c := 10, 20, 30
This makes coding easier and helps to write codes more quickly.
Note that the short variable declaration form can only be used inside a function. You can’t use it in the global scope.
The following is the list of all the basic/built-in types available in Go:
- bool - Consists of the two predefined constants true and false
- string - Represents sequence of characters
- int8 - Signed 8-bit integer (-128 to 127)
- int16 - Signed 16-bit integer (-32768 to 32767)
- int32 - Signed 32-bit integer (-2147483648 to 2147483647)
- int64 - Signed 64-bit integer (-9223372036854775808 to 9223372036854775807)
- int - Signed 32 or 64 bit integer ( depends on system )
- uint8 - Unsigned 8-bit integer (0 to 255)
- uint16 - Unsigned 16-bit integer (0 to 65535)
- uint32 - Unsigned 32-bit integer (0 to 4294967295)
- uint64 - Unsigned 64-bit integer (0 to 18446744073709551615)
- uint - Unsigned 32 or 64 bit integer ( depends on system )
- uintptr - Unsigned 32 or 64 bit integer ( depends on system )
- byte ( alias for uint8 )
- rune (alias for int32 )
- float32 - IEEE-754 32-bit floating-point number
- float64 - IEEE-754 64-bit floating-point number
- complex64 - Complex number with float32 real and imaginary parts
- complex128 - Complex number with float64 real and imaginary parts
Note: The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. When you need an integer value you should use int unless you have a specific reason to use a sized or unsigned integer type.
There are mainly two kinds of user-defined types in Go:
- Structure
- Interface
A structure allows programmers to hold several variables of same or different types together.
In Go, type and struct keywords are needed to be used in order to create a structure. The syntax of creating a structure is the following:
type identifier struct {
structure_members
}
Example of a structure that is consists of four variables of same types:
type Rectangle struct {
left int
top int
right int
bottom int
}
Another example of a structure that is consists of three variables of different types:
type Person struct {
name string
age int
income float32
}
Use of structure:
var rect Rectangle
rect.left = 20
rect.top = 10
rect.right = 50
rect.bottom = 60
fmt.Println("left: ", rect.left)
fmt.Println("top: ", rect.top)
fmt.Println("right: ", rect.right)
fmt.Println("bottom: ", rect.bottom)
Go allows to initialize a structure's members during variable declaration:
var r = Rectangle{20, 10, 50, 60}
The syntax of an interface in Go is the following:
type identifier interface {
interface_methods
}
Example:
type Person interface {
Name() string
Age() int
Flee()
Die()
}
Another practical example of using Interfaces:
package main
import "fmt"
type Greeting interface {
Say()
}
type EngGreeting struct {
Greeting
}
type BanGreeting struct {
Greeting
}
func (e * EngGreeting) Say() {
fmt.Println("Hello Forhad Reja")
}
func (e * BanGreeting) Say() {
fmt.Println("ওহে ফরহাদ রেজা")
}
func say(g Greeting) {
g.Say()
}
func main() {
e := new(EngGreeting)
say(e)
b := new(BanGreeting)
say(b)
}
Arrays creation syntax in Go is slightly different than C++, C# etc programming language. The square brackets are placed just before the element type:
[ array_length_expression ] element_type
The array length expression must be an integer contsant greater than zero.
Example of creating single and multidimensional array:
var ar [10]int
var mat [4][4]int
var big [10][10][10]int
Arrays can be initialized when creating:
var ar = [3]int{12, 10, 32}
Let's see an example of assigning value to a array’s single element. Note that array's index starts at 0 and the last index is one less than the array's total length:
ar[0] = 5
mat[0][0] = 12
Accessing array’s single element:
fmt.Println(ar[0], mat[0][0])
There is no built-in dynamic arrays in Go. But wait… there are slices.
Slices allow us to increase the size of an array dynamically. Slices are can be created using the following syntax in Go:
[ ] element_type
For example:
var da []int = []int{9, 8, 7}
fmt.Println(da)
Prints:
[9 8 7]
Go has built-in functions to perform operations on a slice/dynamic array. Here is an example of adding new item to the array using the built-in append
function:
da = append(da, 4)
fmt.Println(da)
Now it prints:
[9 8 7 4]
Constants are fixed values that are can’t be changed during the program’s execution. Constant declarations are begins with a const keyword. The syntax is similar to the syntax of variable declaration, except the var keyword is replaced with the const keyword:
const Pi float64 = 3.14159265358979323846
const zero = 0.0
const (
size int64 = 1024
eof = -1
)
const a, b, c = 3, 4, "foo"
const u, v float32 = 0, 3
Like C, C++, Go supports pointers. We can create pointer variables that can hold another variable’s address. And through the pointer variable, we can set a value to the original variable. An ampersand & operator is used to take a variable’s address and an asterisk * operator is used for dereferencing.
To create a pointer variable with explicit type, ‘*’ symbol must be placed just before the type name.
Example of creating a pointer variable with explicit type:
var p *int
Usage:
var n = 42
p = &n
*p = 10
Now, if we print the value of the variable n
, we will get exactly 10.
We can also create pointers using the short variable declaration form:
p := &n
One thing to note that Go has no pointer arithmetic. This means you can’t simply do operations like p++
or p += 1
.
So the following statement:
p++
Will generate this error:
invalid operation: p++ (non-numeric type *int)
There are many kinds of statements in Go. We will discuss about few of them.
-
If/Else Statement
if condition_expression {
// if body
} else {
// else body
}
The if/else statement works on condition. If the given condition expression evaluates to true, the “if” body’s codes are executed, otherwise, if present, the “else” body’s codes are executed.
Example:
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
One can use the ‘if’ without an ‘else’. For example:
if age == 12 {
fmt.Println("too young")
}
Note that you don’t need parentheses around conditions in Go that usually needs in C++, C#, Java etc programming languages. And also, there is no ternary operator in Go. You have to use a full if statement even for basic conditions.
-
For Statement
The for is Go’s only loop statement. There is no such while, do-while, foreach etc. loop statements in Go. Although, the for statement of Go can cover all of them.
The following is the simplest form of the for statement and works just like a while loop statement:
i := 0
for i < 10 {
fmt.Println(i)
i = i + 1
}
The classic initial/condition/after for loop:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
And this is another form of the for statement with range clause. This form is used to iterate over a slice or map:
ar := []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range ar {
fmt.Printf(i, v)
}
When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
-
Switch Statement
Go’s switch statement is very much like the switch statement of C++, except each case body breaks automatically. Doesn’t need to put a break statement explicitly:
switch age {
case 10:
fmt.Println("Kid")
case 16:
fmt.Println("Teen")
default:
fmt.Printf("Why are you?")
}
Although, one can use a fallthrough statement to fall through a case to the next case:
switch age {
case 8:
fallthrough
case 10:
fmt.Println("Kid")
case 16:
fmt.Println("Teen")
default:
fmt.Println("Why are you?")
}
-
Defer statement
A defer statement is used to call a function whose execution doesn’t happen immediately. Rather the execution is deferred to the moment the surrounding function returns. The defer statement is usually used to simplify functions that perform various clean-up actions.
Let’s see an example of how the defer statements work:
package main
import "fmt"
func main() {
defer fmt.Println("Hoorraa!… I’m deferred!")
defer fmt.Println("Yeah… I’m also deferred!")
fmt.Printf("Stop deferring!")
return
}
Prints the following:
Stop deferring!
Yeah… I’m also deferred!
Hoorraa!… I’m deferred!
Now we can see that the execution order is totally reversed. And that’s how the defer statement actually works. The last deferred function call gets executed first when the surrounding function returns.
Another example:
func main() {
for i := 0; i <= 3; i++ {
defer fmt.Print(i)
}
}
Prints:
3 2 1 0
Go language supports anonymous functions. Anonymous functions are useful when we want to create functions without names! They are often called lambda functions or simply lambda.
Example:
package main
import "fmt"
func main() {
func() {
fmt.Println("Hello, World!")
}()
}
Example 2:
greeting := func() {
fmt.Println("Hello, World!")
}
greeting()
Go’s anonymous functions can form closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense, the function is "bound" to the variables.
Example:
package main
import "fmt"
func adder() func(int) int {
num := 0
return func(x int) int {
num += x
return num
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(pos(i), neg(-i))
}
}
Go does not have classes. However, we can define methods on struct types. A method basically is a function with a special receiver argument. The receiver argument appears in its own argument list between the func keyword and the method name.
In the following example, we will implement two methods called width
and height
for the Rectangle
struct:
package main
import "fmt"
type Rectangle struct {
left int
top int
right int
bottom int
}
func (r *Rectangle) width() int {
return r.right - r.left
}
func (r Rectangle) height() int {
return r.bottom - r.top
}
func main() {
r := Rectangle{20, 10, 50, 50}
fmt.Println("width: ", r.width())
fmt.Println("height: ", r.height())
}
Prints:
width: 30
height: 40
We can either use value type or pointer type for the receiver argument. Go automatically handles conversion between values and pointers for method calls. Though, you may want to use a pointer receiver type to avoid copying on method calls or to allow the method to modify the receiving structure’s data.