Erlang记录 #

一、记录概述 #

记录(Record)是元组的语法糖,提供了一种命名元组元素的方式,使代码更易读。

1.1 记录的特点 #

  • 编译时转换为元组
  • 提供字段名称访问
  • 支持默认值
  • 类型安全(编译时检查)

二、记录定义 #

2.1 基本定义 #

erlang
-module(record_def).
-export([demo/0]).

-record(person, {name, age, city}).

demo() ->
    Person = #person{name = "Alice", age = 30, city = "Beijing"},
    io:format("Person: ~p~n", [Person]),
    ok.

2.2 默认值 #

erlang
-module(record_default).
-export([demo/0]).

-record(user, {
    name,
    age = 0,
    email = undefined,
    active = true
}).

demo() ->
    U1 = #user{name = "Alice"},
    U2 = #user{name = "Bob", age = 25},
    U3 = #user{name = "Charlie", age = 30, email = "charlie@example.com"},
    
    io:format("U1: ~p~n", [U1]),
    io:format("U2: ~p~n", [U2]),
    io:format("U3: ~p~n", [U3]),
    ok.

2.3 记录本质 #

erlang
-module(record_essence).
-export([demo/0]).

-record(point, {x, y}).

demo() ->
    Point = #point{x = 10, y = 20},
    
    io:format("Record: ~p~n", [Point]),
    io:format("As tuple: ~p~n", [{point, 10, 20}]),
    io:format("Equal: ~p~n", [Point =:= {point, 10, 20}]),
    ok.

记录 #point{x = 10, y = 20} 编译后变成 {point, 10, 20}

三、记录操作 #

3.1 创建记录 #

erlang
-module(record_create).
-export([demo/0]).

-record(book, {title, author, year = 2024}).

demo() ->
    B1 = #book{title = "Erlang Programming"},
    B2 = #book{title = "Learn Erlang", author = "John Doe"},
    B3 = #book{title = "OTP Guide", author = "Jane Doe", year = 2023},
    
    io:format("B1: ~p~n", [B1]),
    io:format("B2: ~p~n", [B2]),
    io:format("B3: ~p~n", [B3]),
    ok.

3.2 访问字段 #

erlang
-module(record_access).
-export([demo/0]).

-record(person, {name, age, city}).

demo() ->
    Person = #person{name = "Alice", age = 30, city = "Beijing"},
    
    Name = Person#person.name,
    Age = Person#person.age,
    City = Person#person.city,
    
    io:format("Name: ~p~n", [Name]),
    io:format("Age: ~p~n", [Age]),
    io:format("City: ~p~n", [City]),
    ok.

3.3 更新字段 #

erlang
-module(record_update).
-export([demo/0]).

-record(person, {name, age, city}).

demo() ->
    P1 = #person{name = "Alice", age = 30, city = "Beijing"},
    
    P2 = P1#person{age = 31},
    P3 = P2#person{city = "Shanghai"},
    
    io:format("P1: ~p~n", [P1]),
    io:format("P2: ~p~n", [P2]),
    io:format("P3: ~p~n", [P3]),
    ok.

四、记录模式匹配 #

4.1 基本匹配 #

erlang
-module(record_match).
-export([demo/0]).

-record(person, {name, age, city}).

demo() ->
    Person = #person{name = "Alice", age = 30, city = "Beijing"},
    
    #person{name = Name, age = Age} = Person,
    
    io:format("Name: ~p~n", [Name]),
    io:format("Age: ~p~n", [Age]),
    ok.

4.2 函数参数匹配 #

erlang
-module(record_func_match).
-export([greet/1, is_adult/1]).

-record(person, {name, age}).

