You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建仅所属模块可读写的Elixir opaque结构体?

如何创建仅当前模块可读写的Elixir结构体(模拟Opaque类型效果)

要实现类似Elm中仅定义模块可读写的Opaque类型效果,仅靠@opaque注解不够,需要结合类型约束和API封装来限制外部模块对结构体的直接操作,具体方案如下:

核心实现思路

Elixir的结构体本身是公开的,但可以通过Dialyzer的类型系统隐藏内部结构,同时只对外暴露受控的构造、读取和修改函数,让外部模块无法直接操作结构体内部。

示例代码

defmodule User do
  # 定义Opaque类型,对外仅暴露User.t(),隐藏内部结构细节
  @opaque t() :: %__MODULE__{id: integer(), name: String.t()}

  # 定义结构体,字段仅在模块内部可见(通过Opaque类型对外隐藏)
  defstruct [:id, :name]

  # 对外公开的唯一构造函数,外部必须通过此函数创建实例
  @spec new(integer(), String.t()) :: t()
  def new(id, name), do: %__MODULE__{id: id, name: name}

  # 对外公开的读取函数,外部只能通过这些方法获取内部值
  @spec get_id(t()) :: integer()
  def get_id(%__MODULE__{id: id}), do: id

  @spec get_name(t()) :: String.t()
  def get_name(%__MODULE__{name: name}), do: name

  # 对外公开的修改函数,外部只能通过这些方法更新实例
  @spec update_name(t(), String.t()) :: t()
  def update_name(%__MODULE__{} = user, new_name), do: %{user | name: new_name}
end

效果说明

  1. 阻止直接构造:如果外部模块尝试直接写%User{id: 1, name: "Alice"},Dialyzer会抛出类型不匹配错误,因为该构造不符合User.t()的Opaque类型定义。
  2. 阻止直接访问字段:外部模块若尝试直接读取user.id或user.name,Dialyzer同样会报错,必须通过模块提供的get_id/1、get_name/1等方法获取数据。
  3. 受控修改:外部模块无法直接修改结构体字段,只能通过update_name/2这类公开方法完成,保证数据修改的可控性。

补充说明

Elixir本身没有真正的私有结构体,但通过上述方式,结合Dialyzer的静态检查,可以实现和Elm Opaque类型几乎一致的效果——外部模块完全无法感知结构体的内部结构,只能通过定义模块提供的接口进行交互。

内容的提问来源于stack exchange,提问作者doobdargent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 13:52:15