Почему отсутствие оператора присваивания позволяет мне изменять константу Ruby без предупреждения компилятора?

В следующих двух примерах я делаю то же самое, создавая константу String и используя метод concat для ее изменения. Поскольку это константа, я ожидаю предупреждения компилятора, но получаю только одно во втором примере, когда использую оператор присваивания. Почему это?

X = "hello"
X.concat(" world")
puts X # no warning

X = "hello"
X = X.concat(" world")
puts X # warning: already initialized

Поскольку метод concat изменяет строку на месте, обычно я бы так и поступил, поскольку нет необходимости использовать оператор присваивания. Итак, почему присутствие оператора присваивания заставляет компилятор идентифицировать эти две операции как разные?


person Community    schedule 21.03.2009    source источник
comment
Строго говоря, это предупреждение интерпретатора, а не компилятора. Ruby обычно не компилируется.   -  person James Mead    schedule 22.03.2009


Ответы (4)


Это связано с тем, что вы переопределяете новый X. Когда вы переопределяете константу, это дает вам ошибку «уже инициализирована». Первый пример не дает этой ошибки, потому что вы не переопределяете X, вы его модифицируете.

person Ryan Bigg    schedule 21.03.2009
comment
Правильно, рубиновые константы не являются неизменяемыми, это просто означает, что вы не переключаете объекты. - person rampion; 22.03.2009

В Ruby переменные — это, по сути, указатели на место в памяти, содержащее объект, а не сам объект. Во втором примере вы инициализируете константу X так, чтобы она указывала на объект в первой строке (X = "hello"), а во второй строке вы снова инициализируете константу, но она уже указывает на объект, поэтому вы получаете ошибка.

Неизменность константы не означает, что вы не можете изменить объект — это просто означает, что вы не можете изменить константу, чтобы она указывала на другой объект.

person mipadi    schedule 21.03.2009

Если вы хотите сделать свою строку «настоящей» постоянной, попробуйте «заморозить»:

X = "foo".freeze        # => "foo" 
X.concat("bar")

TypeError: can't modify frozen string
    from (irb):2:in `concat'
    from (irb):2

Я настоятельно рекомендую вам прочитать Язык программирования Ruby .

person Marcin Urbanski    schedule 21.03.2009
comment
Спасибо за ссылку на книгу, я только что добавил ее на свою книжную полку в Safari. - person Matt Haley; 22.03.2009
comment
Это поможет вам понять некоторые странные и, вероятно, неинтуитивные (по крайней мере, для программиста на C++/Java) рубиновые углы. Руководство по взлому Ruby (rhg.rubyforge.org) также является отличным источником информации, особенно если вы хотите понять, как метапрограммирование Ruby работает под капотом. - person Marcin Urbanski; 22.03.2009

Это связано с тем, что константа X хранит ссылку на объект String. В первом примере вы изменяете внутреннее состояние объекта String, но не ссылку, хранящуюся в константе. Во втором примере вы меняете ссылку, хранящуюся в константе, на новый объект String, возвращаемый методом concat.

Книга PickAxe объясняет это здесь.

person James Mead    schedule 21.03.2009