🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
15-结构体 ========= 在之前的几章中,我们谈到过图: ```elixir iex> map = %{a: 1, b: 2} %{a: 1, b: 2} iex> map[:a] 1 iex> %{map | a: 3} %{a: 3, b: 2} ``` 结构体是基于图的一个扩展。它引入了默认值、编译期验证和多态性。 定义一个结构体,你只需在模块中调用```defstruct/1```: ```elixir iex> defmodule User do ...> defstruct name: "john", age: 27 ...> end ``` 现在可以用```%User()```语法创建这个结构体的“实例”了: ```elixir iex> %User{} %User{ name: "john", age: 27 } iex> %User{ name: "meg" } %User{ name: "meg", age: 27 } iex> is_map(%User{}) true ``` 结构体的编译期验证,指的是代码在编译时会检查结构体的字段存不存在: ```elixir iex> %User{ oops: :field } ** (CompileError) iex:3: unknown key :oops for struct User ``` 当讨论图的时候,我们演示了如何访问和修改图现有的字段。结构体也是一样的: ```elixir 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共享了相同的键结构。 结构体也能用在模式匹配中,它们保证结构体有相同的类型: ```elixir iex> %User{name: name} = john %User{name: "john", age: 27} iex> name "john" iex> %User{} = %{} ** (MatchError) no match of right hand side value: %{} ``` 这里可以用模式匹配,是因为在结构体底层的图中有个叫```__struct__```的字段: ```elixir iex> john.__struct__ User ``` 简单说,结构体就是个光秃秃的图外加一个默认字段。 但是,为图实现的协议都不能用于结构体。 例如,你不能枚举也不能用[]访问一个结构体: ```elixir iex> user = %User{} %User{name: "john", age: 27} iex> user[:name] ** (Protocol.UndefinedError) protocol Access not implemented for %User{age: 27, name: "john"} ``` 结构体也不是字典,因而也不能使用字典模块的函数: ```elixir iex> Dict.get(%User{}, :name) ** (ArgumentError) unsupported dict: %User{name: "john", age: 27} ``` 下一章我们将介绍结构体是如何同协议进行交互的。