go go/book/learninggo

Introduction

Each place where a declaration occurs is called block. Variables, constants, types and functions declared outside of any functions are placed in the package block.

A shadowing variable is a variable that has the same name as a variable in a containing block.

Universe Block

Go has 25 keywords.They cannot be used as identifiers (like variable names, function names, etc.).

break       default      func        interface   select
case        defer        go          map         struct
chan        else         goto        package     switch
const       fallthrough  if          range       type
continue    for          import      return      var

Predeclared identifiers

Predeclared identifiers are built-in names provided by the Go language that you can use in your programs. Unlike keywords, they can be shadowed (redeclared), but it’s usually not recommended.

Go considers these predeclared identifiers and defines them in the universe block, which is the block that contains all other blocks.

// Types
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
 
// Constants
true false iota nil
 
// Functions
append cap close complex copy delete imag len
make new panic print println real recover

The nil value in Go is a predeclared identifier, specifically a predeclared constant, but with some unique properties.
Go - WTF is nil , WTF is Blank Identifier

If

func main() {
	if n := rand.Intn(10); n == 0 {
		fmt.Println("That's too low")
	} else if n > 5 {
		fmt.Println("That's too big:", n)
	} else {
		fmt.Println("That's a good number:", n)
	}
	fmt.Println(n) // 🔴 Out of Scope. n in if scope.
}
func main() {
	n := rand.Intn(10)
	if n == 0 {
		fmt.Println("That's too low")
	} else if n > 5 {
		fmt.Println("That's too big:", n)
	} else {
		fmt.Println("That's a good number:", n)
	}
	fmt.Println(n) // This works. 
}

For

1. Complete, C-style for

for i :=; i < 10; i++ {
	fmt.Println(i)
}
 
i := 0
for ; i < 10; i++ {
fmt.Println(i)
}

2. Condition Only for

i := 1
for i < 100 {
	///
}

3. Infinite Loop

for {
	fmt.Println("Hello")
}

Break and Continue

Go does not have while of do- while loop. we can use following pattern to do-while loop.

for {
	if !condition {
		break
	}
}
 
// 😲 NOTE: that the condition has a leading ! to negate the condition from the Java code. The Go code is specifying how to exit the loop,while the Java code specifies how to stay in it

Go encourages short if statement bodies, as left-aligned as possible. Nested code is more difficult to follow.

4. For-Range

for-range loop only to iterate over the built in compound types and use-defined types that are based on them. compound types are types that are made by combining other types—they are not basic primitives like int or bool.

TypeDescriptionFor-range Returns
ArrayFixed-size sequence of elementsindex, value
SliceDynamic-size array-like structureindex, value
MapKey-value pair collectionkey, value
StringSequence of bytes (interpreted as UTF-8)index, rune (index is byte index, rune is Unicode code point)
ChannelConcurrency-safe communication pipevalue only (until channel is closed)
Anytime you are in a situation where a value is returned, but you want to ignore it, use an underscore to hide the value., Go allows you to just leave off the second variable.
uniqueNames := map[string]bool{"Fred": true, "Raul": true, "Wilma": true}
for k := range uniqueNames {
	fmt.Println(k)
}

Iterating Over Maps

Go - Composite Types > Maps

for-range over a map produces a different order each time, even with the same data. This is security improvement( > 1.12 ).

This behavior is intentional, to:

  • Avoid reliance on iteration order.
  • Protect against Hash DoS attacks.

Go introduces randomness in the internal map hashing at runtime, not compile time.

package main
 
import "fmt"
 
func main() {
    for i := 0; i < 3; i++ {
        for k := range map[string]int{"a": 1, "b": 2, "c": 3} {
            fmt.Print(k, " ")
        }
        fmt.Println()
    }
}
 
// Outputs
// b a c 
// c b a 
// a c b 

Exception

Functions like fmt.Println(m) or fmt.Printf("%v", m) sort the keys so the output is deterministic and readable.

m := map[string]int{"c": 3, "a": 1, "b": 2}
fmt.Println(m) // Output: map[a:1 b:2 c:3] ← sorted by key

for-range over a String in Go

  • When you use a for-range loop on a string, Go iterates over runes, not bytes.
  • A rune is a Unicode code point (an int32), while strings are stored as UTF-8 encoded bytes.

for-range value is a copy

evenVals := []int{2, 4, 6, 8, 10, 12}
for _, v := range evenVals {
    v *= 2 // modifies copy only, not original slice
}
fmt.Println(evenVals)
evenVals := []int{2, 4, 6, 8, 10, 12}
for i := range evenVals {
    evenVals[i] *= 2 // Modified
}
fmt.Println(evenVals) // ✅ [4 8 12 16 20 24]

Labeling for Statements

Outer:
for i := 1; i <= 3; i++ {
    for j := 1; j <= 3; j++ {
        if j == 2 {
            continue Outer
        }
        fmt.Println(i, j)
    }
}

Switch

In Go, switch statements do not fall through by default, unlike in C, Java, or JavaScript.

x := 2
 
switch x {
case 1:
    fmt.Println("One")
case 2:
    fmt.Println("Two")
case 3:
    fmt.Println("Three")
}
 
// Output
// Two

Go provides the fallthrough keyword — but it must be the last statement in the case.

Go does include a fallthrough keyword, which lets one case continue on to the next one. Please think twice before implementing an algorithm that uses it. If you find yourself needing to use fallthrough, try to restructure your logic to remove the dependencies between cases.

x := 1
 
switch x {
case 1:
    fmt.Println("One")
    fallthrough
case 2:
    fmt.Println("Two")
}
 
// Output
// One
// Two

In Go, an empty case means nothing happens.

Blank Switches

Just as Go allows you to leave out parts from a for statement’s declaration, you can write a switch statement that doesn’t specify the value that you’re comparing against.

words := []string{"hi", "salutations", "hello"}
for _, word := range words {
	switch wordLen := len(word); {
	case wordLen < 5:
		fmt.Println(word, "is a short word!")
	case wordLen > 10:
		fmt.Println(word, "is a long word!")
	default:
		fmt.Println(word, "is exactly the right length.")
	}
}

Use a blank switch when you have multiple related boolean cases, like input validation, status checks, or condition-based formatting.

goto

You should try very hard to avoid using goto. But in the rare situations where it makes your code more readable, it is an option.