Concepts / Vector Range Checking and Exception Handling

Vector Range Checking and Exception Handling

For Vec, an out-of-range access throws an exception, and that exception is something the program can catch.

  • Programming
Interactive lab

Try it: try / except / else / finally

How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.

How it works

  1. Statements in try run until one raises an exception; the rest of try is skipped.
  2. Python checks the except clauses in order and runs the first one whose type matches.
  3. If nothing was raised, the else block runs instead.
  4. finally runs in every case.

Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.

Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.

Educational simulation

Loading the simulation…

A Request for Element Seven

Suppose a Vec holds five elements, numbered 0 through 4. Asking for element 2 is routine. Asking for element 7 is a mistake, because there is no element there. This article answers two questions: what the program does at the moment it makes that bad request, and what you can do about it in code.

What do you think happens?

You have a Vec of five elements and you ask for the element at index 7. Does the access quietly return some arbitrary value, or does something else happen?

  • It quietly returns an arbitrary value

  • It throws an exception

Reveal answer
Answer:

It throws an exception.

For Vec, an access outside the valid range does not return an arbitrary value. It throws an exception, which signals that something has gone wrong and that normal execution cannot simply continue along its planned path. The important point is that this exception is something the program can catch.

Two Paths From One Access

Think of an access as a fork in the road. If the index is within range, the access completes and the program moves on with the value it retrieved. If the index is out of range, the access throws an exception and control leaves the ordinary sequence of statements. From that point the program looks for a handler, which is a catch block prepared to deal with the exception.

Access v[i]Index in range?Value used, programmoves onException thrownSearch for handlerHandler runsResume after handlercheckyesnocontrol leaves sequencefound
After the index is checked, execution either continues with the retrieved value or jumps to an exception path that searches for a handler.

Notice that the handler is not part of the access itself. It is a separate piece of code that the program reaches only because the exception was thrown. Both paths begin from the same event, the access. They differ only in whether the index was valid.

Which Indexes Are Valid

For a Vec with n elements, the valid indexes run from 0 up to n minus 1, so that $0 \leq i \leq n-1$. For the five-element Vec in the opening example, the valid indexes are 0, 1, 2, 3 and 4. Any other index, such as 5 or 7, is outside the range and triggers the exception path described above.

Where Execution Goes: try and catch

A try-block wraps the code that might throw. A catch handler placed after it says what to do when that code throws. The handler runs only when the code in the try-block throws. If nothing in the try-block throws, the handler is skipped entirely.

Enter try-blockStatement that maythrowRemaining trystatementsStatements after thehandlerCatch handlerno throwthrow
When the access inside the try-block throws, the remaining try statements are skipped, the catch handler runs, and execution resumes after the handler.

The example below is an original illustration of the pattern. It is not the exact code from the source, which names the pattern but leaves the details of the Vec type to the reader. It assumes, as the source states, that subscripting a Vec with an out-of-range index throws an exception.

Code walkthrough
int main()
{
    Vec v(5);          // five elements, indexes 0 through 4
    try {
        int x = v[7];  // out-of-range access: throws
        cout << x << "\n";   // skipped when the access throws
    }
    catch (...) {      // handler: runs only if the try-block threw
        cout << "index 7 is not valid\n";
    }
    cout << "program continues here\n";
    return 0;
}
  1. 1
    Line 1 declares the Vec with five elements, so the valid indexes are 0 through 4.
  2. 2
    Line 3 opens the try-block, which wraps the code that may throw.
  3. 3
    Line 4 subscripts index 7. Because 7 is out of range, the access throws and control leaves the try-block.
  4. 4
    Line 5 is skipped, because the throw interrupted the try-block before it could run.
  5. 5
    Line 7 is the catch handler. It runs only because the try-block threw, and it deals with the problem in a controlled way.
  6. 6
    Line 9 runs after the handler finishes. The program has continued past the bad access rather than crashing at that point.
  • Vec v(5) creates a container whose valid indexes are 0 through 4
  • The try keyword marks the code that may throw
  • v[7] is outside the valid range, so the access throws
  • The catch handler runs only because the try-block threw
  • The final statement runs because the handler dealt with the exception

Tracing a Handled Exception

Worked example

Following the handled access step by step

