`nfields` vs `fieldcount`

What are the differences between nfields and fieldcount? Is

nfields(x) == fieldcount(typeof(x))

?

1 Like

Where you are working directly with structure of a type itself, fieldcount is appropriate and has related functions e.g. fieldnames fieldtypes. When you need to count of all the fields, including internal fields generally not intended for use outside of programming essential Julia features, nfields works given a Type or given a typed variable.

nfields(::Type{T}) where {T} works with many but not all types T (e.g. Singletons, empty structs).
nfields(x::T) where {T} works given a realization of type T

fieldcount(::Type{T}) where {T} works given a type T
fieldcount(x::T) where {T} throws an error.

Extending Base.fieldcount(x::T) where {T} = fieldcount(T) and then just using fieldcount is robust and efficient.

julia> two = 2; half = 1//2;
julia> struct OneField{T}
              x::T
         end;
julia> onefield = OneField("one")

julia> nfields(two) == nfields(Int) ==  0;
julia> nfields(half) == nfields(Rational) == 2; 
julia> nfields(onefield) == nfields(OneField) == 1

julia> nfields(Int) == fieldcount(Int) == 0;
julia> nfields(Rational) == fieldcount(Rational);
julia> nfields(OneField) == fieldcount(OneField);

# fieldcount(x) where x is two or half or onefield throws and error
3 Likes

Also see this discussion:

https://github.com/JuliaLang/julia/issues/29977

This is incorrect. Look at the example mentioned in the issue @Tamas_Papp linked above:

julia> struct Foo end

julia> nfields(Foo)
21

Another difference is that inference sometimes works only with nfields. (Maybe that’s because nfields is a built-in function rather than a generic function?)

julia> f(x) = Val(nfields(x))
f (generic function with 1 method)

julia> g(::T) where {T} = Val(fieldcount(T))
g (generic function with 1 method)

julia> @inferred f((a=1,b=2,c=3))
Val{3}()

julia> @inferred g((a=1,b=2,c=3))
ERROR: return type Val{3} does not match inferred return type Val{_A} where _A
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] top-level scope at REPL[53]:1
1 Like

Thanks for the additional insight @Tamas_Papp @tkf. I have revised my answer therewith.

2 Likes