Demystifying Go's Syntax: A Sarcastic Journey through Pointers, Structs, Arrays, Slices, and Maps | Part - 2
Tags: go, beginners, microservices, programming
Published: 2023-05-27 07:47:34

In the world of programming languages, Golang (also known as Go) has gained significant popularity for its simplicity, efficiency, and robustness. With its unique syntax and powerful features, Golang offers developers a versatile toolkit to build scalable and high-performance applications. In this blog, we will dive deep into some of the key aspects of Golang syntax, namely pointers, struct, arrays, slices & maps. Let's explore each topic in detail:

### Pointers:
Pointers play a crucial role in Golang and understanding them is essential for effective memory management and manipulating data. This section will cover the basics of pointers, including their declaration, referencing, and dereferencing. We will explore how pointers enable pass-by-reference and allow us to modify variables directly in memory.

- 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.

```
var p *int
```
- The & operator generates a pointer to its operand.

```
i := 177
p = &i
```
- The * operator denotes the pointer's underlying value.

```
fmt.Println(*p) // read i through the pointer p
*p = 146        // set i through the pointer p
```
- This is known as "dereferencing" or "indirecting".

Consider the following example:

```
package main

import "fmt"

func main() {

	i, j := 146, 5256 // initialize the i,j	

	p := &i         // point to i
	fmt.Println(*p) // read i through the pointer
	*p = 177        // set i through the pointer
	fmt.Println(i)  // see the new value of i

	p = &j           // point to j
	*p = *p / 146   // divide j through the pointer
	fmt.Println(j) // see the new value of j
}
```
Output:

```
146
177
36
```

### Structs
In Go, a struct is a composite data type that allows you to define your own custom data structure by grouping together different types of values into a single entity. It is similar to a class in object-oriented programming languages but without methods.

To define a struct in Go, you use the `type` keyword followed by the name of the struct and a set of fields enclosed in curly braces. Here's an example:

- A struct is a collection of fields.
- Struct fields are accessed using a dot.
- Struct fields can be accessed through a struct pointer. To access the field X of a struct when we have the struct pointer p we could write `(*p).X`. However, that notation is cumbersome, so the language permits us instead to write just `p.X`, without the explicit dereference.
- A struct literal denotes a newly allocated struct value by listing the values of its fields. You can list just a subset of fields by using the `Name: syntax`.

```
package main

import "fmt"

// Structs	
type Vertex struct {
	X int
	Y int
}

var (
	v1 = Vertex{1, 2}  // has type Vertex
	v2 = Vertex{X: 1}  // Y:0 is implicit
	v3 = Vertex{}      // X:0 and Y:0
	q  = &Vertex{1, 2} // has type *Vertex
)

func main() {

	// Struct
	fmt.Println(Vertex{1, 2})
	
	// Struct Fields
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v.X,v.Y)
	
	// Pointers to Struct
	f := Vertex{1, 2}
	p := &f
	p.X = 3
	fmt.Println(f)
	
	// Struct Literals
	fmt.Println(v1,v2, v3, q)	
}
```
Output:

```
{1 2}
4 2
{3 2}
{1 2} {1 0} {0 0} &{1 2}
```

### Arrays
In Go, an array is a fixed-size collection of elements of the same type. The size of an array is specified at compile time and cannot be changed during runtime. Here's an example of declaring and using an array:

```
package main

import "fmt"

func main() {
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println(a[0], a[1])
	fmt.Println(a)

	primes := [6]int{2, 3, 5, 7, 11, 13}
	fmt.Println(primes)
}
```
Output:

```
Hello World
[Hello World]
[2 3 5 7 11 13]
```

### Slices
Slices are dynamic, resizable, and more flexible than arrays in Go. They are built on top of arrays and provide additional functionality. Slices do not have a fixed size and can grow or shrink as needed. Here's an example:

```
package main

import "fmt"

func main() {
	primes := []int{2, 3, 5, 7, 11, 13}

    primes = append(primes, 17)   // Appending elements to the slice
    primes = append(primes, 19)
    primes = append(primes, 23)

    fmt.Println(primes)            // Output: [2 3 5 7 11 13 17 19 23]
    fmt.Println(primes[1])         // Output: 3
    fmt.Println(len(primes))       // Output: 9 (length of the slice)

    // Slicing a slice to create a new slice
    newSlice := primes[1:5]         // Contains elements at indices 1 and 4 (inclusive:exclusive)
    fmt.Println(newSlice)           // Output: [3 5 7 11]
}
```
Output:

