Arrays and References are pointers to mutable data, so even when the pointer is stored in a struct, the stored values can be modified.
When you want to control the creation of composite types to maintain invariants or provide defaults, Inner Constructors are handy.
Julia has atomic intrinsics that enforce thread-safe access.
I think what went wrong with the test is that @async starts a new task to go increment the value, but you never wait for that task to complete. So, in the REPL, enough time passed between lines for the task to finish, but during test was scheduled after the comparison.
Try this
using Test
abstract type Metric end
struct Gauge <: Metric
name::String
# A thread-safe reference to a Float64
value::Threads.Atomic{Float64}
# An inner constructor with a default value.
Gauge(name, initial=0.0) = new(name, Threads.Atomic{Float64}(initial))
end
function inc(metric::Metric, amount::Float64=1.0)
# safe modification of the atomic reference
Threads.atomic_add!(metric.value, amount)
return nothing
end
function get(metric::Metric)
metric.value[]
end
@testset "Gauge inc" begin
g = Gauge("test")
inc(g)
@test get(g) == 1.0
end