Ruby Symbol(符号)

Ruby的Symbol(符号)是一种非常有代表性的语法,可以理解为"贴了标签的、不可变的字符串": 在 Ruby 中,String 是可变的(Mutable),而 Symbol 是不可变的(Immutable)

通过打印变量可以看到symbol对象的内存位置始终不变(object_id完全一致)
irb(main):001> puts "hello".object_id
5440
=> nil
irb(main):002> puts "hello".object_id
5496
=> nil
irb(main):003> puts "hello".object_id
5552
=> nil
irb(main):004> puts :helo.object_id
59347212
=> nil
irb(main):005> puts :helo.object_id
59347212
=> nil
irb(main):006> puts :helo.object_id
59347212
=> nil

也就是说不论写多少次 :hello 在整个ruby进程的生命周期内,永远指向内存的同一块地方(object_id完全一致)

Ruby在比较两个Symbol时,不需要像字符串一样逐个字母对比,只需要对比内存地址(object_id),这种速度极其恐怖

Ruby中的Hash

早期的Ruby使用了传统的火箭符 => 来构建哈希

传统hash
instrument_section = {
  'cello' => 'string',
  'clarinet' => 'woodwind',
  'drum' => 'percussion',
  'oboe' => 'woodwind',
  'trumpt' => 'brass',
  'violin' => 'string'
}

在引用是使用如下方法:

引用hash
instrument_section['oboe']
instrument_section['ello']
instrument_section['basson']

这里有一个问题,就是每次引用的时候,ruby都会创建一个内存对象,使用完毕再销毁,带来巨大的GC压力

修改成使用Symbol作为key字符串,那么后续引用hash值时候使用 :cello 方式,不管多少次,都使用内存同一个地方(object_id完全一致)

采用Symbol作为hash的key
instrument_section = {
  cello: 'string',
  clarinet: 'woodwind',
  drum: 'percussion',
  oboe: 'woodwind',
  trumpt: 'brass',
  violin: 'string'
}

注意,在使用Symbol时,引用Symbol的key,必须在字符串前使用 :

使用 采用Symbol作为hash的key
instrument_section[:cello]
instrument_section[:calarinet]

另外,在Ruby 1.9之后引入了火箭符( => )的缩写语法糖,将 : 移动到key字符串后面,就可以代替 => 并同时使用Symbol作为key

更为简洁的symbol语法糖
instrument_section = {
  cello: 'string',
  clarinet: 'woodwind'
}

不过引用的时候依然要在字符串前使用 :

使用 采用Symbol作为hash的key
instrument_section[:cello]
instrument_section[:calarinet]

使用规则

口诀“数据用字符串,身份/内部标识用符号” :

  • 使用 Symbol 的场景(作为“身份标签”)

    • Hash 的 Key:表示固定的属性名

    • 方法传参(关键字参数):比如 Rails 里的 link_to "主页", root_path , method: :post

    • 状态机的值:比如订单状态 status = :pendingstatus = :completed

  • 使用 String 的场景(作为“动态数据”)

    • 需要展示给最终用户的文本(如 "用户名不能为空")

    • 外部输入进来的数据(比如从数据库读出的用户名字、从前端表单提交过来的密码文本)

    • 需要进行拼接、裁剪、大变小等加工处理的文本(因为 Symbol 根本没有 .gsub、.split 这样的字符串修改方法)

参考

  • 《Programming Ruby 3.3》

  • gemini