Keeping a "reference" to a struct field

Edit: See my first reply below for a more correct MWE.

Consider a struct like

mutable struct Test
    a::Int
end

After t = Test(1) I would like to “bind” to the field so that after

x = t.a
t.a = 2

x will evaluate as 2. Is there any way to achieve that?


Context: I have a struct that is only partially initialized during the time that I need to access it (using getproperty(mystruct, :myfield) and “reference” the accessed field in another struct. Some time later I am using that “reference”, after the accessed struct is fully initialized.

Ok, I just noticed that this MWE is in fact not entirely related to my problem, since the Int field is not mutable. What I am really looking at is taking a struct like

import JuMP

mutable struct MyStruct
    myfield::Union{JuMP.VariableRef, Nothing}
end

t = MyStruct(nothing)
x = getfield(t, :myfield)
t.myfield = JuMP.@variable(m)

So essentially I would like to keep a “binding” to a field that is of a mutable type, but is - at the time of binding - still nothing.

There is a PR for that, but it’s not possible out of the box right now. You might be able to roll your own, e.g. copying the relevant bits from this PR:

import Base: RefValue, getindex, setindex!, isassigned, fieldindex

struct RefField{f,T,S} <: Ref{T}
    x::RefValue{S}
    RefField{f,T,S}(x::S) where {f,T,S} = new{f::Int,T,S}(RefValue(x))
end

RefField(x, f::Int) = RefField{f, fieldtype(typeof(x), f), typeof(x)}(x)
RefField(x, f::Symbol) = RefField(x, fieldindex(typeof(x), f))

getindex(b::RefField{f}) where {f} = getfield(b.x[], f)
setindex!(b::RefField{f,T}, x) where {f,T} = (setfield!(b.x[], f, convert(T, x)); b)
isassigned(b::RefField{f}) where {f} = isdefined(b.x[], f)

mutable struct T
    a::Int
end

t = T(1)
x = RefField(t, :a)
t.a = 2
@show x[] # 2

Maybe a package doing this already exists?

2 Likes

Thanks, that solves it for me!

1 Like