Skip to content

Array

An Array is an ordered and integer-indexed generic collection of elements of a specific type T.

Arrays are typically created with an array literal denoted by square brackets ([]) and individual elements separated by a comma (,).

[1, 2, 3]

Generic Type Argument

The array's generic type argument T is inferred from the types of the elements inside the literal. When all elements of the array have the same type, T equals to that. Otherwise it will be a union of all element types.

[1, 2, 3]         # => Array(Int32)
[1, "hello", 'x'] # => Array(Int32 | String | Char)

An explicit type can be specified by immediately following the closing bracket with of and a type. This overwrites the inferred type and can be used for example to create an array that holds only some types initially but can accept other types later.

array_of_numbers = [1, 2, 3] of Float64 | Int32 # => Array(Float64 | Int32)
array_of_numbers << 0.5                         # => [1, 2, 3, 0.5]

array_of_int_or_string = [1, 2, 3] of Int32 | String # => Array(Int32 | String)
array_of_int_or_string << "foo"                      # => [1, 2, 3, "foo"]

Empty array literals always need a type specification:

[] of Int32 # => Array(Int32).new

Percent Array Literals

Arrays of strings and arrays of symbols can be created with percent array literals:

%w(one two three) # => ["one", "two", "three"]
%i(one two three) # => [:one, :two, :three]

Array-like Type Literal

Crystal supports an additional literal for arrays and array-like types. It consists of the name of the type followed by a list of elements enclosed in curly braces ({}) and individual elements separated by a comma (,).

Array{1, 2, 3}

This literal can be used with any type as long as it has an argless constructor and responds to <<.

IO::Memory{1, 2, 3}
Set{1, 2, 3}

For a non-generic type like IO::Memory, this is equivalent to:

array_like = IO::Memory.new
array_like << 1
array_like << 2
array_like << 3

For a generic type like Set, the generic type T is inferred from the types of the elements in the same way as with the array literal. The above is equivalent to:

array_like = Set(typeof(1, 2, 3)).new
array_like << 1
array_like << 2
array_like << 3

The type arguments can be explicitly specified as part of the type name:

Set(Int32){1, 2, 3}

Splat Expansion

The splat operator can be used inside array and array-like literals to insert multiple values at once.

[1, *coll, 2, 3]
Set{1, *coll, 2, 3}

The only requirement is that coll's type must include Enumerable. The above is equivalent to:

array = Array(typeof(...)).new
array << 1
array.concat(coll)
array << 2
array << 3

array_like = Set(typeof(...)).new
array_like << 1
coll.each do |elem|
  array_like << elem
end
array_like << 2
array_like << 3

In these cases, the generic type argument is additionally inferred using coll's elements.