R Programming • Control Flow

next & break — Use next and break to control loop flow

Master R's two loop-control statements that let you skip iterations or terminate loops early for cleaner, more efficient code.

A statistical scatter plot with code braces and a variable symbol for R programming.
Inside this lesson 10 sections 5 questions 6 figures

SECTION 1

Historical Context & Motivation

The ability to alter the natural flow of a loop—skipping an iteration or exiting entirely—has been a staple of structured programming since the 1960s. Early imperative languages relied heavily on goto statements, which made programs notoriously difficult to read and maintain. The structured-programming movement championed by Edsger Dijkstra advocated replacing arbitrary jumps with well-defined control structures—including disciplined loop-exit mechanisms. R, a language whose lineage passes through S and Scheme, inherited these structured alternatives as the next and break keywords, analogous to continue and break in C, Java, and Python.

1966 • Dijkstra's "Go To Statement Considered Harmful"

Dijkstra argued that unstructured jumps impair program reasoning, catalyzing the move toward structured loop-control mechanisms like break and continue.

1972 • C Introduces break & continue

Dennis Ritchie's C language formalized break and continue as first-class loop-control statements, establishing a pattern adopted by virtually every imperative language that followed.

1988 • S Language at Bell Labs

The S language, R's predecessor, included next and break for controlling for, while, and repeat loops. R later adopted these keywords directly.

2000 • R 1.0 Released

R 1.0.0 shipped with next and break as reserved words, ensuring they could not be overridden by user-defined variables and could be relied upon across all loop constructs.

Why do we need these statements at all? Consider a data-processing pipeline that iterates over thousands of records: some records may be incomplete and should be skipped, while others may signal that no further processing is useful. Without next and break, programmers would need to nest deeply or use auxiliary flag variables—both of which reduce clarity. The central question this lesson addresses is: How do next and break provide fine-grained control over loop execution in R, and when should each be used?

SECTION 2

Core Principles & Definitions

R provides three loop constructs—for, while, and repeat—each of which supports both next and break. Understanding these two keywords rests on grasping how R's interpreter advances through a loop body and what happens when control is diverted.

1

next — Skip to the Next Iteration

When the interpreter encounters next, it immediately halts execution of the current iteration's remaining statements and jumps back to the loop's condition check (or the next element in a for-loop). No code below the next call is executed for that iteration.
2

break — Exit the Loop Entirely

The break statement terminates the innermost enclosing loop immediately. Execution resumes at the first statement after the loop's closing brace. It is the only way to exit a repeat loop, which has no built-in termination condition.
3

Scope: Innermost Loop Only

Both next and break affect only the innermost loop that contains them. In nested-loop scenarios, the outer loop continues unaffected unless it also contains its own control statements.
4

Guard Pattern — if + next / break

In practice, next and break are almost always wrapped inside an if statement. The condition acts as a guard clause, making the intent of the skip or exit explicit and readable.
✦

KEY TAKEAWAY

Think of a loop as an assembly line. next is like an inspector who spots a defective part and sends it to the reject bin so the line immediately moves to the next part. break is hitting the emergency-stop button: the entire line halts. One skips a single item; the other shuts down the process.

SECTION 3

Visual Explanation — Loop Flow with next & break

Loop Flow: for (i in seq) { body }Start: i = seq[1]More elements?NoExit loopYesExecute body codeGuard condition?Remaining body codeAdvance iLoop backnextSkip to next ibreakExit loop
The flowchart above traces a for loop's lifecycle. After the guard-condition diamond, the cyan path (next) sends control back to the top of the loop—skipping the remaining body—while the pink path (break) exits the loop entirely and resumes execution after the closing brace.

Observe that next does not terminate the loop; it merely short-circuits the current iteration and returns control to the loop header, where the iterator variable advances to the next value. In a while loop, next jumps back to the condition test; if the condition is still TRUE, the body executes again. Conversely, break unconditionally ends the loop—no further iterations occur, regardless of remaining elements or condition state. Both statements affect only the innermost enclosing loop; outer loops are unaffected.

SECTION 4

How next & break Work Under the Hood

At the interpreter level, R implements next and break via non-local jumps—internally similar to how R handles conditions and restarts. When the evaluator encounters next, it raises an internal signal that the loop handler catches, causing it to skip the remaining body and proceed to the next iteration. The break keyword raises a different signal that causes the loop handler to transfer control past the loop entirely. Importantly, neither statement is a function—they are reserved words with special evaluation semantics, and attempting to use them outside a loop context generates an error.

