Alternative to mutable named tuple?

I think it’s fairly easy to come up with composable Ref based on the idea @vchuravy mentioned:

using Base: fieldindex, unsafe_convert

struct RefField2{f,T,R <: Ref} <: Ref{T}
    x::R
end
function RefField2(r::Ref{T}, f::Int) where {T}
    @assert isbitstype(fieldtype(T, f))
    return RefField2{f, fieldtype(T, f), typeof(r)}(r)
end
RefField2(r::Ref, name::Symbol) = RefField2(r, fieldindex(_reftype(r), name))

_inner(r::RefField2) = getfield(r, :x)
_reftype(::Ref{T}) where {T} = T

function Base.unsafe_convert(::Type{Ptr{T}}, r::RefField2{f, T}) where {f, T}
    iref = _inner(r)
    p::Ptr{T} = unsafe_convert(Ptr{_reftype(iref)}, iref)
    return p + fieldoffset(_reftype(iref), f)
end

Base.pointer(r::RefField2) = unsafe_convert(Ptr{_reftype(r)}, r)

Base.getproperty(r::RefField2, name::Symbol) = RefField2(r, name)
Base.getindex(r::RefField2) = unsafe_load(pointer(r))
Base.setindex!(r::RefField2, x) = unsafe_store!(pointer(r), convert(_reftype(r), x))

struct RefArray2{T,A<:Array{T}} <: Ref{T}
    x::A
    i::Int
    @inline function RefArray2(A::Array, i::Int)
        @boundscheck checkbounds(A, i)
        return new{eltype(A),typeof(A)}(A, i)
    end
end

RefArray2(A::Array, I) = RefArray2(A, Base.to_index(A, I))

_array(r) = getfield(r, :x)
_index(r) = getfield(r, :i)

Base.unsafe_convert(::Type{Ptr{T}}, r::RefArray2{T}) where {T} =
    pointer(_array(r), _index(r))

Base.pointer(r::RefArray2) = unsafe_convert(Ptr{_reftype(r)}, r)

Base.getproperty(r::RefArray2, name::Symbol) = RefField2(r, name)
Base.getindex(r::RefArray2) = unsafe_load(pointer(r))

Then

julia> xs = [(a=(b=(c=x, d=2x), e=3x), f=4x) for x in 1:2]
2-element Array{NamedTuple{(:a, :f),Tuple{NamedTuple{(:b, :e),Tuple{NamedTuple{(:c, :d),Tuple{Int64,Int64}},Int64}},Int64}},1}:
 (a = (b = (c = 1, d = 2), e = 3), f = 4)
 (a = (b = (c = 2, d = 4), e = 6), f = 8)

julia> r = RefArray2(xs, 2).a.b.c;  # reference to `xs[2].a.b.c`

julia> r[]
2

julia> r[] = 2000;

julia> xs
2-element Array{NamedTuple{(:a, :f),Tuple{NamedTuple{(:b, :e),Tuple{NamedTuple{(:c, :d),Tuple{Int64,Int64}},Int64}},Int64}},1}:
 (a = (b = (c = 1, d = 2), e = 3), f = 4)
 (a = (b = (c = 2000, d = 4), e = 6), f = 8)

Though it’s not as efficient as I hoped. For example, looking at the code generated for r[], not all unsafe_convert calls are inlined.