SoFunction
Updated on 2025-03-04

Detailed explanation of Record in Erlang

There are only two mixed data types inside Erlang: List and Tuple, and neither of them supports naming access, so it is impossible to create an associative array like PHP, Ruby or Python (Hash in Ruby) without additional libraries.

In Ruby I can do this:

Copy the codeThe code is as follows:

server_opts = {:port => 8080, :ip => '127.0.0.1', :max_connections => 10} 

This expression is not supported at Erlang's grammar level

To avoid this limitation, the Erlang virtual machine provides a pseudo-data type called Record
Record supports named access, and later we will see why we call it "pseudo" data type

Define Record

Record is more similar to struct in C, rather than associative arrays, which must define the content from the beginning and only hold the data
Here is an example of the Record of a server's connection options:

Copy the codeThe code is as follows:

-module(my_server). 
 
-record(server_opts, 
  {port, 
  ip="127.0.0.1", 
  max_connections=10}). 
 
% The rest of your code goes here. 

Record uses the -record directive to declare. The first parameter is the name of the Record, and the second parameter is a Tuple. Tuple contains the field and default values ​​in the Record.
Here we define the server_opts record, which has three fields: port, IP and maximum number of connections.
There is no default port, the default value of ip is "127.0.0.1", and the default value of max_connections is 10

Create Record

Record is created by using # symbols. Here is the legal way to create an instance of the Record server_opts:

Copy the codeThe code is as follows:

Opts1 = #server_opts{port=80}. 

This code creates a server_opts Record with port set to 80 and other fields use default values
Opts2 = #server_opts{port=80, ip="192.168.0.1"}. 

This code creates a server_opts Record, but the ip is set to "192.168.0.1"

In short, when creating a record you can include any field, the omitted field will use the default value

Visit Record

The way of accessing Record is clumsy, if I want to access this field, I can do this:

Copy the codeThe code is as follows:

Opts = #server_opts{port=80, ip="192.168.0.1"}, 
Opts#server_opts.port 

Every time you want to access a record you must include the name of the record, why do you need this?
Because Record is not a real internal data type, it is just a little trick of the compiler.

Internally, Record is Tuple, as follows:

Copy the codeThe code is as follows:

{server_opts, 80, "127.0.0.1", 10} 

The compiler maps the Record name into Tuple
The Erlang virtual machine records the definition of Record, and the compiler translates all Record logic into Tuple logic
Therefore, there is no Record type at all, so every time you access a Record you have to tell Erlang which Record we are using (for the compiler is very unhappy, programmers are very upset)

Update Record

Updating Record is similar to creating Record:

Copy the codeThe code is as follows:

Opts = #server_opts{port=80, ip="192.168.0.1"}, 
NewOpts = Opts#server_opts{port=7000}. 

Here we first create a server_opts Record

NewOpts = Opts#{port=7000} Creates a copy of Opts and specifies port to 7000 and binds to NewOpts

Match Record and Guard statements

If you don't talk about pattern matching, it won't be considered Erlang
Let's take a look at an example:

Copy the codeThe code is as follows:

handle(Opts=#server_opts{port=8000}) -> 
  % do special port 8080 stuff 
handle(Opts=#server_opts{} -> 
  % default stuff 

The Guard statement is similar to the above, for example, binding a port smaller than 1024 usually requires root permissions, so we can do this:
Copy the codeThe code is as follows:

handle(Opts) when Opts#server_opts.port <= 1024 -> 
  % requires root access 
handle(Opts=#server_opts{}) -> 
  % Doesn't require root access 

Using Record

During my limited time using Erlang, I found that Record is mainly used in two scenarios
First, Record is used to save state, especially when using gen_server's behaviour
Since Erlang cannot maintain the state globally, the state must be passed before the method
Record can then be used to save configuration options, which can be considered a subset of the first point
Despite this, Record has some limitations, the most obvious thing is that it cannot add and delete fields at runtime. This is like C's struct, the structure of Record must be predefined.
If you want to add and remove fields at runtime, or you can determine which fields are available at runtime, you should usedictInstead of Record