Ruby:按引用传值

发布: (2025年12月26日 GMT+8 17:33)
3 min read
原文: Dev.to

Source: Dev.to

大多数 Ruby 开发者都熟悉一种常被称为“按引用值传递”(pass‑by‑reference‑value)的概念。
当一个可变对象(例如 String)被赋值给另一个变量时,两个变量引用的是同一个对象。通过其中一个引用对对象进行修改,会在另一个引用中体现出来。

简单赋值示例

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

服务类示例

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!

实际场景中的陷阱

在生产代码库中,同样的行为可能会成为潜在的 bug。设想有一个 ActiveRecord 模型,其字符串属性被传递给一个负责在模板字符串中插入占位符(例如 %%BRAND%%)的服务:

# Hypothetical usage
Interpolator.custom_interpolate(source, brand_name)

方法名 custom_interpolate 暗示它会返回一个新字符串,但如果实现中在 source 对象上使用了会修改原对象的 gsub!,则模型的属性会在记录保存之前 在内存中 被修改。此副作用可能导致整个应用出现意料之外的数据变化。

最佳实践

  • 优先使用非变异方法gsub 而不是 gsub!),当你只需要得到一个转换后的值时。

  • 在需要保护原对象时进行复制

    def safe_interpolate(str, replacements)
      str = str.dup
      # perform non‑mutating transformations
      str.gsub!("%BRAND%", replacements[:brand])
      str
    end
  • 明确记录副作用,如果方法有意修改其参数,需要在文档中说明。

  • 编写测试,验证在调用服务后对象没有被意外修改。

通过对可变性保持明确、使用防御性复制,你可以避免因 Ruby 的按引用值传递语义而产生的细微 bug。

Back to Blog

相关文章

阅读更多 »

什么是 git?

为什么你需要 Git 对于许多开发者来说,U 盘只是一个存放和检索旧项目或文件的地方。但当你拥有太多文件夹、冗余文件……

敏捷 | Scrum & Kanban 框架

什么是 Agile?在之前的模块中,Agile 这个词描述了 DevOps 文化的一个关键方面:能够快速响应客户需求和反馈。Agil...