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. [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. [1, 2, 3] # => Array(Int32)
  2. [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.

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

Empty array literals always need a type specification:

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

Percent Array Literals

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

  1. %w(one two three) # => ["one", "two", "three"]
  2. %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 (,).

  1. Array{1, 2, 3}

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

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

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

  1. array_like = IO::Memory.new
  2. array_like << 1
  3. array_like << 2
  4. 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:

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

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

  1. 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. [1, *coll, 2, 3]
  2. Set{1, *coll, 2, 3}

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

  1. array = Array(typeof(...)).new
  2. array << 1
  3. array.concat(coll)
  4. array << 2
  5. array << 3
  6. array_like = Set(typeof(...)).new
  7. array_like << 1
  8. coll.each do |elem|
  9. array_like << elem
  10. end
  11. array_like << 2
  12. array_like << 3

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