Skip to content

Commit d3d5e39

Browse files
committed
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
1 parent e76ce52 commit d3d5e39

File tree

1 file changed

+47
-35
lines changed

1 file changed

+47
-35
lines changed

getting_started/15.markdown

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ guide: 15
88

99
{% include toc.html %}
1010

11-
In earlier chapters, we learned about maps:
11+
In [chapter 7](/getting_started/7.html) we learned about maps:
1212

1313
```iex
1414
iex> map = %{a: 1, b: 2}
@@ -19,92 +19,104 @@ iex> %{map | a: 3}
1919
%{a: 3, b: 2}
2020
```
2121

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.
2323

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:
2527

2628
```iex
2729
iex> defmodule User do
28-
...> defstruct name: "john", age: 27
30+
...> defstruct name: "John", age: 27
2931
...> end
30-
{:module, User,
31-
<<70, 79, 82, ...>>, {:__struct__, 0}}
3232
```
3333

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:
3539

3640
```iex
3741
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"}
4345
```
4446

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:
4648

4749
```iex
4850
iex> %User{oops: :field}
4951
** (CompileError) iex:3: unknown key :oops for struct User
5052
```
5153

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:
5357

5458
```iex
5559
iex> john = %User{}
56-
%User{age: 27, name: "john"}
60+
%User{age: 27, name: "John"}
5761
iex> john.name
58-
"john"
59-
iex> meg = %{john | name: "meg"}
62+
"John"
63+
iex> meg = %{john | name: "Meg"}
6064
%User{age: 27, name: "meg"}
6165
iex> %{meg | oops: :field}
6266
** (ArgumentError) argument error
6367
```
6468

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.
6670

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.
6872

6973
```iex
7074
iex> %User{name: name} = john
71-
%User{age: 27, name: "john"}
75+
%User{age: 27, name: "John"}
7276
iex> name
73-
"john"
77+
"John"
7478
iex> %User{} = %{}
7579
** (MatchError) no match of right hand side value: %{}
7680
```
7781

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:
7985

8086
```iex
87+
iex> is_map(john)
88+
true
8189
iex> john.__struct__
8290
User
8391
```
8492

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:
8694

8795
```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"}
92102
```
93103

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:
95105

96106
```iex
97107
iex> Dict.get(%User{}, :name)
98-
** (ArgumentError) unsupported dict: %User{name: "john", age: 27}
108+
** (UndefinedFunctionError) undefined function: User.fetch/2
99109
```
100110

101-
Since structs are just maps, they will work with the `Map` module:
111+
However, since structs are just maps, they work with the functions from the `Map` module:
102112

103113
```iex
104-
iex> Map.put(%User{}, :name, "kurt")
105-
%User{age: 27, name: "kurt"}
106-
iex> Map.merge(%User{age: 27}, %User{name: "takashi"})
107-
%User{age: 27, name: "takashi"}
114+
iex> kurt = Map.put(%User{}, :name, "Kurt")
115+
%User{age: 27, name: "Kurt"}
116+
iex> Map.merge(kurt, %User{name: "Takashi"})
117+
%User{age: 27, name: "Takashi"}
118+
iex> Map.keys(john)
119+
[:__struct__, :age, :name]
108120
```
109121

110-
We will cover how structs interacts with protocols in the next chapter.
122+
We will cover how structs interact with protocols in the next chapter.

0 commit comments

Comments
 (0)