跳至內容

as

as 虛擬方法限制了表達式的型別。例如

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

# a : Int32 | String

在上面的程式碼中,aInt32 | String 的聯合型別。如果由於某些原因,我們確定在 if 之後 aInt32,我們可以強制編譯器將其視為 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) }