In a pipeline where one owning handle to a large chunk buffer carries the release duty, what does moving that handle do?
answer
- one owner, one release
- the duty travels, the bytes stay
- small record copied, not the payload
- source stops owning at the transfer
- invalidation is what prevents double free
basics
~20 sA move transfers the release duty to the destination handle and leaves the source owning nothing. The block stays at the same address; only the small owning record is copied, so the cost is the same for 4 KiB and 64 MiB.
solid answer
~40 sExactly one handle at a time is obliged to release the chunk buffer. A move rewrites that obligation: the destination now refers to the same block and owes the release, and the source is left owning nothing, so its own teardown frees nothing and the block is handed back exactly once. The payload does not travel — what is copied is the owning record, typically a pointer, a length and sometimes a capacity, a handful of machine words regardless of buffer size. That is why passing a chunk from an upload stage to the next by move is constant cost and allocates nothing. The invalidation of the source is the point, not a side effect: two live records claiming the same block is a double free waiting for scope exit.
code
pseudocode · 11 linesA = allocate_chunk(64 MiB) // A owns the block
B = move(A) // B owns it now; A owns nothing
// what move(A) does, expanded:
// B.block = A.block // same address, no bytes copied
// B.length = A.length
// A.block = none // A stops owning
at scope exit:
if B.block is not none: release(B.block) // runs once, frees 64 MiB
if A.block is not none: release(A.block) // A.block is none, nothing happensgo deeper
Recall the one-line shape: the release duty moves to the new handle, the bytes stay where they are, and the old handle stops owning anything at all.
Explain what is physically copied — a pointer, a length, a capacity — and why the source has to be invalidated: two records claiming one block means the allocator is handed it twice.
Show what it buys a running pipeline: one live block per in-flight chunk, handoff cost independent of chunk size, and exactly one release site the compiler can point at.
Frame it as a contract decision. Move-only handoffs make footprint a function of concurrency rather than pipeline depth, which is what makes capacity for a chunked transfer service predictable.
## One owner, one release A heap-allocated chunk buffer is two things at once: a block of bytes, and an obligation that the block be handed back to the allocator **exactly once**. Under single-ownership rules that obligation is attached to exactly one **owning handle** at any moment. The handle is small — a pointer to the block, a length, often a capacity — and it is not the data. The data sits in the heap block and stays at its address for as long as the block lives. A **move** relocates the obligation from one handle to another. Immediately after the buffer's owning handle is moved: - the destination handle refers to the same block, at the same address; - the destination owes the release; - the source owns nothing, so nothing is released when the source's scope ends; - the transfer itself neither reads, writes nor relocates the payload bytes. ## What a move actually copies What travels is the owning record, not the payload — on the order of two or three machine words. The cost is therefore **constant in the payload size**: moving a 4 KiB chunk and moving a 64 MiB chunk cost the same few stores. A staged upload pipeline is built on exactly that property. One chunk is allocated when it is read off the network and then travels by move through checksumming, framing and the uploader: several handoffs, no extra allocation, no extra byte of memory traffic. The mechanism is also defined by what it is not: 1. It is **not a copy of the payload**. No second block appears and the allocator is not consulted. 2. It is **not a second owner**. Ownership is relocated, not shared; sharing is a different scheme with its own bookkeeping. 3. It is **not a non-owning view**. A view leaves the release duty where it was; a move takes the duty away. ## Why the source has to be invalidated Suppose a transfer copied the owning record and left the source intact. Two records would now name the same block, and each would run its release at the end of its own scope. The block would be handed back twice. A double free corrupts the allocator's free-list bookkeeping, and the damage usually surfaces far away and much later — as an allocation that returns a block another part of the program is still writing into. Invalidating the source is what turns *released exactly once* from a convention that reviewers must police into a structural property. Invalidation means the source's record is left in a state its own teardown reads as **owns nothing**: the pointer is cleared, a flag is cleared, or the compiler stops emitting teardown for that variable because it can prove the value left. Ecosystems differ in which of these they do, and in whether the moved-from name may be mentioned again at all — the common part, in all of them, is that the source stops owning. ## Move, copy and view at a glance | Operation | Cost in payload size | Who releases afterwards | Source still an owner | |---|---|---|---| | Move the owning handle | constant | the destination, once | no | | Deep copy the payload | linear, plus an allocation | both, each its own block | yes | | Take a non-owning view | constant | unchanged — the original owner | yes | ## What this buys in the pipeline The pipeline gets three concrete things. **Predictable footprint:** one in-flight chunk means one live block, however many stages it passes through, because no stage duplicates it. **Predictable cost:** a handoff is a few word stores, so throughput does not fall as the chunk size is raised. **A single release site:** whichever stage ends up holding the buffer when the chunk is finished is the one that releases it, and the compiler knows which one that is. It does **not** buy leak freedom. An owner parked in a long-lived queue, or captured by a retry record that is never dropped, keeps its block alive indefinitely; nothing about moves makes that block reclaimable. Single ownership decides *who* releases and *when the duty transfers*; it cannot decide that a still-referenced owner should be thrown away. ## The interview framing Interviewers ask this because the wrong mental model is expensive in both directions. An engineer who believes a move duplicates the payload will write copies into the pipeline to be safe and multiply its footprint. An engineer who believes both handles remain owners will eventually write the double free. The answer an interviewer wants is short and mechanical: the duty moves, the bytes do not, and the source stops owning so the block is released once.
- If a move only copies a small record, why does the discipline need a distinct operation at all — why not plain assignment?Plain assignment duplicates the record and leaves both copies owning the same block, which is a double free at scope exit. A move is that same copy plus the obligatory step: clearing the source so only one record can ever run the release.
- What does the destination get if the source handle was not owning a buffer when it was moved?The empty state, propagated. The destination ends up owning nothing, its teardown releases nothing, and the operation is still well defined — moving is a transfer of whatever duty exists, including none. It is not an error and it does not allocate.
- Does moving an owning handle require the payload to stay at a fixed address?It requires nothing of the payload, because it does not touch it — the block simply is not relocated by a transfer of ownership. Any interior pointers into the block therefore remain valid across the move; what becomes invalid is the source's claim to release it.
Moving an owning handle is like handing over the only key to a storage unit, together with the duty to empty it one day. The unit and everything in it stay exactly where they are, and whoever handed the key over can no longer open it.
saying these in an interview costs you the question
- Thinks a move duplicates the payload bytes into a new block
- Says both handles still own the buffer after the transfer
- Assumes moving a large buffer costs proportional to its size
- Expects the source handle to remain usable as an owner
- Confuses a move with taking a non-owning view of the data
- Believes a double free is harmless because the block is already gone