Control Structures 1 - if


The basic form of an if clause includes:

  • an if keyword,
  • followed by a boolean expression,
  • followed by a code block.
if a == b {
    fmt.Println("They are the same!")

Note that the opening brace must be on the same line as the if statement. This is a result of Go’s lexical rules; if you are curious about the reason for this, I explain the details after the transcript.

Seasoned programmes will immediately have noticed the lack of parentheses around the condition. In fact, they are purely optional. As a result you need to type less, and the code is more readable.

The curly braces, on the other hand, are strictly required, and that’s a good thing. If they weren’t required, it would be possible to write something like this,

if a == b 
    fmt.Println("They are the same!")

and this can become a trap when trying to add more lines to the if branch.

/* Just for illustrating the problem. This does not compile, due to missing curly braces. */
if a == b 
    fmt.Println("They are the same!")
    os.Exit(-1)  // Oops.

The indentation can trick our minds. os.Exit is not part of the if branch here. Rather, it always gets executed, regardless of whether the if condition applies.

/* Still only demo code that does not compile. */
if a == b 
    fmt.Println("They are the same!")
os.Exit(-1)  // This is how the compiler would see it

For this reason, the compiler enforces the use of braces with if statement as well as with for loops.

Of course, as in other languages, an if statement can also have an else branch.

if a == b {
    // if branch
} else {
    // else branch

Be aware that the keyword else must be on the same line as the closing brace of the if branch.

Initialization statement

The next feature is quite specific to Go. if statements can contain an initialization statement. Basically, this statement prepares the variable used in the condition expression. That is, these two statements

err := f(x)
if err != nil {
    return err

can be combined into one:

if err := f(x); err != nil {
    return err

In this case, variable err is declared within the if statement and therefore is only visible in the if condition and in the if branch.

As you can tell from the variable’s name, this option is commonly used for error handling.

To summarize

  • Conditions in if statements need no enclosing parentheses.
  • if and else branches must be enclosed in curly braces.
  • if statements can include an initialization statement to limit the scope of the evaluated variable to the if statement itself.

Not included in the video

Comparison operators

A simple if condition is of the form

<value> <comparison> <value>

The comparison operator can take one of these forms:

operator operation
== equal
!= not equal
< less than
<= less than or equal to
> greater than
>= greater than or equal to

Logical operators

Simple if conditions can be combined through AND, OR, and NOT operators:

operator operation
&& AND
|| OR

Is there no elseif or elsif?

No; instead, simply use else if.

Does Go have a ternary operator?

Many programming languages have a so-called “ternary operator”, which is basically an inline if-then-else operator that can be used as an expression.

A typical example would be (in pseudo-code, no particular language):

print("Width: " + w + "meter" + (w == 1 ? "" : "s"))

The structure of this ternary expression here is cond ? iftrue : iffalse. If cond is true, this expression evaluates to the result of the expression in the iftrue part; else it evaluates to the iffalse expression.

Go has no such ternary operator.

The Go team intentionally decided against including that operator into the language because they “had seen the operation used too often to create impenetrably complex expressions.” (Go FAQ)

Why can’t the opening brace and the else keyword start on a new line?

In the video, you learned that the opening brace of an if structure must be on the same line as the if statement, and the else statement must be on the same line as the closing brace of the if block. This rule might seem overly restrictive, but there is a technical reason behind this.

Formally, Go’s syntax uses semicolons at the end of each statement. They do not appear in the source, however, as the lexer1 inserts semicolons automatically as needed.

This is very convenient when writing code, and also makes the code much clearer to read. In fact, it is considered idiomatic Go to not use semicolons at line endings.

But there is a caveat. When the lexer reads source code that contains no semicolons, then it cannot exactly tell where a statement ends. Therefore, the lexer assumes that if a statement looks complete at the end of a line, it must be complete.

So given this line:

if a > 0

the lexer considers this a complete statement with empty if and else branches, and inserts a semicolon right at the end of the line.

Hence to tell the lexer that the statement continues, opening braces and else keywords must stay in the same line.

You may or may not agree with this way of evaluating if statements. However, consider that Go encourages one single formatting style anyway, to prevent never-ending discussions about spaces, braces, or indenting. This particular placement of the open brace fits well into the idea of using a single formatting style.


Effective Go: If

Language Reference: If statements

  1. In case you are not familiar with compiler construction, the lexer is responsible for turning the source code (a stream of characters) into a sequence of tokens suitable for syntax analysis.