Ruby: Pass-by-Reference-Value

Published: (December 26, 2025 at 04:33 AM EST)
2 min read
Source: Dev.to

Source: Dev.to

Most Ruby developers are familiar with the concept often referred to as “pass‑by‑reference‑value.”
When a mutable object (e.g., a String) is assigned to another variable, both variables reference the same object. Mutating the object through one reference is reflected in the other.

Simple Assignment Example

var1 = "Hello, World"
var2 = var1
var2.gsub!("World", "Ruby")
puts var1   # => Hello, Ruby

Service Class Example

class DummyClass
  attr_accessor :value

  def initialize(value)
    @value = value
  end
end

dummy = DummyClass.new("Hello, World!")

puts "Before service action:"
puts dummy.value   # => Hello, World!

class ServiceClass
  def initialize(value)
    @value = value
  end

  def perform_action
    local_value = @value
    local_value.gsub!("World", "Ruby")
  end
end

ServiceClass.new(dummy.value).perform_action

puts "After service action:"
puts dummy.value   # => Hello, Ruby!

Real‑World Pitfall

In a production codebase, the same behavior can become a stealthy bug. Consider an ActiveRecord model with a string attribute that is passed to a service responsible for interpolating placeholders (e.g., %%BRAND%%) within a template string:

# Hypothetical usage
Interpolator.custom_interpolate(source, brand_name)

The method name custom_interpolate suggests it returns a new string, but if the implementation uses a mutating method like gsub! on the source object, the model’s attribute is modified in memory before the record is saved. This side effect can lead to unexpected data changes throughout the application.

Best Practices

  • Prefer non‑mutating methods (gsub instead of gsub!) when you only need a transformed value.

  • Duplicate inputs when you want to protect the original object:

    def safe_interpolate(str, replacements)
      str = str.dup
      # perform non‑mutating transformations
      str.gsub!("%BRAND%", replacements[:brand])
      str
    end
  • Document side effects clearly if a method intentionally mutates its arguments.

  • Write tests that verify objects are not unintentionally altered after service calls.

By being explicit about mutability and using defensive copying, you can avoid subtle bugs caused by Ruby’s pass‑by‑reference‑value semantics.

Back to Blog

Related posts

Read more »

What is git?

Why You Need Git For many developers, a pendrive is just a place to store and retrieve old projects or files. But when you have too many folders, redundant fil...

Agile | Scrum & Kanban Framework

What is Agile? In earlier modules, the term agile described a key aspect of DevOps culture: the ability to respond quickly to customer needs and feedback. Agil...