I had quite some fun with this one. I think the recommended way is the following way via generated functions (you proposed the naive way):
to_named_tuple_naive(p) = (; (v=>getfield(p, v) for v in fieldnames(typeof(p)))...)
gentup(struct_T) = NamedTuple{( fieldnames(struct_T)...,), Tuple{(fieldtype(struct_T,i) for i=1:fieldcount(struct_T))...}}
@generated function to_named_tuple_generated(x)
nt = Expr(:quote, gentup(x))
tup = Expr(:tuple)
for i=1:fieldcount(x)
push!(tup.args, :(getfield(x, $i)) )
end
return :($nt($tup))
end
So, let’s compare!
using BenchmarkTools
struct Person
age::Float64
name::String
end
struct point
x::Float64
y::Float64
end
pers=Person(34, "Jane")
pt = point(3.4, 4.5)
@show to_named_tuple_naive(pers);
#to_named_tuple_naive(pers) = (age = 34.0, name = "Jane")
@show to_named_tuple_generated(pers);
#to_named_tuple_generated(pers) = (age = 34.0, name = "Jane")
@btime to_named_tuple_naive($pers);
#4.471 ��s (25 allocations: 1.27 KiB)
@btime to_named_tuple_naive($pt);
#2.772 ��s (21 allocations: 1.17 KiB)
@btime to_named_tuple_generated($pers);
#8.554 ns (1 allocation: 32 bytes)
@btime to_named_tuple_generated($pt);
#2.208 ns (0 allocations: 0 bytes)
PS.
versioninfo()
#Julia Version 0.7.0-DEV.3943
#Commit dcc39f4d8d* (2018-02-09 22:47 UTC)