Allocators provide storage that satisfies size and alignment requirements, type rules determine which bytes constitute a valid value, and ownership determines when destruction and deallocation occur. Separating these layers is essential to understanding why "obtaining a non-null pointer" does not equate to already possessing a valid T.
Global Allocator and System Allocator
Rust does not specify "all platforms default to calling glibc malloc" as a language contract. System represents the platform's system allocator, using different infrastructure on different systems; default choices and static/dynamic library environments should refer to the target documentation. System
The following complete program explicitly selects System, without needing to hand-write a GlobalAlloc implementation just for forwarding:
usestd::alloc::System;#[global_allocator]staticGLOBAL: System = System;fnmain(){let values =vec![10,20,30];assert_eq!(values.iter().sum::<i32>(),60);}
Implementing GlobalAlloc is an unsafe responsibility: you must satisfy the allocation and deallocation contracts, and these methods must not unwind. Additionally, avoid using logging or containers inside the allocator that might trigger further allocations, as this could lead to infinite recursion.
Do not rely on "the source code writes Box::new, so it must call the allocator exactly once" to maintain program correctness. The optimizer may eliminate or alter allocations; allocation counts can be used for restricted observation but cannot serve as a safety invariant. GlobalAlloc
Layout: Size, Alignment, and Representable Range
Layout::new::<T>() derives the layout from a type, and Layout::array::<T>(n) checks array dimensions. from_size_align requires alignment to be non-zero and a power of two, and guarantees that the size, rounded up to the alignment, does not exceed isize::MAX.
Layout itself allows zero size, but the calling contract for GlobalAlloc::alloc requires a non-zero size. Do not treat "successfully creating a Layout" as "directly callable on any allocation API." Different allocation interfaces have different rules for zero sizes that must be checked separately. Layout
usestd::alloc::Layout;fnmain(){assert!(Layout::from_size_align(16,3).is_err());assert!(Layout::array::<u64>(usize::MAX).is_err());let first =Layout::from_size_align(1,1).unwrap();let next =Layout::from_size_align(4,4).unwrap();let(combined, offset)= first.extend(next).unwrap();let padded = combined.pad_to_align();assert_eq!(offset,4);assert_eq!(padded.size(),8);assert_eq!(padded.align(),4);}
extend returns the offset of the second part; do not blindly add field sizes without checking. pad_to_align pads the tail so that contiguous objects also satisfy alignment; this example combines storage using explicit Layout, it does not prove that arbitrary Rust structs use the same field order.
Why Tail Padding Affects Array Stride
Suppose a record first places a 4-byte field with alignment 4, followed by a 1-byte field. The second field's offset is 4, and the combined effective end is 5; if the next record is placed immediately at offset 5, its first field will not satisfy 4-byte alignment. Therefore, the overall stride needs to be padded to 8.
usestd::alloc::Layout;fnmain(){let word =Layout::from_size_align(4,4).unwrap();let tag =Layout::from_size_align(1,1).unwrap();let(record, tag_offset)= word.extend(tag).unwrap();assert_eq!(tag_offset,4);assert_eq!(record.size(),5);assert_eq!(record.pad_to_align().size(),8);assert_eq!(record.pad_to_align().align(),4);}
The 3 bytes of tail padding here are used to ensure the next element is aligned; they do not carry business fields. When encoding for external protocols, you should write out the fields, rather than sending or hashing the padding bytes as stable content. Layout arithmetic solves spatial arrangement; it does not solve whether padding is initialized, whether values are valid, or whether pointers can access them.
Raw Allocation: Check for Null First, Then Initialize
usestd::alloc::{alloc, dealloc, handle_alloc_error, Layout};fnmain(){let layout =Layout::new::<u64>();// Non-zero size
// SAFETY: layout meets the requirements of alloc.
let storage =unsafe{alloc(layout)};if storage.is_null(){handle_alloc_error(layout);}let pointer = storage.cast::<u64>();// SAFETY: Storage is large enough and aligned; exclusive access, write before read.
let answer =unsafe{
pointer.write(42);let value = pointer.read();// u64 requires no additional destruction; allocator, address, and Layout match the request.
dealloc(storage, layout);
value
};assert_eq!(answer,42);}
Storage returned by alloc is uninitialized. Ordinary assignment may involve semantics of replacing old values; raw initialization should use appropriate write or MaybeUninit interfaces. For T containing resources, you must explicitly determine if read has moved ownership, if drop_in_place is still needed, to prevent leaks or double destruction.
dealloc only releases storage; it does not automatically call T::drop. This u64 example deliberately avoids adding fallible business logic between allocation and deallocation; more complex constructions need to handle partial initialization and panic paths, and should typically be handed over to Box, Vec, or specialized RAII wrappers.
Vec: Initialize First, Then Extend Valid Length
Vec::with_capacity reserves space, but len remains 0; you cannot directly read empty slots via indexing. spare_capacity_mut exposes the spare part as [MaybeUninit<T>]; after writing, you must still update len to bring these values into Vec's access and destruction responsibilities. Vec spare capacity
fnmain(){letmut values =Vec::<u32>::with_capacity(5);assert_eq!(values.len(),0);assert!(values.capacity()>=5);{let spare = values.spare_capacity_mut();
spare[0].write(10);
spare[1].write(20);}// SAFETY: Slots 0 and 1 have been written with valid u32, and 2 does not exceed capacity.
unsafe{ values.set_len(2);}assert_eq!(values,[10,20]);
values.push(30);assert_eq!(values,[10,20,30]);}
This is a low-level example for understanding initialization boundaries; ordinary appending via push is easier to maintain. Do not set len first and then initialize items one by one: if a panic occurs midway, Vec may attempt to destruct Ts that are not yet valid. Conversely, if you write a String but never count it in len, Vec won't know to destruct it, potentially leaking its resources.
When batch construction has fallible steps, prefer letting the container take ownership of completed objects item by item. The following returns an error at the second element; the first object already placed in the Vec will be automatically released:
usestd::cell::Cell;usestd::rc::Rc;structTracked(Rc<Cell<usize>>);implDrop forTracked{fndrop(&mutself){self.0.set(self.0.get()+1);}}fnbuild(drops:&Rc<Cell<usize>>)->Result<Vec<Tracked>, &'staticstr>{letmut items =Vec::with_capacity(3);for index in0..3{if index ==1{returnErr("Construction failed");}
items.push(Tracked(Rc::clone(drops)));}Ok(items)}fnmain(){let drops =Rc::new(Cell::new(0));assert!(build(&drops).is_err());assert_eq!(drops.get(),1);}
The value of RAII lies in allowing error paths and normal paths to share ownership rules. If you truly need raw batch initialization, a dedicated guard should record the number of initialized items and only destruct that prefix upon early exit; you cannot apply the destruction logic of the full array to partially valid storage.
Allocate Box First, Then Initialize Value
fnmain(){letmut storage =Box::<u64>::new_uninit();
storage.write(42);// SAFETY: The previous line fully initialized the unique u64, and it hasn't been invalidated since.
let value:Box<u64>=unsafe{ storage.assume_init()};assert_eq!(*value,42);}
Box::new_uninit returns Box<MaybeUninit<T>>; assume_init relies on the caller proving that T is valid. Allocating but not initializing and then deallocating this Box is allowed, but MaybeUninit will not automatically call destructors for manually written Ts inside it; responsibility must be correctly transferred after initialization. Zero-filling cannot replace this proof: all zeros are not a valid representation for any arbitrary T. Box::new_uninit
Capacity, initialization and len are distinct
Treat len as the container’s promise of valid values: it controls indexing and which elements are dropped. Moving the initialization and len boundaries separately exposes two different errors: growing len too early asserts nonexistent valid values; writing a String without transferring responsibility can omit its destructor. Ordinary push combines these steps safely.
Preparing the visual
Capacity, initialization and len are distinct
Every slot below len must be valid; initialized Strings beyond len are not automatically dropped by Vec.
{"id":"rust-initialization-prefix","title":"Capacity, initialization and len are distinct","summary":"Every slot below len must be valid; initialized Strings beyond len are not automatically dropped by Vec.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"init\" data-i18n=\"init\"></label><output for=\"init\" id=\"init-value\"></output><input id=\"init\" type=\"range\" min=\"0\" max=\"6\" value=\"3\" step=\"1\"></div><div class=\"control\"><label for=\"length\" data-i18n=\"length\"></label><output for=\"length\" id=\"length-value\"></output><input id=\"length\" type=\"range\" min=\"0\" max=\"6\" value=\"2\" step=\"1\"></div><div class=\"track-row\"><span data-i18n=\"slots\"></span><div id=\"slots\" class=\"track\"></div></div><div class=\"metrics\"><div><span data-i18n=\"drop\"></span><strong id=\"drop\"></strong></div><div><span data-i18n=\"leak\"></span><strong id=\"leak\"></strong></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"ok\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"bad\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"lost\"></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)));function draw(){let i=n('init'),l=n('length');set('init-value',i);set('length-value',l);cells('slots',Array.from({length:6},(_,j)=>({text:j+'\\n'+(j<l?'len':'spare'),cls:j<i?'on':j<l?'error':'empty'})));set('drop',l);set('leak',Math.max(0,i-l));set('explanation',t(l>i?'bad':l<i?'lost':'ok'));document.body.dataset.valid=String(l<=i);}inputs(draw);","audio":false,"strings":{"bad":"Invalid: len exceeds initialization; safe access or drop would assume valid Strings that do not exist.","drop":"Slots Vec would drop","heading":"Capacity, initialization and len are distinct","init":"Initialized prefix","intro":"Initialize String slots, then change the length managed by Vec and inspect drop responsibility.","leak":"Strings outside len ownership","length":"Proposed len","lost":"Extra Strings need another guard; dropping Vec alone will not drop values outside len.","ok":"All elements below len are valid and no initialized owned values are omitted.","reset":"Reset","scope":"A six-slot MaybeUninit<String> teaching model. Invalid boundaries are shown without executing UB.","scopeLabel":"Model scope and assumptions","slots":"Slots: teal valid, dashed uninitialized"}}
Global Replacement and Per-Container Allocators Are Not the Same API
GlobalAlloc and #[global_allocator] are stable interfaces. The standard library Allocator trait and many new_in interfaces that select allocators per container are still marked as experimental under allocator_api in the documentation reviewed here; do not directly call nightly examples stable Rust usage. Allocator
Choose an allocator based on fragmentation, latency, thread contention, and workload measurements, not based on name promises of being universally faster. For predictable batch objects, first consider reducing allocation counts, reserving capacity, or changing data structures.
Reserved Capacity, Freed Capacity, and Process Memory Are Not the Same Metric
Operation or Metric
What you can judge from it
What you cannot directly infer
Vec::clear
Elements have been removed and destructed according to rules, len is zero
Capacity has been returned to the allocator
drop(Vec)
Container releases the storage it owns
OS RSS drops by the same number of bytes immediately
try_reserve
Can handle capacity overflow or allocation failure Result
Input scale already fits business resource budget
shrink_to_fit
Request to shrink excess capacity
Capacity definitely equals len, memory definitely returned to OS immediately
Allocators may cache freed blocks, and pages may mix still-alive objects, so RSS differs from the valid bytes owned by objects. When analyzing "leaks," distinguish between continuously growing reachable objects, containers retaining capacity, allocators retaining, and the definitions used by system memory metrics; observing RSS not dropping once is insufficient to locate responsibility.
Before reserving the declared input quantity, verify business limits, then use try_reserve to handle resource failures. Even if size arithmetic does not overflow, malicious or erroneous input may still require unacceptable capacity. Generic containers must also consider zero-sized types: their capacity semantics cannot be directly converted to actual requested bytes.
What repr and Pin Guarantee Respectively
repr(C) constrains struct layout to follow corresponding C layout rules, but does not automatically recursively change the representation of field types; repr(align(N)) increases alignment, which may also increase padding. Target ABI, field valid values, and endianness still need consideration; do not directly treat struct memory as a cross-platform serialization format.
Pin expresses address-related contracts; it does not determine the allocator, nor does it automatically make a type immovable. For Unpin types, you can still safely obtain a movable &mut T through safe interfaces; a self-referential struct containing only String and raw pointers is typically still automatically Unpin, and wrapping it in Pin<Box<_>> is not sufficient to make it safe.
Self-referential abstractions require initialization at stable addresses, choosing appropriate !Unpin designs (such as PhantomPinned), restricting projections and moves, and adhering to destruction contracts. What is fixed is the pointee; the Box or Pin wrapper holding it can still move. See async/await for related mechanisms of asynchronous Futures, and Raw Pointers and FFI for raw access contracts.
Pin Does Not Prohibit Ordinary Field Modification
usestd::pin::Pin;fnmain(){letmut pinned =Box::pin(String::from("old"));let text:&mutString=Pin::as_mut(&mut pinned).get_mut();let old =std::mem::replace(text,String::from("new"));assert_eq!(old,"old");assert_eq!(pinned.as_ref().get_ref(),"new");}
String is Unpin, so safe interfaces allow replacing it. Even if changed to a custom type with PhantomPinned, it only restricts relevant move entry points; the initialization order of raw pointer targets, whether projections allow moving fields, and whether contracts are maintained during destruction remain the responsibility of the abstraction author. First clarify which address relationships must be maintained, then decide if Pin is needed; do not treat Pin as a universal "heap object hardening."