Elixir结构体 #
一、结构体基础 #
1.1 定义结构体 #
结构体是带有固定字段的映射:
elixir
defmodule User do
defstruct [:name, :email, :age]
end
1.2 创建结构体 #
elixir
iex(1)> %User{name: "Alice", email: "alice@example.com", age: 30}
%User{age: 30, email: "alice@example.com", name: "Alice"}
iex(2)> %User{}
%User{age: nil, email: nil, name: nil}
1.3 默认值 #
elixir
defmodule User do
defstruct name: "Anonymous", email: nil, age: 0
end
%User{}
%User{name: "Alice"}
1.4 访问字段 #
elixir
iex(1)> user = %User{name: "Alice", email: "alice@example.com", age: 30}
%User{age: 30, email: "alice@example.com", name: "Alice"}
iex(2)> user.name
"Alice"
iex(3)> user.email
"alice@example.com"
1.5 更新字段 #
elixir
iex(1)> user = %User{name: "Alice"}
%User{age: 0, email: nil, name: "Alice"}
iex(2)> %{user | name: "Bob"}
%User{age: 0, email: nil, name: "Bob"}
iex(3)> %{user | name: "Bob", age: 25}
%User{age: 25, email: nil, name: "Bob"}
二、结构体特性 #
2.1 结构体是映射 #
elixir
iex(1)> user = %User{name: "Alice"}
%User{age: 0, email: nil, name: "Alice"}
iex(2)> is_map(user)
true
iex(3)> map_size(user)
4
2.2 __struct__字段 #
结构体包含 __struct__ 字段:
elixir
iex(1)> user = %User{name: "Alice"}
%User{age: 0, email: nil, name: "Alice"}
iex(2)> user.__struct__
User
2.3 类型检查 #
elixir
iex(1)> user = %User{name: "Alice"}
%User{age: 0, email: nil, name: "Alice"}
iex(2)> is_struct(user)
true
iex(3)> is_struct(user, User)
true
iex(4)> is_struct(%{}, User)
false
三、模式匹配 #
3.1 基本匹配 #
elixir
iex(1)> %User{name: name} = %User{name: "Alice", email: "alice@example.com"}
%User{age: 0, email: "alice@example.com", name: "Alice"}
iex(2)> name
"Alice"
3.2 函数参数匹配 #
elixir
defmodule User do
defstruct [:name, :email, :age]
def greet(%User{name: name}), do: "Hello, #{name}!"
def adult?(%User{age: age}) when age >= 18, do: true
def adult?(%User{}), do: false
end
3.3 匹配特定结构体 #
elixir
defmodule Handler do
def handle(%User{name: name}), do: "User: #{name}"
def handle(%Post{title: title}), do: "Post: #{title}"
def handle(_), do: "Unknown"
end
四、类型规范 #
4.1 定义类型 #
elixir
defmodule User do
@type t :: %__MODULE__{
name: String.t(),
email: String.t(),
age: non_neg_integer()
}
defstruct [:name, :email, :age]
end
4.2 使用类型 #
elixir
defmodule UserService do
@spec create(String.t(), String.t(), non_neg_integer()) :: User.t()
def create(name, email, age) do
%User{name: name, email: email, age: age}
end
@spec update(User.t(), keyword()) :: User.t()
def update(%User{} = user, attrs) do
struct(user, attrs)
end
end
五、实现协议 #
5.1 String.Chars协议 #
elixir
defmodule User do
defstruct [:name, :email]
defimpl String.Chars do
def to_string(%User{name: name}), do: "User(#{name})"
end
end
to_string(%User{name: "Alice"})
5.2 Inspect协议 #
elixir
defmodule User do
defstruct [:name, :email, :password]
defimpl Inspect do
def inspect(%User{name: name, email: email}, _opts) do
"#User<name: #{name}, email: #{email}>"
end
end
end
inspect(%User{name: "Alice", email: "alice@example.com", password: "secret"})
5.3 Enumerable协议 #
elixir
defmodule Pair do
defstruct [:first, :second]
defimpl Enumerable do
def count(_pair), do: {:ok, 2}
def member?(%Pair{first: first}, element) when first == element, do: {:ok, true}
def member?(%Pair{second: second}, element) when second == element, do: {:ok, true}
def member?(_, _), do: {:ok, false}
def reduce(%Pair{first: first, second: second}, acc, fun) do
case acc do
{:cont, acc} -> fun.(second, fun.(first, acc))
{:halt, acc} -> {:halted, acc}
{:suspend, acc} -> {:suspended, acc, &reduce(%Pair{first: first, second: second}, &1, fun)}
end
end
def slice(_pair), do: {:error, __MODULE__}
end
end
Enum.to_list(%Pair{first: 1, second: 2})
5.4 Access协议 #
elixir
defmodule User do
defstruct [:name, :email, :settings]
defimpl Access do
def fetch(%User{settings: settings}, key) do
Map.fetch(settings || %{}, key)
end
def get_and_update(%User{settings: settings} = user, key, fun) do
{current, new_settings} = Map.get_and_update(settings || %{}, key, fun)
{current, %{user | settings: new_settings}}
end
def pop(%User{settings: settings} = user, key) do
{current, new_settings} = Map.pop(settings || %{}, key)
{current, %{user | settings: new_settings}}
end
end
end
user = %User{settings: %{theme: "dark"}}
user[:theme]
六、派生协议 #
6.1 使用@derive #
elixir
defmodule User do
@derive {Jason.Encoder, only: [:name, :email]}
defstruct [:name, :email, :password]
end
Jason.encode(%User{name: "Alice", email: "alice@example.com", password: "secret"})
6.2 派生多个协议 #
elixir
defmodule User do
@derive [Jason.Encoder, Access]
defstruct [:name, :email]
end
七、结构体最佳实践 #
7.1 完整示例 #
elixir
defmodule MyApp.User do
@moduledoc """
User struct and related functions.
"""
@type t :: %__MODULE__{
id: integer() | nil,
name: String.t(),
email: String.t(),
age: non_neg_integer(),
inserted_at: DateTime.t() | nil,
updated_at: DateTime.t() | nil
}
@derive {Jason.Encoder, except: [:__struct__]}
defstruct [
:id,
:name,
:email,
:age,
:inserted_at,
:updated_at
]
@doc "Creates a new user"
@spec new(String.t(), String.t(), non_neg_integer()) :: t()
def new(name, email, age) do
%__MODULE__{
name: name,
email: email,
age: age
}
end
@doc "Validates user"
@spec valid?(t()) :: boolean()
def valid?(%__MODULE__{name: name, email: email, age: age}) do
is_binary(name) and is_binary(email) and is_integer(age) and age >= 0
end
@doc "Updates user"
@spec update(t(), keyword()) :: t()
def update(%__MODULE__{} = user, attrs) do
struct(user, Keyword.take(attrs, [:name, :email, :age]))
end
end
7.2 工厂函数 #
elixir
defmodule User do
defstruct [:id, :name, :email]
def new(attrs \\ []) do
struct!(__MODULE__, Keyword.put_new(attrs, :id, generate_id()))
end
defp generate_id, do: :rand.uniform(1000)
end
八、结构体与映射的区别 #
| 特性 | 结构体 | 映射 |
|---|---|---|
| 固定字段 | 是 | 否 |
| 类型信息 | 有 | 无 |
| 模式匹配 | 精确 | 部分 |
| 默认值 | 支持 | 不支持 |
| 协议实现 | 可自定义 | 有限 |
九、总结 #
本章学习了:
| 特性 | 示例 |
|---|---|
| 定义 | defstruct [:name, :email] |
| 创建 | %User{name: "Alice"} |
| 访问 | user.name |
| 更新 | %{user | name: "Bob"} |
| 类型 | @type t :: %__MODULE__{} |
| 协议 | defimpl String.Chars |
准备好学习协议了吗?让我们进入下一章。
最后更新:2026-03-27