I get an
undefined method 'start_time' for Nil (compile-time type is (Reservation | Nil))
for the code
if game.reservation && other_game.reservation
if(game.reservation.start_time == other_game.reservation.start_time)
return false
end
end
But if I do this
reservation : Reservation | Nil = game.reservation
other_reservation : Reservation | Nil = other_game.reservation
if reservation && other_reservation
if(reservation.start_time == other_reservation.start_time)
return false
end
end
Why aren't these expressions equivalent? Normally, the if is a type filter that removes the Nil
union from the type, but not when it is a nested object. The second way works, but feels needlessly verbose.
What's the right way to perform a type filter with an if on a nested object?
Let's simplify this a bit (it's still the same error):
if game.reservation
game.reservation.starting_time
end
The conditional ensures that the return value from game.reservation
is not nil
. This expression is just calling the method reservation
on game
. The returned value is not reused after that and there is no way of knowing if the second call to that same method might return nil
.
You can solve this easily by storing the return value in a local variable. This way the compiler can be sure that it's value is not nil
. This is even more performant because it saves an (potentially costly) additional call to the same method.
if reservation = game.reservation
reservation.starting_time
end
The exact behaviour is explained in more detail in the language reference: https://crystal-lang.org/docs/syntax_and_semantics/if_var.html