# Looping over struct fieldnames in a type-stable way

**URL:** <https://discourse.julialang.org/t/looping-over-struct-fieldnames-in-a-type-stable-way/102508>\
**Category:** Performance\
**Created:** [August 4, 2023, 10:51pm UTC](https://discourse.julialang.org/t/looping-over-struct-fieldnames-in-a-type-stable-way/102508 "2023-08-04T22:51:44Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![elenev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elenev/32/18440_2.png) [@elenev](https://discourse.julialang.org/u/elenev)\
**Post date:** [August 4, 2023, 10:51pm UTC](https://discourse.julialang.org/t/looping-over-struct-fieldnames-in-a-type-stable-way/102508/1 "2023-08-04T22:51:44Z")

</div>

I have a function that updates all fields of a struct in a field-specific way. Here an MWE:

```julia
julia> mutable struct Foo
           a
           b
end

julia> calc_new_value(x, y) = x*y
calc_new_value (generic function with 1 method)

julia> function update!(foo, rules)
           for name in fieldnames(Foo)
             setfield!(foo, name, calc_new_value(getfield(foo,name), getfield(rules,name)) )
          end
end
update! (generic function with 1 method)

julia> foo = Foo(1,2)
Foo(1, 2)

julia> update!(foo, (a=1, b=2))

julia> foo
Foo(1, 4)

```

where rules may be a struct, a NamedTuple, or something else. I mean “may” not in the sense that the code must be flexible enough to deal with all possibilities but in the sense that I can make the best design choice here.

Right now, `update!()` is not type-stable.

I think there’s some way I can use `@generated` to make it type-stable but I can’t figure out how to do that. Any advice?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [August 4, 2023, 11:36pm UTC](https://discourse.julialang.org/t/looping-over-struct-fieldnames-in-a-type-stable-way/102508/2 "2023-08-04T23:36:00Z")

</div>

`Foo`’s fields are implicitly annotated `::Any` so they must be type-unstable. I checked and if you annotate the fields with `::Int`, the example becomes type-stable.

---

<div class="post-metadata">

**Author:** ![elenev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elenev/32/18440_2.png) [@elenev](https://discourse.julialang.org/u/elenev)\
**Post date:** [August 4, 2023, 11:55pm UTC](https://discourse.julialang.org/t/looping-over-struct-fieldnames-in-a-type-stable-way/102508/3 "2023-08-04T23:55:56Z")

</div>

You’re right, of course! My MWE was a bit too simple.

A more realistic scenario – and the one I actually face – declares Foo as:

```julia
mutable struct Foo2{T}
    a::T
    b::T
end

```

This still yields instability when looping over `fieldnames(Foo2)` because T isn’t known until an instance of Foo2 is created.

But looping over `propertynames(foo)` works to get rid of instability.

Thanks for pointing me in the right direction!

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [August 5, 2023, 1:10am UTC](https://discourse.julialang.org/t/looping-over-struct-fieldnames-in-a-type-stable-way/102508/4 "2023-08-05T01:10:52Z")

</div>

> [@elenev](#):
>
> This still yields instability when looping over `fieldnames(Foo2)` because T isn’t known until an instance of Foo2 is created.

Oh that’s interesting, but not exactly what you said. `fieldnames` ideally works for `Foo2` the same way it did for `Foo` because the field names are the same. Thing is, `Foo` is a concrete type, an instance of `DataType`, while `Foo2` is a parametric type, an instance of `UnionAll`. Before computing the field names, `unwrap_unionall` extracts a `DataType` from the `UnionAll`’s `body::Any` field, hence the uninferrability. I’m not sure if the method or field could be patched without causing problems.

Your fix is a good idea, but use `fieldnames(typeof(foo))` to work with fields instead of properties. Those can diverge, for example public properties computed from private fields. Since you’re `setfield!`ing, it makes more sense to work with fields.
