跳至内容

as

as 伪方法限制表达式的类型。例如

if some_condition
  a = 1
else
  a = "hello"
end

# a : Int32 | String

在上面的代码中,aInt32 | String 的联合类型。如果出于某种原因,我们确定 aif 之后是一个 Int32,我们可以强制编译器将其视为 Int32

a_as_int = a.as(Int32)
a_as_int.abs # works, compiler knows that a_as_int is Int32

as 伪方法执行运行时检查:如果 a 不是 Int32,则会抛出 异常

表达式的参数是一个 类型

如果不可能通过另一种类型限制类型,则会发出编译时错误。

1.as(String) # Compile-time error

注意

您不能使用 as 将类型转换为不相关的类型:as 不像其他语言中的 cast。整数、浮点数和字符上的方法用于这些转换。或者,可以使用指针转换,如下所述。

指针类型之间的转换

as 伪方法还允许在指针类型之间进行转换

ptr = Pointer(Int32).malloc(1)
ptr.as(Int8*) # :: Pointer(Int8)

在这种情况下,不会执行运行时检查:指针是不安全的,这种类型的转换通常只在 C 绑定和低级代码中需要。

指针类型和其他类型之间的转换

指针类型和引用类型之间的转换也是可能的

array = [1, 2, 3]

# object_id returns the address of an object in memory,
# so we create a pointer with that address
ptr = Pointer(Void).new(array.object_id)

# Now we cast that pointer to the same type, and
# we should get the same value
array2 = ptr.as(Array(Int32))
array2.same?(array) # => true

在这些情况下不会执行运行时检查,因为同样,涉及指针。对这种转换的需求甚至比前面提到的更少见,但它允许在 Crystal 本身中实现一些核心类型(如 String),并且它还允许通过将其转换为 void 指针来将引用类型传递给 C 函数。

用于转换为更大类型的用法

as 伪方法可用于将表达式转换为“更大”类型。例如

a = 1
b = a.as(Int32 | Float64)
b # :: Int32 | Float64

上面的代码可能看起来没什么用,但在例如映射元素数组时很有用

ary = [1, 2, 3]

# We want to create an array 1, 2, 3 of Int32 | Float64
ary2 = ary.map { |x| x.as(Int32 | Float64) }

ary2        # :: Array(Int32 | Float64)
ary2 << 1.5 # OK

Array#map 方法使用代码块的类型作为数组的泛型类型。如果没有 as 伪方法,推断的类型将是 Int32,我们无法向其中添加 Float64

当编译器无法推断代码块类型时的用法

有时编译器无法推断代码块的类型。这可能发生在相互依赖的递归调用中。在这些情况下,您可以使用 as 来让它知道类型

some_call { |v| v.method.as(ExpectedType) }