rubyguides

String#insert

str.insert(index, other_str)

String#insert inserts the given string at a given index. It returns a modified copy, leaving the original string untouched.

Unlike most string methods that take a single index, insert accepts negative indices — counting from the end of the string, similar to Array#insert.

Signature

str.insert(index, other_str)

Parameters:

  • index — an Integer indicating the position (0-based, or negative to count from end)
  • other_str — the String to insert

Returns: String — a new string with other_str inserted at index

Raises IndexError if index is out of range, and TypeError if other_str is not a string.

Basic Usage

"hello".insert(1, " X")       # => "hXello"
"hello".insert(5, "!")        # => "hello!"
"hello".insert(-1, "!")        # => "hello!"

Positive indices insert at that character position. The character at that position is not replaced — everything from there onward shifts to the right.

Negative Indices

This is where insert differs from most string methods:

"hello".insert(-2, "X")        # => "helXlo"
"hello".insert(-5, "X")        # => "Xhello"
"hello".insert(-10, "X")       # => IndexError

-1 is the last character, -2 is second-to-last, and so on. An index that would place before the start of the string raises an IndexError.

Prepending and Appending

Use index 0 to insert at the beginning:

"world".insert(0, "hello ")    # => "hello world"

Use index -1 to insert before the last character — effectively appending if you follow it:

"hello".insert(-1, " world")   # => "hello world"

Or more directly, use + str or concat to append without the index gymnastics.

Edge Cases

IndexError for Out-of-Range

"hello".insert(100, "!")       # => IndexError: index 100 too big for string of length 5
"hello".insert(-100, "!")     # => IndexError: index -100 too small for string of length 5

The valid range is -length..length. On an empty string, valid indices are 0 and -1.

TypeError for Non-String Argument

"hello".insert(0, 123)         # => TypeError: no implicit conversion of Integer into String

The second argument must be a string. Numeric values need explicit conversion:

"hello".insert(0, 123.to_s)    # => "123hello"

Modifying a String in Place

insert returns a new string — the original is unchanged. For in-place modification:

s = "hello"
s = s.insert(0, "j")           # rebind to the new string
puts s                         # => "jhello"

There is no insert! variant.

Use Cases

Inserting a Separator

"20260430".insert(4, "-").insert(7, "-")   # => "2026-04-30"

Chaining multiple insert calls can format strings where you know the exact positions.

Adding Delimiters in a Loop

numbers = ["a", "b", "c"]
result = ""
numbers.each_with_index do |n, i|
  result = result.insert(i * 2, n)
end
result  # => "abc"

This gets awkward fast — join is usually a better choice for concatenation with separators.

Dynamic Text Insertion

def label(type, version)
  "App #{type}".insert(-1, " v#{version}")
end

label("Beta", 2)    # => "App Beta v2"

insert vs Other Methods

MethodReturns new?Accepts negative index?
insertYesYes
[]=No (in-place)Yes
concatYesNo
prependYesNo

insert is the only string mutator that combines non-destructive behavior with negative indexing.

See Also