Struct 是建立在 maps 之上的擴充機制,提供編譯期檢查與預設值。Struct 會以定義它的模組來命名。要定義 struct,請使用defstruct建構。這個建構通常緊接在模組定義之後。defstruct可以接受 atom 的陣列(用於nil預設值),或關鍵字陣列(用於指定的預設值)。沒有預設值的欄位必須排在具有預設值的欄位前面。
defmodule Plane do
defstruct [:engine, wings: 2]
end
plane = %Plane{}
# => %Plane{engine: nil, wings: 2}
由於 struct 建立在 map 之上,我們可以使用大多數的 map 函式來取得與操作值。struct 並未實作 Access Behaviour。建議改用_靜態存取運算子_.來存取 struct 的欄位。
取得/擷取欄位值:
plane = %Plane{}
plane.engine
# => nil
Map.fetch(plane, :wings)
# => {:ok, 2}
更新欄位值
plane = %Plane{}
%{plane | wings: 4}
# => %Plane{engine: nil, wings: 4}
struct 可以用於模式配對,帶或不帶 struct 名稱都可以。
plane = %Plane{}
%Plane{wings: wings} = plane
%{wings: wings} = plane
在模式中帶上 struct 名稱,就能確保左右兩側都是相同型別的 struct。
defmodule Helicopter do
defstruct [:engine, rotors: 1]
end
%Plane{} = %Helicopter{}
# => (MatchError) no match of right hand side value: %Helicopter{engine: nil, rotors: 1}
我們可以使用@enforce_keys模組屬性,搭配欄位鍵的陣列,確保 struct 建立時這些值就已經初始化。如果某個鍵沒有列在其中,它的值就會是nil,就像上面的範例一樣。如果強制要求的鍵沒有被初始化,就會引發錯誤。
defmodule User do
@enforce_keys [:username]
defstruct [:username]
end
%User{}
# => (ArgumentError) the following keys must also be given when building struct User: [:username]
在這道練習裡,你可以玩玩一台遙控車,你終於存夠錢把它買下來了。
遙控車一開始的電量都是滿的(100%)。每次你用遙控器駕駛這台車,它就會前進 20 公尺,並消耗 1% 的電量。這台車的暱稱要等到建立之後才會知道。
這台遙控車有個炫麗的 LED 顯示器,會顯示兩項資訊:
"<METERS> meters"。"Battery at <PERCENTAGE>%"。如果電量剩下 0%,你就不能再駕駛這台車,電池顯示器會顯示 "Battery empty"。
實作RemoteControlCar.new/0函式,回傳一個全新的遙控車 struct:
RemoteControlCar.new()
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "none"
# }
struct 必須要有暱稱,請確保在new函式中初始化這個值,而不是在 struct 裡。
實作RemoteControlCar.new/1函式,回傳一個帶有指定暱稱的全新遙控車 struct:
RemoteControlCar.new("Blue")
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "Blue"
# }
實作RemoteControlCar.display_distance/1函式,回傳 LED 顯示器上顯示的距離:
car = RemoteControlCar.new()
RemoteControlCar.display_distance(car)
# => "0 meters"
請確認這個函式只接受RemoteControlCar struct 作為引數。
實作RemoteControlCar.display_battery/1函式,回傳 LED 顯示器上顯示的電量百分比:
car = RemoteControlCar.new()
RemoteControlCar.display_battery(car)
# => "Battery at 100%"
請確認這個函式只接受RemoteControlCar struct 作為引數。如果電量剩下 0%,電池顯示器會顯示 "Battery empty"。
實作RemoteControlCar.drive/1函式,它會:
RemoteControlCar.new("Red")
|> RemoteControlCar.drive()
# => %RemoteControlCar{
# battery_percentage: 99,
# distance_driven_in_meters: 20,
# nickname: "Red"
# }
請確認這個函式只接受RemoteControlCar struct 作為引數。
更新RemoteControlCar.drive/1函式,讓電池耗盡(0%)時,既不增加行駛距離,也不減少電量百分比:
%RemoteControlCar{
battery_percentage: 0,
distance_driven_in_meters: 1980,
nickname: "Red"
}
|> RemoteControlCar.drive()
# => %RemoteControlCar{
# battery_percentage: 0,
# distance_driven_in_meters: 1980,
# nickname: "Red"
# }