15-結構體

StraightDave發表於2014-10-05

15-結構體

在之前的幾章中,我們談到過圖:

iex> map = %{a: 1, b: 2}
%{a: 1, b: 2}
iex> map[:a]
1
iex> %{map | a: 3}
%{a: 3, b: 2}

結構體是基於圖的一個擴充套件。它將預設值,編譯時保證和多型性帶入Elixir。

定義一個結構體,你只需在某模組中呼叫defstruct/1

iex> defmodule User do
...>   defstruct name: "john", age: 27
...> end
{ :module, User, <<70, 79, 82, ...>>, {:__struct__, 0} }

現在可以用%User()語法建立這個結構體的“例項”了:

iex> %User{}
%User{name: "john", age: 27}
iex> %User{name: "meg"}
%User{name: "meg", age: 27}
iex> is_map(%User{})
true

結構體的編譯時保證指的是編譯時會檢查結構體的欄位存不存在:

iex> %User{oops: :field}
** (CompileError) iex:3: unknown key :oops for struct User

當討論圖的時候,我們演示瞭如何訪問和修改圖現有的欄位。結構體也是一樣的:

iex> john = %User{}
%User{name: "john", age: 27}
iex> john.name
"john"
iex> meg = %{john | name: "meg"}
%User{name: "meg", age: 27}
iex> %{meg | oops: :field}
** (ArgumentError) argument error

通過使用這種修改的語法,虛擬機器知道沒有新的鍵增加到圖/結構體中,使得圖可以在記憶體中共享它們的結構。 在上面例子中,john和meg共享了相同的鍵結構。

結構體也能用在模式匹配中,它們保證結構體有相同的型別:

iex> %User{name: name} = john
%User{name: "john", age: 27}
iex> name
"john"
iex> %User{} = %{}
** (MatchError) no match of right hand side value: %{}

匹配能工作,是由於結構體再底層圖中有個欄位叫__struct__

iex> john.__struct__
User

總之,結構體就是個光禿禿的圖外加一個預設欄位。我們這裡說的光禿禿的圖指的是,為圖實現的協議都不能用於結構體。 例如,你不能美劇也不能訪問一個結構體:

iex> user = %User{}
%User{name: "john", age: 27}
iex> user[:name]
** (Protocol.UndefinedError) protocol Access not implemented for %User{age: 27, name: "john"}

結構體也不是字典,因為也不適用字典模組的函式:

iex> Dict.get(%User{}, :name)
** (ArgumentError) unsupported dict: %User{name: "john", age: 27}

下一章我們將介紹結構體是如何同協議進行互動的。

相關文章