Working with nilable types
Since Crystal supports union types, it’s possible to have methods whose returns are nilable.
Here’s a method with a return signature of (String | Nil) or in short String?.
def maybe_string : String?
condition = true
if condition
"A string..."
else
nil
end
end
And say we want to upcase the returned String:
val = maybe_string
p! val.upcase # => Error: undefined method 'upcase' for Nil (compile-time type is (String | Nil))
The result would be an error because the compiler wouldn’t be able to guarantee that the return type is a String and
not Nil. If the return type is in fact Nil, then it would not have an upcase method available.
To avoid this error, we can check for nil with the tools Crystal provides. Here’s at least three different ways we could do this:
def maybe_string : String?
condition = true
if condition
"A string..."
else
nil
end
end
val = maybe_string
# Method one - Check if `val` is truthy:
p! val.upcase if val
# Method two - Check if `val` is nil:
p! val.upcase unless val.nil?
# Method three - Check if `val` is nil by usinkg `try`:
p! val.try &.upcase
#OUTPUT:
val.upcase # => "A STRING..."
val.upcase # => "A STRING..."
val.try(&.upcase) # => "A STRING..."
We managed to call upcase in all three cases because maybe_string actually returned String and we satisfied the
compiler by branching in a way that our code was called only when maybe_string returned a String.
- In the first case, we just checked if
valwas truthy, because it wouldn’t be truthy ifvalwasNil. - In the second case we used a method provided by the compiler,
.nil?1, this would returntrueifvalwasNil. - And finally on the third case, we’ve used another built-in method
try2.tryonly executes the passed code block if val is notNil.