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, each separated by whitespace. 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.