Preventing Common Runtime Errors
The Guardian Pattern strategically places a guard condition before a risky operation in an 'and' expression to prevent runtime errors.
Why Order Matters
A runtime error can occur when a program attempts an operation that its current data cannot safely support. A common prevention strategy is the Guardian Pattern: place a guard condition before the risky operation in an and expression. The guard checks whether the operation is safe. If the guard is false, short-circuit evaluation stops the expression before the risky operation runs.
Imagine checking a denominator before using it in a division calculation. The denominator is the guard target: the calculation should proceed only when the denominator is non-zero. The check must happen before the division, because checking afterward would be too late.
The Guardian Pattern
The Guardian Pattern strategically places a guard condition before a risky operation in an and expression to prevent runtime errors.
The two parts of the expression have different jobs. The guard condition tests whether the data is valid or safe. The risky operation uses that data. Because the guard appears first, a false guard prevents the later operation from running. A true guard allows evaluation to continue to the risky operation.
Checking a Denominator Before Division
Construct an and expression that permits a division calculation only when its denominator is non-zero.
- 1Identify the guard
The guard checks that the denominator is non-zero.
- 2Identify the risky operation
The risky operation is the division calculation that uses the denominator.
- 3Place the guard first
Write the non-zero check before the division in the and expression.
- 4Trace the false case
If the denominator is zero, the guard is false, so short-circuit evaluation stops before the division is attempted.
- 5Trace the true case
If the denominator is non-zero, the guard is true, so evaluation continues to the division.
The guard protects the division because it is evaluated before the operation it protects.
Tracing Evaluation
What do you think happens?
An and expression begins with a guard that is false. Will the later risky operation be evaluated?
Reveal answer
No, evaluation stops at the false guard.
Short-circuit evaluation stops the expression as soon as a condition is false. Since the guard is first, the later operation is not run.
Evaluation proceeds from the first condition toward the later condition. When the first condition is false, the and expression already cannot succeed, so the remaining operation is unnecessary. This is the control-flow advantage of the Guardian Pattern: invalid data is blocked before it reaches the risky operation.
Ordering Safe Conditions
| Condition order | What happens | Protection |
|---|---|---|
| Guard and risky operation | The guard is evaluated first. A false guard stops evaluation before the risky operation. | Protects the operation. |
| Risky operation and guard | The risky operation is reached before the guard can stop it. | Does not reliably protect the operation. |
A guard protects a risky operation only when the guard comes first.
- Identify the operation that could fail with invalid data.
- Identify the condition that proves the data is safe or valid.
- Place the guard condition first in the and expression.
- Check that a false guard prevents evaluation from reaching the risky operation.
- Allow the operation only when the guard is true.
Common Guard Targets
The specific guard depends on the operation and its data. A non-zero check can guard a division denominator. A valid-index check can guard an array access. A valid-data check can guard a calculation that requires acceptable input values. In each case, the same structure applies: test the data first, then perform the operation only when the test succeeds.
| Risky operation | Guard condition | Purpose |
|---|---|---|
| Division | Denominator is non-zero | Blocks an unsafe denominator before division. |
| Array access | Index is valid | Blocks an invalid index before access. |
| Calculation using input | Data value is valid | Blocks unacceptable data before the calculation. |
Examples of operations and the guards that can precede them.
Mistakes with Guards
Common mistake Putting the risky operation before the guard
What it looks likeRisky operation and data is valid
Why it failsEvaluation reaches the risky operation before the later condition can prevent it.
Do this insteadPlace the condition that checks the data before the operation that uses the data.
Common mistake Assuming that any guard in the expression will provide protection
What it looks likeThe expression contains a non-zero check, but the division appears first.
Why it failsGuard placement is critical. A guard that comes after the risky operation cannot stop that earlier operation.
Do this insteadTrace the expression from left to right and verify that the guard is evaluated first.
Common mistake Skipping the guard because the input usually looks valid
What it looks likeUsing a denominator, index, or data value without checking it.
Why it failsRuntime errors can occur when the current value is invalid, even if other values were valid previously.
Do this insteadUse the appropriate non-zero, valid-index, or valid-data guard before the operation.
A guard protects only the operation that comes after it in the expression. If an expression contains several risky operations, inspect the ordering of each one and confirm that its relevant guard appears before it.
Practice the Pattern
For each situation, describe the guard condition and state what must appear first in the and expression: a division with a possibly zero denominator, an array access with a possibly invalid index, and a calculation using possibly invalid data.
Hints
- Name the value that could make the operation unsafe.
- Write the condition that establishes the value is safe or valid.
- Place that condition before the operation that uses the value.
Diagnosing a Failed Guard
An expression is intended to prevent an invalid array access, but the array access is written before the index-validity check. Does the guard protect the access?
- 1Locate the risky operation
The array access is the operation that may fail when the index is invalid.
- 2Locate the guard
The index-validity check is the guard.
- 3Compare their order
The risky access appears first, while the guard appears later.
- 4Apply short-circuit reasoning
The later guard cannot stop an operation that has already been reached.
The guard does not protect the array access. Move the index-validity check before the access.
Key Takeaways
- 1The Guardian Pattern places a guard condition before a risky operation in an and expression.
- 2Short-circuit evaluation stops when a condition is false, so later operations are not run.
- 3Condition order is essential: the guard must come before the operation it protects.
- 4Useful guards check non-zero denominators, valid array indices, and valid data values.
- 5To design a guard, identify the risk, define the safety check, place the check first, and trace the false case.
At a glance
- Use the Guardian Pattern to check data before a risky operation.
- Rely on short-circuit evaluation to stop an and expression when the guard is false.
- Place every guard before the operation it protects.
- Check denominators, array indices, and data values before using them.