15 구조체 - Structs

앞선 장에서는 맵에 대해 배웠습니다:

In earlier chapters, we learned about maps:

iex> map = %{a: 1, b: 2}
%{a: 1, b: 2}
iex> map[:a]
1
iex> %{map | a: 3}
%{a: 3, b: 2}

구조체는 컴파일시간을 보장하고 기본값과 다형성을 가지는 맵의 확장판입니다.

Structs are extensions on top of maps that bring default values, compile-time guarantees and polymorphism into Elixir.

구조체를 정의하기 위해서는 모듈에서 단순히 defstruct/1를 호출하면 됩니다:

To define a struct, we just need to call defstruct/1 inside a module:

iex> defmodule User do
...>   defstruct name: "john", age: 27
...> end
{:module, User,
 <<70, 79, 82, ...>>, {:__struct__, 0}}

%User{}문법을 사용하여 구조체의 "instances"를 생설할수 있습니다:

We can now create "instances" of this struct by using the %User{} syntax:

iex> %User{}
%User{age: 27, name: "john"}
iex> %User{name: "meg"}
%User{age: 27, name: "meg"}
iex> is_map(%User{})
true

구조체는 컴파일시간에 제공된 필드만 사용할 수 있습니다:

Structs give compile-time guarantees that the provided fields exist in the struct:

iex> %User{oops: :field}
** (CompileError) iex:3: unknown key :oops for struct User

맵을 다뤘을때 어떻게 존재하는 필드에 대해 접근하고 업데이트하는지 보여줬습니다. 구조체도 동일하게 적용됩니다:

When discussing maps, we demonstrated how we can access and update existing fields of a map. The same applies to structs:

iex> john = %User{}
%User{age: 27, name: "john"}
iex> john.name
"john"
iex> meg = %{john | name: "meg"}
%User{age: 27, name: "meg"}
iex> %{meg | oops: :field}
** (ArgumentError) argument error

업데이트 문법을 사용함으로써 새로운 키는 맵/구조체에 추가되지 않을것이며 메모리에 있는 맵의 구조를 공유할 수 있습니다. 위의 예제에서 johnmeg는 메모리에 있는 동일 키 구조를 공유합니다.

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.

구조체는 동일 타입의 구조체를 패턴매칭할때도 사용할 수 있습니다:

Structs can also be used in pattern matching and they guarantee the structs are of the same type:

iex> %User{name: name} = john
%User{age: 27, name: "john"}
iex> name
"john"
iex> %User{} = %{}
** (MatchError) no match of right hand side value: %{}

맵안에 __struct__ 필드 네임을 저장하기 때문에 구조체를 매칭 시킬수 있습니다:

Matching works because structs store a field named __struct__ inside the map:

iex> john.__struct__
User

전반적으로 구조체는 단지 디폴트 필드를 가지는 bare 맵입니다. 맵에서는 사용할 수 있는 구현된 프로토콜이 없기 때문에 bare 맵이라고 할 수 있습니다. 예를들어, 구조체를 열거할수도 접근할수도 없습니다:

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:

iex> user = %User{}
%User{age: 27, name: "john"}
iex> user[:name]
** (Protocol.UndefinedError) protocol Access not implemented for %User{age: 27, name: "john"}

구조체는 딕셔너리도 아니기 때문에 Dict모듈로도 사용할 수 없습니다:

A struct also is not a dictionary and therefore can't be used with the Dict module:

iex> Dict.get(%User{}, :name)
** (ArgumentError) unsupported dict: %User{name: "john", age: 27}

구조체는 단순히 맵이기 때문에 Map모듈로만 사용할 수 있습니다:

Since structs are just maps, they will work with the Map module:

iex> Map.put(%User{}, :name, "kurt")
%User{age: 27, name: "kurt"}
iex> Map.merge(%User{age: 27}, %User{name: "takashi"})
%User{age: 27, name: "takashi"}

다음장에서는 구조체가 어떻게 프로토콜하고 상호작용하는지 살펴 볼 것입니다.

We will cover how structs interacts with protocols in the next chapter.