You are getting a garbage pointer here. If you want ptrA to point to A then you need to copy what ccall(... (Ref{A},), a::A) does. i.e. v = cconvert(Ref{A}, a); GC.@preserve v begin b = B(unsafe_convert(Ref{A}, v)) end
After a while of investigation two more questions:
Ref(a::A) → Base.RefValue(a::A) → Base.RefValue{A}(a), whereas Base.cconvert(Ref{A}, a) → convert(Ref{A}, a) → Base.RefValue{A}(a). Why do you suggest ra = Base.cconvert(Ref{A}, a) instead of simple ra = Ref(a)?
On the other hand Base.unsafe_convert(Ref{A}, ra) → Base.unsafe_convert(Ptr{A}, v) → black_magic so Base.unsafe_convert(Ref{A}, ra) leads to the correct thing, but why is this encouraged instead of more direct Base.unsafe_convert(Ptr{A},ra)?
You know how to do the black magic part and then you can just write ra = Ref(a) and do the black magic part yourself. That’s totally fine.
You don’t know how to do the black magic part and then you should use the generic mechanism setup for this, i.e. matching pair of cconvert and unsafe_convert.
Either way is fine, However, you must use unsafe_convert and cconvert in matching pair, the type and value for the intermediate result (i.e. return value of cconvert) is completely implementation detail and isn’t something you should rely on (unless you implemented it yourself, of course).
Therefore,
and
Are both looking at implementation detail.
This is fine as long as you don’t call anyunsafe_convert on it.
And this would be wrong if you use cconvert on Ref{A}. If you call Base.unsafe_convert(Ptr{A},ra), ramust be the return value from a Base.cconvert(Ptr{A}, ...) (see below)
Now what I said above is not the only way to use matching cconvert and unsafe_convert. Converting to Ptr{A} is also allowed but you just won’t get it from a normal object off type A. You need to replace it with Ref(a). In another word, it is valid and well supported too if you replace ra = cconvert(Ref{A}, a) with ra = cconvert(Ptr{A}, Ref(a)) and unsafe_convert(Ref{A}, ra) with unsafe_convert(Ptr{A}, ra).