You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Update prose and code in the chapter about structs
- Updated some code samples to be more explanatory
- Updated some code samples to bring them up to date with Elixir 1.0
- Rephrased some sentences here and there
- Moved some stuff around so that the chapter flows more naturally
- Added section titles so that the chapter is more structured
Copy file name to clipboardExpand all lines: getting_started/15.markdown
+47-35Lines changed: 47 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ guide: 15
8
8
9
9
{% include toc.html %}
10
10
11
-
In earlier chapters, we learned about maps:
11
+
In [chapter 7](/getting_started/7.html) we learned about maps:
12
12
13
13
```iex
14
14
iex> map = %{a: 1, b: 2}
@@ -19,92 +19,104 @@ iex> %{map | a: 3}
19
19
%{a: 3, b: 2}
20
20
```
21
21
22
-
Structs are extensions on top of maps that bring default values, compile-time guarantees and polymorphism into Elixir.
22
+
Structs are extensions built on top of maps that provide compile-time checks and default values.
23
23
24
-
To define a struct, we just need to call `defstruct/1` inside a module:
24
+
## 15.1 Defining structs
25
+
26
+
To define a struct, the `defstruct` construct is used:
25
27
26
28
```iex
27
29
iex> defmodule User do
28
-
...> defstruct name: "john", age: 27
30
+
...> defstruct name: "John", age: 27
29
31
...> end
30
-
{:module, User,
31
-
<<70, 79, 82, ...>>, {:__struct__, 0}}
32
32
```
33
33
34
-
We can now create "instances" of this struct by using the `%User{}` syntax:
34
+
The keyword list used with `defstruct` defines what fields the struct will have along with their default values.
35
+
36
+
Structs take the name of the module they're defined in. In the example above, we defined a struct named `User`.
37
+
38
+
We can now create `User` structs by using a syntax similar to the one used to create maps:
35
39
36
40
```iex
37
41
iex> %User{}
38
-
%User{age: 27, name: "john"}
39
-
iex> %User{name: "meg"}
40
-
%User{age: 27, name: "meg"}
41
-
iex> is_map(%User{})
42
-
true
42
+
%User{age: 27, name: "John"}
43
+
iex> %User{name: "Meg"}
44
+
%User{age: 27, name: "Meg"}
43
45
```
44
46
45
-
Structs give compile-time guarantees that the provided fields exist in the struct:
47
+
Structs provide *compile-time* guarantees that only the fields (and *all* of them) defined through `defstruct` will be allowed to exist in a struct:
46
48
47
49
```iex
48
50
iex> %User{oops: :field}
49
51
** (CompileError) iex:3: unknown key :oops for struct User
50
52
```
51
53
52
-
When discussing maps, we demonstrated how we can access and update existing fields of a map. The same applies to structs:
54
+
## 15.2 Accessing and updating structs
55
+
56
+
When we discussed maps, we showed how we can access and update the fields of a map. The same techniques (and the same syntax) apply to structs as well:
53
57
54
58
```iex
55
59
iex> john = %User{}
56
-
%User{age: 27, name: "john"}
60
+
%User{age: 27, name: "John"}
57
61
iex> john.name
58
-
"john"
59
-
iex> meg = %{john | name: "meg"}
62
+
"John"
63
+
iex> meg = %{john | name: "Meg"}
60
64
%User{age: 27, name: "meg"}
61
65
iex> %{meg | oops: :field}
62
66
** (ArgumentError) argument error
63
67
```
64
68
65
-
By using the update syntax, the VM is aware no new keys will be added to the map/struct, allowing the maps to share their structure in memory. In the example above, both `john` and `meg` share the same key structure in memory.
69
+
When using the update syntax (`|`), the VM is aware that no new keys will be added to the struct, allowing the maps underneath to share their structure in memory. In the example above, both `john` and `meg` share the same key structure in memory.
66
70
67
-
Structs can also be used in pattern matching and they guarantee the structs are of the same type:
71
+
Structs can also be used in pattern matching, both for matching on the value of specific keys as well as for ensuring that the matching value is a struct of the same type as the matched value.
68
72
69
73
```iex
70
74
iex> %User{name: name} = john
71
-
%User{age: 27, name: "john"}
75
+
%User{age: 27, name: "John"}
72
76
iex> name
73
-
"john"
77
+
"John"
74
78
iex> %User{} = %{}
75
79
** (MatchError) no match of right hand side value: %{}
76
80
```
77
81
78
-
Matching works because structs store a field named `__struct__` inside the map:
82
+
## 15.3 Structs are just bare maps underneath
83
+
84
+
In the example above, pattern matching works because underneath structs are just bare maps with a fixed set of fields. As maps, structs store a "special" field named `__struct__` that holds the name of the struct:
79
85
80
86
```iex
87
+
iex> is_map(john)
88
+
true
81
89
iex> john.__struct__
82
90
User
83
91
```
84
92
85
-
Overall, a struct is just a bare map with default fields. Notice we say it is a bare map because none of the protocols implemented for maps are available for structs. For example, you can't enumerate nor access a struct:
93
+
Notice that we referred to structs as **bare** maps because none of the protocols implemented for maps are available for structs. For example, you can't enumerate nor access a struct:
86
94
87
95
```iex
88
-
iex> user = %User{}
89
-
%User{age: 27, name: "john"}
90
-
iex> user[:name]
91
-
** (Protocol.UndefinedError) protocol Access not implemented for %User{age: 27, name: "john"}
96
+
iex> john = %User{}
97
+
%User{age: 27, name: "John"}
98
+
iex> john[:name]
99
+
** (Protocol.UndefinedError) protocol Access not implemented for %User{age: 27, name: "John"}
100
+
iex> Enum.each john, fn({field, value}) -> IO.puts(value) end
101
+
** (Protocol.UndefinedError) protocol Enumerable not implemented for %User{age: 27, name: "John"}
92
102
```
93
103
94
-
A struct also is not a dictionary and therefore can't be used with the `Dict` module:
104
+
A struct also is not a dictionary and therefore can't be used with the functions from the `Dict` module:
0 commit comments