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.
Identify the guard: The guard checks that the denominator is non-zero.
Identify the risky operation: The risky operation is the division calculation that uses the denominator.
Place the guard first: Write the non-zero check before the division in the and expression.
Trace the false case: If the denominator is zero, the guard is false, so short-circuit evaluation stops before the division is attempted.
Trace 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
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. |
- 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
Putting the risky operation before the guard
Evaluation reaches the risky operation before the later condition can prevent it.
Fix:
Place the condition that checks the data before the operation that uses the data.Assuming that any guard in the expression will provide protection
Guard placement is critical. A guard that comes after the risky operation cannot stop that earlier operation.
Fix:
Trace the expression from left to right and verify that the guard is evaluated first.Skipping the guard because the input usually looks valid
Runtime errors can occur when the current value is invalid, even if other values were valid previously.
Fix:
Use 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?
Locate the risky operation: The array access is the operation that may fail when the index is invalid.
Locate the guard: The index-validity check is the guard.
Compare their order: The risky access appears first, while the guard appears later.
Apply 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
- The Guardian Pattern places a guard condition before a risky operation in an and expression.
- Short-circuit evaluation stops when a condition is false, so later operations are not run.
- Condition order is essential: the guard must come before the operation it protects.
- Useful guards check non-zero denominators, valid array indices, and valid data values.
- To design a guard, identify the risk, define the safety check, place the check first, and trace the false case.
Key Takeaways
- 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.