@@ -843,6 +843,56 @@ defmodule Macro do
843
843
`escape/2` is used to escape *values* (either directly passed or variable
844
844
bound), while `quote/2` produces syntax trees for
845
845
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
+
846
896
"""
847
897
@ spec escape ( term , escape_opts ) :: t ( )
848
898
def escape ( expr , opts \\ [ ] ) do
0 commit comments