Hi everyone! this is Jimmy , and this is the third article in my series “Breaking Things with Go.” In this series, I document my journey through Jon Bodner’s Second Edition: Learning Go – An Idiomatic Approach to Real-World Go Programming and explore how to use Go in the most practical way I can

in this series the resources are the book itself, go documentation, and any AI model to clarify some things

lets Jump into it Jump Into A Hole Stickers - Find & Share on GIPHY


after mentioning the Blocks and Shadowing and some if else statements we are ready to jump into the loops

Loops in Go

there is multiple ways to use loops in Go, some of them are already part of the language like the for-loop and some we will stimulate like while loop and do while loops so lets start with For loops

C-style For loop

just like c-style loops it consists of 3 parts: initializer, condition, incrementation

  • initialization in For-loop has to be done by := only, var isn’t valid there
  • you can leave the initialization part out if the variable identified before
  • initialized variables are block-defined so they are accessible through the loop only
  • you can leave the incrementation part out if you will apply some complicated incrementation inside the loop body
go
for i:=0; i<10; i++{ //3 parts 
    fmt.Prinln(i)
}

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

for i:=0; i<10; { //{not recommended if the incrementation is simple just for readability}
    fmt.Println(i)
    i++ // incrementation inside the loop body instead
}

While statement simulation

Go doesn’t provide while loops (at least not directly) but we can stimulate it using what’s known as condition-only for loop

go
i:=0
for i<20{
    fmt.Println(i)
    i++
}

the last example will print the i value while it’s less than 20

Infinite for statement

go
for {
    fmt.Println("hello")
}

loops forever that’s why we need some break statement

break statement breaks out of the loop
continue statement skips the current iteration execution

so we can combine this with infinite loops to stimulate the do while statement

go
for { // loops for ever 
    // do something before checking
    if ! SOME_CONDITION{ // invert the condition to break if it isn't satisfied 
        break
    }
}

and the most used loop of all time is for range statement

For-range statement

instead of iterating over composite types using index, Go provides the range statement that is used to iterate over built-in types like strings, arrays, slices, maps, user-defined types it behaves kinda differently with each data type so we will demonstrate each data type alone

range with slices

it returns two values, first is the index and second is the value itself

go
evenVals := []int{2, 4, 6, 8, 10, 12}
for i, v := range(evanVals) {
    fmt.Println(i, v)
}
// 0 2
// 1 4
// 2 6
// 3 8
// 4 10
// 5 12

we gotta a rule in Go that all variables must be used

but what if i don’t need the index or the value what to do with it ? lucky us Go provides underscore _ as a variable name for the variables that we need the compiler to ignore

if we need the value only we will do this

go
evenVals := []int{2, 4, 6, 8, 10, 12}
for _, v := range(evanVals) {
    fmt.Println(v)
}

and if we need the indices only (which is useful in set implementation, go see it in the Fifth Break) we can do it this two ways

go
evenVals := []int{2, 4, 6, 8, 10, 12}
for i, _ := range(evanVals) { // not idiomatic 
    fmt.Println(i)
} 
for i := range(evanVals) { //declare i only and it will ignore the value by default
    fmt.Println(i)
}

variables are named i for index, and v for value which is best practice but you can name it whatever you want

range with maps

remember maps are hashmap implemented in Go so the order of the items isn’t guaranteed cause it includes a random generated number every time a map variable is created

try this example

go
m := map[string]int{
    "a": 1,
    "b": 2, 
    "d": 3, 
    "c": 4
}
fmt.Println(m)

how is it unordered and non-guaranteed order when this map always ordered by the key in ascending order ?

  • the fmt package this for easier debugging but in loops you will see that maps aren’t actually ordered

for example this one which will print different order on each iteration

go
m := map[string]int{
    "a": 1,
    "b": 2,
    "c": 3,
}
for i := 0; i < 3; i++ {
    fmt.Println("loop", i)
    for k, v := range m {
        fmt.Println(v, k)
    }
}

and just like i in arrays, slices or strings → maps use k but you can name it whatever you want

range with strings

you will find this surprising (specially cause we mentioned that strings in go are sequence of bytes not characters) but when we are using range with strings it will return characters not bytes !

go
funFace := "Hi 🌞 c"
for i, v := range funFace {
    fmt.Println(i, v, string(v))
}
//this code will print this 
// 0 72 H
// 1 105 i
// 2 32
// 3 127774 🌞 //no way byte can hold that number
// 7 32  //notice the index jump
// 8 99 c

if it returns bytes it can’t hold more than 256 but this returns the actual character

that’s because string with range returns two values, first is the index of the byte (yes byte) but the second value is a rune (yes rune = int32) so it will hold the entire character so you can explicitly convert it to string to print it

the index of the byte doesn’t just increment one it will get the actual index of the size so if there is a character with multiple bytes → the number of bytes will be incremented not just one

For-range declaration is a value copy

every time you iterate over a compound type it copies the value from the type to the value variable in our case is v so if we tried to modify the type value it won't change

go
func main() {
    evenVals := []int{2, 4, 6, 8, 10}
    for _, v := range evenVals {
        v *= 2
    }
    fmt.Println(evenVals) // [2 4 6 8 10]
}

btw Go used to use the same variable for each iteration with range but this caused some bugs so now each iteration create its new variable to copy to consider it like v1, v2, v3, v4 (just for clarification)

Loop statements labeling

using continue or break in an inner loop will break or continue only the inner loop but what if i need to continue the outer loop based on condition in the inner loop

go
func main() {
outer:
    for i := 0; i < 10; i++ {
        for j := 0; j < 10; j++ {
            if i%2 == 0 {
                continue outer
            }
            fmt.Println(i, j)
        }
    }
}

for example i need to print 2 numbers in the same line here first one is the odd numbers from 0 to 10 but second one is all numbers here i will pass the iteration if the number is even and go to the outer loop and you get the idea

labels are indented to the same level as their containing block so here they are indented just like main function just to be easier to notice

Which loop statement should you use?

  1. if you are iterating over composite type from the first element to last element use for range cause it gives you a lot of mobility
  2. if you are iterating over composite types from the middle or specific(non-starting index) use the for loop
  3. condition only statement (while simulation) used only if you are iterating based on pre-calculated value
  4. infinite loop as its very special use cases

and that was it for this break

Takes

  • just like other languages variables declared or initialized within the loop are only accessible within the loop
  • Maps aren’t ordered so iterating over it will be in an unexpected order so adapt your algorithm to that
  • iterating over strings return rune not byte (so each code point will be returned at once not splitted)
  • for range variables are a copy of the actual data so it won’t change the original data

Coming Next

the next break will be switch statements and goto statement so stick around for next break where we will break more stuff

feel free to reach out to me Peace Out GIFs | Tenor