crystal-chinaChina
  • 文档
  • Github
  • 注册
  • 登录
目录
  • 前言
  • 简介
  • 安装 ➤
    • 包管理
  • 写给 Rubyists ➤
    • 类型
    • 方法
    • 代码块
    • 杂项
    • 性能因素(WIP)
    • 迁移 Ruby 代码到 Crystal
  • 基础知识
  • 查找性能瓶颈 (WIP)
  • 交叉编译

类型

最后编辑于: 2025年03月09日

§ 字符串

Crystal 使用双引号来表示字符串,单引号是 Char 类型(在栈中分配)。

   1  
   2  p typeof("Hello") # => String
   3  p typeof("汉") # => String
   4  p typeof('汉') # => Char
   5  

这几乎总是从 Ruby 切换到 Crystal 最先遇到的坑。

而且,Crystal 字符串是不可变的, 因此,Ruby 中常用的方法 String#<< 是没有的。

尝试修改一个字符串,例如 String#+ 或 interpolation 都会产生新的字符串,你需要将修改后字符串重新赋值给一个变量。

   1  
   2  str = "Hello"
   3  p str.object_id # => 104918042676384
   4  str = str.sub("H", "h")
   5  p str.object_id # => 132565044809664
   6  

你仍然可以使用 % 或 %q 来分别表示 Ruby 中等价的 "双引号" 和 '单引号'

   1  
   2  recipient = "Billy"
   3  p %(Hello #{recipient}!) # => "Hello Billy!"
   4  p %q(Hello #{recipient}!) # => "Hello \#{recipient}!"
   5  

§ Crystal 中没有全局变量

Crystal 不支持定义 $ 开头的全局变量,事实上 $ 开头的其实根本不是全局变量,因为只是方法可见的。

默认,只有很少的几个预定义的 $ 开头的变量,正则匹配相关的 $~, $1, $2 ... 以及前一个被执行的进程退出状态 $?

   1  
   2  "hello" =~ /(ll)o/
   3  
   4  p $~ # => Regex::MatchData("llo" 1:"ll")
   5  p $0 # => llo, 等价于 $~[0],等价于 Ruby 中的 $&
   6  p $1 # => ll,等价于 $~[1]
   7  p $2 # => Unhandled exception: Invalid capture group index: 2 (IndexError)
   8  
   9  `ls -alh`
  10  p $? # => Process::Status[0]
  11  

§ 整数与布尔类型

整数类型细分为 8 个,而 Ruby 是 Integer 一个。

   1  
   2  p typeof(1_i8) # => Int8
   3  p typeof(1_u8) # => UInt8
   4  p typeof(1_i16) # => Int16
   5  p typeof(1_u16) # => UInt16
   6  p typeof(1) # => Int32 这是整数默认类型, 等价于:typeof(1_i32)
   7  p typeof(1_u32) # => UInt32
   8  p typeof(1_i64) # => Int64
   9  p typeof(1_u64) # => UInt64
  10  

Ruby 中,当数字大到 Fixnum 无法容纳它时,会自动转化为 Bignum, Crystal 则抛出 OverflowError 异常

   1  
   2  x = 127_i8 # An Int8 type
   3  x          # => 127
   4  x += 1     # Unhandled exception: Arithmetic overflow (OverflowError)
   5  

Crystal 标准库提供了专门的任意大小和精度的类型

BigDecimal | BigFloat | BigInt | BigRational

Crystal 中,true/false 类属于 Bool 类型, 而 Ruby 是单独的 TrueClass 和 FalseClass

§ 哈希与 NamedTuple

Crystal 当中:哈希火箭表示一个哈希,而 1.9 新哈希表示法

{:foo => 100} 这是定义了一个哈希。

{foo: 100} 则是定义了一个 NamedTuple

后者是一个不可变的、编译时确定大小的特定的 NamedTuple 类型, 并且是栈分配的,可以简单的将理解为就是一个不可变的 Struct。

   1  
   2  x = {:foo => 100, :bar => "Hello"}
   3  y = {foo: 100, bar: "Hello"}
   4  
   5  p typeof(x) # => Hash(Symbol, Int32 | String)
   6  p typeof(y) # => NamedTuple(foo: Int32, bar: String), 编译时类型就是一个包含了整数类型属性 foo, 以及字符串类型属性 bar 的 NamedTuple
   7  
   8  # 通过符号和字符串取值都是可以的。
   9  p y[:foo] # => 100
  10  p y["foo"] # => 100
  11  

个人认为这是对 Ruby 错误设计的完美修复,关键字参数本来就应该设计成这样。

§ 实例变量和类变量

Crystal 中不存在 Ruby 的类变量那样的东西,Crystal 的 类变量 的行为和 Ruby 中 类的实例变量 相同。

   1  
   2  class Foo
   3    @@value = 1
   4  
   5    def self.value
   6      @@value
   7    end
   8  
   9    def self.value=(@@value)
  10    end
  11  end
  12  
  13  class Bar < Foo
  14  end
  15  
  16  p Foo.value # => 1
  17  p Bar.value # => 1
  18  
  19  Foo.value = 2
  20  
  21  p Foo.value # => 2
  22  p Bar.value # => 1
  23  
  24  Bar.value = 3
  25  
  26  p Foo.value # => 2
  27  p Bar.value # => 3
  28  

即:@@类变量 是属于类自身的变量,每一个子类也会继承父类的类变量值(类型必须相同), 但是会创建自己独立的类变量实例, 因此修改子类的类变量的值,不会影响父类,反之亦然。

这是再一次对 Ruby 错误行为的修复。


因为 Crystal 中的 @@类变量 其行为表现就是 Ruby 中的 类级别 的 @实例变量, 那么问题来了,Crystal 中类级别定义的实例变量又是什么呢?

   1  
   2  class Foo
   3    @value = 100
   4  
   5    def value
   6      @value
   7    end
   8  
   9    def initialize
  10      puts "Foo"
  11    end
  12  end
  13  
  14  class Bar < Foo
  15  end
  16  
  17  bar = Bar.new
  18  p bar.value  # => Crystal 输出 100, Ruby 输出 nil
  19  

结果为:上面的 @value 其实就是一个普通的实例变量,只不过在类定义中进行了初始化, 如果这个类有签名不同的多个不同版本的构造器(initialize 方法),它相当于为所有构造器 初始化了变量 @value,避免了重复初始化。 而且当这样的类被 reopen 时,@value 值也是存在的。

由于目前编译器的限制,在方法定义中初始化一个的可空的实例变量是不合法的。

   1  
   2  class A
   3    def initialize
   4      @x : Int32? 
   5    end
   6  end
   7  
   8  a = A.new 
   9  
  10  # 3 | @x : Int32?
  11  # ^-
  12  # Error: declaring the type of an instance variable must be done at the class level
  13  

即使赋初值为 nil 也不行。

   1  
   2  class A
   3    def initialize
   4      @x : Int32? = nil
   5    end
   6  end
   7  
   8  a = A.new
   9  
  10  #  3 | @x : Int32? = nil
  11  #      ^
  12  # Error: instance variable @x of A was inferred to be Nil, but Nil alone provides no information
  13  

只能通过类级别来初始化

   1  
   2  class A
   3    @x : Int32?
   4  
   5    def initialize
   6    end
   7  end
   8  
   9  p A.new # => #<A:0x7a69c688bfc0 @x=nil>
  10  

当然,在方法中直接使用 literal 值初始化一个实例变量是可以的。

   1  
   2  # 但是赋值一个非 nilable 的值,来推断类型是可以的。
   3  
   4  class A
   5    def initialize
   6      @x = 100
   7    end
   8    
   9    def hello
  10      @hello = "Hello"
  11    end
  12  end
  13  
  14  p  A.new # => #<A:0x7d736b629ce0 @x=100, @hello=nil>
  15  
previous_page写给 Rubyists

类型

next_page方法
欢迎在评论区留下你的见解、问题或建议

正在预览...
正在读取评论...
Crystal China
admin@crystal-china.org
githubtwittercrystal-lang