Skip to content

Commit 5eaecd6

Browse files
committed
Document __escape__/1 in Macro.escape/2 docs
1 parent d1536b4 commit 5eaecd6

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

lib/elixir/lib/macro.ex

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,56 @@ defmodule Macro do
843843
`escape/2` is used to escape *values* (either directly passed or variable
844844
bound), while `quote/2` produces syntax trees for
845845
expressions.
846+
847+
## Customizing struct escapes
848+
849+
By default, structs are escaped to generate the AST for their internal representation.
850+
851+
This approach does not work if the internal representation contains references (like
852+
`Regex` structs), because references can't be escaped.
853+
This is a common issue when working with NIFs.
854+
855+
Let's imagine we have the following struct:
856+
857+
defmodule WrapperStruct do
858+
defstruct [:ref]
859+
860+
def new(...), do: %WrapperStruct{ref: ...}
861+
862+
# efficiently dump to / load from binaries
863+
def dump_to_binary(%WrapperStruct{ref: ref}), do: ...
864+
def load_from_binary(binary), do: %WrapperStruct{ref: ...}
865+
end
866+
867+
Such a struct could not be used in module attributes or escaped with `Macro.escape/2`:
868+
869+
defmodule Foo do
870+
@my_struct WrapperStruct.new(...)
871+
def my_struct, do: @my_struct
872+
end
873+
874+
** (ArgumentError) cannot inject attribute @my_struct into function/macro because cannot escape #Reference<...>
875+
876+
To address this, structs can re-define how they should be escaped by defining a custom
877+
`__escape__/1` function which returns the AST. In our example:
878+
879+
defmodule WrapperStruct do
880+
# ...
881+
882+
def __escape__(struct) do
883+
# dump to a binary representation at compile-time
884+
binary = dump_to_binary(struct)
885+
quote do
886+
# load from the binary representation at runtime
887+
WrapperStruct.load_from_binary(unquote(Macro.escape(binary)))
888+
end
889+
end
890+
end
891+
892+
Now, our example above will be expanded as:
893+
894+
def my_struct, do: WrapperStruct.load_from_binary(<<...>>)
895+
846896
"""
847897
@spec escape(term, escape_opts) :: t()
848898
def escape(expr, opts \\ []) do

0 commit comments

Comments
 (0)