Może dokładniej: s1.clone sklonował obiekt s1. Ale zawartością obiektu s1 był adres jakiegoś Stringa. Wartość tego adresu pojawiła się też w obiekcie s2, więc wskazywany String wciąż był ten sam.
To co chciałeś uzyskać nazywa się “głębokim” klonowaniem i zazwyczaj musisz sam pisać takie metody. Możesz albo zdefiniować sobie metodę clone_deep albo nadpisać metodę clone:
def clone
klon = super
klon.str = str.clone
klon
end
To głębokie klonowanie ma sens dla tych atrybutów, których wartości są obiektami mogącymi zmieniać swój stan. Integery, booleany, symbole nie mogą, ale cała reszta raczej tak.
Ha, chciałem Ci podesłać wynik ‘ri Object#clone’ ale właśnie widzę, że przykład wziąłeś właśnie stamtąd
Rozumiem, że o Object#dup i Object#duplicable? też już poczytałeś. Różnica jest malutka: dup nie klonuje stanu “frozen”, a clone tak.