# Setfield: set properties of nested subfields

**URL:** <https://discourse.julialang.org/t/setfield-set-properties-of-nested-subfields/41304>\
**Category:** General Usage\
**Created:** [June 12, 2020, 11:38pm UTC](https://discourse.julialang.org/t/setfield-set-properties-of-nested-subfields/41304 "2020-06-12T23:38:42Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [June 12, 2020, 11:38pm UTC](https://discourse.julialang.org/t/setfield-set-properties-of-nested-subfields/41304/1 "2020-06-12T23:38:42Z")

</div>

I’m trying to use the setproperties function of Setfield.jl to set multiple different fields at once for immutable structs, but I’m running into this issue.

```julia
using Setfield

struct A
    foo::Int
    bar::Int
end

struct B
    baz::A
end

y = A(1, 2)
x = B(A(1, 2))

y = setproperties(y, (foo = 2, bar = 1))
x = setproperties(x, (baz.foo = 2, baz.bar = 1))

```

The second to last line works, but the last line has this error

> syntax: invalid named tuple field name “baz.foo”
> 
> Stacktrace:  
> [1] top-level scope at …/.julia/packages/IJulia/DrVMH/src/kernel.jl:52

I know I can use `@set` to do this, but I’m trying to limit the number of times the struct is copied. Is there any way to do this using Setfield.jl or a similar tool?

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [June 13, 2020, 8:44am UTC](https://discourse.julialang.org/t/setfield-set-properties-of-nested-subfields/41304/2 "2020-06-13T08:44:37Z")

</div>

Looking at  
`help?> setproperties`  
using it in the nested case is not supported, which makes sense to me.

The `patch::NamedTuple` argument is meant to be using parts of the properties of the struct.  
So it should be e.g.

```julia
x = setproperties(x, (baz = setproperties(y, (foo = 2, bar = 1)) ))

```

if you like this (I don’t). Or  
`x = setproperties(x, (baz = A(2,1) ))`

By the way, your examples do not need setproperties. They are just the same as setting a new y overwritting the old one:

```julia
y = A(2,1)
y = A(1,2)

```

It would make more sense if these are the examples:

```julia
y = A(1, 2)
y = setproperties(y, (foo = 2))

```

and

```julia
x = B(A(1, 2))
x = setproperties(x, (baz = setproperties(y, (foo = 2)) ))

```

now using setproperties here is a good MWE for larger structs with many properties.

> [@Ian\_Slagle](#):
>
> but I’m trying to limit the number of times the struct is copied

`setproperties` does not reduce the number of copies. It is a convenience function for large structs where only small number of properties are changing when copied.

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [June 13, 2020, 4:20pm UTC](https://discourse.julialang.org/t/setfield-set-properties-of-nested-subfields/41304/3 "2020-06-13T16:20:55Z")

</div>

Thanks! If I do

```julia-auto
using Setfield

struct A
    foo::Int
    bar::Int
end

struct B
    baz::A
end

x = B(A(1, 2))

function with_set_properties(x) 
    return setproperties(x, (baz = setproperties(x.baz, foo = 2, bar = 1)))
end
function without_set_properties(x)
    return @set (@set x.baz.foo = 2).baz.bar = 1
end
@time y = with_set_properties(x)
@time z = without_set_properties(x);

```

It seems the former is more performant

```julia-auto
  0.006966 seconds (19.32 k allocations: 1.048 MiB)
  0.010215 seconds (29.68 k allocations: 1.623 MiB)

```

and does fewer allocations (which I suspect is where the performance comes from).

> [@oheil](#):
>
> It would make more sense if these are the examples:

Yeah, my real use case for this is much more complicated, I was just trying to make as simple an example as possible that involved changing multiple, nested fields

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [June 13, 2020, 5:32pm UTC](https://discourse.julialang.org/t/setfield-set-properties-of-nested-subfields/41304/4 "2020-06-13T17:32:36Z")

</div>

I don’t want to dive too deep into the issue but if I check performance with BenchmarkTools it is different:

```julia
julia> using BenchmarkTools

julia> @benchmark with_set_properties($x)
BenchmarkTools.Trial: 
  memory estimate: 0 bytes
  allocs estimate: 0
  --------------
  minimum time: 1.399 ns (0.00% GC)
  median time: 1.500 ns (0.00% GC)
  mean time: 1.516 ns (0.00% GC)
  maximum time: 4.200 ns (0.00% GC)
  --------------
  samples: 10000
  evals/sample: 1000

julia> @benchmark without_set_properties($x)
BenchmarkTools.Trial:
  memory estimate: 0 bytes
  allocs estimate: 0
  --------------
  minimum time: 1.199 ns (0.00% GC)
  median time: 1.201 ns (0.00% GC)
  mean time: 1.251 ns (0.00% GC)
  maximum time: 17.300 ns (0.00% GC)
  --------------
  samples: 10000
  evals/sample: 1000

```

But the difference is so small that it is perhaps just an artefact.

You may search the discussions for allocations and benchmarking. I remember there are some good explanations about this, which may help in understanding whats going on.

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [June 13, 2020, 6:11pm UTC](https://discourse.julialang.org/t/setfield-set-properties-of-nested-subfields/41304/5 "2020-06-13T18:11:56Z")

</div>

Just confirming that on my machine, BenchmarkTools does indeed show this is probably within random error.

```julia
BenchmarkTools.Trial: 
  memory estimate: 0 bytes
  allocs estimate: 0
  --------------
  minimum time: 1.179 ns (0.00% GC)
  median time: 1.200 ns (0.00% GC)
  mean time: 1.199 ns (0.00% GC)
  maximum time: 8.540 ns (0.00% GC)
  --------------
  samples: 10000
  evals/sample: 1000

BenchmarkTools.Trial: 
  memory estimate: 0 bytes
  allocs estimate: 0
  --------------
  minimum time: 1.189 ns (0.00% GC)
  median time: 1.200 ns (0.00% GC)
  mean time: 1.216 ns (0.00% GC)
  maximum time: 26.410 ns (0.00% GC)
  --------------
  samples: 10000
  evals/sample: 1000

```

Not really sure why Benchmark tools is showing no allocations here though
