Shanraq.org Shanraq.org
Branching and loops in Go: if, else and the only for
IT

Go: from zero to your own blog Lesson 6 of 50

Branching and loops in Go: if, else and the only for

The sixth lesson of the Go course. How a program picks between two paths: if, else if and else, comparisons, and the AND, OR and NOT that join them. And the loop — Go has exactly one, the word for, in three shapes: with a counter, on a condition, and endless with a break. The language has no while, no do-while and no foreach.

Why this matters

So far your program does the same thing whatever the values are. To answer differently — to call an article with a thousand views popular and a fresh one new — you need branching.

And to repeat something without writing it out ten times you need a loop. Here Go is unusual: it has one. The words while, do-while and foreach are not in the language — for does all of it, in different shapes.

The whole thing at once

A new folder, go mod init sabaq05, main.go:

package main

import "fmt"

func label(views int) string {
	if views >= 1000 {
		return "popular"
	} else if views >= 100 {
		return "being read"
	}
	return "new"
}

func main() {
	fmt.Println(12, "—", label(12))
	fmt.Println(340, "—", label(340))
	fmt.Println(1500, "—", label(1500))

	for i := 1; i <= 3; i++ {
		fmt.Println("lesson", i)
	}

	words := 0
	for words < 600 {
		words += 200
	}
	fmt.Println("words so far:", words)
}

Before you run it, and without looking below, say out loud what it will print. Then run it and compare: the difference is exactly where you do not know the language yet.

go run . prints:

12 — new
340 — being read
1500 — popular
lesson 1
lesson 2
lesson 3
words so far: 600

Taking it apart

if: a fork

if views >= 1000 {

The condition is written without round brackets — Go does not need them and nobody puts them in. The body is always in braces, even for a single line: there is no bracket-less short form in the language, and that removes a whole class of mistakes people trip over elsewhere.

Picture it. A fork in the road with a sign. The sign does nothing itself — it only settles which side you drive on. Both roads exist; you take one.

else if and else

The tests run top to bottom and the first one that fits is the one that runs. Order therefore matters: put >= 100 above >= 1000 and a thousand lands in “being read”, because the second test is never reached.

The closing else is optional. There is none in the example: after all the tests there is simply return "new" — the same meaning, one line shorter.

What compares

== equal, != not equal, < > <= >=. The double equals of a comparison and the single equals of an assignment are different things, and Go will not let you confuse them: if views = 1000 does not build.

Conditions join with && for both, || for either, ! for the opposite.

if views >= 100 && views < 1000 {

for with a counter

for i := 1; i <= 3; i++ {

Three parts separated by semicolons: start a counter, test while, do after each turn. i++ adds one.

Picture it. The three things you tell a storeman: which shelf to start at, how far to count, and how many to step by. Give him all three and he walks the warehouse himself.

for on a condition

for words < 600 {

One part instead of three. Other languages call this while; in Go it is the same for, just without the counter and the step.

for with no condition

for {
	// until told to leave
}

It turns forever until a break is met inside. Servers and event loops are written this way.

break leaves the loop altogether. continue abandons the current turn and starts the next.

Picture it. A queue. break is leaving the queue for good. continue is giving up your turn and going straight to the next one without finishing what you came to do.

if with a short declaration

Here the last lesson comes back. A function hands back two values, and both are wanted only inside the test:

if minutes, ok := readingTime(700); ok {
	fmt.Println(minutes)
}

The semicolon separates the declaration from the condition. minutes and ok live only inside this if and do not exist outside it — so they cannot get in the way or be confused with anything else. In Go this is how it is nearly always written when a result is wanted for exactly one test.

The fourth shape, later

for has one more form, range: it walks whatever is made of parts — a string letter by letter, a list item by item. We meet it on strings in the very next lesson, and on lists in the lesson on slices.

A function can call itself

A loop is not the only way to repeat. A function can call itself, and that is called recursion:

func countdown(n int) {
	if n == 0 {
		fmt.Println("go")
		return
	}
	fmt.Println(n)
	countdown(n - 1)
}

func main() {
	countdown(3)
}
3
2
1
go

There are two parts here and both are compulsory. The first is the way out: if n == 0 and return, the case in which the function no longer calls itself. The second is the step that brings that case closer: countdown(n - 1), with a smaller number every time.

Take the way out away and the program will not stop. Go will not let it hang for ever, but it will not thank you either:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

Every call takes room in memory, and when the room runs out the program falls over. A for loop cannot fall over like this: it piles up no calls.

Picture it. A nesting doll. You open it, inside is the same doll a size smaller, and so on down to the smallest — which is solid. Were there no smallest one, you would be opening them for ever.

When it is chosen over a loop: when the data nests inside itself. A comment on a comment, a folder inside a folder, a reply to a reply — walking that with a loop is hard and with recursion natural. For a flat list a loop is simpler and faster, and in this course it is nearly always a loop.

The lesson map

Lesson map: one word, for, in three shapes

Say it in your own words

Answer out loud or on paper without looking. The answers are at the end of the lesson.

  1. Why can the tests in label not be reordered?
  2. How does break differ from continue?
  3. Why write if n, ok := f(); ok when n and ok could be declared on the line above?

Exercise

Required. Write a function label(views int) string: at 1000 or more it returns "popular", at 100 or more "being read", otherwise "new". Call it from main for 12, 340 and 1500 and print the results.

Optional.

  • A counting loop from 1 to 10 that prints only the even numbers. Hint: the remainder of a division is %, and even means i%2 == 0.
  • Go back to the last lesson’s exercise that was waiting for an if: make readingTime return two values — the minutes, and a bool for whether the word count is sensible.
  • A for with no condition that leaves on the fifth turn with a break.

Where this goes in your blog

Branching is everywhere in a blog: show the article or answer 404, allow the edit or refuse it, print “no comments” or the list of them. And a loop is wanted the moment there is more than one article — in the lesson on slices you will walk all of them in a single line.

Answers

Show the answers
  1. Because the test that runs is the first one that fits, not the most exact one. A thousand views satisfies both >= 100 and >= 1000; with the hundred first, the thousand is never reached and “popular” never comes out. Conditions go from narrow to wide.
  2. break ends the loop entirely and execution carries on after it. continue ends only the current turn: the rest of the body is skipped, but the loop goes on with the next value.
  3. So that names do not outlive their use. Declared on the line above, n and ok stay to the end of the function, block those names for anything else, and suggest they can be used further down. Inside an if they vanish at the closing brace.

Sources

If you have found a mistake or a typo in this article, tell us about it

Check your exercise

Solve it and run it in VS Code first — the editor shows the mistake where you made it. Paste the finished solution here. A model reads it: it will point at the mistake but will not hand you the answer.

Sign in to have it checked. Sign in

Comments (0)

No comments yet. Be the first.