为什么没有赋值运算符允许我在没有编译器警告的情况下修改Ruby常量?

为什么没有赋值运算符允许我在没有编译器警告的情况下修改Ruby常量?,ruby,constants,concatenation,warnings,Ruby,Constants,Concatenation,Warnings,在下面的两个例子中,我做了同样的事情,创建一个常量字符串并使用concat方法对其进行修改。因为它是一个常量,所以我期望编译器发出警告,但在第二个示例中,当我使用赋值运算符时,只会收到一个警告。为什么会这样 X = "hello" X.concat(" world") puts X # no warning X = "hello" X = X.concat(" world") puts X # warning: already initialized 由于concat方法在适当的位置修改字符

在下面的两个例子中,我做了同样的事情,创建一个常量字符串并使用concat方法对其进行修改。因为它是一个常量,所以我期望编译器发出警告,但在第二个示例中,当我使用赋值运算符时,只会收到一个警告。为什么会这样

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

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

由于concat方法在适当的位置修改字符串,所以我通常会这样做,因为不需要使用assignment操作符。那么,为什么赋值运算符的存在会导致编译器将这两个操作识别为不同的操作呢?

在Ruby中,变量本质上是指向包含对象的内存中某个位置的指针,而不是对象本身。在第二个示例中,您正在初始化一个常量
X
以指向第一行中的对象(
X=“hello”
),在第二行中,您再次初始化该常量,但它已经指向一个对象,因此您会得到错误


常数的不变性并不意味着你不能改变对象——它只是意味着你不能改变常数指向另一个对象。

这是因为你正在定义一个新的X。当你重新定义一个常数时,它会给你“已经初始化”的错误。第一个示例没有给出此错误,因为您没有重新定义X,而是在修改它。

这是因为常量
X
正在存储对
字符串
对象的引用。在第一个示例中,您修改的是
字符串
对象的内部状态,而不是常量存储的引用。在第二个示例中,您正在将常数存储的引用更改为从
concat
方法返回的新
String
对象


鹤嘴锄手册对此进行了解释。

如果要使字符串“真实”为常量,请尝试“冻结”:

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

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

我真的鼓励你读书

严格来说,这是一个解释器警告,而不是编译器警告。Ruby通常不会编译。感谢这本书的链接,我刚刚将它添加到我的Safari书架上。它将帮助你理解一些“奇怪的”和可能不直观的Ruby角落(至少对于C++/Java程序员来说)。《Ruby黑客指南》(Ruby Hacking Guide)也是一个值得一看的好地方——特别是当你想了解Ruby元编程在幕后是如何工作的时候。没错,Ruby常量不是一成不变的,它只是意味着你不需要切换对象。