Overview
Ruby implements index assignment through the []=
method across multiple core classes, each with distinct behaviors and restrictions. Arrays support integer index assignment with automatic expansion, hashes accept any object as keys, and strings restrict assignment to character replacement within existing bounds.
The Array#[]=
method assigns values at integer positions, expanding the array with nil
elements when assigning beyond current bounds. Negative indices count backward from the array end, with -1
representing the last element.
arr = [1, 2, 3]
arr[5] = 'new'
arr
# => [1, 2, 3, nil, nil, 'new']
arr[-1] = 'last'
arr
# => [1, 2, 3, nil, nil, 'last']
Hash index assignment accepts any object as a key through Hash#[]=
, storing key-value pairs without size restrictions. Ruby uses object equality and hash codes to manage key uniqueness.
hash = {}
hash['string'] = 1
hash[42] = 2
hash[[1, 2]] = 3
hash
# => {"string"=>1, 42=>2, [1, 2]=>3}
String index assignment replaces single characters at valid positions through String#[]=
. Ruby restricts assignments to existing character boundaries and raises exceptions for invalid indices or multi-character replacements at single positions.
str = "hello"
str[1] = 'a'
str
# => "hallo"
str[10] = 'x' # Raises IndexError
Ruby defines index assignment behavior through method dispatch, allowing custom classes to implement []=
with domain-specific restrictions. The interpreter converts assignment syntax obj[key] = value
to method calls obj.[]=(key, value)
.
Basic Usage
Array index assignment supports integer indices with automatic size management. Ruby expands arrays when assigning beyond current bounds, filling intermediate positions with nil
values.
numbers = [10, 20, 30]
numbers[1] = 25
numbers[4] = 50
numbers
# => [10, 25, 30, nil, 50]
Negative array indices provide reverse indexing from the array end. Ruby calculates actual positions by adding the negative index to array length, enabling assignments relative to the final element.
data = ['a', 'b', 'c']
data[-1] = 'z' # Equivalent to data[2] = 'z'
data[-3] = 'A' # Equivalent to data[0] = 'A'
data
# => ["A", "b", "z"]
Range-based array assignment replaces multiple consecutive elements with new values. Ruby adjusts array size automatically when replacement arrays differ in length from the target range.
letters = ['a', 'b', 'c', 'd', 'e']
letters[1..3] = ['x', 'y']
letters
# => ["a", "x", "y", "e"]
letters[2...4] = ['p', 'q', 'r', 's']
letters
# => ["a", "x", "p", "q", "r", "s"]
Hash assignment accepts any object as keys, including complex data structures. Ruby evaluates key equality using the eql?
method and manages internal storage through hash codes.
registry = {}
registry[:symbol] = "symbol value"
registry["string"] = "string value"
registry[42] = "number value"
registry[{id: 1}] = "hash value"
registry
# => {:symbol=>"symbol value", "string"=>"string value", 42=>"number value", {id: 1}=>"hash value"}
String character assignment replaces individual characters at specific positions within existing string bounds. Ruby maintains string encoding and raises errors for assignments beyond string length.
word = "programming"
word[0] = 'P'
word[4] = 'R'
word
# => "ProgRamming"
# Multi-character replacement at single position
word[5] = "XX" # Raises ArgumentError
Advanced Usage
Multi-dimensional array assignment creates nested structures through chained index operations. Ruby evaluates assignment expressions from left to right, creating intermediate arrays when assigning to undefined nested positions.
matrix = []
matrix[0] = []
matrix[0][0] = 1
matrix[1] = [2, 3]
matrix[1][1] = 4
matrix
# => [[1], [2, 4]]
# Direct nested assignment creates intermediate arrays
grid = []
grid[2][1] = 'x' # Raises NoMethodError - grid[2] is nil
Custom objects implement index assignment by defining []=
methods with domain-specific logic. Ruby calls these methods during assignment operations, passing indices and values as parameters.
class TimeSeriesData
def initialize
@data = {}
end
def []=(timestamp, value)
raise ArgumentError, "Invalid timestamp" unless timestamp.is_a?(Time)
raise ArgumentError, "Value must be numeric" unless value.is_a?(Numeric)
@data[timestamp] = value
end
def [](timestamp)
@data[timestamp]
end
end
series = TimeSeriesData.new
series[Time.now] = 42.5
series[Time.now - 3600] = 38.2
Splat operators in array assignment distribute multiple values across array positions. Ruby unpacks arrays and other enumerable objects during assignment operations.
coords = []
coords[0], coords[1], coords[2] = *[10, 20, 30]
coords
# => [10, 20, 30]
# Parallel assignment with array expansion
positions = [100, 200]
coords[3], coords[4] = *positions
coords
# => [10, 20, 30, 100, 200]
Hash assignment with complex keys requires careful consideration of object identity and mutability. Ruby uses object hash codes and equality methods to determine key uniqueness.
mutable_key = [1, 2, 3]
data = {}
data[mutable_key] = "original"
# Modifying the key affects hash access
mutable_key << 4
data[[1, 2, 3]] # => nil (key not found)
data[[1, 2, 3, 4]] # => "original"
String assignment with character encodings requires matching byte boundaries. Ruby validates character assignments against string encoding and raises exceptions for incompatible character sequences.
utf8_string = "café"
utf8_string[3] = 'é' # Valid - replaces 'é' with 'é'
# Encoding-aware assignment
ascii_string = "test".force_encoding('ASCII')
ascii_string[0] = 'T' # Valid ASCII character
Common Pitfalls
Negative array indices beyond array bounds raise IndexError
exceptions instead of expanding arrays. Ruby only allows negative indices within the range -array.length
to -1
.
arr = [1, 2, 3]
arr[-1] = 'last' # Valid - replaces element at position 2
arr[-5] = 'error' # Raises IndexError: index -5 too small for array
Array assignment with non-integer indices attempts type conversion through to_int
method calls. Ruby raises TypeError
when objects cannot convert to integers, creating confusion during index operations.
data = [10, 20, 30]
data['1'] = 40 # Raises TypeError: no implicit conversion of String into Integer
data[1.5] = 40 # Raises TypeError: no implicit conversion of Float into Integer
# Custom objects with to_int work
class Position
def initialize(value)
@value = value
end
def to_int
@value
end
end
data[Position.new(1)] = 50 # Works - converts to integer 1
Hash key mutability creates invisible key changes that break hash access patterns. Modifying objects used as hash keys after assignment corrupts hash internal structure.
mutable_keys = {}
key_array = [1, 2]
mutable_keys[key_array] = "value"
# Key modification breaks hash access
key_array << 3
mutable_keys[[1, 2]] # => nil (cannot find original key)
mutable_keys.keys.first # => [1, 2, 3] (key was mutated)
String index assignment fails silently with frozen strings in some Ruby contexts, while raising exceptions in others. Frozen string behavior depends on method call context and Ruby version.
frozen_str = "hello".freeze
frozen_str[0] = 'H' # Raises FrozenError
# String literals in certain contexts may be frozen
def modify_literal
"test"[0] = 'T' # May raise FrozenError depending on Ruby configuration
end
Range assignment with incompatible array sizes creates unexpected array modifications. Ruby replaces range elements entirely, potentially changing array structure dramatically.
original = [1, 2, 3, 4, 5]
original[1..3] = [10] # Replaces 3 elements with 1 element
original
# => [1, 10, 5] - Array shrunk unexpectedly
empty_replacement = [1, 2, 3, 4, 5]
empty_replacement[1..3] = [] # Removes elements entirely
empty_replacement
# => [1, 5]
Parallel assignment with arrays creates shallow copies rather than references, leading to unexpected behavior when modifying nested structures.
source = [[1, 2], [3, 4]]
target = []
target[0], target[1] = source # Creates references, not copies
source[0] << 3
target[0] # => [1, 2, 3] - Shared reference modified
Method dispatch during index assignment can trigger unexpected side effects when objects override []=
with complex logic. Custom implementations may modify object state beyond simple value assignment.
class LoggingArray < Array
def []=(index, value)
puts "Setting #{index} = #{value}"
super
puts "Array now: #{self}"
end
end
logged = LoggingArray.new([1, 2, 3])
logged[1] = 'changed'
# Output:
# Setting 1 = changed
# Array now: [1, "changed", 3]
Reference
Array Index Assignment Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#[]=(index, value) |
index (Integer), value (Object) |
Object |
Assigns value at integer index, expands array if needed |
#[]=(start, length, value) |
start (Integer), length (Integer), value (Object) |
Object |
Replaces elements from start position for length count |
#[]=(range, value) |
range (Range), value (Object) |
Object |
Replaces elements within range with value or array |
Hash Index Assignment Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#[]=(key, value) |
key (Object), value (Object) |
Object |
Associates key with value, overwrites existing keys |
#store(key, value) |
key (Object), value (Object) |
Object |
Alias for []= method |
String Index Assignment Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#[]=(index, char) |
index (Integer), char (String) |
String |
Replaces character at index position |
#[]=(range, string) |
range (Range), string (String) |
String |
Replaces characters within range |
#[]=(regex, string) |
regex (Regexp), string (String) |
String |
Replaces first regex match with string |
Index Assignment Exceptions
Exception | Condition | Description |
---|---|---|
IndexError |
Array negative index too small | Negative index exceeds array bounds |
IndexError |
String index out of bounds | String index beyond string length |
TypeError |
Invalid index type | Index cannot convert to required type |
ArgumentError |
Invalid assignment length | String multi-character assignment to single position |
FrozenError |
Frozen object modification | Assignment to frozen array, hash, or string |
Array Index Behavior Matrix
Index Type | Positive Bounds | Negative Bounds | Beyond Bounds | Result |
---|---|---|---|---|
Integer | arr[0..n-1] |
arr[-n..-1] |
Expands with nil | Assigns value |
Range | arr[0..n-1] |
arr[-n..-1] |
Partial expansion | Replaces elements |
Float | N/A | N/A | N/A | TypeError |
String | N/A | N/A | N/A | TypeError |
Hash Key Constraints
Key Type | Restrictions | Hash Method | Equality Method |
---|---|---|---|
String | None | hash |
eql? |
Symbol | None | hash |
eql? |
Integer | None | hash |
eql? |
Array | Avoid mutation | hash |
eql? |
Hash | Avoid mutation | hash |
eql? |
Custom | Must implement hash/eql? | hash |
eql? |
String Assignment Constraints
Assignment Type | Valid Indices | Character Limits | Encoding Requirements |
---|---|---|---|
Single character | 0...length |
Single codepoint | Compatible encoding |
Range replacement | Valid ranges | Any length string | Compatible encoding |
Regex replacement | Match positions | Any length string | Compatible encoding |
Performance Characteristics
Operation | Time Complexity | Space Complexity | Notes |
---|---|---|---|
Array index assignment | O(1) amortized | O(1) per element | May trigger array expansion |
Array range assignment | O(m + n) | O(n) | m = range size, n = replacement size |
Hash key assignment | O(1) average | O(1) per entry | May trigger hash resize |
String character assignment | O(1) | O(1) | Within existing bounds only |
String range assignment | O(n) | O(n) | n = replacement string length |