# Preferred way to initialize read-only variable using mutations

I wonder what your preferred ways of initializing a read-only variable using imperative code are. This becomes important, e.g., when there are closures involved and Julia wants to box the variable.

Something like

``````function g(x) = nothing # or something... who knows
function f(x::Integer, a::Integer)
# Initialize y
y = 1
# let's pretend this sum can't be reformulated to something in closed form
for i in 1:x
y = y + i
end

# From this point on, y is basically constant
# [...]
sum(g(y + b) for b in 1:a)
end
``````

My ideas were

``````function f(x, a)
y = 1
for i in 1:x
y = y + i
end

let y = y
sum(g(y + b) for b in 1:a)
end
end
``````

and

``````function f(x::Integer, a::Integer)
y = begin
j = 1
for i in 1:x
j = j + i
end
j
end

sum(g(y + b) for b in 1:a)
end
``````

I believe, those two variants should be equivalent. Although I am not completely certain. Personally I went with the first option, as it felt more concise to me.

What are your preferred ways of preventing a Box there and why?

My preference is to write a specific function for initialization if possible. Out of the two proposed, the second feels â€śbetterâ€ť, only Iâ€™d write it as

``````    y = let y = 1
for i in 1:x
y = y + i
end
y
end
``````

Thereâ€™s also the â€śRef trickâ€ť:

``````function f(x::Integer, a::Integer)
# Initialize y
y = Ref(1)
for i in 1:x
y[] = y[] + i
end

sum(g(y[] + b) for b in 1:a)
end
``````

Now, there is no binding change for `y`, so no boxing needed.

1 Like

Interesting. I didnâ€™t know the `Ref` trick.

Is Julia able to use a plain value for `y`? In that case, `Ref(1)` would be quite different from `[1]`, right?

Julia compiler is free to decide if a struct (mutable or not) is stack- or heap-allocated. `Ref(1)` can be stack allocated if the compiler can prove it is safe. My guess is that arrays cannot be stack allocated because they donâ€™t have a fixed size. But heap allocation of a `Ref` must be cheaper than array, too, because arrays have to store more meta information.

In this case, `@btime` shows no allocations with `Ref` for simple initialization procedures, but switches to heap allocation if thereâ€™s something complicated in the loop. So, using one of your initial solutions is a faster way, and both seem equivalent in performance to me.

But `Ref` is useful if `y` has to be mutated inside the closure or if you want to share a mutable value between multiple closures.

1 Like