Why Array#slice and Array#slice! behave differently?
Categories:
Understanding the Nuances: Array#slice vs. Array#slice! in Ruby

Explore the fundamental differences in behavior between Ruby's Array#slice
and Array#slice!
methods, focusing on their impact on the original array and common use cases.
Ruby's Array
class provides a rich set of methods for manipulating collections. Among these, slice
and slice!
often cause confusion due to their similar names but distinct behaviors. While both methods extract elements from an array, one is non-destructive, returning a new array, and the other is destructive, modifying the original array in place. Understanding this core difference is crucial for writing predictable and bug-free Ruby code.
Array#slice: The Non-Destructive Approach
The Array#slice
method (also accessible via []
) is designed to extract a portion of an array without altering the original array. It returns a new array containing the selected elements, or nil
if the starting index is out of bounds. This behavior is often preferred when you need to work with a subset of data while preserving the integrity of the original collection.
original_array = [1, 2, 3, 4, 5]
# Using slice with a range
subset_array = original_array.slice(1..3)
puts "Subset (range): #{subset_array}" # => [2, 3, 4]
puts "Original after slice: #{original_array}" # => [1, 2, 3, 4, 5]
# Using slice with start index and length
another_subset = original_array.slice(0, 2)
puts "Subset (start, length): #{another_subset}" # => [1, 2]
puts "Original after slice: #{original_array}" # => [1, 2, 3, 4, 5]
# Accessing an out-of-bounds index
out_of_bounds = original_array.slice(10)
puts "Out of bounds slice: #{out_of_bounds.inspect}" # => nil
Demonstrating Array#slice
's non-destructive behavior.
Array#slice
is an alias for Array#[]
. Both methods provide the same non-destructive slicing functionality. Choose the one that enhances readability in your specific context.Array#slice!: The Destructive Counterpart
In contrast, Array#slice!
(note the bang !
) is a destructive method. It extracts a portion of the array and, crucially, removes those elements from the original array. This method returns the removed elements as a new array, or nil
if the starting index is out of bounds. Use slice!
when you intend to modify the array in place, such as when processing items and removing them from a queue or list.
mutable_array = ['a', 'b', 'c', 'd', 'e']
# Using slice! with a range
removed_elements = mutable_array.slice!(1..3)
puts "Removed elements (range): #{removed_elements}" # => ["b", "c", "d"]
puts "Array after slice!: #{mutable_array}" # => ["a", "e"]
# Resetting array for another example
mutable_array = ['x', 'y', 'z']
# Using slice! with start index and length
another_removed = mutable_array.slice!(0, 1)
puts "Removed elements (start, length): #{another_removed}" # => ["x"]
puts "Array after slice!: #{mutable_array}" # => ["y", "z"]
# Accessing an out-of-bounds index
out_of_bounds_bang = mutable_array.slice!(10)
puts "Out of bounds slice!: #{out_of_bounds_bang.inspect}" # => nil
puts "Array after out of bounds slice!: #{mutable_array}" # => ["y", "z"] (original array unchanged if nothing removed)
Illustrating Array#slice!
's destructive modification of the original array.
flowchart TD A[Start with Array] --> B{Choose Method} B -->|Array#slice| C[Create New Array] C --> D[Original Array Unchanged] C --> E[Return New Subset] B -->|Array#slice!| F[Modify Original Array] F --> G[Remove Elements from Original] G --> H[Return Removed Subset] D & H --> I[End]
Decision flow for choosing between Array#slice
and Array#slice!
.
When to Use Which?
The choice between slice
and slice!
boils down to whether you intend to modify the original array. If you need to preserve the original data structure, always opt for slice
. If your goal is to extract elements and simultaneously remove them from the source, slice!
is the appropriate choice. Misusing these methods can lead to unexpected side effects, especially in larger applications where array references are passed around.
slice!
. They permanently alter the object they are called on. If other parts of your program rely on the original state of the array, using slice!
could introduce hard-to-debug issues.