A Hash is a generic collection of key-value pairs mapping keys of type K
to values of type V
.
Hashes are typically created with a hash literal denoted by curly braces ({ }
) enclosing a list of pairs using =>
as delimiter between key and value and separated by commas ,
.
{"one" => 1, "two" => 2}
The generic type arguments for keys K
and values V
are inferred from the types of the keys or values inside the literal, respectively. When all have the same type, K
/V
equals to that. Otherwise it will be a union of all key types or value types respectively.
{1 => 2, 3 => 4} # Hash(Int32, Int32)
{1 => 2, 'a' => 3} # Hash(Int32 | Char, Int32)
Explicit types can be specified by immediately following the closing bracket with of
(separated by whitespace), a key type (K
) followed by =>
as delimiter and a value type (V
). This overwrites the inferred types and can be used for example to create a hash that holds only some types initially but can accept other types as well.
Empty hash literals always need type specifications:
{} of Int32 => Int32 # => Hash(Int32, Int32).new
Crystal supports an additional literal for hashes and hash-like types. It consists of the name of the type followed by a list of comma separated key-value pairs enclosed in curly braces ({}
).
Hash{"one" => 1, "two" => 2}
This literal can be used with any type as long as it has an argless constructor and responds to []=
.
HTTP::Headers{"foo" => "bar"}
For a non-generic type like HTTP::Headers
, this is equivalent to:
headers = HTTP::Headers.new
headers["foo"] = "bar"
For a generic type, the generic types are inferred from the types of the keys and values in the same way as with the hash literal.
MyHash{"foo" => 1, "bar" => "baz"}
If MyHash
is generic, the above is equivalent to this:
my_hash = MyHash(typeof("foo", "bar"), typeof(1, "baz")).new
my_hash["foo"] = 1
my_hash["bar"] = "baz"
The type arguments can be explicitly specified as part of the type name:
MyHash(String, String | Int32) {"foo" => "bar"}