Ruby符号 #

一、符号概述 #

符号(Symbol)是Ruby特有的数据类型,它是一个不可变的标识符。符号以冒号开头,在整个程序中唯一。

ruby
:name
:age
:user_name
:"hello world"

二、符号的特点 #

2.1 不可变性 #

符号一旦创建就不能修改:

ruby
s = :name
s[0] = 'N'

str = "name"
str[0] = 'N'

2.2 唯一性 #

相同名称的符号是同一个对象:

ruby
:name.object_id
:name.object_id
:name.object_id == :name.object_id

"name".object_id
"name".object_id
"name".object_id == "name".object_id

2.3 内存效率 #

ruby
symbols = []
strings = []

10000.times { symbols << :name }
10000.times { strings << "name" }

symbols.uniq.size
strings.uniq.size

symbols.map(&:object_id).uniq.size
strings.map(&:object_id).uniq.size

三、创建符号 #

3.1 字面量语法 #

ruby
:name
:user_name
:UserClass
:method_name?
:dangerous_method!

3.2 引号语法 #

ruby
:"hello world"
:"user-name"
:"class name"
:'single quotes'

3.3 动态创建 #

ruby
"name".to_sym
"user_name".intern
:"prefix_#{name}"

3.4 符号数组 #

ruby
%i[apple banana cherry]
%I[apple banana #{fruit}]

%i(apple banana cherry)
%w[apple banana cherry]

四、符号与字符串转换 #

4.1 符号转字符串 #

ruby
:name.to_s
:name.id2name
:name.inspect

4.2 字符串转符号 #

ruby
"name".to_sym
"name".intern

4.3 安全转换 #

ruby
def to_safe_symbol(str)
  str.to_s.gsub(/[^a-zA-Z0-9_]/, '_').to_sym
end

to_safe_symbol("hello world")
to_safe_symbol("user-name")

五、符号方法 #

5.1 基本方法 #

ruby
:name.length
:name.empty?
:name.size
:name.to_s
:name.to_sym
:name.id2name
:name.inspect
:name.downcase
:name.upcase
:name.capitalize

5.2 比较方法 #

ruby
:name == :name
:name.eql?(:name)
:name.equal?(:name)
:name <=> :name
:name.casecmp(:NAME)
:name.casecmp?(:NAME)

5.3 匹配方法 #

ruby
:name =~ /name/
:name.match(/name/)
:name.match?(/name/)
:name.start_with?(:n)
:name.end_with?(:e)

5.4 转换方法 #

ruby
:name.to_proc
:name.to_s
:name.to_sym
:name.dup
:name.clone

六、符号的使用场景 #

6.1 哈希键 #

ruby
user = {
  name: "Ruby",
  age: 30,
  email: "ruby@example.com"
}

puts user[:name]
puts user[:age]

user[:name] = "Rails"
user[:city] = "Beijing"

6.2 方法名引用 #

ruby
class Person
  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

person = Person.new("Ruby", 30)
person.send(:name)
person.public_send(:age)
person.respond_to?(:name)
person.methods.include?(:name)

6.3 关键字参数 #

ruby
def greet(name:, age:, city: "Beijing")
  puts "Hello, #{name} from #{city}, age #{age}"
end

greet(name: "Ruby", age: 30)
greet(age: 25, name: "Rails", city: "Shanghai")

6.4 attr系列方法 #

ruby
class User
  attr_reader :id
  attr_writer :password
  attr_accessor :name, :email

  def initialize(id)
    @id = id
  end
end

6.5 定义方法 #

ruby
class Person
  define_method :greet do |name|
    puts "Hello, #{name}!"
  end
end

person = Person.new
person.greet("Ruby")

6.6 回调与钩子 #

ruby
class Model
  def before_save
    puts "Before save"
  end

  def after_save
    puts "After save"
  end

  def save
    before_save
    puts "Saving..."
    after_save
  end
end

七、符号与字符串的选择 #

7.1 使用符号的场景 #

  • 哈希键
  • 方法名引用
  • 关键字参数
  • 枚举值
  • 固定的标识符
ruby
STATUS = {
  pending: :pending,
  approved: :approved,
  rejected: :rejected
}

config = {
  adapter: :postgresql,
  encoding: :unicode,
  pool: 5
}

7.2 使用字符串的场景 #

  • 用户输入
  • 动态内容
  • 需要修改的内容
  • 文本处理
ruby
user_input = gets.chomp
dynamic_content = "Hello, #{name}"
mutable_text = "Hello"
mutable_text << " World"

7.3 性能对比 #

ruby
require 'benchmark'

n = 1_000_000

Benchmark.bm do |x|
  x.report("Symbol: ") do
    n.times { :name }
  end

  x.report("String: ") do
    n.times { "name" }
  end

  x.report("Symbol hash: ") do
    n.times { { name: "value" } }
  end

  x.report("String hash: ") do
    n.times { { "name" => "value" } }
  end
end

八、符号表 #

8.1 查看所有符号 #

ruby
Symbol.all_symbols
Symbol.all_symbols.size
Symbol.all_symbols.grep(/user/)

8.2 符号垃圾回收 #

Ruby 2.2+ 支持符号垃圾回收:

ruby
GC.start
Symbol.all_symbols.size

8.3 避免符号泄露 #

ruby
def dangerous_method(user_input)
  user_input.to_sym
end

def safe_method(user_input)
  user_input.to_s
end

九、符号与to_proc #

9.1 基本用法 #

ruby
[1, 2, 3].map(&:to_s)
[1, 2, 3].map { |n| n.to_s }

["a", "b", "c"].map(&:upcase)
["a", "b", "c"].map { |s| s.upcase }

9.2 原理解析 #

ruby
class Symbol
  def to_proc
    Proc.new { |obj, *args| obj.send(self, *args) }
  end
end

[1, 2, 3].map(&:to_s)
[1, 2, 3].map { |n| n.send(:to_s) }

9.3 高级用法 #

ruby
users = [{ name: "Alice" }, { name: "Bob" }]
users.map { |u| u[:name] }
users.map(&:[]).map(&:name)

[1, 2, 3, 4, 5].select(&:even?)
[1, 2, 3, 4, 5].reject(&:odd?)
[1, 2, 3, 4, 5].sum(&:abs)

十、实用示例 #

10.1 状态机 #

ruby
class Order
  STATES = %i[pending processing shipped delivered cancelled].freeze

  attr_reader :state

  def initialize
    @state = :pending
  end

  def process!
    transition_to(:processing)
  end

  def ship!
    transition_to(:shipped)
  end

  def deliver!
    transition_to(:delivered)
  end

  def cancel!
    transition_to(:cancelled)
  end

  private

  def transition_to(new_state)
    @state = new_state if STATES.include?(new_state)
  end
end

10.2 配置选项 #

ruby
class Config
  OPTIONS = %i[debug verbose log_level].freeze

  def initialize
    @options = {}
  end

  def configure(options)
    options.each do |key, value|
      @options[key] = value if OPTIONS.include?(key)
    end
  end

  def method_missing(name, *args)
    if OPTIONS.include?(name) && args.empty?
      @options[name]
    else
      super
    end
  end
end

10.3 属性访问器 #

ruby
class Attributes
  def initialize
    @attributes = {}
  end

  def [](key)
    @attributes[key.to_sym]
  end

  def []=(key, value)
    @attributes[key.to_sym] = value
  end

  def method_missing(name, *args)
    if name.end_with?('=')
      self[name[0..-2].to_sym] = args.first
    else
      self[name]
    end
  end
end

attrs = Attributes.new
attrs.name = "Ruby"
attrs.age = 30
puts attrs.name

十一、最佳实践 #

11.1 哈希键使用符号 #

ruby
# 推荐
user = { name: "Ruby", age: 30 }

# 不推荐
user = { "name" => "Ruby", "age" => 30 }

11.2 关键字参数使用符号 #

ruby
def create_user(name:, email:, role: :member)
  puts "Creating #{role} user: #{name} <#{email}>"
end

create_user(name: "Ruby", email: "ruby@example.com")
create_user(name: "Admin", email: "admin@example.com", role: :admin)

11.3 避免动态创建符号 #

ruby
# 危险:可能导致符号泄露
def process(user_input)
  user_input.to_sym
end

# 安全:使用字符串
def process(user_input)
  user_input.to_s
end

十二、总结 #

本章我们学习了:

  1. 符号特点:不可变、唯一、内存高效
  2. 创建方式:字面量、引号语法、动态创建
  3. 使用场景:哈希键、方法引用、关键字参数
  4. 符号与字符串:选择合适的类型
  5. to_proc:简化迭代器调用
  6. 最佳实践:避免符号泄露

接下来让我们学习Ruby的空值nil!

最后更新:2026-03-27