Code Fix

中級

シンボルキーのハッシュを文字列キーで参照すると nil になる

:name(シンボル)と"name"(文字列)は別のオブジェクトとして扱われるため、シンボルキーで作ったハッシュを文字列キーで参照すると見つからずnilが返ります。

なぜエラーが出ないのか

出力: nil
(エラーなし)
Rubyは何も報告しません。文法として正しいためです
出力: nil
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  user = {name: "Taro"}
 2  puts user["name"].inspect
                 ^
出力: nil

:name(シンボル)と"name"(文字列)は別のオブジェクトです。シンボルキーのハッシュを文字列キーで探すと見つかりません。

直し方: "name" を :name にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  profile = {email: "taro@example.com"}
 2  puts profile["email"].inspect
                    ^
出力: nil

キーの見た目の文字が同じでも、シンボルと文字列はハッシュの中では別物として扱われます。

直し方: "email" を :email にします。

この問題を解いてみる →

パターン3

 1  config = {host: "localhost"}
 2  puts config["host"].inspect
                   ^
出力: nil

ハッシュリテラルで {host: ...} と書くと、キーは自動的にシンボル:hostになります。

直し方: "host" を :host にします。

この問題を解いてみる →

パターン4

 1  item = {sku: "A100"}
 2  puts item["sku"].inspect
                ^
出力: nil

キーの型を取り違えると、存在するはずの値がnilとして扱われ、後続の処理でバグにつながります。

直し方: "sku" を :sku にします。

この問題を解いてみる →

パターン5

 1  ticket = {seat: "12A"}
 2  puts ticket["seat"].inspect
                   ^
出力: nil

ハッシュを作った側の書き方(シンボルか文字列か)に合わせてアクセスする必要があります。

直し方: "seat" を :seat にします。

この問題を解いてみる →

よくある誤解

「キーの見た目の文字が同じなら、シンボルでも文字列でも同じキーとして扱われるはず」という思い込みは誤りです。ハッシュのキー照合はオブジェクトとしての等価性で行われるため、:nameと"name"は別のキーです。

実務での勘所

シンボルが文字列と別物として、しかもハッシュのキーとしてよく好まれる理由は、シンボルが不変かつ「同じ名前のシンボルはプログラム全体で必ず同じ1つのオブジェクト」であることが保証されている(インターン化されている)ためです。文字列はリテラルを書くたびに新しいオブジェクトが作られる可能性があり、比較や検索のたびに中身を照合する必要があります。JSON由来のデータ(文字列キー)とRubyのコード内で作ったハッシュ(シンボルキー)が混在するWebアプリでは、この不一致が頻発するため、Railsのwith_indifferent_accessのような専用のツールがよく使われます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)