Crystal書いてて気づいたこと
今日はuser agent parserのruby版をcrystalに移植していた。
crystalはv0.18.7を使っているのだけど書いてて気づいたことがあるのでメモ
not nilの推論はインスタンス変数には効かない
crystalは型に厳しいです。変数fooがStringあるいはNil型であるときはどちらの型にも存在するメソッドしか呼べません。
Stringにしかないメソッドを呼ぶときには条件分岐でnot nilであることを保証させます。
# foo : String?とする if foo.nil? # ここではfooはNil型 else # ここではfooはnot nilなのでString型 end
ローカル変数ならこれでおkですがインスタンス変数はこれだとダメです。
class Klass @foo : String? def size if @foo.nil? 0 else @foo.size end end end puts Klass.new.size # Error in line 13: instantiating 'Klass#size()' # # in line 8: undefined method 'size' for Nil (compile-time type is (String | Nil))
いちどローカル変数にとれば通るみたいです。インスタンス変数だと関数スコープ外から変更可能だからかな、って推測しています。
class Klass @foo : String? def size bar = @foo if bar.nil? 0 else bar.size end end end puts Klass.new.size
#to_sと#inspectのoverride
Object#to_s
と Object#inspect
はoverrideするべきではないらしい(usually MUST NOT)
ref: https://crystal-lang.org/api/0.18.7/Object.html#to_s-instance-method
代わりに #to_s(io : IO)
と #inspect(io : IO)
に対してやれとのこと。
def Klass def to_s(io : IO) "this is Klass".to_s(io) end end puts Klass.new.to_s #=> "this is Klass" puts "#{Klass.new}" #=> "this is Klass"
*argsはTuple型
調べればわかるんだけど調べたのでメモ。型違うのでちょっと注意。
def foo(*args) puts args.class end foo(1, 2, 3) #=> Tuple(Int32, Int32, Int32) foo("bar", "foobar") #=> Tuple(String, String)