An object reached by two paths must be copied once, not twice. Today you build the forwarding pointer - a marker left in the old object recording where its copy went - so the second visit follows it instead of copying again.
Copy an object at most once, leaving a forwarding pointer so repeat visits reuse the copy.
Naive copying has a fatal bug on any graph with sharing: copy a, which copies the
shared node s; later copy b, which copies s again - now there are two copies
of s, and the graph is broken. The fix is a forwarding pointer. The first time an
object is copied, you overwrite its old (from-space) slot with a note saying “I have
moved to to-space slot N.” Every later visit checks for that note first, and if it is
there, returns N instead of copying.
forward is the guarded copy that every reference will go through: nil stays nil,
an already-forwarded object returns its recorded destination, and only a genuinely new
object is copied and marked. One subtlety worth pinning: the forwarding field must
start as the nil reference, not its numeric zero value, because slot 0 is a real
id - an object whose forwarding field defaulted to 0 would look like it had already
moved to slot 0. With copy-once in hand, the next lesson walks the copied objects and
rewrites their stale fields to point at the copies.
// add a forwarding field to object; it MUST start as Nil, not the zero value 0// (0 is a valid id) - set forward = Nil wherever an object is createdfunc (h *CopyingHeap) forward(r Ref) Ref {if r == Nil { return Nil }src := h.from[r]if src.forward != Nil { return src.forward } // already copied: follow itdst := h.copy(r) // copies fields verbatim into the next to-space slotsrc.forward = dst // leave the forwarding pointer behindreturn dst}