Search code examples
crystal-lang

crystal lang : in case of a Class as a field


I'm just writting an Exception, which should stores a Class object as a field for the error message process.

class BadType < Exception
    getter should_be : Class
    getter actual : Class
end
def feed(pet : Animal, food : Food)
    raise BadType.new should_be: Cat, actual: pet.class if food == "fish" && !pet.is_a?(Cat)
end

But, Class is abstract so cannot be used here as a field type.

So, how to solve this in my case? I hadn't found any derived class of Class, so does it mean one can never store a Class object as a field? But here my problem is a meaningful usage for this (any type check depends on input may require this BadType).

I'm not sure whether I missed something, so I came here first.


Solution

  • Class can't (yet) be used as a ivar type. Maybe it never will, I don't know.

    But you can actually use generics for concrete data types, inferred from the arguments to the constructor:

    # possible workaround
    class Foo(T, U)
      def initialize(@bar : T, @baz : U)
      end
    end
    
    Foo.new String, Int32
    

    I don't know your exact use case, but chances are you don't really need these values as classes. You probably can't do much with it anyway and drawing from your example I guess it's mostly for showing debugging information.

    So it is very likely that just storing the names of the classes (as strings) would be a better solution for this problem.

    # better solution
    class Foo
      @bar : String
      @baz : String
      def initialize(bar : Class, baz : Class)
        @bar = bar.name
        @baz = baz.name
      end
    end
    
    Foo.new String, Int3
    

    The generic arguments mean a new concrete type is created for every combination of classes used with Foo. That can have an impact on compiler performance.

    I would most definitely use strings for this. Even if you need the classes for some particularly processing later on, it's probably better to just map the strings to the constants using a macro-generated lookup table.