How to break out of nested loops in Go?

LoopsGo

Loops Problem Overview


I have an outer and inner loop, each iterating over a range. I want to exit the outer loop when a condition is satisfied inside the inner loop.

I have a solution which works using two 'break's, one inside the inner loop and one inside the outerloop, just outside the inner loop (a very simplified case for demonstration):

package main

import (
	"fmt"
)

func main() {
	
	word := ""
	for _, i := range("ABCDE") {
		for _,j := range("ABCDE") {
			word = string(i) + string(j)
			fmt.Println(word)
			if word == "DC" {
				break
			}
		}
		if word == "DC" {
			break
		}
	}
	// More logic here that needs to be executed
}

Go Playground

There is no problem with this solution, but it just looks patched and ugly to me. Is there a better way to do this?

I can try and have another for conditional loop outside the outer loop in the previous solution and have a label and use continue with the label. But as you can see, this approach isn't any more elegant than the solution with break.

package main

import (
	"fmt"
)

func main() {

	word := ""

Exit:
	for word != "DC" {
		for _, i := range "ABCDE" {
			for _, j := range "ABCDE" {
				word = string(i) + string(j)
				fmt.Println(word)
				if word == "DC" {
					continue Exit
				}
			}
		}
	}
	// More logic here that needs to be executed
}

Go Playground

I have seen similar questions here pertaining to other languages (C, C#, Python etc). But what I am really interested to see is whether there is any trick with Go constructs such as 'for select'.

Loops Solutions


Solution 1 - Loops

Use break {label} to break out of any loop as nested as you want. Just put the label before the for loop you want to break out of. This is fairly similar to the code that does a goto {label} but I think a tad more elegant, but matter of opinion I guess.

package main

func main() {
	out:
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if i + j == 20 {
				break out
			}
		}
	}
}

More details: https://www.ardanlabs.com/blog/2013/11/label-breaks-in-go.html

Solution 2 - Loops

use function

package main

import (
    "fmt"
)

func getWord() string {
    word := ""
    for word != "DC" {
        for _, i := range "ABCDE" {
            for _, j := range "ABCDE" {
                word = string(i) + string(j)
                fmt.Println(word)
                if word == "DC" {
                    return word
                }
            }
        }
    }
    return word
}

func main(){
    word := getWord()
}

Edit: thanks to @peterSO who points on some mistakes in the details and provides this playground https://play.golang.org/p/udcJptBW9pQ

Solution 3 - Loops

How about goto?

package main

import (
    "fmt"
)

func main() {

    word := ""

        for _, i := range "ABCDE" {
            for _, j := range "ABCDE" {
                word = string(i) + string(j)
                fmt.Println(word)
                if word == "DC" {
                    goto Exit
                }
            }
        }
	Exit: // More logic here that needs to be executed
}

Solution 4 - Loops

The most straightforward seems to be something like:

func main() {
    word := ""
    isDone := false
    for _, i := range("ABCDE") {
        for _,j := range("ABCDE") {
            word = string(i) + string(j)
            fmt.Println(word)
            isDone = word == "DC"
            if isDone {
                break
            }
        }
        if isDone {
            break
        }
    }
    //  other stuff
}

An Alternative using a Generator

However you could also do a generator to create the sequence of words as in:

func makegen () chan string {
    c:= make(chan string)
    go func () {
        for _, i := range ("ABCDE") {
            for _, j := range ("ABCDE") {
                c <- string(i) + string(j)
            }
        }
        close (c)
    }()

    return c
}


func main() {
    word := ""
    for word = range makegen() {
        fmt.Println (word)
        if word == "DC" {
          break
        }
    }
    // other code
}

Solution 5 - Loops

Just defer anything you need to do and return as normal.

package main

import (
    "fmt"
)

func main() {
    defer func() {
	    // More logic here that needs to be executed
	}()

	word := ""

	for _, i := range "ABCDE" {
    	for _, j := range "ABCDE" {
	    	word = string(i) + string(j)
		    fmt.Println(word)
		    if word == "DC" {
		    	return
		    }
		}
	}
}

Solution 6 - Loops

Wrap your for loops in an anonymous self-invoked function, then just return whenever you want to break out

package main

func main() {
    func() {
        for i:= 0; i < 100; i++ {
            for j:= 0; j < 100; j++ {
                if (i == 5 && j == 5) {
                    return
                }
            }
        }
    }()
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJayView Question on Stackoverflow
Solution 1 - LoopslazieburdView Answer on Stackoverflow
Solution 2 - LoopskkesleyView Answer on Stackoverflow
Solution 3 - LoopsaultimusView Answer on Stackoverflow
Solution 4 - LoopsRichard ChambersView Answer on Stackoverflow
Solution 5 - Loopskrob0ltView Answer on Stackoverflow
Solution 6 - LoopsDozatronView Answer on Stackoverflow