Generics in Crystal
Crystal supports generics, which allow you to write flexible, reusable code that works with different types.
# As an example of a generic function, `slices_index` takes
# a slice of any comparable type and an element of that
# type and returns the index of the first occurrence of
# v in s, or -1 if not present.
def slices_index(s, v)
s.index(v) || -1
end
# As an example of a generic type, `List` is a
# singly-linked list with values of any type.
class List(T)
@head : Node(T)?
@tail : Node(T)?
private class Node(T)
property next : Node(T)?
property value : T
def initialize(@value : T)
end
end
# We can define methods on generic types just like we
# do on regular types.
def push(v : T)
new_node = Node.new(v)
if @tail.nil?
@head = @tail = new_node
else
@tail.not_nil!.next = new_node
@tail = new_node
end
end
# all_elements returns all the List elements as an array.
def all_elements
elements = [] of T
current = @head
while current
elements << current.value
current = current.next
end
elements
end
end
# Main function to demonstrate the usage
def main
s = ["foo", "bar", "zoo"]
# When invoking generic functions, Crystal can often infer
# the types automatically.
puts "index of zoo: #{slices_index(s, "zoo")}"
lst = List(Int32).new
lst.push(10)
lst.push(13)
lst.push(23)
puts "list: #{lst.all_elements}"
end
main
To run the program, save it as generics.cr
and use the crystal
command:
$ crystal generics.cr
index of zoo: 2
list: [10, 13, 23]
In this Crystal version:
We’ve implemented
slices_index
using Crystal’s built-inindex
method, which returnsnil
if the element is not found. We use the||
operator to return -1 in that case.The
List
class is implemented as a generic type. In Crystal, we use(T)
to declare a generic type parameter.Crystal uses
?
to denote nullable types, so@head
and@tail
are declared asNode(T)?
.We’ve implemented the
push
andall_elements
methods similarly to the original example.In the
main
function, we demonstrate the usage of both the generic function and the generic type.Crystal can infer types in many cases, so we don’t need to explicitly specify types when calling
slices_index
or creating aList(Int32)
.
Crystal’s syntax for generics is similar to that of other languages, making it easy to write reusable code that works with different types.