You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby/Rspec中let定义数组赋值后修改副本致原数组变更的问题

问题原因

在Ruby中,数组、哈希这类属于引用类型对象,变量赋值时并不会复制整个对象的内容,只是让新变量和原变量指向内存中同一个对象。你这里array_b = array_a的操作,本质是让array_b和array_a引用同一个数组;而数组内的哈希元素同样是引用类型,所以修改array_b里的哈希属性时,操作的是内存中同一个哈希对象,自然array_a的对应元素也会同步变化。

解决方法:创建深拷贝

因为你的数组包含嵌套的哈希引用,所以需要做深拷贝——不仅复制数组本身,还要复制数组内的每个哈希对象。以下是两种常用方案:

方案1:用Marshal序列化实现通用深拷贝

这是Ruby中处理嵌套引用类型深拷贝的通用方法,能覆盖大多数场景:

let(:array_b) { Marshal.load(Marshal.dump(array_a)) }

方案2:手动遍历复制每个哈希元素

如果不想依赖Marshal,也可以手动遍历数组,为每个哈希创建副本:

let(:array_b) { array_a.map { |hash| hash.dup } }

这里hash.dup会生成哈希的浅拷贝(对于你的场景足够,因为哈希值都是字符串、数字这类值类型),再通过map生成新数组,确保数组和内部哈希都是独立的新对象。

修正后的测试代码

另外注意你测试里的temp_slug是笔误,应该改为temp_title,修正后的完整测试代码如下:

describe 'changing hash' do
  let(:array_a) do
    [{:title=>'first', :position=>0}, {:title=>'second', :position=>1}]
  end

  context 'when changing values of array b' do
    # 使用深拷贝创建独立的array_b
    let(:array_b) { Marshal.load(Marshal.dump(array_a)) }

    it 'array a doesnt change' do
      # 交换两个元素的title
      temp_title = array_b[0][:title]
      array_b[0][:title] = array_b[1][:title]
      array_b[1][:title] = temp_title

      expect(array_b).not_to eq(array_a)
    end
  end
end

内容的提问来源于stack exchange,提问作者James B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:30:53