```
[2 3 5 7 11 13 17 19 23]
3
9
[3 5 7 11]
```
#### Slices are like references to arrays
- A slice does not store any data, it just describes a section of an underlying array.
- Changing the elements of a slice modifies the corresponding elements of its underlying array.
- Other slices that share the same underlying array will see those changes.

```
package main

import "fmt"

func main() {
	names := [4]string{
		"Ram",
		"Lakshman",
		"Parshuram",
		"Samvad",
	}
	fmt.Println(names)

	a := names[0:2] // 0 to 1
	b := names[1:3] // 1 to 2
	fmt.Println(a, b)
	
	b[0] = "Krishna"
	// Changing the element of a Slice b
	// Modifies the corresponding elements of its underlying array names
	fmt.Println(a, b)
	fmt.Println(names)
}
```
Output:

```
[Ram Lakshman Parshuram Samvad]
[Ram Lakshman] [Lakshman Parshuram]
[Ram Krishna] [Krishna Parshuram]
[Ram Krishna Parshuram Samvad]
```
#### Slice literals
A slice literal is like an array literal without the length.

This is an array literal:
`[3]bool{true, true, false}`

And this creates the same array as above, then builds a slice that references it:
`[]bool{true, true, false}`

```
package main

import "fmt"

func main() {
	q := []int{2, 3, 5, 7, 11, 13}
	fmt.Println(q)

	r := []bool{true, false, true, true, false, true}
	fmt.Println(r)

	s := []struct {
		i int
		b bool
	}{
		{2, true},
		{3, false},
		{5, true},
		{7, true},
		{11, false},
		{13, true},
	}
	fmt.Println(s)
}
```
Output:

```
[2 3 5 7 11 13]
[true false true true false true]
[{2 true} {3 false} {5 true} {7 true} {11 false} {13 true}]
```
#### Slice Defaults

- When slicing, you may omit the high or low bounds to use their defaults instead.
- The default is zero for the low bound and the length of the slice for the high bound.

For the array

```
var a [10]int
```

these slice expressions are equivalent:

```
a[0:10]
a[:10]
a[0:]
a[:]
```

```
package main

import "fmt"

func main() {
	s := []int{2, 3, 5, 7, 11, 13}

	s = s[1:4] // [3 5 7]
	fmt.Println(s)

	s = s[:2] // [3 5]
	fmt.Println(s)

	s = s[1:] // [5]
	fmt.Println(s)
}
```
Output:

```
[3 5 7]
[3 5]
[5]
```
#### Slice length & capacity
- A slice has both a length and a capacity.
- _The length of a slice is the number of elements it contains_.
- _The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice_.
- The length and capacity of a slice s can be obtained using the expressions `len(s)` and `cap(s)`.

```
package main

import "fmt"

func main() {
	s := []int{2, 3, 5, 7, 11, 13}
	printSlice(s)

	// Slice the slice to give it zero length.
	s = s[:0]
	printSlice(s)

	// Extend its length.
	s = s[:4]
	printSlice(s)

	// Drop its first two values.
	s = s[2:]
	printSlice(s)
}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
```
Output:
```
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
```

#### Nil Slices
- The zero value of a slice is nil.
- A nil slice has a length and capacity of 0 and has no underlying array.

```
package main

import "fmt"

func main() {
	var s []int
	fmt.Println(s, len(s), cap(s))
	if s == nil {
		fmt.Println("nil!")
	}
}
```
Output:
```
[] 0 0
nil!
```
#### Creating a slice with make
- Slices can be created with the built-in `make` function; this is how you create dynamically-sized arrays.
- The `make` function allocates a zeroed array and returns a slice that refers to that array:
```
a := make([]int, 5)  // len(a)=5
```
- To specify a capacity, pass a third argument to `make`:

```
b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
```
Here's an example:

```
package main

import "fmt"

func main() {
	a := make([]int, 5)
	printSlice("a", a)

	b := make([]int, 0, 5)
	printSlice("b", b)

	c := b[:2]
	printSlice("c", c)

	d := c[2:5]
	printSlice("d", d)
}

func printSlice(s string, x []int) {
	fmt.Printf("%s len=%d cap=%d %v\n",
		s, len(x), cap(x), x)
}
```
Output:

```
a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0]
d len=3 cap=3 [0 0 0]
```

#### Slices of Slices
Slices can contain any type, including other slices.