A Vec has five elements. The try-block asks for index 7, has a statement after the access, and a catch handler follows. What happens, in order?

  1. 1
    Enter the try-block

    Execution begins the protected code. No handler has run yet.

  2. 2
    Perform the access v[7]

    Index 7 is outside the valid range 0 through 4, so the access throws an exception instead of returning an ordinary value.

  3. 3
    Skip the rest of the try-block

    Control leaves the ordinary sequence, so the statement that prints the value never runs.

  4. 4
    Run the handler

    Because the code in the try-block threw, the catch handler is the code prepared to respond, and it runs.

  5. 5
    Resume after the handler

    The statement following the try-catch construct runs, so the program keeps going past the problem.

Result

The bad access is handled. The program does not crash at the point of the access, and it continues with the statements after the handler.

When No Handler Is Waiting

A thrown exception does not disappear simply because no one wrote a handler for it. If the program does not catch an exception, the program terminates in a well-defined manner. The intent of that phrase matters. The program does not keep doing work in some unpredictable state, and it does not fail in an undefined way. It ends in an orderly fashion that the language defines.

main()helper()v[7] accessProgram terminatescallscallsexception propagatesno handler, propagatesno handler anywhere
When the access in the helper throws and no handler is present there, the exception moves up the chain of calls until it reaches a handler or the program ends.

Read the diagram from the bottom up. The access inside the helper throws. If the helper has no handler, the exception leaves the helper and travels to the function that called it. If that caller has no handler either, it keeps moving up toward main(). Once it reaches the top with no handler found, the program ends in its orderly, language-defined way.

Wrapping main() in a try-block

The source offers one practical way to reduce surprises from uncaught exceptions: make the body of main() a try-block. Any exception that escapes the rest of the program then reaches a handler attached to main(), so the program can report the problem in a controlled way rather than relying on the default outcome. This is useful because an exception thrown deep inside a helper function still reaches the try-block in main() on its way out.

cpp
Notes on the code
  • The whole body of main() sits inside the try-block
  • An exception from helper() propagates up to this handler
  • The handler reports the problem instead of relying on the default outcome

Mistakes With Vec Access and Handlers

  • Common mistake

    Assuming an out-of-range access returns an ordinary value

    What it looks like

    Reading v[7] on a five-element Vec and expecting some default number back

    Why it fails

    For Vec, an out-of-range access throws an exception. It does not quietly produce an arbitrary value.

    Do this instead

    Treat an out-of-range index as an exception event, and handle it in a try-block and catch handler where the risk exists.

  • Common mistake

    Expecting the statements after the bad access in the try-block to run

    What it looks like

    Placing a print statement right after v[7] inside the try-block and expecting it to show the value

    Why it fails

    When the access throws, control leaves the ordinary sequence, so the rest of the try-block is skipped.

    Do this instead

    Put only the code that depends on a valid access after that access, and let the handler deal with the failure.

  • Common mistake

    Believing a try-block alone makes a bad index safe

    What it looks like

    Wrapping main() in a try-block and assuming every index problem is now handled gracefully

    Why it fails

    The source says the try-block in main() reduces surprises, but it does not replace checking indexes where they are used.

    Do this instead

    Keep the index checks close to where the access happens and use main()'s try-block as a final safety net.

  • Common mistake

    Thinking an uncaught exception is harmless because the program simply stops

    What it looks like

    Leaving a helper's access unprotected and assuming the program will recover on its own

    Why it fails

    An uncaught exception ends the program in a well-defined but final way. Work in progress is not completed.

    Do this instead

    Handle the exception where you can respond sensibly, or at least place a handler in main() to report it in a controlled way.

Practice: Tracing the Exception Path

MEDIUM

A Vec holds six elements. A try-block contains three statements in order: first a read of index 2, then a read of index 6, then a print of the value just read. A catch handler follows the try-block. Which statements run, which are skipped, and does the program continue after the handler?

Hints
  • Valid indexes for six elements run from 0 through 5, so index 6 is out of range.
  • The read of index 2 completes normally, because it is inside the valid range.
  • Once the read of index 6 throws, the statements after it in the try-block do not run.

Key Takeaways

  • For Vec, an out-of-range access throws an exception rather than returning an ordinary value, and the program can catch that exception.
  • A try-block marks the code that may throw, and a catch handler runs only when that code throws.
  • A handled exception lets the program continue after the handler, while an uncaught exception ends the program in a well-defined, orderly way.
  • Making the body of main() a try-block reduces surprises from uncaught exceptions, but it does not replace checking indexes where they are used.

More concepts starting with V