Oświecająca to będzie jak ją przeczytasz. Masz przecież:
[quote]I think “pass by reference” is not the proper term because that would
imply that you could change a variable in the calling scope, i.e. you
could do
def magic(x) x = 10 end
foo = 1
puts foo # prints 1
magic(foo)
puts foo # prints 10
which you can’t. I’d rather call it “call by reference value”, i.e.
the reference is copied.[/quote]
A żeby Ci to trochę jeszcze jaśniej przekazać: przypisanie
str = "<h1>#{str}</h1>"
tworzy nową zmienną str i ustawia jej wartość jako nowy string, do którego kopiowana jest zawartość wcześniej dostępna pod str. Niemniej są to dwa różne obiekty. Przyjmij w tym miejscu że stringi są w rubym immutable (niezmienne), zmiana zawartości przez przypisanie jest po prostu stworzeniem nowej zmiennej o nowej, innej zawartości, a starą zawartością z racji braku referencji zajmie się śmieciarz przy najbliższej okazji.
Prosty trik – object_id podaje Ci wewnętrzny numer obiektu w interpreterze, co pozwala stwierdzić czy wciąż mowa o tym samym obiekcie czy też jego nowej kopii:
ruby-1.9.2-head > str = "dupa"
=> "dupa"
ruby-1.9.2-head > str.object_id
=> 76798210
ruby-1.9.2-head > str = "<h1>#{str}</h1>"
=> "<h1>dupa</h1>"
ruby-1.9.2-head > str.object_id
=> 76792740
Metody które chciałbyś zreplikować, czyli String#gsub!, nie zmieniają object_id i faktycznie działają in-place,
ruby-1.9.2-head > str.object_id
=> 76792740
ruby-1.9.2-head > str.gsub!("a","AA")
=> "<h1>dupAA</h1>"
ruby-1.9.2-head > str.object_id
=> 76792740
ale odbywa się toto, jeśli dobrze pamiętam (dawno nie czytałem źródeł MRI), za pomocą małego triku, to znaczy dostępu nie tyle do stringa, co wejście głębiej w jego elementy składowe i potraktowanie go jak tablicę:
ruby-1.9.2-head > str[1]
=> "h"
ruby-1.9.2-head > str[1] = "z"
=> "z"
ruby-1.9.2-head > str
=> "<z1>dupAA</h1>upa"
ruby-1.9.2-head > str.object_id
=> 76728140
W skrócie: zewnętrzną metodą nie zmienisz obiektu (interpreter stworzy kopię przy pierwszym przypisaniu), ale możesz mu pomieszać w elementach składowych. Jeśli już musisz to robić (to naprawdę jest kiepski pomysł, zapytaj kolegów od programowania funkcyjnego), to przynajmniej nazywaj takie niebezpieczne metody z wykrzyknikami
ruby-1.9.2-head > str = "dupa"
=> "dupa"
ruby-1.9.2-head > def upupiaj!(s)
ruby-1.9.2-head ?> s[0] = "p"
ruby-1.9.2-head ?> end
=> nil
ruby-1.9.2-head > str
=> "dupa"
ruby-1.9.2-head > upupiaj!(str)
=> "p"
ruby-1.9.2-head > str
=> "pupa"