http://elixir-lang.org/getting_started/15.html

在先前的章节中,我们已经学习了map:

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

Struct 是在map上做的扩展,增加了默认值,编译时担保(compile-time guarantees), 多态性

定义一个struct,需要在module里调用 defstruct/1 :

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

使用%User{}创建struct的"实例":

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

Struct 提供了编译时担保(compile-time guarantees )确保给定的字段存在:

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

在介绍map时,讨论访问,更新map的字段,同样也适用于struct:

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

通过使用更新语法, VM是知道没有新的键将被添加到map/struct,允许map在内存中共享结构,在上面的例子中,joseeric在内存中共享相同的键结构(key structure)

结构也可以用在模式匹配中使用,要保证结构是相同的类型:

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

能够进行模式匹配,因为struct在map内部存储了__struct__字段:

iex> jose.__struct__
User

总体而言,struct就是原始map增加了默认字段。请注意,我们说这是一个原始的map,因为对map实现的协议都不能用于结构。例如,你不能使用枚举,也不能用访问map的方式访问struct:

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

struct 也不是dictionary(字典), 所以你也不能用 Dict 模块:

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

我们会在下节介绍struct怎么和protocol进行交互。


lidashuang
6.7k 声望165 粉丝

$ Ruby/Elixir/Golang