Slices in Crystal

# Unlike arrays, slices are typed only by the
# elements they contain (not the number of elements).
# An uninitialized slice equals to nil and has
# length 0.

s = Array(String).new
puts "uninit: #{s} #{s == nil} #{s.size == 0}"

# To create an empty slice with non-zero length, use
# Array.new. Here we make a slice of
# Strings of size 3 (initially nil-valued).

s = Array(String).new(3)
puts "emp: #{s} len: #{s.size} cap: #{s.capacity}"

# We can set and get just like with arrays.

s[0] = "a"
s[1] = "b"
s[2] = "c"
puts "set: #{s}"
puts "get: #{s[2]}"

# `size` returns the length of the slice as expected.

puts "len: #{s.size}"

# In addition to these basic operations, slices
# support several more that make them richer than
# arrays. One is the `<<` operator, which
# appends one or more new values to the slice.

s << "d"
s.concat(["e", "f"])
puts "apd: #{s}"

# Slices can also be duplicated. Here we create a
# new slice c and copy s into it.

c = s.dup
puts "cpy: #{c}"

# Slices support a "slice" operator with the syntax
# slice[start..end]. For example, this gets a slice
# of the elements s[2], s[3], and s[4].

l = s[2..4]
puts "sl1: #{l}"

# This slices up to (but excluding) s[5].

l = s[0...5]
puts "sl2: #{l}"

# And this slices up from (and including) s[2].

l = s[2..]
puts "sl3: #{l}"

# We can declare and initialize a variable for slice
# in a single line as well.

t = ["g", "h", "i"]
puts "dcl: #{t}"

# Crystal doesn't have a built-in slices package, but we can
# compare arrays directly.

t2 = ["g", "h", "i"]
if t == t2
  puts "t == t2"
end

# Slices can be composed into multi-dimensional data
# structures. The length of the inner slices can
# vary, unlike with multi-dimensional arrays.

two_d = Array(Array(Int32)).new(3) { |i| Array(Int32).new(i + 1) { |j| i + j } }
puts "2d: #{two_d}"

Note that while slices are different types than arrays, they are rendered similarly by puts.

When you run this program, you’ll see output similar to:

uninit: [] false true
emp: [nil, nil, nil] len: 3 cap: 3
set: ["a", "b", "c"]
get: c
len: 3
apd: ["a", "b", "c", "d", "e", "f"]
cpy: ["a", "b", "c", "d", "e", "f"]
sl1: ["c", "d", "e"]
sl2: ["a", "b", "c", "d", "e"]
sl3: ["c", "d", "e", "f"]
dcl: ["g", "h", "i"]
t == t2
2d: [[0], [1, 2], [2, 3, 4]]

In Crystal, arrays are used in a similar way to slices in Go. They are dynamic and can grow or shrink as needed. The Array class in Crystal provides methods similar to slice operations in Go, such as << for appending, [] for accessing elements, and range syntax for slicing.

Crystal’s arrays are type-safe at compile-time, ensuring that all elements in an array are of the same type or a common supertype.

Now that we’ve seen arrays in Crystal, which serve a similar purpose to both arrays and slices in Go, we’ll look at Crystal’s other key built-in data structure: hashes (which are similar to maps in Go).