Overview
Go is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Go is syntactically similar to C, but with the addition of memory safety, garbage collection, structural typing, and CSP-style concurrency. The language is often referred to as Golang because of its former domain name, golang.org, but the proper name is Go.
Variables
Variables in Go can be declared and initialized in a few different ways. We can use the var keyword followed by the name of the variable and the type of the variable. Variables are given default values when they are declared. If we are declaring a variable with an initial value, we can omit the type since Go will infer the type in that case.
Another nice syntactic sugar in Go is the short-hand initialization of variables (with an exception of the global scope). The short-hand initialization can be done by specifying the variable name, followed by a colon and an equals sign (:=). Everything following that can be considered as a right side operand of the assignment operator.
Examples:
var a string
a="This is a string!"
var a = "This is a string!"
func main() {
a := "This is a string!"
}
Conditionals
The conditional structures in Go are similar to those in JavaScript, just without the parentheses. The standard conditial structures exist (if, else if, else, switch) - with the exception of the ternary operator ?:. Another important thing to note is that we can omit the break statement in switch structure since Go by default breaks out of the switch after a case is satisfied (evaluating from top to bottom).
Examples:
a := "Apple"
switch a {
case "Apple":
return a
default:
return "Not an apple"
}
a := "Apple"
if a == "Apple" {
return a
} else {
return "Not an apple"
}
Loops
Golang uses one syntax for iterating, using the for loop. It can be written as a standard for loop, but it can easily replace the while loop that exists in many other programming languages and with the use of range, it can replace the for..in loop as well.
Examples:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
i := 0
for i < 10 {
fmt.Println(i)
i++
}
var colors = []string{"red", "green", "blue", "yellow", "black"}
for _, color := range colors {
fmt.Println(color)
}
Functions
Functions in Go work almost the same as in most of the other programming languages. We declare functions by using the func keyword followed by the name of our function with typed arguments wrapped inside of parantheses. By default the functions in Go are void (they don't return anything). If we opt to write a function that returns some value, we must specify the type of the returned value. That can be done by writing the type before the function body and after the arguments. We can return multiple values from a function and in that case we can add multiple return types separated by a comma.
Go also supports variadic functions. They can be called with varying number of arguments. They are defined with a single argument that we can iterate over if we specify the type of the argument with the preceding three dots (...). Of course there are also anonymous functions that are part of any modern programming language. They are defined by omitting the function name. These functions have access to the entire lexical environment so they can refer to variables of the enclosing function.
Examples:
func Print(a string) {
fmt.Println(a)
}
func Add(args ...int) int {
sum := 0
for _, val := range args {
sum += val
}
return sum
}
func Divide(divisor int) (func(int)) {
return func(dividend int) float64 {
return dividend / divisor
}
}
divideByTen = Divide(10)
fmt.Println(divideByTen(230))
// Output: 23
func MultiplyByTwoAndSquare(number int) (int, int) {
return number * 2, number * number
}
double, squared := MultiplyByTwoAndSquare(12)
fmt.Println(double, squared)
// Output: 24 144
Arrays and slices
Arrays are values in Go. Assigning one array to another copies all the elements. They are used as a building block for slices.
Slices wrap arrays to give a more general, powerful, and convenient interface to sequences of data. Most array programming in Go is done with slices rather than simple arrays. A slice type is written []T, where the elements have type T. The array type looks the same, but with a size specified in the brackets. A slice is a small data structure that gives access to a subsequence (or perhaps all) of the elements of an array. We can also use the builtin function make for creating slices.
Examples:
// This slice has both length and capacity of 6
numbers := []int{2, 3, 5, 7, 11, 13}
// This slice has a length of 0, but a capacity of 15
numbers = make([]int, 0, 15)
Maps
In Go, a map is a reference to a hash table, and a map type is written map[K]V where K and V are the types of its keys and values. The key type K must be comparable using ==, so that the map can test whether a given key is equal to one already within it. We can use the builtin function make for creating maps.
Examples:
ages := make(map[string]int)
ages["Jack"] = 22
ages["Jane"] = 35
ages := map[string]int{
"Jack": 22,
"Jane": 35
}
Pointers
Go has pointers. A pointer holds the memory address of a value. The type *T is a pointer to a T value. Its zero value is nil. The & operator generates a pointer to its operand. The * operator denotes the pointer's underlying value.
Examples:
var num = 3
otherNum := &num
*otherNum++
fmt.Println(*otherNum, num)
// Output: 4, 4
func mutate(s *int, p int) {
*s += p
}
s := 3
mutate(&s, 3)
fmt.Println(s)
// Output: 6
Structs and interfaces
Structs are similar to classes in OOP. They consist of multiple fields with different types and they can have methods. An interface type specifies a method set that a struct which extends the interface needs to implement. A struct defines its methods by writing a function with an extra parameter before the function name. In Go, we don’t use a special name like this or self for the receiver but instead use the name of the new parameter that is specified before the function name. A struct extends an interface implicitly. It extends an interface by implementing the methods of that interface, nothing more. If we want to mutate the receiver of a method, we'll need to define the receiver type as a pointer.
Examples:
type Student struct {
name string
level string
gpa string
}
func (s Student) PrintReport() {
fmt.Printf("%s is a %s student who is a %s", s.Name, s.Gpa, s.Level)
}
func (s *Student) SetGpa(gpa string) {
s.gpa = gpa
}
student := Student{
"Jane Doe",
"Sophomore",
"4.0",
}
fmt.Println(student.level)
// Output: Sophomore
student.SetGpa("3.5")
student.Printreport()
// Output: Jane Doe is a 3.5 student who is a Sophomore
type Shape interface {
area()
}
type Circle struct {
pi float64
radius float64
}
type Rectangle struct {
height float64
width float64
}
func (c Circle) area() float64 {
return c.pi * math.Pow(c.radius, 2)
}
func (r Rectangle) area() float64 {
return r.height * r.width
}
func getArea(s Shape) float64 {
return s.area()
}
rectangle := Rectangle{
height: 4,
width: 5,
}
fmt.Println(getArea(rectangle))
// Output: 20
Part 2 of this article is coming up soon


