Understanding layout requires distinguishing three things: which values a type allows, what storage method this compilation uses, and what representations the language or standard library guarantees. A size_of output can help observe the current target, but not all observations should be treated as cross-version ABI guarantees.
Size Does Not Equal Alignment
Type
Size
Notes
u8/i8, u16/i16, u32/i32, u64/i64, u128/i128
1, 2, 4, 8, 16 bytes respectively
Alignment still depends on the target
f32, f64
4, 8 bytes
Values like NaN affect comparison semantics
bool
1 byte
Valid values are only false/true, not arbitrary u8
char
4 bytes
Unicode scalar values, not arbitrary u32
usize/isize
Size corresponding to target address width
Not suitable as cross-platform file fields
()
0 bytes, alignment 1
Zero size does not mean all ZSTs have alignment 1
Fixed-width integers have fixed sizes, but alignment does not necessarily equal size; for example, on some 32-bit targets, u64 has an alignment of 4. Pointers to Sized types have the same size and alignment as usize; metadata for DST pointers is another layer of representation. Type Layout
char is a Unicode code point excluding the surrogate range, i.e., a Unicode scalar value. What a user sees as a single grapheme may consist of multiple scalar values; 'あ' occupies 4 bytes as a char but 3 bytes as UTF-8 text. See Strings and Paths for string details.
Integer Overflow: Choose Operations by Intent
Whether standard arithmetic checks for overflow is controlled by compilation configuration and cannot be strictly divided by debug/release names. When a predictable strategy is needed, directly call the checked, wrapping, saturating, or overflowing methods:
Integer division by zero and signed minimum divided by -1 also need consideration; do not assume that disabling addition overflow checks makes all integer operations safely wrap around. Errors in constant evaluation can also be diagnosed at compile time. Operators
Numeric Conversion: Representable Range Is Part of the Type Contract
as is not a check to "confirm the value fits." Narrowing integers truncates high bits, and signed/unsigned conversions have explicit bit interpretation rules; if file length comes from u64 but the native type uses usize, perform a fallible conversion first, then check business and allocation limits. Being able to convert to usize does not mean allocating that much memory is allowed.
fnmain(){let count =300u16;assert_eq!(count asu8,44);assert!(u8::try_from(count).is_err());assert_eq!(u32::from(255u8),255);assert_eq!((-1i8)asu8,255);// Numeric to byte conversion uses explicit endianness, not dependent on current CPU.
let bytes =300u32.to_be_bytes();assert_eq!(bytes,[0,0,1,44]);assert_eq!(u32::from_be_bytes(bytes),300);}
Floating-point numbers also have valid values that do not satisfy ordinary mathematical intuition: NaN is not uninitialized memory, but a valid floating-point value; partial_cmp may return None. If sorting uses total_cmp, it uses a specified total order, which is not exactly the same semantics as ordinary ==; for example, it distinguishes positive zero from negative zero. Whether NaN, infinity, or negative zero are allowed should be defined separately at business boundaries. Numeric Conversion, f64::total_cmp
repr(C) aligns each field in declaration order and then pads the total size to the struct's alignment. Tail padding ensures that the next struct in an array is also aligned. The default repr(Rust) does not guarantee field declaration order, nor does it promise to always choose the smallest layout.
repr(C) is not a serialization format: padding bytes may be uninitialized, endianness and target ABI still differ, and fields themselves may not be C-compatible types. Do not simply read the entire struct as bytes and treat it as a safe protocol encoding.
Deriving Array Stride and Field Reordering Using Offsets
For the Header in the diagram, a occupies offset 0; b requires 4-byte alignment, so it starts at 4; c follows at offset 8. The last field ends at 10, and since the struct's maximum alignment is 4, the total size is padded to 12. The second element of the array starts at 12, and its b is located at 16, still satisfying alignment.
If you are allowed to change the field declaration order of repr(C), arranging fields as b, c, a would result in offsets 0, 4, 6 under these alignment assumptions, with a total size of 8. It reduces space but simultaneously changes the ABI and field offsets, so you cannot quietly optimize already published external interfaces this way.
Outer repr(C) does not recursively rewrite the representation of internal Rust structs. Two types having the same size and alignment does not mean they have the same set of valid values, drop behavior, or calling ABI; these conditions must be checked separately. Rust Layout Rules
What repr(transparent), align, and packed Change
Representation
Effect
Conclusions That Cannot Be Derived
repr(C)
Organizes fields according to corresponding C layout rules
All internal fields automatically become C ABI types
repr(transparent)
Wrapper satisfying constraints inherits the layout and ABI of the wrapped field
Allows wrappers with arbitrary numbers of fields, or implies identical business semantics
repr(align(N))
Increases the type's alignment requirement
The actual object definitely occupies a fixed hardware cache line
repr(packed) / packed(N)
Reduces alignment constraints for fields and structs
Ordinary references can point to misaligned fields
packed is most prone to implicit borrowing issues: reading a field by value may be legal, but creating &packed.field creates an invalid misaligned reference, and some formatting or assertion macros also implicitly borrow. Copy to a local variable before borrowing, or use &raw const and read_unaligned when pointer reading is truly needed, while satisfying initialization, range, and ownership requirements.
#[repr(C, packed)]structPacked{kind:u8, count:u32}fnmain(){let packet = Packed { kind:1, count:300};let copied = packet.count;// Read Copy field by value, does not create &u32.
assert_eq!(copied,300);let ptr =&raw const packet.count;// SAFETY: ptr points to a still-living and initialized u32 field; read_unaligned does not require alignment.
// u32 is Copy, so reading does not introduce double-free or other ownership duplication issues.
let read_back =unsafe{ ptr.read_unaligned()};assert_eq!(read_back,300);assert_eq!(packet.kind,1);assert_eq!(std::mem::size_of::<Packed>(),5);}
This only explains the impact of packed on local layout and cannot replace protocol decoding: integers in fields still follow host numeric representation, and external data may still be illegal or insufficient. read_unaligned only relaxes alignment requirements; it does not make arbitrary addresses, uninitialized values, or non-Copy ownership copies safe. read_unaligned
See fields and padding byte by byte
Rounding upward follows from address constraints: each field starts at a multiple of its alignment, and array stride is a multiple of aggregate alignment. Dots are padding, not data or guaranteed initialized bytes. Reordering can remove dots while changing the external layout.
repr(C) aligns each field upward and pads the final size to aggregate alignment; reordering changes the ABI.
{"id":"rust-layout-padding","title":"See fields and padding byte by byte","summary":"repr(C) aligns each field upward and pads the final size to aggregate alignment; reordering changes the ABI.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"order\"><button data-mode=\"abc\" data-i18n=\"abc\"></button><button data-mode=\"bca\" data-i18n=\"bca\"></button></div><label class=\"check\"><input type=\"checkbox\" id=\"packed\"><span data-i18n=\"packed\"></span></label><div class=\"track-row\"><span data-i18n=\"bytes\"></span><div id=\"bytes\" class=\"track\"></div></div><div class=\"metrics\"><div><span data-i18n=\"size\"></span><strong id=\"size\"></strong></div><div><span data-i18n=\"padding\"></span><strong id=\"padding\"></strong></div></div><section class=\"panel\"><h3 data-i18n=\"second\"></h3><div id=\"offsets\"></div></section><div id=\"math\" class=\"formula\"></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"normal\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"bad\"></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(6,minmax(0,1fr));min-height:108px}.formula{min-height:5.4em}","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='abc';function draw(){pressed(mode);let pk=$('#packed').checked,off=0,buf=[],offsets={},sizes={a:1,b:4,c:2};for(let f of mode){let a=pk?1:sizes[f],p=Math.ceil(off/a)*a;while(off<p){buf.push({text:off+++' ·',cls:'empty'})}offsets[f]=off;for(let j=0;j<sizes[f];j++)buf.push({text:off+++' '+f,cls:f==='b'?'gold':'on'});}let end=off,A=pk?1:4,size=Math.ceil(off/A)*A;while(off<size)buf.push({text:off+++' ·',cls:'empty'});cells('bytes',buf);set('size',size+' B');set('padding',size-7+' B');table('offsets',[['a','b','c'],[''+(size+offsets.a),''+(size+offsets.b),''+(size+offsets.c)]]);set('math','end = '+end+', align = '+A+'\\nsize = ceil('+end+' / '+A+') × '+A+' = '+size);set('explanation',t(pk?'bad':'normal'));document.body.dataset.size=size;}document.querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{mode=b.dataset.mode;draw()});inputs(draw);","audio":false,"strings":{"abc":"a → b → c","bad":"packed lowers alignment to 1; smaller storage does not permit an &u32 to an unaligned b.","bca":"b → c → a","bytes":"First element: 1 B per cell","heading":"See fields and padding byte by byte","intro":"Change declaration order and alignment assumptions; inspect the second array element.","normal":"Normal layout: b aligns to 4 and c to 2; trailing padding aligns the next array element.","order":"Declaration order","packed":"Use repr(C, packed)","padding":"Padding bytes","reset":"Reset","scope":"Fields are a:u8, b:u32, c:u16. Two explicit target assumptions; not a simulator of arbitrary repr(Rust) layout.","scopeLabel":"Model scope and assumptions","second":"Field offsets in the second element","size":"size / stride"}}
Array Parameters: Separate Ownership Semantics from Actual Copies
The length of [T; N] participates in the type, the layout is contiguous, and the size is size_of::<T>() * N. Arrays do not inherently belong to the stack: they can also exist in Box, static storage, or other objects.
Arrays are Copy when T: Copy; passing non-Copy arrays like [String; N] by value moves ownership. The compiler may optimize the machine-level movement, but you cannot describe move as "element-by-element Clone" or guarantee that a full array memcpy is always executed.
When read-only access to data of arbitrary length is needed, &[T] is usually convenient; when handing an array to the receiver to manage, passing by value has clear meaning. The choice depends first on ownership, and then on measuring the overhead of passing large values.
ZSTs Still Have Types, Alignment, and Behavior
Zero-sized types do not need to occupy bytes for field data, but they can have methods, Drop behavior, and alignment requirements. Multiple ZSTs may have the same address, so you cannot use addresses to identify logical objects.
Therefore, the statement "ZSTs do not exist at runtime at all" is incorrect. Vec<ZST> still has length and per-element drop semantics, even though element storage does not require typical non-zero allocations.
Niches: Leveraging Unused Representations of Inner Types
References cannot be null, NonZeroU32 cannot be 0; these unused representations can be used to encode None for Option. The standard library explicitly guarantees that Option for a set of types has the same size, alignment, and calling ABI as the original type; rely on this list. Option Representation Guarantees
Option<bool> can often leverage extra bit patterns for compression, but do not fix the specific byte value of None to 2. Nested Options need to distinguish between outer None, Some(None), and Some(Some(_)), so you cannot directly reuse the existing null pointer representation of the inner type.
Representation optimization does not mean there is no need to check for the presence of a value, nor does it allow creating invalid bool or invalid references. Memory size, control flow cost, and valid value constraints should be understood separately.
External Bytes and Valid Values: Do Not Create Invalid Values First and Then Check
bool occupies 1 byte, but only 0 and 1 are valid representations; char occupies 4 bytes, but surrogate code points are not valid chars. References have constraints beyond non-null, including alignment, lifetime, and accessible memory. Using unsafe to forcibly cast bytes to these types first, and then checking if they are legal, places the check too late. Undefined Behavior and Valid Values
A more direct approach is to start from the weakest promise of bytes and build stronger types item by item. The following defines a 5-byte wire record: the first byte is 0/1 enabled, followed by four bytes for big-endian uint32 count. Its wire length is defined by the protocol and is unrelated to the size of any Rust struct.
MaybeUninit<T> is used to represent storage where a valid T is not yet guaranteed. Only after all necessary fields are correctly initialized can you obtain T; all-zero memory is not a valid value for all Ts, especially for references, NonZero, and certain enums. The size and alignment relationship between MaybeUninit<T> and T does not mean Option<MaybeUninit<T>> can reuse the original niche optimization of T. MaybeUninit
When reading a piece of layout code, you can ask in sequence: Is it only observing the current target? Does it rely on promised representations? Are valid values established? Are ownership requirements met? Is cross-platform wire encoding still needed? Having clear answers to these five questions is more reliable than proving safety solely by stating "memory sizes are the same."