cfg determines whether certain code participates in the build, making it suitable for platform-specific modules, optional dependencies, and test helper code. It differs from runtime selection based on the program's startup environment and cannot replace compile-time checks for all supported configurations.
Key Differences Between cfg and cfg!
#[cfg(unix)]fnplatform_name()->&'staticstr{"unix"}#[cfg(not(unix))]fnplatform_name()->&'staticstr{"other"}fnmain(){println!("{}",platform_name());let word =ifcfg!(debug_assertions){"checks"}else{"optimized"};println!("{word}");}
cfg! expands to true/false, but the two branches of the surrounding ordinary if are still subject to name resolution and type checking. Functions that only exist on Linux cannot be hidden from other platforms using if cfg!(target_os="linux"); you should use #[cfg] on items or blocks. Conditional Compilation Rules
#[cfg] is a syntax-level configuration removal; source files must still be readable as valid Rust syntax. You cannot place arbitrarily malformed tokens in disabled regions. For module-level platform implementations, you can select different files using cfg, allowing each implementation to maintain the same public interface.
Ensure Branch Coverage First, Then Implement Platform Differences
Multiple implementations of the same function must satisfy two conditions: at least one must exist under any supported configuration, and no more than one should be selected simultaneously. The easiest-to-maintain binary split is between condition P and not(P), rather than manually listing "other platforms currently thought of."
Here, both implementations have the same signature, so callers don't need to repeat platform checks. all means all conditions must be true, any means at least one is true, and not inverts a condition; an empty all() is true, and an empty any() is false. These empty conditions are also suitable for constructing minimal experiments: marking a function with an undefined name as #[cfg(any())] prevents it from participating in name checking; changing it to a regular if cfg!(any()) will still report an undefined name.
When selecting implementations by operating system, do not define functions with the same name using two overlapping conditions, such as unix and target_os="linux", which would both be true. When selecting by feature, consider unions as well: two user features might be enabled simultaneously. Only use compile_error! to reject combinations if the product contract explicitly forbids them; additive features should generally be allowed to coexist. Configuration Predicates
Why does a false branch still fail checking?
Runtime reachability does not determine compile-time checking. With linux_only defined only on Linux, compare what remains at name checking. #[cfg] removes the call, whereas cfg! merely expands to a Boolean constant.
Preparing the visual
Why does a false branch still fail checking?
#[cfg] removes disabled code; if cfg! is a Boolean branch whose arms still undergo name and type checking.
{"id":"rust-cfg-removal","title":"Why does a false branch still fail checking?","summary":"#[cfg] removes disabled code; if cfg! is a Boolean branch whose arms still undergo name and type checking.","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=\"attr\" data-i18n=\"attr\"></button><button data-mode=\"expr\" data-i18n=\"expr\"></button></div><label class=\"check\"><input type=\"checkbox\" id=\"linux\"><span data-i18n=\"linux\"></span></label><section class=\"panel\"><h3 data-i18n=\"parsed\"></h3><div id=\"parsed\"></div></section><div class=\"metrics\"><div><span data-i18n=\"check\"></span><strong id=\"check\"></strong></div><div><span data-i18n=\"run\"></span><strong id=\"run\"></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=\"removed\"></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#parsed{min-height:7em;font:14px/1.8 ui-monospace,monospace;white-space:pre-line}.metrics strong{font-size:16px}","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='attr';function draw(){let l=$('#linux').checked,removed=mode==='attr'&&!l,ok=l||removed;pressed(mode);set('parsed',removed?'/* ∅ */':mode==='expr'?'if '+l+' {\\n linux_only();\\n}':'linux_only();');set('check',ok?'✓':'×');set('run',ok?(l?'linux_only()':'∅'):'—');set('explanation',t(l?'ok':removed?'removed':'bad'));document.body.dataset.valid=ok;}document.querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{mode=b.dataset.mode;draw()});inputs(draw);","audio":false,"strings":{"attr":"#[cfg(...)]","bad":"Even with false, names inside an ordinary if must resolve; the non-Linux target lacks the function.","check":"Name checking","expr":"if cfg!(...)","heading":"Why does a false branch still fail checking?","intro":"Switch between a cfg attribute and cfg! expression, then change target platform.","linux":"Target is Linux","mode":"Selection mechanism","ok":"The call is checked and the target provides the function.","parsed":"Code reaching name checking","removed":"The call was removed during configuration; this name need not resolve.","reset":"Reset","run":"Runtime branch","scope":"linux_only() exists only for Linux targets; disabled code still needs valid Rust syntax.","scopeLabel":"Model scope and assumptions"}}
Common Conditions by Dimension
Condition
Fact Expressed
Note
target_os
Target OS like linux, windows, macos
Not the host system where the build script runs
target_arch
Target architecture like x86_64, aarch64, wasm32
Not sufficient to represent all ABIs
target_env
Target environment like gnu, musl, msvc
Some targets have empty values
target_pointer_width
Pointer width
Not equal to all integer or address implementation widths
target_feature
Instruction features enabled for this compilation target
Not equal to support on any deployed machine
debug_assertions
Corresponding compilation switch
Can be overridden by profile; not equivalent to fixed dev/release labels
test
The crate is compiled for testing
Integration tests depending on regular libraries do not automatically gain cfg(test)
The second command views the target configuration known to the compiler; it does not install the target standard library or complete linking for you. Finding a name for a target does not mean the environment can already run the artifact.
cfg_attr Selects Attributes, Not Runtime Flow
#[cfg_attr(debug_assertions,derive(Debug))]structState{value:u32}fnmain(){let state = State { value:7};assert_eq!(state.value,7);#[cfg(debug_assertions)]println!("{state:?}");}
When the condition is met, derive(Debug) is attached; otherwise, the attribute is not attached. The usage of Debug in the example is also controlled by the same condition; if println! is placed inside if cfg!(debug_assertions), builds with debug assertions disabled will still require State to implement Debug. Similarly, you can select link attributes, documentation annotations, or representation attributes, but if changing repr across platforms, consider FFI, storage formats, and the layout expectations of dependents.
When the same set of conditions appears repeatedly, centralize them at the module boundary; scattered cfgs might cause methods, imports, or trait implementations to be missed in certain configurations. Compiling the default configuration only proves that this one branch combination is valid.
Compile-Time CPU Features and Runtime Dispatch
#[cfg(target_feature="avx2")] indicates that AVX2 is enabled in the current compilation environment, not that it performs detection on the running machine. Distributing binaries built with target-cpu=native to different CPUs may also result in lost compatibility.
When you need a universal binary to select specialized implementations based on the machine, use the target platform's runtime feature detection, such as is_x86_feature_detected! for x86, and then call functions that meet the safety conditions of #[target_feature], while retaining a baseline implementation. Do not unconditionally execute specialized instructions first and then check for support with if. x86 Feature Detection
build.rs Generates Custom cfg
For example, whether a native library provides an API needs to be determined by the build script. You should declare both the valid name and value of the cfg so that rustc's checks can catch typos. The following demonstrates explicit environment input, without claiming to have detected a real system library:
Run the following commands sequentially in a POSIX shell. The second and third runs do not require manual cleanup of target; changes in environment input will cause the script to re-run:
DEMO_API=0cargo run --quietDEMO_API=1cargo run --quietDEMO_API=0cargo run --quiet
The output will be fallback, enabled, fallback in order. The script explicitly stipulates that only the string "1" enables the feature; "true", empty values, and missing values will not enable it. Real library detection usually needs to distinguish between "not supported" and "detection failed"; otherwise, a missing toolchain might be mistaken for a missing feature, silently selecting a degraded implementation.
The main code uses #[cfg(has_demo_api)]. check-cfg only declares what conditions are valid, it does not enable conditions; rustc-cfg is what actually enables them. The cargo::KEY form requires support from the corresponding Cargo version; older projects can use the cargo:KEY form according to their MSRV. Build Script Output
If the object of detection is a cross-compilation target, you should check the target library and its headers; you cannot detect the host-installed library and directly write the conclusion to the target. Custom cfgs do not automatically propagate to other crates that depend on them.
Spelling Checks and Enable Switches Are Independently Verified
Copy the above main.rs to a temporary directory to directly verify the two types of rustc inputs:
The first run only registers the valid condition and prints fallback; the second run additionally enables the condition and prints enabled. If you misspell the name in the source code as has_demo_ap, unexpected_cfgs will reject it, preventing typos from being silently interpreted as "not supported." Declaring all possible names should be done unconditionally; do not declare them only within the branch where detection succeeds, otherwise, when the feature is disabled, it will generate unexpected configuration warnings itself. rustc Configuration Checking
Configuration Matrix Should Cover Interface Combinations
For a library claiming to support optional features, at least check the default, default disabled, single critical features, and promised combinations separately. all-features is only a union and cannot replace tests with default features disabled; helper imports in cfg(test) might also mask missing features in regular builds.
You can combine cargo check, cargo test, and --target to check different levels. Cross-compilation cargo check does not verify the target linker, native dependencies, or runtime behavior; these conclusions should be accurately distinguished.
Arrange the Matrix by Failure Reasons, Not Just by Increasing Command Count
Usage points of attributes like Debug not synchronously protected
Release build or explicitly override this switch
Custom condition true/false
Fallback branches, declaration omissions, input changes not rebuilding
The above 0 → 1 → 0 experiment
Different targets
Platform APIs, type widths, native library linking
Target check, then link and run in available environments
For example, "json feature + has_demo_api" are two independent axes; you must consider four configurations: both disabled, only json, only API, and both enabled. If the actual contract allows one axis to appear only on specific targets, clearly write down the restrictions and verify diagnostics for unsupported combinations. Do not artificially inject built-in conditions like unix into rustc just to make it pass on the current machine; this will not change the target ABI or install the corresponding standard library.