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
| Method | Returns new? | Accepts negative index? |
|---|---|---|
insert | Yes | Yes |
[]= | No (in-place) | Yes |
concat | Yes | No |
prepend | Yes | No |
insert is the only string mutator that combines non-destructive behavior with negative indexing.
See Also
- /reference/string-methods/prepend/ — prepend a string to the beginning
- /reference/string-methods/concat/ — concatenate one or more strings
- /reference/hash-methods/slice/ — extract or replace substrings by index