As I’ve mentioned in previous articles, there are three kinds of statement in Swift: simple statements such as expressions or declarations, compiler control statements that control aspects of the compilers behaviour and control flow statements that control the different paths through our code.
In previous posts we’ve already looked at some of the control flow statements such as the loop statements, the if
statement and the guard
statement. In this article we’re going to continue on that journey by looking at Swift’s break
and continue
statements. First though, we’re going to look at statement labels.
Table of Contents
Labelled Statements
In Swift we have the option of prefixing statements such as the if
, switch
do
or loop statements with something called a statement label.
A statement label is nothing more than an identifier by which we can refer to that particular statement from elsewhere in our code. As such the statement identifier consisting of an identifier (much like an identifier you would use when declaring constants or variables) followed immediately by a colon (:
) and then the statement that we want to label.
For example, say we wanted to provide a statement label for a for-in
loop, we could write something like this:
var x = 0
mainLoop: for x in 1...4 {
// Do stuff.
}
Here we’ve given the for-in
loop the label mainLoop
. The statement then follows after the colon. Once declared, that label can then be used anywhere within the scope of the labelled statement such as in the for-in
loop itself.
We also have the option of nesting statement labels. For example, if we had a switch
statement nested within our for-in
loop above we could also give the switch
statement it’s own label. The only caveat with nested statement labels is that all the labels must be unique:
var x = 0
mainLoop: for x in 1...4 {
innerSwitch: switch x % 2 {
case 0:
print("\(x) is even")
default:
print("\(x) is odd")
}
}
// 1 is odd
// 2 is even
// 3 is odd
// 4 is even
Here, the outer loop remains much as we just saw, but this time we also have a switch
statement within the body of the loop that we’ve given the label innerSwitch
.
Ok, pretty straight forward. By now you’ve probably got the hang of giving the different types of statement a label but you may also be wondering why you would want to. I promise, I will get to that shortly, but for now, let’s move onto and look at the break
statement. Once we’ve looked at that, we’ll see how statement labels fit in.
The break
Statement
In Swift, the break
statement is used to immediately end the execution of a loop, a switch
statement, an if
statement of a do
statement. As such, the break
statement consists of a single break
keyword followed optionally by a (you guessed it) statement label!
Let’s look at using the non-labelled version of the break
statement first.
When a break
statement is NOT followed by a statement label, the break
statement causes execution of a switch
statement or the inner most enclosing loop within which the break
statement occurs to immediately end it’s execution. Execution then transfers control to the first statement after the closing curly brace of the named statement.
For example, say we had a switch
statement, we could use a break
statement within one of the cases to ignore that particular case:
let x = 0
switch x {
case 0:
break;
case 1:
print("One for the money...")
case 2:
print("Two for the road...")
default:
print("Any other values");
}
print("Finished")
// Finished
Here, the switch statement matches the value of x
to the first case, where the break
statement immediately transfers execution to the first statement outside of the closing brace (}
) of the switch
statement (the print
statement).
There is however, another caveat. When using the break
statement without an associated statement label we can’t actually use it to break out of an unlabelled if
statement or do
statement. For example the following is NOT allowed:
var x = 10
if x > 5 {
break // Causes a compiler error.
}
And this isn’t either:
do {
print("Hello World")
break // Causes a compiler error.
}
But slightly confusingly this is perfectly valid:
var array = [1, 3, 6, 8, 2]
for i in array {
if (i > 4) {
break
}
print(i)
}
// 1
// 3
So what’s going on?
What’s happening here is that instead of the break
statement applying to the unlabelled if
statement (which we’ve just seen is not allowed), Swift applies the break
statement to the for-in
loop instead, terminating the loops execution as soon as it encounters a value that is greater than 4
. Slightly confusing but now you know what to watch out for.
Ok, that’s what happens if you use a break
statement without an associated statement label, but what about if we include one?
Using break
in Conjunction with Labelled Statements
When followed by a statement label, the break
statement in Swift can be used to end the execution of the loop, if
statement, switch
statement or do
statement that is identified by the supplied label.
This can sometimes be useful when working with nested statements, as it allows us to be explicit about which loop or conditional statement we want the break
statement to affect.
Let’s start off with a simple example. Remember our if
statement that we couldn’t break out of because it wasn’t labelled? Well you can if you provide the if
statement with a label:
var x = 10
myCondition: if x > 5 {
break myCondition
}
Similarly this is also valid (though granted it’s also a bit of a silly example):
welcome: do {
print(“Hello World”)
break welcome
}
Where things get a little more interesting is when we combine a break
statement with a label with nested statements:
outer: for i in 1...5 {
inner: for j in 1...5 {
if (j * i) > 3 {
break outer
}
print("\(i) * \(j) = \(i * j)")
}
}
// 1 * 1 = 1
// 1 * 2 = 2
// 1 * 3 = 3
In this example, we have two nested loops, one labelled outer
and one labelled inner
. Each of the loops iterates through the integer numbers 1
to 5
. Within the body of the inner loop, we also have an if
statement wrapped around our break
statement which causes the break
to only be executed when the product of j
and i
is greater than 3
.
In this case, when the break
statement executes, instead of terminating the inner loop (the loop labelled with inner
) which it would do by default, the break
statement actually terminates the outer loop (the loop labelled with outer
) causing the entire nested loop construct to terminate and execution to continue immediately after the closing brace of the outer
loop.
Ok, we’ve seen how to terminate execution of statements by using the break
statement but what if we didn’t want anything quite so drastic. What about if we simply wanted to skip the execution of the rest of a loop and wanted to continue with the next iteration? This is where our next statement the continue
statement comes in.
Continue
In Swift, the continue
statement tells a loop to stop what it is doing and to jump to the start of the next iteration of the loop. At that point, execution would continue with the the condition of the loop just like normal. In doing so, it essentially terminates the current iteration of the loop without exiting the loop as a whole, skipping the remaining statements within the body of the loop.
In similar fashion to the break
statement, the continue
statement consists of the continue
keyword and is then optionally followed by a statement label. This obviously results in two forms; one with an associated label and on without.
When we use the continue
statement on its own without a statement label, the statement terminates the execution of the current iteration of the inner most enclosing loop in which the continue
statement occurs. Control is then transferred to the condition of that loop and execution continues as normal, either executing the next iteration of the loop or terminating.
We can see this in this example:
for i in 1...3 {
for j in 1...3 {
if j == 2 {
continue
}
print("\(i) * \(j) = \(i * j)")
}
}
// 1 * 1 = 1
// 1 * 3 = 3
// 2 * 1 = 2
// 2 * 3 = 6
// 3 * 1 = 3
// 3 * 3 = 9
In this example, we have two nested loops with each loop iterating through the integer numbers 1
to 3
.
Within these loops is a nested if
statement which checks the current iteration value of the inner loop to see if it is equal to 2
. If it is, it executes the continue
statement.
Each time the continue
statement is executed, the current iteration of the inner for-in
loop is terminated and execution starts at the next iteration of that inner loop. The result is that in those cases where the inner loop value is equals to 2
, the print
statement is skipped. We can see this with the output.
Ok, now what about if we use the continue statement with an associated statement label?
Using the continue
Statement With an Associated Statement Label
As we saw with the break
statement, the continue
statement can also be followed with a statement label. When used in this manner, the continue
statement causes the current iteration of the loop statement named by the label to be aborted and execution to continue with the next iteration of that loop.
In this next example, we have two nested loops, the outer one labelled with outer
and the inner one labelled with inner
:
outer: for i in 1...3 {
inner: for j in 1...3 {
if j > 2 {
continue outer
}
print("i: \(i) j: \(j)")
}
}
// i: 1 j: 1
// i: 1 j: 2
// i: 2 j: 1
// i: 2 j: 2
// i: 3 j: 1
// i: 3 j: 2
Each loop iterates through the numbers 1
to 3
. Once the loop value of the inner loop (j
) exceeds 2
, the continue
statement executes and the outer
loop is interrupted. Execution is then transferred to the condition of the outer
loop statement where execution continues.
A Word of Warning About Labelled Statements
By now, you should have got a pretty good idea of how the break
and continue
statements work and how, in conjunction with labelled statements, you can construct some pretty complex logic within your applications. There is however a word of caution.
Due to the non-linear flow through your application that the use of labelled statements creates, it can make understanding your code more challenging.
For this reason, try to minimise your use of labelled statements using them only where absolutely necessary.