Syntax in Each Loop Type

Loop Typenext Behaviorbreak Behavior
for (var in seq) { ... }Advances iterator to next element of seq; if none remain, loop ends normally.Exits the loop immediately; remaining elements of seq are not processed.
while (cond) { ... }Jumps to the condition re-evaluation. If cond is still TRUE, the body runs again.Exits the loop regardless of whether cond is TRUE or FALSE.
repeat { ... }Jumps to the top of the repeat body. Since repeat has no condition, this restarts the body.The only way to exit a repeat loop. Without break, the loop runs forever.

Behavior of next and break across R's three loop constructs.

Canonical Code Patterns

The idiomatic pattern in R is to place the guard condition at the top of the loop body—a technique sometimes called a guard clause. For next, this looks like: if (should_skip) next. The remainder of the loop body then executes only when the guard condition is FALSE, avoiding deeply nested else blocks. The analogous pattern for break is: if (should_stop) break. This pattern is particularly powerful in data-cleaning pipelines where certain records are malformed or where a sentinel value signals end-of-data.

⚠

Common Pitfall: Infinite while Loops

When using next inside a while loop, ensure that the counter or state variable is updated before the next call. If the increment appears after next, it will be skipped, and the loop will never terminate.

SECTION 5

Common Patterns & Nested Loop Behavior

In real-world R scripts, next and break appear in several recurring patterns. Understanding these patterns helps you recognize when to reach for a loop-control statement versus restructuring your logic with vectorized operations or the apply family. The most important nuance arises in nested loops: since both keywords affect only the innermost loop, controlling an outer loop from within an inner loop requires a flag variable or refactoring into a function with return().

Nested Loop: break Affects Only the Inner LoopOuter loop: for (i in 1:3)Inner loop: for (j in 1:5)i=1j=1j=2j=3 ✕j=4j=5← break herei=2j=1j=2j=3 ✕j=4j=5← break herei=3j=1j=2j=3 ✕j=4j=5← break hereCondition: if (j == 3) break → inner loop exits, outer continuesExecutedbreak triggeredSkipped (never reached)
This diagram shows a nested loop where break is called inside the inner loop when j == 3. Green cells represent executed iterations, the pink cell is where break fires, and dashed cells are skipped. Notice that the outer loop (i) continues for all three values—only the inner loop is terminated each time.

Pattern Catalog

PatternKeywordUse Case
Filter-in-LoopnextSkip NA values, malformed rows, or records that fail a validation check before processing.
Early TerminationbreakStop iterating once a target value is found (e.g., linear search) or a convergence criterion is met.
Sentinel LoopbreakUse repeat with break to process input of unknown length, terminating on a sentinel value.
Nested Searchbreak + flagSet a flag variable in the inner loop, then check it in the outer loop to decide whether to break there as well.

Common patterns involving next and break in R.

SECTION 6

Worked Example — Cleaning & Searching a Data Vector

Suppose you have a numeric vector containing some NA values and you need to find the first element greater than 100, skipping any missing data. This example demonstrates both next and break in the same loop.

