Generics

    Generics are especially useful for implementing collection types. , Hash, Set are generic types, as is Pointer.

    More than one type parameter is allowed:

    1. class MyDictionary(K, V)
    2. end

    Any name can be used for type parameters:

    1. class MyDictionary(KeyType, ValueType)
    2. end

    Type restrictions in a generic type’s constructor are free variables when type arguments were not specified, and then are used to infer them. For example:

    • MyBox.new(value) delegates to initialize(@value : T)
    • T isn’t bound to a type yet, so the compiler binds it to the type of the given argument

    In this way generic types are less tedious to work with.

    Structs and modules can be generic too. When a module is generic you include it like this:

    1. module Moo(T)
    2. def t
    3. T
    4. end
    5. end
    6. include Moo(U)
    7. def initialize(@value : U)
    8. end
    9. end
    10. foo = Foo.new(1)
    11. foo.t # Int32

    Note that in the above example T becomes Int32 because Foo.new(1) makes U become Int32, which in turn makes T become Int32 via the inclusion of the generic module.

    Generic classes and structs can be inherited. When inheriting you can specify an instance of the generic type, or delegate type variables:

    1. class Parent(T)
    2. end
    3. class Int32Child < Parent(Int32)
    4. end
    5. end

    Let’s see an example where we define a Generic class called Foo and then we will use it with different number of type variables:

    In the following example we define classes by inheritance, specifying instances for the generic types:

    1. end
    2. # We define `StringChild` inheriting from `Parent` class
    3. # using `String` for generic type argument:
    4. class StringChild < Parent(String)
    5. end
    6. # We define `Int32StringChild` inheriting from `Parent` class
    7. # using `Int32` and `String` for generic type arguments:
    8. class Int32StringChild < Parent(Int32, String)
    9. end

    And if we need to instantiate a class with 0 arguments? In that case we may do:

    1. class Parent(*T)
    2. end
    3. foo = Parent().new

    But we should not mistake 0 arguments with not specifying the generic type variables. The following examples will raise an error: