Start learning free

LESSON 61 OF 414 · STRINGS I

Exceeding String Bounds in Scala: StringIndexOutOfBounds

Lesson Read the full explanation

When accessing string characters with indices, the most common problem we can run into is exceeding the string's bounds.

Remember, the characters of a string are normally indexed from 0 to length - 1.

Any index which is strictly greater than length - 1, or negative, is invalid.

When you try to access a character with an invalid index, Scala throws a StringIndexOutOfBoundsException:

val car = "ford"

// Valid indices: 0, 1, 2, 3
println(car.charAt(0)) // Output: f
println(car.charAt(3)) // Output: d

// Invalid indices: 4, 30
println(car.charAt(4)) // StringIndexOutOfBoundsException
println(car.charAt(30)) // StringIndexOutOfBoundsException

This code would throw a StringIndexOutOfBoundsException: Index 4 out of bounds for length 4

In conclusion, we always want to Double check our code to make sure that we don't exceed one string's bounds in our programs.


Assignment
Follow the Coding Tutorial and let's practice with string bounds!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    SCALA · STRINGS I

    Exceeding String Bounds in Scala, explained

    What goes wrong

    A string with n characters has valid indexes 0 to n - 1. Any index outside that range is invalid, whether it is too big or negative. Reading one throws a StringIndexOutOfBoundsException:

    val car = "ford"
    
    println(car(0))        // f
    println(car(3))        // d
    println(car(4))        // throws
    println(car.charAt(4)) // throws too
    

    The exception's message names the bad index and the length: Index 4 out of bounds for length 4. If nothing catches it, the program stops at that line, and later lines never run.

    The exercise, step by step

    The lesson has code meant to print the last character of a message, but it crashes:

    val message = "I love pancakes"
    
    println(message(message.length))
    
    1. Find the length. "I love pancakes" has 15 characters, so message.length is 15.
    2. Find the valid indexes. They are 0 to 14. Index 15 is one past the end, which is why the program throws with Index 15 out of bounds for length 15.
    3. Subtract one. The last character is at message.length - 1.
    val message = "I love pancakes"
    
    println(message(message.length - 1))
    

    This prints s.

    Common ways to go out of bounds

    • Using length as an index. This is the lesson's bug. Length is a count; the last index is one smaller.
    • Empty strings. ""(0) fails because no index is valid. "".last fails differently, with a NoSuchElementException.
    • Negative indexes. message(-1) throws. Scala does not count from the end the way some languages do. Write message(message.length - 1).
    • Loops that run one step too far. A loop for (i <- 0 to s.length) includes s.length and eventually reads past the end. Use 0 until s.length, which stops before the end.
    • substring bounds. "abc".substring(0, 10) throws too, with Range [0, 10) out of bounds for length 3.

    Safe access

    When an index might be invalid, check it, or ask for an Option with lift:

    val fruit = "Avocado"
    
    println(fruit.lift(2))  // Some(o)
    println(fruit.lift(10)) // None
    

    A plain comparison works as well: if (i >= 0 && i < fruit.length) fruit(i). When a bug throws this exception, read the message first. It tells you the index you used and the real length, which usually points straight to the off-by-one.

    Catching the exception

    You can also handle the error instead of preventing it:

    val fruit = "Avocado"
    
    try {
      println(fruit(20))
    } catch {
      case e: StringIndexOutOfBoundsException => println("caught: " + e.getMessage)
    }
    println("after")
    
    caught: Index 20 out of bounds for length 7
    after
    

    Catching is useful for input you don't control. For your own index arithmetic it is better to fix the arithmetic, because an exception hides the real mistake.

    Reading the error

    When the exception appears in a program, the message has two numbers: the index you asked for and the length of the string. If the index equals the length, you are one step too far, and the fix is usually - 1. If the index is negative, a calculation went below zero. If the length is 0, the string was empty, and you need a check before indexing.

    Fixing it for good

    After you fix the immediate bug, ask what the code should do with an empty or short string. A function that takes any string from outside should either check the length first or return an Option. The lesson's string always has 15 characters, so the plain fix is enough there. In general, writing the check once, near the place where the index is computed, is easier than finding the crash later.

    Key takeaways

    • Valid indexes are 0 to length - 1.
    • An invalid index throws StringIndexOutOfBoundsException.
    • The last character is s(s.length - 1), never s(s.length).
    • Check the index, or use lift, when it may be invalid.
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free