Ruby符号是否存在,因为字符串是可变的而不是实体的?

我已经使用Ruby大约一年了,并且有一个语言问题:是否必要符号,因为Ruby字符串是可变的而不是实习的?

比方说,Java,字符串是不可变的和实际的。 所以“foo”在值和引用中总是等于“foo”,它的值不能改变。 在Ruby中,字符串是可变的而不是实体,因此"a".object_id == "a".object_id将为false。

如果Ruby实现了像Java这样的字符串,那么符号就没有必要了,对吧?

从Ruby 2.3开始,不可变的String已经可选地通过RUBYOPT标志实现--enable-frozen-string-literals即ie

 RUBYOPT=--enable-frozen-string-literals ruby /some/file 

这将导致所有String文字(使用""%q{}%Q{}或“#{}”样式创建的字符串)变为不可变。 此function目前被认为是Ruby 3.0的默认function。 按照function#11473进行操作 。 此function在文件级别而非全局级别上也可用作“魔术评论”

 # frozen_string_literal: true 

这将与RUBYOPT版本具有相同的影响,但仅适用于特定文件。 (另一种方式是直接与VM交互RubyVM::InstructionSequence.compile_option = {frozen_string_literal: true}

由于这是可选的,显然它可以打开和关闭,并且仍然是3.0中的一个选项,只是默认为打开而不是关闭。 仍然可以使用String.new创建Mutable String并且可以String.new不可变String以使其dup计数器部分可变。 ( 请注意以上内容:插值"#{}"也会创建一个新的不可变字符串,因为""

所有这一切都说明它并不能取代ruby中Symbols的需要。 首先,支持ruby的底层C通过rb_itern大量利用Symbols来处理方法定义之类的引用(这些引用标题为“不朽符号” ,永远不会被GCed)。

另外像ruby中的所有东西的Symbols都是它们自己的Object并且有自己有用的function集。 以Symbol#to_proc为例。 这起源于猴子补丁解决方案,在语法上很容易,并且在1.8.7中被用于核心。 这种风格受到ruby社区的高度鼓励和定期利用。 请告知您如何建议使用String而不是Symbol来降低此function。

虽然Symbols过去被认为有点“危险”(缺乏一个更好的词),因为它们的内容和记忆消耗与ruby的动态相结合。 从Ruby 2.2开始,大多数Symbols (见上文)可以被垃圾收集,即通过String internment( #intern#to_sym等)在ruby内部创建的符号。 (这些已被创造出“凡人符号”

小问题包括像

  define_method(param[:meth].to_sym) {} 

这似乎是因为它调用to_sym它应该是一个“凡人符号”但是因为define_method调用rb_intern来保持方法引用它实际上会创建一个“不朽符号”

希望这次运行有助于解释Symbol在ruby中的必要性,不仅从开发人员的角度来看,还有作为Ruby实现的C内部部分的大量使用。

差不多,是的。 我的理解是编译器保留一个符号表,它可以动态增长。 这就是为什么你必须永远不要接受用户输入并将其取消选中转换为符号的原因,因为你可以创建所谓的符号溢出攻击

我相信在Ruby 2.2中修补了符号溢出漏洞。

请参阅获取警告:拒绝服务

类似Java的字符串将取代符号的function,因此从这个意义上说,你是正确的。 但是,我不认为Matz会对只有不可变和实习字符串的语言感到满意。

使用字符串和符号,Ruby提供了两全其美的优势。 符号为只读字符串(如散列键)提供内存效率,而可变字符串对字符串操作(如串联)具有内存效率。

因此,“如果Ruby实现了像Java这样的字符串”,那么也许不是正确的思路。 Ruby 确实实现了Java之类的字符串。 他们被称为“符号”。 然后Ruby实现了第二种类型的字符串,它称之为“字符串”。 命名纯粹是美学,但我认为这是有道理的。

我已经使用Ruby大约一年了,并且有一个语言问题:是否必要符号,因为Ruby字符串是可变的而不是实习的?

没有。

SymbolString只是两种不同的数据类型。 String用于文本, Symbol用于标签。

比方说,Java,字符串是不可变的和实际的。

不,他们不是。 他们是不可改变的,有时是实习生,有时不是。 如果String被实习,那么为什么有一个方法java.lang.String.intern()实现了一个String ? Java中的String仅在实例中实现

  • 你调用java.lang.String.intern()
  • StringString文字表达式的结果或
  • StringString -typed常量值表达式的结果

否则,他们不是。

所以“foo”在值和引用中总是等于“foo”,它的值不能改变。

同样,这不是真的:

 class Test { public static void main(String... args) { System.out.println("foo".equals(args[0])); System.out.println("foo" == args[0]); } } 

叫它

 java Test foo # true # false 

在Ruby中,字符串是可变的而不是实体,因此"a".object_id == "a".object_id将为false。

在现代Ruby中,这不一定是真的:

 #frozen_string_literal: true "a".object_id == "a".object_id #=> true 

如果Ruby实现了像Java这样的字符串,那么符号就没有必要了,对吧?

不,就像我说的那样,它们针对不同的用例是不同的类型。

例如,看一下Scala ,它实现了“Java之类的字符串”(实际上,在Scala的JVM实现上, 没有 String ,Scala String只是java.lang.String )。 然而,它也有一个Symbol类。

同样, Clojure不只有两种数据类型,比如Ruby的Symbol : 关键字完全等同于Ruby的Symbol ,它们只对自己进行评估并且只为自己服务。 符号 OTOH 可能代表其他东西。

Erlang具有不可变的字符串和primefaces ,就像Clojure / Lisp符号一样。

ECMAScript具有不可变字符串 ,最近添加了一个Symbol数据类型。 但是,它们并不是100%相当于Ruby Symbol ,因为它们还有一个额外的保证:它们不仅仅评估自己并且仅仅为自己服务,而且它们也是不可伪造的(这意味着它不可能创建一个Symbol ,它是等于另一个Symbol )。

请注意,Ruby正在远离可变字符串:

  • Ruby 2.1优化模式'literal string'.freeze从全局字符串池返回一个冻结的字符串。
  • Ruby 2.3引入了# frozen_string_literal: true pragma和--enable=frozen-string-literal特性切换开关,默认情况下,在每个脚本(pragma)或每个进程(特征切换)上冻结(和池化)所有字符串文字基础。
  • Ruby 3会将这两者的默认值切换为true ,因此您必须明确说出# frozen_string_literal: false--disable=frozen-string-literal才能获得当前行为。
  • 一些更高版本将完全删除对可变字符串的支持。