For a first reading: work through the opening minimal examples for move, Copy/Clone, and Drop. Identify who may still use each value and who releases it, then continue to references and borrowing. Revisit shared pointers, closure capture, partial moves, and detailed destruction order later.
Before reading: be comfortable with variables and functions and able to run a Cargo example. After reading: explain who owns a resource after a move and distinguish Copy, Clone and Drop. New to Rust? Start with the beginner learning path.
Ownership answers "who is responsible for this value, when can it be handed off, and how is it finally cleaned up." Rust combines movement, borrow checking, and type encapsulation to statically prevent double frees and dangling accesses; however, the resource itself may still incur runtime costs for allocation, reference counting, or destruction.
When first learning, you can start by looking at a String that owns a resource: on the normal path, its resource responsibility moves with the owner and is finally cleaned up by the still-valid owner. Rc/Arc allow multiple handles to share resources, and each handle itself is governed by standard ownership rules, so the statement "there is always only one owner of any resource at all levels" is not accurate.
Move Transfers Availability and Resource Responsibility
fnconsume(text: String)->usize{ text.len()}fnmain(){let first =String::from("hello");let second = first;assert_eq!(consume(second),5);// first has been moved; second is then handed to consume, neither can be read again.
}
Assignment, pass-by-value, return values, and pass-by-value destructuring can all trigger moves. After a move, the original location cannot continue to be used as an initialized value, but it can be reassigned if allowed. The compiler does not need to write a "invalidation flag" at runtime every time; concrete implementations can eliminate the copy, while certain control flows may need to track whether destruction is required.
The String buffer is not cloned by a simple move. This conclusion comes from the semantics of types and operations; it cannot be simplified to "bitwise copy on the stack is always safe": the stack may also hold handles to non-duplicable resources.
Interfaces taking ownership can also return resources on failure
After the first call, input has been moved, but the returned Err hands the ownership of the same string back to the caller. Failure does not mean the value must be discarded, nor does it mean the original variable automatically becomes usable again; whether a resource can be retried depends on whether the error type retained it. Real-world interfaces might use Rejected { input, reason } to return both the input and the error reason.
Parameter or Return Form
What Caller Hands Over
Common Use Case
fn inspect(value: &T)
Temporary shared access
Read content, retain original value
fn update(value: &mut T)
Temporary exclusive access
Modify valid state in place
fn consume(value: T)
Ownership of the value
Save, transfer, or finally clean up
Result<T, (T, E)>
Return a T in both success and failure branches
Need to retain retryable resources
Design interfaces based on these behaviors first, then decide if clone is needed. Cloning everything "just in case it's needed after the call" might mask APIs that only require borrowing, or inadvertently create shared state instead of independent snapshots.
Copy is Implicit Copy Capability, Clone is an Explicit Operation
Operation
Can Original Variable Still Be Used?
Can Custom Logic Be Executed?
Move of non-Copy value
Usually cannot continue reading the moved part
Does not invoke Clone
Assignment of Copy type
Yes
Copy itself does not execute custom copy methods
value.clone()
Usually yes
Copy and sharing behavior determined by Clone implementation
User-defined structs do not automatically implement Copy just because their fields are Copy; it requires explicit implementation or derive, satisfying field constraints. Copy requires Clone and cannot be used on types implementing Drop. Shared references &T are Copy, mutable references &mut T are not, to prevent arbitrary copying of exclusive access rights. Copy
Clone does not mean deep copy. String::clone typically creates an independent buffer; Arc::clone creates a new owner pointing to the same resource. Deriving Clone calls Clone on fields, so Arcs inside remain shared. Clone
usestd::rc::Rc;fnmain(){let original =String::from("a");letmut independent = original.clone();
independent.push('b');assert_eq!(original,"a");let shared =Rc::new(original);let another =Rc::clone(&shared);assert!(Rc::ptr_eq(&shared,&another));}
What moves, and what gets cloned?
Separate binding usability from resource identity. A move changes the responsible binding, String::clone adds independent text, and Rc::clone adds a shared owner. Arrows denote ownership, not borrowing or particular machine code.
Preparing the visual
What moves, and what gets cloned?
Moving invalidates the old binding; String cloning creates an independent buffer, while Rc cloning shares it.
{"id":"rust-move-resource","title":"What moves, and what gets cloned?","summary":"Moving invalidates the old binding; String cloning creates an independent buffer, while Rc cloning shares it.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"mode\"><button data-mode=\"move\" data-i18n=\"move\"></button><button data-mode=\"clone\" data-i18n=\"clone\"></button><button data-mode=\"rc\" data-i18n=\"rc\"></button></div><svg id=\"map\" class=\"diagram\" viewBox=\"0 0 360 180\" role=\"img\" data-i18n-label=\"heading\"></svg><div class=\"metrics\"><div><span data-i18n=\"buffers\"></span><strong id=\"buffers\"></strong></div><div><span data-i18n=\"owners\"></span><strong id=\"owners\"></strong></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"xmove\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"xclone\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"xrc\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:550;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:550 20px ui-monospace,monospace}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:ui-monospace,monospace;font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px ui-monospace,monospace;background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px ui-monospace,monospace}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 ui-monospace,monospace;white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:ui-monospace,monospace}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px ui-monospace,monospace;padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 ui-monospace,monospace;overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n.chart-wrap{margin:18px 0}.chart-label{font-size:12px;color:var(--muted);min-height:3.2em}.curve{display:block;width:100%;height:auto;max-height:320px;overflow:visible}.curve text{font:11px ui-monospace,monospace;fill:var(--muted)}.gridline{stroke:var(--rule);stroke-width:1}.metrics strong{font-size:20px;min-height:3.2em}.formula{font:12px/1.8 ui-monospace,monospace;min-height:9em}.readout{min-height:8em}.presets button{flex-basis:30%}.scope p{overflow-wrap:anywhere} .unit-note{font-size:12px;color:var(--muted);margin:10px 0}\n\n.metrics span{word-break:break-all;line-break:strict}.metrics>div{padding-right:6px}\n\n.cell{min-height:72px}.metrics strong{min-height:3.6em}\n\n ","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';row.style.gridTemplateColumns='repeat('+r.length+',minmax(0,1fr))';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function plot(id,series,xr,yr,opts={}){const e=$('#'+id),X=x=>42+(x-xr[0])/(xr[1]-xr[0])*298,Y=y=>175-(y-yr[0])/(yr[1]-yr[0])*148;let out='<defs><clipPath id=\"clip-'+id+'\"><rect x=\"42\" y=\"27\" width=\"298\" height=\"148\"/></clipPath></defs>';for(let i=0;i<3;i++){let x=xr[0]+i*(xr[1]-xr[0])/2,y=yr[0]+i*(yr[1]-yr[0])/2;out+='<path class=\"gridline\" d=\"M '+X(x)+' 27V175M42 '+Y(y)+'H340\"/><text x=\"'+X(x)+'\" y=\"194\" text-anchor=\"middle\">'+esc(opts.xfmt?opts.xfmt(x):Number(x.toFixed(2)))+'</text><text x=\"36\" y=\"'+(Y(y)+4)+'\" text-anchor=\"end\">'+esc(opts.yfmt?opts.yfmt(y):Number(y.toFixed(2)))+'</text>';}out+='<g clip-path=\"url(#clip-'+id+')\">';for(const s of series){out+='<path fill=\"none\" stroke=\"'+(s.color||'var(--accent)')+'\" stroke-width=\"2.5\" '+(s.dash?'stroke-dasharray=\"5 4\"':'')+' d=\"'+s.pts.map((p,i)=>(i?'L':'M')+X(p[0]).toFixed(2)+','+Y(p[1]).toFixed(2)).join(' ')+'\"/>';}for(const p of opts.points||[])out+='<circle cx=\"'+X(p[0])+'\" cy=\"'+Y(p[1])+'\" r=\"4\" fill=\"var(--second)\" stroke=\"var(--paper)\" stroke-width=\"1.5\"/>';if(opts.cursor!==undefined)out+='<path d=\"M'+X(opts.cursor)+' 27V175\" stroke=\"var(--muted)\" stroke-dasharray=\"3 3\"/>';e.innerHTML=out+'</g>';e.dataset.curves=JSON.stringify(series.map(s=>s.pts));}const curve=(fn,lo,hi,n=120)=>Array.from({length:n+1},(_,i)=>{const x=lo+(hi-lo)*i/n;return[x,fn(x)]});function inputs(draw){document.querySelectorAll('input,select').forEach(e=>{e.addEventListener('input',draw);e.addEventListener('change',draw)});draw()}const n=id=>+$('#'+id).value;const show=(id,v,d=3)=>set(id,Number(v.toFixed(d)));let mode='move';function draw(){pressed(mode);graph('map',[{name:'first',x:65,y:40,cls:mode==='move'?'':'on',sub:t(mode==='move'?'dead':'live')},{name:'second',x:290,y:40,cls:'on',sub:t('live')},{name:'A',x:120,y:160,cls:'gold'},...(mode==='clone'?[{name:'B',x:270,y:160,cls:'gold'}]:[])],mode==='move'?[[1,2,true]]:mode==='clone'?[[0,2,true],[1,3,true]]:[[0,2,true],[1,2,true]]);set('buffers',mode==='clone'?2:1);set('owners',mode==='move'?1:2);set('explanation',t('x'+mode));}document.querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{mode=b.dataset.mode;draw()});draw();","audio":false,"strings":{"a":"Original binding","b":"New binding","buffers":"Buffer count","clone":"String: clone","dead":"Cannot read","heading":"What moves, and what gets cloned?","intro":"Change the operation and trace bindings to actual buffers.","live":"Usable","mode":"Operation","move":"String: move","owners":"Usable handles","rc":"Rc: clone","reset":"Reset","scope":"Logical resource graph; IDs are not addresses or a claim about generated copies.","scopeLabel":"Model scope and assumptions","xclone":"Each String owns its buffer; modifying one does not change the other.","xmove":"first is invalid; second owns the same String buffer.","xrc":"Two Rc handles keep the same String alive; clone did not copy its text."}}
Closure Capture Depends on Usage Mode
Non-Copy does not necessarily mean it will be moved by the closure. Closures reading a Vec typically share-borrow it; when modification is needed, it might require exclusive borrowing, and taking ownership is only required when consuming the value. The move keyword requires capturing by value, but moving a reference only transfers the reference itself, not extending the lifetime of the referenced object. Closure Capture
fnmain(){let values =vec![1,2,3];letread=||values.len();assert_eq!(values[0],1);// Closure only borrows read-only, external can also read
assert_eq!(read(),3);let owned =move|| values.len();assert_eq!(owned(),3);assert_eq!(owned(),3);// move capture does not mean it can only be called once
}
Whether a closure implements Fn, FnMut, or FnOnce depends on how the captured values are used during invocation, which is a different issue from whether move is written.
Partial Moves and Re-establishing Complete Values
fnmain(){letmut pair =(String::from("left"),String::from("right"));let left = pair.0;assert_eq!(pair.1,"right");// Unmoved fields are still accessible
pair.0=String::from("new");assert_eq!(pair,(String::from("new"),String::from("right")));assert_eq!(left,"left");}
After partially moving fields, the whole cannot be used as a complete value; it can be restored by filling in that field again. For structs that implement Drop themselves, ordinary code cannot arbitrarily move out non-Copy fields because their destructor still requires a valid self. Common practices include using mem::replace, mem::take, or Option::take to leave a valid alternative value.
Extracting fields from objects implementing Drop requires leaving a destructible state
usestd::{cell::Cell,rc::Rc};structPackage{payload:Option<String>,
released:Rc<Cell<usize>>,
}implDrop forPackage{fndrop(&mutself){self.released.set(self.released.get()+1);}}fnmain(){let released =Rc::new(Cell::new(0));letmut package = Package {
payload:Some(String::from("data")),
released:Rc::clone(&released),};let text = package.payload.take().unwrap();assert!(package.payload.is_none());drop(package);assert_eq!(released.get(),1);assert_eq!(text,"data");}
Option::take replaces the field with None and hands over the original Some content; Package's Drop still faces a complete value, while the moved String survives independently. If the business logic does not allow empty states, you should use other valid alternative values, or design methods that consume the entire object, rather than changing invariants to arbitrary nullable just to bypass errors.
mem::take leaves a replacement value using Default, while mem::replace uses a specified new value. They transfer the old value; the old value is not immediately destroyed just because it was "taken"; old and new resources are subsequently managed by their respective owners. mem::take
Drop Order and Boundaries
Ordinary local variables are released in reverse order of declaration when exiting their destruction scope; struct fields are released in declaration order, and the struct's own Drop method executes first. Array elements are released from front to back. Temporaries, patterns, and control flow have finer-grained scope rules; you cannot apply a single "reverse order" rule to all objects. Destruction Rules
Explicit drop(value) hands the value to a pass-by-value parameter, thus releasing it early; drop(&value) only handles a reference and does not release the original resource. Borrow ending early does not mean the owner's Drop executes early.
Verify Order Using Destruction Events, Not Guessing Stack Layout
_group is a named binding; the leading underscore only suppresses the unused binding warning, but it still holds the value until the scope ends. It is different from the wildcard pattern _: let _ = construct_resource(); does not create such an owner binding; temporary resources are usually released at the end of that statement; lock guards especially should not rely on this pattern to persist.
Group::drop runs while fields are still valid; after the method ends, Rust recursively destructs the fields, requiring no manual repeated release of first/second. Here, RefCell is used to record events for observation; real-world Drop implementations should generally avoid adding work that might fail, especially paths that could panic again during unwinding. Destruction Rules
Normal exit and panic unwinding follow applicable destruction rules, but mem::forget, reference cycles, process termination, or abort may prevent cleanup. Rust allows memory leaks, so safe abstractions cannot rely on destruction definitely executing to avoid UB. The key point provided by ownership is safe access and responsibility transfer, not a promise that all resources are reclaimed on all termination paths.
Continue reading References and Borrowing to understand how to use values without taking ownership.