查看源代码 Record (Elixir v1.16.2)
用于处理、定义和导入记录的模块。
记录仅仅是元组,其中第一个元素是原子
iex> Record.is_record({User, "john", 27})
true
该模块提供了在编译时处理记录的便利性,其中编译时字段名称用于操作元组,在元组的紧凑结构之上提供快速操作。
在 Elixir 中,记录主要用于两种情况
- 处理简短的内部数据
- 与 Erlang 记录交互
宏 defrecord/3
和 defrecordp/3
可用于创建记录,而 extract/2
和 extract_all/1
可用于从 Erlang 文件中提取记录。
类型
可以使用 record/2
宏为元组定义类型(仅在类型规范中可用)。该宏将扩展为元组,如下例所示
defmodule MyModule do
require Record
Record.defrecord(:user, name: "john", age: 25)
@type user :: record(:user, name: String.t(), age: integer)
# expands to: "@type user :: {:user, String.t(), integer}"
end
反射
可以通过读取 @__records__
模块属性来检索模块中所有记录(如果有)。它返回一个包含记录类型、名称、标签和字段的映射列表。该属性仅在模块定义内可用。
摘要
守卫
函数
定义一组宏来创建、访问和模式匹配记录。
生成的宏的名称将是 name
(它必须是原子)。tag
也是原子,用作记录的“标签”(即记录元组的第一个元素);默认情况下(如果为 nil
),它与 name
相同。kv
是一个关键字列表,其中包含新记录的 name: default_value
字段。
将生成以下宏
name/0
用于创建具有所有字段默认值的新记录name/1
用于创建具有给定字段和值的新记录,用于获取给定字段在记录中的零基索引或将给定记录转换为关键字列表name/2
用于使用给定字段和值更新现有记录或访问给定记录中的给定字段
所有这些宏都是公共宏(由 defmacro
定义)。
有关如何使用这些宏的示例,请参阅“示例”部分。
示例
defmodule User do
require Record
Record.defrecord(:user, name: "meg", age: "25")
end
在上面的示例中,将定义一组名为 user
但具有不同元数的宏来操作底层记录。
# Import the module to make the user macros locally available
import User
# To create records
record = user() #=> {:user, "meg", 25}
record = user(age: 26) #=> {:user, "meg", 26}
# To get a field from the record
user(record, :name) #=> "meg"
# To update the record
user(record, age: 26) #=> {:user, "meg", 26}
# To get the zero-based index of the field in record tuple
# (index 0 is occupied by the record "tag")
user(:name) #=> 1
# Convert a record to a keyword list
user(record) #=> [name: "meg", age: 26]
生成的宏也可以用于对记录进行模式匹配并在匹配过程中绑定变量
record = user() #=> {:user, "meg", 25}
user(name: name) = record
name #=> "meg"
默认情况下,Elixir 使用记录名称作为元组的第一个元素(“标签”。但是,在定义记录时可以指定不同的标签,如下例所示,在该例中我们使用 Customer
作为 defrecord/3
的第二个参数
defmodule User do
require Record
Record.defrecord(:user, Customer, name: nil)
end
require User
User.user() #=> {Customer, nil}
使用匿名函数在值中定义提取的记录
如果记录在默认值中定义了匿名函数,则会引发 ArgumentError
。这在从使用匿名函数作为默认值的 Erlang 库中提取记录后定义记录时可能会无意中发生。
Record.defrecord(:my_rec, Record.extract(...))
** (ArgumentError) invalid value for record field fun_field,
cannot escape #Function<12.90072148/2 in :erl_eval.expr/5>.
要解决此错误,请使用自己的 &M.f/a 函数重新定义字段,如下所示
defmodule MyRec do
require Record
Record.defrecord(:my_rec, Record.extract(...) |> Keyword.merge(fun_field: &__MODULE__.foo/2))
def foo(bar, baz), do: IO.inspect({bar, baz})
end
与 defrecord/3
相同,但生成私有宏。
从 Erlang 文件中提取记录信息。
返回一个包含字段作为元组列表的引用表达式。
name
(即提取记录的名称)预期是编译时的原子。
选项
此函数需要以下选项之一,这些选项是互斥的(即,同一调用中只能使用一个选项)
:from
- (表示文件路径的二进制文件)包含要提取的记录定义的 Erlang 文件的路径;使用此选项,此函数使用与 Erlang 模块中使用的-include
属性相同的路径查找。:from_lib
- (表示文件路径的二进制文件)包含要提取的记录定义的 Erlang 文件的路径;使用此选项,此函数使用与 Erlang 模块中使用的-include_lib
属性相同的路径查找。
此外,它还接受以下可选的、非互斥选项
:includes
- (作为二进制文件的目录列表)如果要提取的记录依赖于相对包含,则此选项允许开发人员指定这些相对包含所在的目录。:macros
- (宏名称和值的关键字列表)如果要提取的记录依赖于宏的值,则此选项允许设置这些宏的值。
这些选项预期在编译时是文字(包括二进制值)。
示例
iex> Record.extract(:file_info, from_lib: "kernel/include/file.hrl")
[
size: :undefined,
type: :undefined,
access: :undefined,
atime: :undefined,
mtime: :undefined,
ctime: :undefined,
mode: :undefined,
links: :undefined,
major_device: :undefined,
minor_device: :undefined,
inode: :undefined,
uid: :undefined,
gid: :undefined
]
从 Erlang 文件中提取所有记录信息。
返回一个包含 {record_name, fields}
元组的关键字列表,其中 record_name
是提取的记录的名称,fields
是一个包含 {field, value}
元组的列表,表示该记录的字段。
选项
接受与 Record.extract/2
列出的相同选项。