Find the First Value > 100, Skipping NAs

  1. 1

    Step 1 — Define the Data

    Create a vector with a mix of numeric values and NAs: data <- c(23, NA, 47, NA, 105, 88, 210). Initialize a result variable to NULL to store the first qualifying value.
    result <- NULL
  2. 2

    Step 2 — Write the Loop with Guard Clauses

    Iterate over each element. The first guard uses next to skip NA values: for (x in data) { if (is.na(x)) next. This ensures that subsequent code never has to handle NAs.
  3. 3

    Step 3 — Add the break Condition

    After the NA guard, check whether the current value exceeds 100. If so, store it and exit immediately: if (x > 100) { result <- x; break }. The closing brace ends the loop body.
  4. 4

    Step 4 — Trace Execution

    Iteration 1: x = 23, not NA, not > 100 → continue. Iteration 2: x = NA → next fires, skip. Iteration 3: x = 47, not NA, not > 100 → continue. Iteration 4: x = NA → next fires, skip. Iteration 5: x = 105, not NA, 105 > 100 → store and break.
  5. 5

    Step 5 — Verify the Result

    After the loop, result holds the value 105. Elements 88 and 210 were never examined because break terminated the loop as soon as the first qualifying value was found.
    result # [1] 105
💡

Complete Code

data <- c(23, NA, 47, NA, 105, 88, 210) result <- NULL for (x in data) { if (is.na(x)) next if (x > 100) { result <- x break } } cat("First value > 100:", result, "\n") # Output: First value > 100: 105

SECTION 7

Strengths, Limitations & Alternatives

While next and break are invaluable in imperative loop contexts, R's functional programming paradigm often offers vectorized or apply-based alternatives that can be more idiomatic and performant. Understanding the tradeoffs helps you choose the right tool for each situation.

AspectStrengthsLimitations
ReadabilityGuard clauses with next/break flatten nested if-else structures, making intent explicit.Overuse in deeply nested loops can create spaghetti-like flow that is hard to trace.
Performancebreak enables early termination, avoiding unnecessary computation (e.g., linear search stops at first match).R's for loops are generally slower than vectorized operations; adding next/break doesn't fix the overhead of interpreted iteration.
Nested LoopsSimple to use within a single loop level.Cannot break out of multiple levels at once; requires flag variables or function-based refactoring.
Functional AlternativesLoops with next/break handle side effects and stateful logic naturally.Filter + which, vapply, or purrr::detect can replace many next/break patterns more concisely.
DebuggingEasy to set breakpoints around next/break in RStudio's debugger.When next is triggered, browser() or print() statements after it are silently skipped, which can confuse debugging.

Strengths and limitations of next and break in R.

✦

WHEN TO USE LOOPS VS. VECTORIZED CODE

If your task can be expressed as a filter followed by a transformation—e.g., "process all non-NA values"—prefer vectorized code like data[!is.na(data)]. Reserve explicit loops with next and break for situations that require sequential state, side effects (writing files, updating databases), or truly conditional early termination that cannot be expressed declaratively.

SECTION 8

Connection to Advanced Control Flow

The next and break keywords are the simplest form of non-local transfer of control in R. As you move into more advanced R programming, you will encounter a richer family of control-flow mechanisms—tryCatch for exception handling, withCallingHandlers for restartable conditions, and the rlang package's abort/warn/inform system—that generalize the idea of jumping out of normal execution flow.

Featurenext / breakAdvanced Mechanism
ScopeInnermost loop onlytryCatch can unwind multiple call frames; return() exits a function from any depth.
Use Outside LoopsError: no loop to break fromtryCatch/tryCatchLog works anywhere; return() works inside any function body.
Data PassingNo value is returned by next or break; results must be stored in variables before invoking them.tryCatch handlers receive the condition object; return(value) passes data to the caller.
Iteration AbstractionManual iteration with for/while/repeat.purrr::detect(), purrr::keep(), and base R's Filter() abstract iteration + early exit into functional idioms.

Comparison of basic loop control with advanced control-flow mechanisms.

As a forward-looking note, many tasks that seem to require a loop with break—such as finding the first element satisfying a predicate—can be elegantly handled by Find(f, x) in base R or purrr::detect(x, f) in the tidyverse. Similarly, filtering with next can often be replaced by Filter(f, x) or purrr::keep(x, f). Mastering next and break provides the conceptual foundation for understanding why these higher-level abstractions exist and when the imperative approach remains the better choice.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain, in your own words, the difference between next and break in terms of what happens to (a) the current iteration, (b) remaining iterations, and (c) execution after the loop.
Show answer
The next statement terminates the current iteration—any code below it in the loop body is not executed—but the loop itself continues with the next element or condition check. Remaining iterations are unaffected. The break statement terminates both the current iteration and all remaining iterations, transferring control to the first statement after the loop's closing brace. In both cases, code after the loop executes normally; the difference is that break reaches that post-loop code immediately, while next only reaches it after all remaining iterations have been processed or the loop's condition becomes FALSE.

Question 2: BASIC CALCULATION

Given the code below, what is the final value of total? total <- 0 for (i in 1:10) { if (i %% 3 == 0) next total <- total + i } Trace through each iteration to justify your answer.
Show answer
The next statement skips all multiples of 3, so i = 3, 6, and 9 are skipped. The values added are 1 + 2 + 4 + 5 + 7 + 8 + 10 = 37. Tracing: iteration 1 adds 1 (total = 1), iteration 2 adds 2 (total = 3), iteration 3 skips, iteration 4 adds 4 (total = 7), iteration 5 adds 5 (total = 12), iteration 6 skips, iteration 7 adds 7 (total = 19), iteration 8 adds 8 (total = 27), iteration 9 skips, iteration 10 adds 10 (total = 37). The final value is 37.

Question 3: INTERMEDIATE

Write an R while loop that reads integers from a predefined vector inputs <- c(4, -1, 7, 0, 12, 3) one at a time, accumulates a running sum, but (a) skips negative values using next, and (b) stops immediately if the running sum exceeds 20 using break. Be careful about updating the index before calling next.
Show answer
A correct solution is: inputs <- c(4, -1, 7, 0, 12, 3) idx <- 1 total <- 0 while (idx <= length(inputs)) { val <- inputs[idx] idx <- idx + 1 # increment BEFORE next if (val < 0) next total <- total + val if (total > 20) break } cat(total) The key insight is that idx <- idx + 1 must appear before the next guard; otherwise, a negative value would cause an infinite loop because idx would never advance. Tracing: val = 4, total = 4; val = −1, skip; val = 7, total = 11; val = 0, total = 11; val = 12, total = 23 > 20, break. The final total is 23.

Question 4: APPLIED

You are processing a list of file paths and need to read each file, but some paths are invalid. Write a for loop that iterates over paths <- c("data1.csv", "missing.csv", "data3.csv"). Use file.exists() combined with next to skip files that don't exist, and process the rest. Include a message indicating which files were skipped.
Show answer
paths <- c("data1.csv", "missing.csv", "data3.csv") results <- list() for (p in paths) { if (!file.exists(p)) { message("Skipping ", p, ": file not found") next } results[[p]] <- read.csv(p) } The next statement after the message call prevents read.csv() from being called on a nonexistent file, which would otherwise produce an error. The pattern is a classic guard clause: validate first, skip on failure. This approach is cleaner than wrapping the read.csv call in a large if-else block, especially if the processing logic below is lengthy. An alternative approach using tryCatch would handle errors more gracefully in production but is more verbose.

Question 5: CRITICAL THINKING

Consider the following nested loop that searches a matrix for a target value: mat <- matrix(1:20, nrow = 4, ncol = 5) target <- 14 found <- FALSE for (i in 1:nrow(mat)) { for (j in 1:ncol(mat)) { if (mat[i, j] == target) { found <- TRUE break } } if (found) break } (a) Explain why a single break inside the inner loop is insufficient to exit both loops. (b) Propose an alternative design that avoids the flag variable entirely—hint: wrap the search in a function.
Show answer
(a) In R, break only terminates the innermost enclosing loop. When break fires inside the j-loop, control returns to the i-loop, which would continue to the next row unless we add a flag check. Without if (found) break in the outer loop, the search would continue unnecessarily through remaining rows. (b) Encapsulate the nested loop in a function and use return() to exit both loops at once: find_in_matrix <- function(mat, target) { for (i in 1:nrow(mat)) { for (j in 1:ncol(mat)) { if (mat[i, j] == target) return(c(i, j)) } } return(NULL) } The return() statement exits the entire function, bypassing both loops. This is cleaner, more reusable, and eliminates the mutable flag variable. A third alternative is to use which(mat == target, arr.ind = TRUE), which is fully vectorized and avoids explicit loops altogether.

SUMMARY

Summary

Summary

R provides two loop-control keywords: next, which skips the remainder of the current iteration and advances to the next element or condition check, and break, which terminates the innermost enclosing loop entirely. Both are typically paired with guard clauses (if-statements) that make the skip or exit condition explicit. They work identically across R's three loop constructs—for, while, and repeat—with the caveat that break is the only way to exit a repeat loop.

Key practical considerations include: always update loop counters before calling next in while loops to avoid infinite loops; remember that both keywords affect only the innermost loop in nested contexts; and prefer vectorized alternatives (such as Filter, Find, or purrr's detect and keep) when the logic can be expressed declaratively. Mastering next and break builds the conceptual foundation for R's more advanced control-flow mechanisms, including tryCatch, condition handling, and functional iteration.

Varsity Tutors • R Programming • next & break — Use next and break to control loop flow