DEV Community

Cover image for Go To Statement Considered Harmful – Does Go Need It?
Wycliffe A. Onyango
Wycliffe A. Onyango

Posted on

Go To Statement Considered Harmful – Does Go Need It?

Introduction: A 55-Year-Old Debate in Modern Go

In 1968, Edsger Dijkstra wrote the now-famous article “Go To Statement Considered Harmful” in the Communications of the ACM​.

It was a short but powerful critique of the GOTO statement, arguing that it leads to unstructured and unreadable "spaghetti code." His ideas shaped modern programming, paving the way for structured programming paradigms that we rely on today.

Fast-forward to today, and we have Go (Golang)—a modern, concise, and efficient language designed for simplicity and performance. And guess what? Go still has goto.

But should we use it? Does Dijkstra’s argument still apply to a language designed half a century later?

What Was Dijkstra's Problem with GOTO?

Before structured programming, most languages (like Fortran and Assembly) heavily relied on GOTO for control flow. This led to chaotic jumps in execution, making debugging and maintenance a nightmare.

Spaghetti Code in Action

Here’s a simple example of unstructured GOTO usage in Go:


package main

import "fmt"

func main() {
    num := 7

    if num%2 == 0 {
        goto even
    } else {
        goto odd
    }

even:
    fmt.Println("Even number")
    return

odd:
    fmt.Println("Odd number")
}

Enter fullscreen mode Exit fullscreen mode

🔴 Why is this bad?

  • The program jumps unpredictably between labels.
  • If the logic becomes more complex, tracking execution flow becomes painful.
  • If a bug appears, finding the root cause requires tracing multiple jumps instead of following a clear path.

This is exactly the problem Dijkstra warned us about in 1968

Go’s Take on GOTO: Discouraged, But Not Forbidden

Go allows goto, but strongly discourages its use unless absolutely necessary. The official Go documentation states:

"goto jumps to a labeled statement and should be used sparingly. It can simplify error handling but often leads to unstructured code."

Unlike Java, Python, and Rust, which completely disallow GOTO, Go keeps it for specific cases—mainly for breaking deeply nested loops or reducing redundant error handling code.

When Can GOTO Be Useful in Go?

✅ Breaking Out of Nested Loops (A Rare Exception)

Imagine you have a deeply nested loop structure, and you want to exit immediately once a condition is met. Using goto can simplify this:

package main

import "fmt"

func main() {
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if i*j > 4 {
                goto exitLoop // Break out of both loops
            }
            fmt.Println(i, j)
        }
    }

exitLoop:
    fmt.Println("Exited the loop")
}

Enter fullscreen mode Exit fullscreen mode

🔹 Why is this acceptable?

  • Without goto, you might need extra flags or complex if conditions.
  • goto allows immediate escape without unnecessary logic.

Still, this should be used sparingly, as Go provides better alternatives.

Structured Programming in Go: What to Use Instead of GOTO

Go is designed with structured programming in mind, offering better alternatives to goto.

✅ 1. Using break with Labels (Cleaner Than goto)

Instead of goto, Go allows labeled break statements, which are cleaner and more structured:


package main

import "fmt"

func main() {
outer:
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if i*j > 4 {
                break outer // Exits both loops cleanly
            }
            fmt.Println(i, j)
        }
    }
    fmt.Println("Exited the loop")
}

Enter fullscreen mode Exit fullscreen mode

✅ Why is this better?

  • More readable than goto.
  • The label (outer) makes it clear which loop is being exited.

✅ 2. Handling Errors Without GOTO

A common argument for goto is reducing repeated error-handling code. But Go already has defer and error handling patterns to simplify this.

package main

import (
    "errors"
    "fmt"
)

func doSomething() error {
    return errors.New("Something went wrong")
}

func main() {
    if err := doSomething(); err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println("Success")
}
Enter fullscreen mode Exit fullscreen mode

✅ Why is this better?

  • Error handling remains structured.
  • No unnecessary jumps—errors are handled where they occur.

Did Dijkstra Win? (Go’s Balance Between Old and New)

Dijkstra’s 1968 paper revolutionized programming, leading to structured programming and the decline of goto. But Go did not completely remove goto—instead, it keeps it for rare, justified cases.

So, did Dijkstra win? Yes, mostly.

  • Almost all modern programming follows structured programming.
  • goto is considered harmful unless used in very specific cases.
  • Even in Go, structured constructs are always preferred over goto.

Dijkstra’s Influence on Go

Dijkstra's Influence on Go

Final Thoughts: Should You Use GOTO in Go?

No, unless absolutely necessary.
Yes, but only in very specific cases like breaking nested loops.

If you're considering using goto in Go, ask yourself:

  • Can I use break, return, or defer instead?
  • Will this make debugging harder in the future?
  • Would Dijkstra approve? (Spoiler: Probably not.)

Go is a modern language, and Dijkstra’s insights from 1968 still hold true today. The best Go code follows structured programming principles, making it readable, maintainable, and scalable.

So, next time you're tempted to use goto, just remember: Dijkstra is watching.

References

Dijkstra, E. W. (1968). Go To Statement Considered Harmful. Communications of the ACM, 11(3), 147-148​

Golang Official Documentation: https://golang.org/

Top comments (0)