Elixir 从零 · 第 16 课(展开目录)
01 · 先让代码说话02 · 先认六种值03 · 把值装起来04 · 从旧 list 生成新 list05 · 让形状对上06 · 用 case 做选择07 · 把文字安全变成整数08 · 拆开 &1 和管道09 · 把代码放进项目10 · 真假不只靠 true11 · 走进嵌套数据12 · 一字不一定一字节13 · 让函数按形状接活14 · 把一列数收成一个值15 · 给不同问题选不同路16 · 给 map 一张名片17 · 让项目自己验答案18 · 让成功和失败长得一样19 · 读写文件先管好路20 · 只算眼前需要的21 · 同一句话,不同做法22 · 先约好模块会做什么23 · 把数据形状写在门口24 · 测试不只看一条好路25 · 把项目磨成一个命令26 · 定下小项目边界27 · 把真实文字洗干净28 · 只开三扇门29 · 把约定钉在代码旁30 · 打包,也留下脚印31 · 照任务书交卷
ELIXIR · 基础 · LESSON 1635 分钟

给 map 一张名片

用 struct 固定领域数据的名字,再用模块属性写文档与默认值。

01 · 先看完整例子

定义一本书

struct!/2 让整段代码能一次粘贴运行;在模块内部仍用 %__MODULE__{} 创建或更新 struct。

Elixir
defmodule Book do
  @moduledoc "一本馆藏书。"
  @default_pages 1

  # title 必填,其余字段有默认值
  @enforce_keys [:title]
  defstruct title: nil, pages: @default_pages

  # 在模块内部,__MODULE__ 就是 Book
  def revise(%__MODULE__{} = book, pages) do
    %__MODULE__{book | pages: pages}
  end
end

book = struct!(Book, title: "小城", pages: 120)
updated = Book.revise(book, 121)
{book.pages, updated.pages}
先对照结果
  • 最后得到 {120, 121}。
02 · 回头拆代码

从第一行往下读

  1. @enforce_keys 要求创建时明确提供书名。
  2. defstruct 决定可用字段,未声明字段不能随意加入。
  3. 更新语法返回新 struct,旧值仍保留 120。
03 · 认清新符号

符号不是暗号

defstruct

声明 struct 字段与默认值。

%Book{...}

创建或匹配 Book struct。

@moduledoc / @doc

为模块和函数写文档。

04 · 代码里的概念

把名字和意思对上

01

struct

带模块身份和固定字段的 map,适合表示书、订单等领域数据。

02

模块属性

编译模块时使用的标记或值,如文档、类型说明与常量。

05 · 再说清楚

这些写法为什么有用

普通 map 很自由,但领域数据常需要固定名字。struct 在 map 基础上加上模块身份与字段清单。

defstruct 声明字段和默认值。创建时写 %Book{},更新时写 %Book{book | ...}。

模块属性以 @ 开头。@moduledoc、@doc 写文档;自定义属性也可保存编译时常量。

06 · 自己改一次

合上答案,动手

定义 Lantern,字段为 color 和默认亮度 1。

练习起点
defmodule Lantern do
  @enforce_keys [:color]
  defstruct color: nil, brightness: ____
end

struct!(Lantern, color: "红")

目标结果: 得到一个颜色为 "红"、亮度为 1 的 Lantern。

卡住了,再看提示

把默认值直接写成整数 1。

运行过以后,再看一种答案
一种答案
defmodule Lantern do
  @enforce_keys [:color]
  # brightness 没传入时使用 1
  defstruct color: nil, brightness: 1
end

struct!(Lantern, color: "红")
想一想:struct 能随时加入一个没有声明的新字段吗?

不能。它的字段由 defstruct 固定;需要新字段时要修改模块定义。

带走

记住三句

  1. 1

    struct 让领域数据有明确身份。

  2. 2

    struct 更新仍会返回新值。

  3. 3

    模块属性可承载文档、类型和编译时常量。

本课结束代码也跑过一次,就把这一课收好。