greet(#person{name = Name, age = Age}) ->
    io:format("Hello ~s, you are ~p years old~n", [Name, Age]).

is_adult(#person{age = Age}) when Age >= 18 -> true;
is_adult(_) -> false.

4.3 特定值匹配 #

erlang
-module(record_value_match).
-export([classify/1]).

-record(user, {name, role = guest}).

classify(#user{role = admin}) -> administrator;
classify(#user{role = moderator}) -> moderator;
classify(#user{role = guest}) -> guest.

4.4 守卫中使用 #

erlang
-module(record_guard).
-export([can_vote/1]).

-record(citizen, {name, age, country}).

can_vote(#citizen{age = Age, country = Country}) 
    when Age >= 18, Country =:= "USA" -> true;
can_vote(#citizen{age = Age, country = Country}) 
    when Age >= 18, Country =:= "China" -> true;
can_vote(_) -> false.

五、记录与函数 #

5.1 工厂函数 #

erlang
-module(record_factory).
-export([new_person/2, new_person/3]).

-record(person, {name, age = 0, city = "Unknown"}).

new_person(Name, Age) ->
    #person{name = Name, age = Age}.

new_person(Name, Age, City) ->
    #person{name = Name, age = Age, city = City}.

5.2 访问器函数 #

erlang
-module(record_accessor).
-export([get_name/1, get_age/1, set_age/2]).

-record(person, {name, age, city}).

get_name(#person{name = Name}) -> Name.
get_age(#person{age = Age}) -> Age.
set_age(Age, Person) -> Person#person{age = Age}.

5.3 转换函数 #

erlang
-module(record_convert).
-export([to_list/1, from_list/1, to_map/1, from_map/1]).

-record(person, {name, age, city}).

to_list(#person{name = N, age = A, city = C}) ->
    [{name, N}, {age, A}, {city, C}].

from_list([{name, N}, {age, A}, {city, C}]) ->
    #person{name = N, age = A, city = C}.

to_map(#person{name = N, age = A, city = C}) ->
    #{name => N, age => A, city => C}.

from_map(#{name := N, age := A, city := C}) ->
    #person{name = N, age = A, city = C}.

六、记录包含 #

6.1 包含记录定义 #

创建 records.hrl 文件:

erlang
-record(person, {name, age, city}).
-record(address, {street, city, country}).

在模块中使用:

erlang
-module(use_include).
-include("records.hrl").
-export([demo/0]).

demo() ->
    Person = #person{name = "Alice", age = 30},
    io:format("Person: ~p~n", [Person]),
    ok.

6.2 从其他模块包含 #

erlang
-module(cross_module).
-include_lib("kernel/include/file.hrl").
-export([file_info/1]).

file_info(Path) ->
    {ok, Info} = file:read_file_info(Path),
    #file_info{size = Size, type = Type} = Info,
    io:format("Size: ~p, Type: ~p~n", [Size, Type]).

七、记录最佳实践 #

7.1 命名规范 #

erlang
-module(record_naming).
-export([demo/0]).

-record(user_state, {id, name, status}).
-record(http_request, {method, url, headers, body}).
-record(db_config, {host, port, database, username, password}).

demo() -> ok.

规范:

  • 记录名使用下划线分隔的小写单词
  • 字段名使用下划线分隔的小写单词
  • 使用有意义的名称

7.2 使用类型规范 #

erlang
-module(record_types).
-export([new_user/2]).

-type name() :: binary() | string().
-type age() :: non_neg_integer().

-record(user, {
    name :: name(),
    age :: age(),
    email :: binary() | undefined
}).

-type user() :: #user{}.

-spec new_user(name(), age()) -> user().
new_user(Name, Age) ->
    #user{name = Name, age = Age, email = undefined}.

7.3 使用默认值 #

erlang
-module(record_best_default).
-export([new/0, new/1]).

-record(config, {
    host = "localhost" :: string(),
    port = 8080 :: non_neg_integer(),
    timeout = 5000 :: non_neg_integer(),
    debug = false :: boolean()
}).

new() -> #config{}.

new(Overrides) when is_list(Overrides) ->
    lists:foldl(
        fun({host, V}, C) -> C#config{host = V};
           ({port, V}, C) -> C#config{port = V};
           ({timeout, V}, C) -> C#config{timeout = V};
           ({debug, V}, C) -> C#config{debug = V}
        end,
        #config{},
        Overrides
    ).

八、记录与元组比较 #

8.1 使用记录 #

erlang
-module(compare_record).
-export([demo/0]).

-record(person, {name, age, city}).

demo() ->
    Person = #person{name = "Alice", age = 30, city = "Beijing"},
    Name = Person#person.name,
    io:format("Name: ~p~n", [Name]),
    ok.

8.2 使用元组 #

erlang
-module(compare_tuple).
-export([demo/0]).

demo() ->
    Person = {person, "Alice", 30, "Beijing"},
    {person, Name, _, _} = Person,
    io:format("Name: ~p~n", [Name]),
    ok.

8.3 比较表 #

特性 记录 元组
字段名 支持 不支持
默认值 支持 不支持
编译时检查
代码可读性
运行时开销

九、实际应用 #

9.1 状态机 #

erlang
-module(state_machine_record).
-export([new/0, connect/1, send_data/2, disconnect/1]).

-record(state, {
    status = disconnected :: disconnected | connected,
    buffer = [] :: list(),
    count = 0 :: non_neg_integer()
}).

new() -> #state{}.

connect(State) ->
    State#state{status = connected}.

send_data(Data, State = #state{buffer = Buf, count = Cnt}) ->
    State#state{buffer = [Data | Buf], count = Cnt + 1}.

disconnect(State) ->
    State#state{status = disconnected, buffer = [], count = 0}.

9.2 配置管理 #

erlang
-module(config_record).
-export([default/0, validate/1]).

-record(app_config, {
    db_host :: string(),
    db_port :: non_neg_integer(),
    db_name :: string(),
    api_key :: string(),
    log_level :: debug | info | warning | error
}).

default() ->
    #app_config{
        db_host = "localhost",
        db_port = 5432,
        db_name = "myapp",
        api_key = "",
        log_level = info
    }.

validate(#app_config{db_host = ""}) -> {error, missing_db_host};
validate(#app_config{db_port = Port}) when Port < 1 orelse Port > 65535 -> 
    {error, invalid_port};
validate(#app_config{api_key = ""}) -> {error, missing_api_key};
validate(_) -> ok.

9.3 HTTP请求处理 #

erlang
-module(http_record).
-export([new_request/2, add_header/3, get_header/2]).

-record(http_request, {
    method :: get | post | put | delete,
    url :: string(),
    headers = [] :: [{string(), string()}],
    body = <<>> :: binary()
}).

new_request(Method, Url) ->
    #http_request{method = Method, url = Url}.

add_header(Name, Value, Req) ->
    Req#http_request{headers = [{Name, Value} | Req#http_request.headers]}.

get_header(Name, #http_request{headers = Headers}) ->
    proplists:get_value(Name, Headers).

十、记录的限制 #

10.1 编译时依赖 #

erlang
-module(record_limitation).
-export([demo/0]).

-record(person, {name, age}).

demo() ->
    ok.

记录定义必须在编译时可用,无法在运行时动态创建。

10.2 模块间共享 #

需要在 .hrl 文件中定义,并在每个使用模块中包含:

erlang
-include("person.hrl").

10.3 热升级问题 #

修改记录定义需要重新编译所有使用该记录的模块。

十一、总结 #

本章学习了:

  • 记录的定义和本质
  • 记录的创建、访问、更新
  • 记录模式匹配
  • 记录与函数
  • 记录包含
  • 最佳实践
  • 记录与元组比较
  • 实际应用
  • 记录的限制

准备好学习模式匹配了吗?让我们进入下一章。

最后更新:2026-03-27