1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

rubyでhashを再定義するとfetchできなくなる時がある

Last updated at Posted at 2018-09-20

Railsで新しいActiveRecordをキーにしたあと、saveするとfetchできなかった

class Item < ActiveRecord; end

item = Item.new

item_hash = { item => 'fetchできたよ' }
item.save!

# fetchできない
p item_hash[item]
# => nil

どうしてこうなったか

ActiveRecordはhashを再定義している

def hash
  if id
    self.class.hash ^ id.hash
  else
    super
  end
end

以下の現象が起る

class Item
  attr_accessor :id
  def hash
    id
  end
end

item = Item.new

item.id = 1

items = []
item_hash = {}

items << item
item_hash[item] = 'fetchできたよ'

p item_hash[items[0]]
# => 'fetchできたよ'

# hash methodに依存している値を変える
item.id = 2

# fetchできない
p item_hash[items[0]]
# nil

# keys経由なら何故か一致する
p item_hash.keys[0] == items[0]
# => true

# 同様にhashも一致する
p item_hash.keys[0].hash == items[0].hash
# => true

# 何がなんだかわからない
p item_hash[item_hash.keys[0]]
# => nil

p item_hash.has_key?(item_hash.keys[0])
# => false

なんでこうなるの?(予測)

key側のhash値は代入のタイミングでしか計算されないので、その後hashが変わると一致しなくなる。
item_hash[item] = 'fetchできたよ'

.hashや.eql?で比較するとhashが再計算されるためtrueになる

object_idで比較するようにすれば回避できる

追記

コメントでHash#rehashを教えていただきました

リファレンスにがっつり書いてありますね。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?