Found a related question from 2018: Destructuring and broadcast. The motivation was similar (avoid storage and allocation of intermediate terms).
Adding broadcasting to tuple functions to Base is still under development. In the meantime, I’ve found that Destruct.jl is fast and achieves what I’m looking for. The “unzip” function from this StackExchange post
unzip(a) = map(x->getfield.(a, x), fieldnames(eltype(a)))
x = [1 2 3]
y = [2 3 4]
bar = (x,y)->(y,x)
f,g = unzip(bar.(x,y)) # usage
also seems pretty efficient.