[ruby-list:50845] Re: Stringの派生オブジェクトにsubを取った時のインスタンス変数のコピー

From: MASAKI Haruka <yek@...>
Date: 2019-11-21 17:32:05 UTC
List: ruby-list #50845
正木です。

String#subは自身を返すわけではありませんから、
戻り値となっているオブジェクトはsubを呼び出したオブジェクトとは別物ですので、
インスタンス変数は共有されません。

サブクラスではなく、特異クラスを使った場合でも違うオブジェクトなので、
特異クラスは持っていない状態になります。

#!ruby
str = "Hello"

class <<str
  @foo = "foo"
  def foo
    @foo
  end
end

str.foo # foo
str2 = str.sub("foo", "bar")
str2.foo # error

varの値を継承したいなら次のようにするのはいかがでしょう。

class NString < String
  #...

  def var=(val)
    @var = val
  end

  def sub
    new_nstr = super
    new_nstr.var = @val
    new_nstr
  end
end


On Fri, 22 Nov 2019 01:55:42 +0900
Takefumi URA <ura.takefumi@gmail.com> wrote:

> 浦といいます。
> 
> Stringを派生させたクラスのオブジェクトにインスタンス変数を設定しました。
> このオブジェクトにsubを適用してできたオブジェクトにはそのインスタンス
> 変数がコピーされていませんでした。以下のような感じです。
> 
> 
> $ cat test-string.rb
> class NString < String
>   def initialize(s)
>     @var = s.hash # ハッシュ値を取ってるのは特に意味はない
>     super s
>   end
> 
>   def inspect
>     "#<NString:#{@var}:#{self}>"
>   end
> end
> 
> ns = NString.new('abc')
> p ns                            # => #<NString:val:abc>
> p ns.sub(/a/, 'A')              # => #<NString::Abc>
> 
> $ ruby -v test-string.rb
> ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]
> #<NString:3775343761872790250:abc>
> test-string.rb:8: warning: instance variable @var not initialized
> #<NString::Abc>
> 
> ご覧の通り @var が引き継がれておらず初期化もなされていません。
> このあたり、インスタンス変数もケアしてくれる (subでcloneなりdupなりやってる) 
> のかなと思っていたので少し意外でした。
> 
> Stringのソースを見てないのでなんともなんですけど、
> subを取って新たなオブジェクトが得られた時 @var の値をコピーするにはどうしたらよいでしょうか?
> 
> 別解としてsub!を使ってそもそもコピーする必要をなくすというのはあるでしょうけど 
> (というかそれでとりあえず解決しました)、純粋に疑問に思ったので質問してみることにしました。
> 

In This Thread