檢視原始碼 Inspect 協定 (Elixir v1.16.2)
Inspect
協定將 Elixir 資料結構轉換成代數文件。
這通常在你想要自訂在記錄檔和終端機中如何檢查自己的結構時執行。
此文件說明如何為自己的資料結構實作 Inspect
協定。如需深入了解如何使用檢查,請參閱 Kernel.inspect/2
和 IO.inspect/2
。
檢查表示
檢查表示通常有三個選項。為了了解它們,我們假設我們有以下 User
結構
defmodule User do
defstruct [:id, :name, :address]
end
我們的選項是
使用 Elixir 的結構語法列印結構,例如:
%User{address: "Earth", id: 13, name: "Jane"}
。這是預設表示,而且如果所有結構欄位都是公開的,這是最佳選擇。使用
#User<...>
符號列印,例如:#User<id: 13, name: "Jane", ...>
。此符號不會發出有效的 Elixir 程式碼,而且通常在結構有私人欄位時使用(例如,你可能想要隱藏欄位:address
以刪除個人可識別資訊)。使用表達式語法列印結構,例如:
User.new(13, "Jane", "Earth")
。這假設有一個User.new/3
函數。此選項大多用作表示自訂資料結構的選項 2 的替代方案,例如MapSet
、Date.Range
等。
你可以為自己的結構實作 Inspect 協定,同時遵守上述慣例。選項 1 是預設表示,而且你可以透過衍生 Inspect
協定快速達成選項 2。對於選項 3,你需要自訂實作。
衍生
可以衍生 Inspect
協定,以自訂欄位的順序(預設為字母順序),並隱藏結構中的特定欄位,讓它們不會顯示在記錄、檢查等中。後者對於包含私人資訊的欄位特別有用。
支援的選項為
:only
- 檢查時僅包含指定的欄位。:except
- 檢查時移除指定的欄位。:optional
- (自 v1.14.0 起)如果欄位符合其預設值,則不包含該欄位。這可以用於簡化結構表示,但代價是隱藏資訊。
每當使用 :only
或 :except
來限制欄位時,結構將使用 #User<...>
表示法列印,因為結構無法再複製貼上為有效的 Elixir 程式碼。我們來看一個範例
defmodule User do
@derive {Inspect, only: [:id, :name]}
defstruct [:id, :name, :address]
end
inspect(%User{id: 1, name: "Jane", address: "Earth"})
#=> #User<id: 1, name: "Jane", ...>
如果你只使用 :optional
選項,結構仍會列印為 %User{...}
。
自訂實作
你也可以透過定義 inspect/2
函數來定義自訂協定實作。函數接收要檢查的實體,後接檢查選項,由結構 Inspect.Opts
表示。代數文件建立使用 Inspect.Algebra
進行。
很多時候,檢查結構可以透過現有實體的函數實作。例如,以下是 MapSet
的 inspect/2
實作
defimpl Inspect, for: MapSet do
import Inspect.Algebra
def inspect(map_set, opts) do
concat(["MapSet.new(", Inspect.List.inspect(MapSet.to_list(map_set), opts), ")"])
end
end
concat/1
函數來自 Inspect.Algebra
,它將代數文件串接在一起。在上面的範例中,它將字串 "MapSet.new("
、Inspect.Algebra.to_doc/2
回傳的文件,以及最後的字串 ")"
串接在一起。因此,包含數字 1、2 和 3 的 MapSet 將列印為
iex> MapSet.new([1, 2, 3], fn x -> x * 2 end)
MapSet.new([2, 4, 6])
換句話說,MapSet
的檢查表示會回傳一個表達式,在評估時會建立 MapSet
本身。
錯誤處理
如果在檢查結構時發生錯誤,Elixir 會引發 ArgumentError
錯誤,並自動回退到原始表示法來列印結構。此外,在除錯自己的 Inspect 實作時,您必須小心,因為呼叫 IO.inspect/2
或 dbg/1
可能會觸發無限迴圈(因為要檢查/除錯資料結構,您必須呼叫 inspect
本身)。
以下是幾個提示
對於除錯,請使用
IO.inspect/2
搭配structs: false
選項,這會停用自訂列印並避免遞迴呼叫 Inspect 實作若要存取自訂
Inspect
實作上的底層錯誤,您可以直接呼叫協定。例如,我們可以呼叫上述Inspect.MapSet
實作,如下所示Inspect.MapSet.inspect(MapSet.new(), %Inspect.Opts{})
摘要
函式
將 term
轉換為代數文件。
類型
函式
@spec inspect(t(), Inspect.Opts.t()) :: Inspect.Algebra.t()
將 term
轉換為代數文件。
此函式不應直接呼叫,除非在實作自訂 inspect_fun
以提供給 Inspect.Opts
時。在其他所有地方,應優先使用 Inspect.Algebra.to_doc/2
,因為它會處理結構和例外狀況。