LoginSignup
4
4

More than 5 years have passed since last update.

Crystal:オブジェクトに対する暗黙の文字列変換

Last updated at Posted at 2015-06-20

追記あり

RubyからCrystalへ移植しようとして躓いた点その1。
Rubyだとオブジェクトに to_s() メソッドが定義されていれば,puts や文字列内の式展開で暗黙のうちに to_s() で文字列変換された結果が使用される。一方,Crystalでは to_s() を定義しただけでは,暗黙の文字列変換で使用されない。

person.rb
class Person
  def initialize(name)
    @name = name
  end

  def to_s
    "Hi, I'm #{@name}."
  end
end

puts Person.new("John")

上記のRubyソースコードは,Crystalでも問題なく実行可能だが,その実行結果はRubyとは異なっている。

$ ruby person.rb
Hi, I'm John.
$ crystal person.rb
#<Person:0x10cda3ec0>

Crystalでオブジェクトが暗黙の文字列変換を行うためには,to_s() ではなく引数に IO オブジェクトを取る to_s(IO) を実装しなければならない。(Crystalではオーバーロードが可能)
この to_s(IO) で,変換後の文字列を to_sliceSliceオブジェクト化したものを,IO オブジェクトの write メソッドに与えてやる。
例えば,

person.cr
class Person
  def initialize(name)
    @name = name
  end

  def to_s
    "Hi, I'm #{@name}."
  end

  def to_s(io : IO)
    io.write to_s.to_slice
  end

end

puts Person.new("John")

のようにすることで,暗黙の文字列変換が機能するようになる。

$ crystal person.rb
Hi, I'm John.

(とはいえ,同じ to_s という名前なのに to_s(IO) が文字列を返さないのはどうなんかなぁ)

追記

コメントで指摘していただいた内容を追記します。

Rubyにおける to_s と同じことをしたい場合, to_s() はオーバーロードせず, to_s(IO)IO オブジェクトに出力したい内容を与えてやれば良い,との事です。

よくよくソース見をたら,Object#to_s() の中で to_s(IO) が呼ばれてたし,ドキュメントにも

def to_s
Returns a string representation of this object.

Descendants must usually not override this method. Instead, they must override #to_s(io), which must append to the given IO object.

って書いてあった。

Rubyの意識で to_s() が文字列を返すもの,という先入観があって to_s() のオーバーロード有りきで考えてしまったのが良くなかったです。反省。

というわけで,コメントで示してもらった代替案が以下のコード。

person2.cr
class Person
  def initialize(name)
    @name = name
  end

  def to_s(io : IO)
    io << "Hi, I'm " << @name << "."
  end

end

puts Person.new("John")
$ crystal person2.rb
Hi, I'm John.
4
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4