When choosing a collection, first list the operations: how you look up, where you add/remove, whether ordering is required, whether range scans are needed, and the size and distribution of keys and values. Comparing O(1) against O(log n) in isolation misses the costs of hashing, string comparison, allocation, and cache access.
Quick Reference by Operation
The following complexities isolate the cost of a single key comparison or hash for specific types; amortized does not mean every operation is equally fast.
Insertion/deletion in the middle typically moves O(n) elements
VecDeque
Add/remove at both ends, FIFO, O(1) indexing
Ring buffer layout may result in two slices
HashMap<K,V>
Expected fast key-based lookup and update
No ordering guarantee; cost influenced by hash and distribution
BTreeMap<K,V>
Ordered traversal, range and nearest-neighbor queries
Lookup/update typically involves O(log n) structural operations
BinaryHeap
Peek max in O(1), priority queue
Does not provide globally ordered iteration; pop is typically O(log n)
Sorted Vec
Batch construction, frequent reads and binary search
Maintaining sorted order during insertion is typically O(n)
HashSet/BTreeSet correspond to key-only storage needs; arrays can be used for fixed counts. Whether to use inline small containers depends on actual capacity distribution and library rules; there is no universal threshold like "zero heap allocation for fewer than 20 items". Standard Library Collections Overview
Vec: Capacity is Not Length
Vec contains pointer, length, and capacity information, but field order is not a stable C layout promise. It can reside on the stack, heap, or inside other objects; it cannot be universally called a "24-byte stack object." Zero-sized elements and zero capacity have special allocation behaviors.
with_capacity(n) requests at least n items of capacity but does not create n initialized T values. reserve(additional) calculates the requirement based on current length plus additional, rather than directly setting the final capacity. Ordinary push does not need to grow if capacity is sufficient; growth strategy and multiplier are not permanent formulas that applications can rely on. Vec Guarantees
fnmain(){letmut values =Vec::with_capacity(8);assert_eq!(values.len(),0);assert!(values.capacity()>=8);
values.extend([10,20,30]);
values.clear();assert!(values.is_empty());assert!(values.capacity()>=8);// clear does not require returning the buffer
}
Operations that may cause growth invalidate raw pointers, and safe borrowing rules restrict conflicting access. Even without growth, operations like insert, remove, and sort may change the objects corresponding to indices; "address hasn't changed" and "still the same logical element" are not the same thing.
swap_remove replaces the deleted position with the last item, avoiding the move required to maintain order, at the cost of changing the order. When long-term stable identifiers are needed, design IDs, generation indices, or appropriate indirection; do not treat current indices as permanent identities.
Separate Order from Identity on Deletion
fnmain(){let original =vec![(10,"a"),(20,"b"),(30,"c"),(40,"d")];letmut ordered = original.clone();assert_eq!(ordered.remove(1),(20,"b"));assert_eq!(ordered,[(10,"a"),(30,"c"),(40,"d")]);letmut unordered = original;assert_eq!(unordered.swap_remove(1),(20,"b"));assert_eq!(unordered,[(10,"a"),(40,"d"),(30,"c")]);assert_eq!(unordered[1].0,40);// Index 1 now belongs to another ID
}
If another HashMap<ID, usize> maintains positions, you must update the index of the item moved to the end after swap_remove, otherwise queries will locate the wrong object. IDs resolve logical identity, and mappings maintain storage locations; both are indispensable. Simply changing Vec elements to Box only makes the addresses of un-moved heap objects more stable; it does not prevent objects from being deleted or old IDs from being reused.
HashMap: Hash, Equality, and Random Seed Form the Contract
Equal keys must produce equal hash results. Custom Hash and Eq implementations must be consistent; once a key is inserted, you cannot change parts involved in hashing or equality comparison via interior mutability, as this breaks collection contracts.
The standard library's current implementation is based on SwissTable and uses a default hasher with random state; the default algorithm and details may evolve. The random state is not simply "a single fixed seed shared by all tables on each process start." When choosing an alternative hasher, consider input threat models, key lengths, and measurements; do not promise a fixed 10% or 30% speedup. HashMap
The Entry API combines lookup and insertion intent into a single operation:
usestd::collections::HashMap;fnmain(){letmut counts =HashMap::new();for word in["red","blue","red"]{*counts.entry(word).or_insert(0_u32)+=1;}assert_eq!(counts.get("red"),Some(&2));}
Do not rely on HashMap iteration order to generate stable snapshots or test expectations. The current implementation may scan by capacity rather than just length; tables that were once large but later became sparse deserve separate measurement. For stable order, explicitly sort the output or choose an ordered structure.
String keys can be queried by borrowing str because the corresponding Borrow contract ensures consistent equality and hashing for both representations. This capability does not mean any custom type "convertible to string" is suitable for Borrow<str>: if keys compare case-insensitively while standard str compares case-sensitively, they do not share the same key semantics.
or_insert_with constructs the value only when absent, but the String passed to entry is still created before the call. If the hot path is primarily hits, measure the path of checking get_mut first and creating the key if missing; it reduces key allocation on hits but may add an extra lookup on misses. Do not understand Entry as automatically lazily creating both key and value. HashMap Borrowed Queries
BTreeMap: Ordering is an Interface Capability, Not a Performance Byproduct
BTreeMap sorts based on Ord, making it suitable for range queries, finding boundaries, and ordered output. Nodes store multiple items, providing some locality, but the entire tree is not a contiguous array. A sorted Vec also provides contiguous layout and ordered reads, so the notion that "cache locality and ordering are mutually exclusive" is incorrect.
Choosing between a tree and a sorted Vec requires comparing update frequency and range reads: if data is built in batches and read-only for a long time, a sorted Vec is often worth measuring; frequent online insertions and deletions may be better suited for a tree. If key comparison involves long strings, the cost of comparison itself should be factored in.
Here, the query is for the "last change not later than time." The tree uses an ordered range followed by next_back, while the sorted slice uses partition_point to find the first position greater than time. The slice returns 0 for times before the earliest, so checked_sub is needed to handle the absence of a predecessor. If duplicate times are allowed, Map insertion with the same key replaces the value, while Vec can retain multiple items; a tie-breaking order must be defined separately. BTreeMap Range
VecDeque: Logical Continuity Does Not Equal Physical Continuity
Ring buffers allow efficient operations at both ends, but growth may still occur. as_slices returns two segments; concatenating them in order yields the logical content. make_contiguous can consolidate them into a single contiguous slice, but consolidation itself may require moving elements. VecDeque
Do not constantly call remove(0) to use Vec as a FIFO: each call moves remaining elements, accumulating significant work for long queues. If the business logic only requires batch reading followed by a complete clear, Vec may still be appropriate; the operation pattern is more important than the container name.
How does a logical queue cross the buffer end?
Treat A,B,C as logical identities and slot numbers as physical positions. Near the buffer end, queue order stays A,B,C while storage splits into two regions. Modulo explains wrapping; it does not make arbitrary out-of-bounds Rust indexing valid.
p(i)=(h+i)modC,0≤i<len≤C
Preparing the visual
How does a logical queue cross the buffer end?
A ring buffer maps logical i to (head+i) mod capacity; concatenating slices preserves queue order.
{"id":"rust-vecdeque-wrap","title":"How does a logical queue cross the buffer end?","summary":"A ring buffer maps logical i to (head+i) mod capacity; concatenating slices preserves queue order.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"head\" data-i18n=\"head\"></label><output for=\"head\" id=\"head-value\"></output><input id=\"head\" type=\"range\" min=\"0\" max=\"7\" value=\"6\" 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=\"8\" value=\"5\" step=\"1\"></div><div class=\"track-row\"><span data-i18n=\"physical\"></span><div id=\"physical\" class=\"track\"></div></div><section class=\"panel\"><h3 data-i18n=\"first\"></h3><div id=\"first\"></div></section><section class=\"panel\"><h3 data-i18n=\"second\"></h3><div id=\"second\"></div></section><section class=\"panel\"><h3 data-i18n=\"logical\"></h3><div id=\"logical\"></div></section><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"explain\"></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.track{display:grid;grid-template-columns:repeat(4,minmax(0,1fr))}.panel>div{min-height:2.5em;font:14px/1.8 ui-monospace,monospace;overflow-wrap:anywhere}","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 h=n('head'),l=n('length'),a=Array.from({length:l},(_,i)=>String.fromCharCode(65+i)),cut=Math.min(l,8-h);set('head-value',h);set('length-value',l);cells('physical',Array.from({length:8},(_,j)=>{let i=(j-h+8)%8;return{text:j+': '+(i<l?a[i]:'—'),cls:i<l?(j>=h?'on':'gold'):'empty'}}));set('first','['+a.slice(0,cut).join(', ')+']');set('second','['+a.slice(cut).join(', ')+']');set('logical','['+a.join(', ')+']');set('explanation',t('explain'));}inputs(draw);","audio":false,"strings":{"explain":"Teal is the first slice; orange wraps to the start. Logical traversal joins the first slice then the second.","first":"as_slices().0","head":"Physical head","heading":"How does a logical queue cross the buffer end?","intro":"Move head and length; see how two as_slices regions form the logical order.","length":"Queue length","logical":"Logical order","physical":"Physical slots","reset":"Reset","scope":"An eight-slot ring model, without guarantees about std VecDeque growth or internal field layout.","scopeLabel":"Model scope and assumptions","second":"as_slices().1"}}
BinaryHeap: Scheduling Order is a Result of Ord
The standard BinaryHeap is a max-heap; to execute earliest deadlines first, wrap the comparison key with Reverse. If tied tasks require FIFO, include a sequence number assigned by enqueue order in the key; do not rely on internal permutation order.
usestd::{cmp::Reverse,collections::BinaryHeap};fnmain(){letmut pending =BinaryHeap::from([
Reverse((20u64,0u64)),
Reverse((10,2)),
Reverse((10,1)),]);assert_eq!(pending.pop(),Some(Reverse((10,1))));assert_eq!(pending.pop(),Some(Reverse((10,2))));assert_eq!(pending.pop(),Some(Reverse((20,0))));assert_eq!(pending.pop(),None);letmut scores =BinaryHeap::from([9,5,3]);{letmut top = scores.peek_mut().unwrap();*top =1;}// guard drops normally, maintaining new heap order
assert_eq!(scores.peek(),Some(&5));}
Sequence number allocation and wrapping are the scheduler's responsibility; long-lived systems should handle overflow before it occurs, rather than letting duplicate sequence numbers silently change order. Real tasks can store only deadline, sequence number, and task ID in the heap, keeping large objects in a separate table, so the task body does not participate in Ord.
Ordinary iter does not provide sorted traversal; continuous pop retrieves items according to heap priority. Do not secretly modify sorting fields of heap elements via interior mutability; updating the heap top can use peek_mut, while arbitrary task rescheduling requires explicit delete/re-insert or versioned invalidation strategies. The latter allows old entries to remain in the heap, checking the version upon pop and skipping if stale, but accumulation of stale entries must be controlled. BinaryHeap Contract
Measure Real Costs
Record element count, key distribution, insert/delete ratio, peak capacity, and whether output order is required, then test both the construction and steady-state phases. Pre-allocation reduces growth frequency but may increase long-term memory usage; shrink_to_fit does not guarantee an immediate drop in RSS, as there are allocators and the OS in between.
Using safe APIs for collections does not mean all logical conditions are proven by the compiler: index validity, business ID lifetimes, hash consistency, and sorting rules remain part of the design.