Reason #26 • January 26th, 2026

String#[] (brackets)

This week we're throwing a String appreciation party! 🎉

First up, let's explore the versatile String#[] method, which allows us to access substrings in various ways.

Ruby
string = "Hello, Ruby!"

# We can access characters with a single index
string[0] # => "H"

# Negative indices count from the end
string[-1] # => "!"

# We can access substrings with a start index and length
string[7, 4] # => "Ruby"

# Or with a range
string[7..10] # => "Ruby"

# Ranges from positive to negative indices are fine too
string[7..-2] # => "Ruby"

# We can also pass a String
string["Ruby"] # => "Ruby"

# If the substring is not found, it returns nil
string["Python"] # => nil

# We can use regex to extract patterns
string[/R\w+/] # => "Ruby"

# If no match is found, it returns nil
string[/P\w+/] # => nil
      
JavaScript
const string = "Hello, Ruby!";

// JavaScript can bracket access single characters too
string[0]; // => "H"

// But negative indices must be manually calculated
string[string.length - 1]; // => "!"

// We can get a substring with index and length via substr
string.substr(7, 4); // => "Ruby"

// Or with start and end indices via substring
string.substring(7, 11); // => "Ruby"

// Negative indices must be calculated
string.substring(7, string.length - 1); // => "Ruby"

// We can use match to find substrings
string.match("Ruby")&[0]; // => "Ruby"

// If not found, match returns null, hence we guard with &
string.match("Python")&[0]; // => 0

// Match accepts both strings and regex
string.match(/R\w+/)&[0]; // => "Ruby"

// No match with regexp behaves the same as with strings
string.match(/P\w+/)&[0]; // => 0
      

As you can see the [] method is incredibly flexible as well as intuitive. JavaScript also provides decent alternatives, but gets clunky at times and in particular suffers from substr vs substring confusion.

History

The String#[] method has been pretty steady all the way back to Ruby's original release. The only evolution I could find was a subtle change to the return value when passing a single index. Prior to Ruby 1.9, this would return the numerical ASCII code of the character at that position (similar to String#ord), but from Ruby 1.9 onwards it returns a single-character string instead, making all the return values consistent.