```
package main

import (
	"fmt"
	"strings"
)

func main() {
	// Create a tic-tac-toe board.
	board := [][]string{
		[]string{"_", "_", "_"},
		[]string{"_", "_", "_"},
		[]string{"_", "_", "_"},
	}

	// The players take turns.
	board[0][0] = "X"
	board[2][2] = "O"
	board[1][2] = "X"
	board[1][0] = "O"
	board[0][2] = "X"

	for i := 0; i < len(board); i++ {
		fmt.Printf("%s\n", strings.Join(board[i], " "))
	}
}
```
Output:

```
X _ X
O _ X
_ _ O
```

#### Appending to a Slice
To append elements to a slice in Go, you can use the built-in `append()` function. The `append()` function takes a slice and one or more elements as arguments, and it returns a new slice with the appended elements. Here's an example:

```
package main

import "fmt"

func main() {
	var s []string
	printSlice(s)

	// append works on nil slices.
	s = append(s, "Ram")
	printSlice(s)

	// The slice grows as needed.
	s = append(s, "Lakshman")
	printSlice(s)

	// We can add more than one element at a time.
	s = append(s, "Parshuram", "Samvad", "Krishna", "Balarama")
	printSlice(s)
}

func printSlice(s []string) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
```
Output:

```
len=0 cap=0 []
len=1 cap=1 [Ram]
len=2 cap=2 [Ram Lakshman]
len=6 cap=6 [Ram Lakshman Parshuram Samvad Krishna Balarama]
```

- The range form of the for loop iterates over a slice or map.
- 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.
- You can skip the index or value by assigning to `_`.

```
for i, _ := range pow
for _, value := range pow
```
- If you only want the index, you can omit the second variable.

```
for i := range pow
```

Here's an example:
```
package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
	for i, v := range pow {
		fmt.Printf("2**%d = %d\n", i, v)
	}

	fmt.Println()

	pow := make([]int, 10)
	for i := range pow {
		pow[i] = 1 << uint(i) // == 2**i
	}
	for _, value := range pow {
		fmt.Printf("%d ", value)
	}
}
```
Output:

```
2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128

1 2 4 8 16 32 64 128 256 512 
```

### Maps
In Go, a map is a built-in data structure that represents an unordered collection of key-value pairs. It is similar to dictionaries or hash tables in other programming languages. Maps provide an efficient way to store and retrieve values based on unique keys. Here's an example of using maps in Go:

```
package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": Vertex{
		40.68433, -74.39967,
	},
	"Google": Vertex{
		37.42202, -122.08408,
	},
}

func main() {
	fmt.Println(m["Bell Labs"])
	fmt.Println(m)	
}
```
Output:

```
{40.68433 -74.39967}
map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
```

#### Mutating Map

- Insert or update an element in map m:
```
m[key] = elem
```
- Retrieve an element:
```
elem = m[key]
```
- Delete an element:
```
delete(m, key)
```
- Test that a key is present with a two-value assignment:
```
elem, ok = m[key]
```
- If `key` is in `m`, `ok` is `true`. If not, `ok` is `false`.
- If `key` is not in the map, then `elem` is the zero value for the map's element type.

```
package main

import "fmt"

func main() {
	m := make(map[string]int)

	m["Answer"] = 177
	fmt.Println("The value:", m["Answer"])

	m["Answer"] = 146
	fmt.Println("The value:", m["Answer"])

	delete(m, "Answer")
	fmt.Println("The value:", m["Answer"])

	v, ok := m["Answer"]
	fmt.Println("The value:", v, "Present?", ok)
}
```
Output:

```
The value: 177
The value: 146
The value: 0
The value: 0 Present? false
```

### Function Values
- Functions are values too. They can be passed around just like other values.
- Function values may be used as function arguments and return values.

```
package main

import (
	"fmt"
	"math"
)

func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5, 12)) // sqrt(5^2 + 12^2) = sqrt(144 + 25) = 13

	fmt.Println(compute(hypot)) // sqrt(3^2 + 12^2) = sqrt(16+9) = 5
	
	fmt.Println(compute(math.Pow)) // pow(3,4) = 3^4 = 81
}
```
Output:

```
13
5
81
```

#####Thank you for diving into this chapter of the blog! We've covered a lot of ground, but the journey doesn't end here. The next chapter awaits, ready to take you further into the depths of our topic.

#####To continue reading and explore the next chapter, simply follow this link: [Link To Next Chapter](https://dev.to/sahil_4555/mastering-go-unleashing-the-potential-of-methods-interfaces-generics-and-concurrency-part-3-5gki)

> _"Unlock the enigmatic world of pointers, structs, arrays, slices, and maps – where syntax humorously baffles and amuses!✨👀"_