🧊

【Ruby】配列の要素追加・変更・削除できないようにする方法

2024/09/17に公開

概要

配列を扱う場合、参照渡しに担ってしまうという点に注意する必要があります。
参照渡しによって意図せず配列の要素が変更されることを防ぐために、配列の要素を追加・変更・削除できないようにする方法を記載します。

ユースケース

以下のような実装をした場合、メソッドが配列を返した後に、その返却値の配列の要素を変更できてしまいます。これが想定された変更でない場合に問題になってしまいます。

class BreakfastRecipe
    attr_reader :items
    
    def initialize
        @items = []
    end
    
    def add_items
       @items.push("egg", "solt", "bacon")
    end
    
    def do_something
        # itemsにはこのクラスで設定した値以外が入らない想定の処理
    end
end


recipe = BreakfastRecipe.new
hungry_items = recipe.add_items
puts "変更前: #{hungry_items}"
hungry_items.push("rice") # 配列の要素を追加できる
hungry_items[0] = "soup" # 配列の要素を変更できる
hungry_items.delete(1) # 配列の要素を削除できる
puts "変更後: #{hungry_items}"

実行結果
配列の要素を追加・変更・削除できる

変更前: ["egg", "solt", "bacon"]
変更後: ["soup", "solt", "bacon", "rice"]

解決方法

配列に対してfreezeを使うとこの問題を防げます。

class BreakfastRecipe
    attr_reader :items
    
    def initialize
        @items = []
    end
    
    def add_items
       @items.push("egg", "solt", "bacon").freeze # 配列の要素を追加・変更・削除できないようにする
    end
    
    def do_something
        # itemsにはこのクラスで設定した値以外が入らない想定の処理
    end
end


recipe = BreakfastRecipe.new
hungry_items = recipe.add_items
puts "変更前: #{hungry_items}"
# 要素の変更ができない!
hungry_items.push("rice")
hungry_items[0] = "soup"
hungry_items.delete(1)
puts "変更後: #{hungry_items}"

要素の追加や変更を行おうとするとFrozenErrorが発生して、削除(delete)しようとしても配列に変更が発生しません。

Discussion