Cast error

The following program fails to compile. What is the idiomatic way .

alias ANumber= Int32 | ::Nil
def addme(a : ANumber,b : ANumber) : ANumber
	if ((a!=nil)&&(b!=nil))
			a2=a.as(Int32)
			b2=b.as(Int32)
			a2+b2
		else
			nil
	end

end
c=addme(2,3)
p c
d=addme(2,nil)
p d

The idiomatic way would be like:

def addme(a : Int32?, b : Int32?) : Int32?
  return unless a && b
  
  a + b
end

To handle nil you need to either use the special .nil? method, .try, or just check the variables directly as nil is a falsely value so the compiler can know they won’t be nil. != does not work because it’s possible to override it to return whatever, so the compiler cannot trust it.

Another way would be to have two overloads of the method, one only accepting Int32 and the other accepting Int32? with the former just being a + b and the latter nil return values.

EDIT: Maybe also checkout [Screencast] Handling Nil in Crystal

It’s still unclear why it works with
a&&b
and not with comparison to nil:
((a!=nil)&&(b!=nil))

I also tried with exception and that did not worked also:

begin
  a+b
rescue
  nil
end

a&&b and ((a!=nil)&&(b!=nil)) are not equivalent, as @Blacksmoke16 said, != is overloadable and can be return whatever, if you really like write code following your pattern, write like this:

def addme(a : Int32?, b : Int32?) : Int32?
  if !a.nil? && !b.nil?
    a2 = a.as(Int32)
    b2 = b.as(Int32)
    a2 + b2
  else
    nil
  end
end

p! addme(2, nil) # => nil
1 Like

Right, it’s a compile time error. rescue blocks can only handle runtime exceptions, not compile time errors.

FWIW you do not need the .as(Int32) the compiler is smart enough to do that for you, so can just do a + b in that branch as well. .as is mostly used when you have a value that could actually be say Int32 | String and you know its one or the other in a specific context the compiler can’t figure out; rather than using it to try and remove Nil.

3 Likes