RubyでString型でも引数展開する

Rubyでは引数を指定する際に*を付けることで引数展開ができます。

def foo(arg1,arg2,arg3)
  p arg1
  p arg2
  p arg3
end

ary = [1,2,3]

foo(*ary)
1
2
3

同様に、ハッシュやRangeオブジェクトも引数展開できます。

def foo(arg1,arg2,arg3)
  p arg1
  p arg2
  p arg3
end

hash = {a:1,b:2,c:3}
range = 1..3
foo(*hash)
foo(*range)
[:a, 1]
[:b, 2]
[:c, 3]
1
2
3

では、String型で引数展開するとどうなるでしょう?

def foo(arg1,arg2,arg3)
  p arg1
  p arg2
  p arg3
end

str = "str"

foo(*str)
test.rb:1:in `foo': wrong number of arguments (given 1, expected 3) (ArgumentError)
        from test.rb:9:in `<main>'

出来ませんでした!

*を使った引数展開は内部的にto_aメソッドを呼び出しているので、to_aの無いクラスでは使えません。

メソッド呼び出し(super・ブロック付き・yield) (Ruby 3.3 リファレンスマニュアル)

ということで、Stringクラスにto_aを追加してみます。

def foo(arg1,arg2,arg3)
  p arg1
  p arg2
  p arg3
end

class String
  def to_a
    self.chars
  end
end

str = "str"

foo(*str)
"s"
"t"
"r"

今度は出来ました!

同様にto_aメソッドを実装すれば他のクラスでも*を使った引数展開ができるようになります。

# Integerクラスで引数展開する
def foo(arg1,arg2,arg3)
  p arg1
  p arg2
  p arg3
end

class Integer
  def to_a
    self.digits.reverse
  end
end

int = 123

foo(*int)
1
2
3

まとめ

既存のクラスでもオープンクラスを使ってto_aを追加することで引数展開できるようになります!
とはいえモンキーパッチのご利用は計画的に~。