Field names and values of a struct defined as NTuple

Hi! Let

struct ex1
    name::String
    age::Int
end

I understand that

(1) the statements above define a new type of record containing a string and a number
(2) an instance of that type can be created as

a = ex1("John", 34)

(3) the field names of ex1 can be listed with the function fieldnames
(4) the values of those fields can be retrieved as ex1.name or ex1.age

However, if another structure is defined as

struct ex2
    data::NTuple{4, UInt8}
end

then

julia> b = ex2(0, 0, 0, 0)
ERROR: MethodError: no method matching ex2(::Int64, ::Int64, ::Int64, ::Int64)
Closest candidates are:
  ex2(::Any) at REPL[1]:2
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

julia> b = ex2(0::UInt8, 0::UInt8, 0::UInt8, 0::UInt8)
ERROR: TypeError: in typeassert, expected UInt8, got a value of type Int64
Stacktrace:
 [1] top-level scope
   @ REPL[4]:1

Question 1: How do I define an instance of that ex2 data type with all values set to 0 as a UInt8 (or any other value, for that matter)? and why are those instantiation syntaxes problematic?

and

julia> fieldnames(ex2)
(:data,)

Question 2: How can I access individual values of ex2 since they have no apparent individual names?

Thanks for any insight into these two questions.

The struct have one field, and if you check the constructor (with e.g. methods(ex2)) you see that it expects one argument – the tuple. You are passing 4 arguments. If you pass a tuple it works (and the Ints are converted to UInt8s, since the default constructor converts):

julia> struct ex2
           data::NTuple{4, UInt8}
       end

julia> a = ex2((1, 2, 3, 4)) # one argument, the tuple (1, 2, 3, 4)
ex2((0x01, 0x02, 0x03, 0x04))

You extract the tuple with .data, then use getindex:

julia> a.data[1]
0x01

julia> a.data[3]
0x03
4 Likes

Thanks a lot, @ fredrikekre: that’s very clear indeed!

Note 1:

Idiomatically, types in Julia should follow upper-CamelCase. So in your examples, the struct names would be Ex1 and Ex2.

Note 2:

If you want, you can overload Ex2’s constructor and Base.getindex to obtain the behavior you expected:

julia> struct Ex2 data::NTuple{4, UInt8} end

julia> Ex2(a, b, c, d) = Ex2((a, b, c, d));

julia> Base.getindex(e::Ex2, i) = e.data[i]

julia> a = Ex2(0, 0, 0, 0)
Ex2((0x00, 0x00, 0x00, 0x00))

julia> a[1]
0x00

Keep in mind though, that other behaviors you’d likely expect (such as iteration, length measurement, etc.) would also have to be implemented. There are some macros out there to help with defining these I believe, but I’m not familiar with them.

1 Like

@uniment: Thanks for your input. My question arose in the context of writing a Julia wrapper for a C function, so I’ll try to remain as close as possible to the original code, but I’m always happy to learn something new. So, thanks a lot for that!

1 Like