[{"data":1,"prerenderedAt":26060},["ShallowReactive",2],{"all-pages":3},[4,380,855,1316,2065,2491,3017,3509,4002,4435,5074,5611,6129,6682,7517,8143,9017,9671,10445,11435,12014,12592,13331,14092,14875,15809,16646,17545,18356,19318,20109,21003,21614,23268,24234,25108],{"id":5,"title":6,"body":7,"description":117,"extension":373,"meta":374,"navigation":375,"path":376,"seo":377,"stem":378,"__hash__":379},"content\u002Frust\u002F01-introduction-and-setup.md","01 — Introduction & Setup",{"type":8,"value":9,"toc":356},"minimark",[10,14,19,32,93,97,100,104,110,120,123,129,134,154,160,164,171,179,203,207,215,219,225,229,235,239,318,322,328,346,350],[11,12,6],"h1",{"id":13},"_01-introduction-setup",[15,16,18],"h2",{"id":17},"why-rust","Why Rust?",[20,21,22,23,27,28,31],"p",{},"Rust is a systems programming language that guarantees ",[24,25,26],"strong",{},"memory safety"," and ",[24,29,30],{},"thread safety"," without a garbage collector. It achieves this through a unique ownership model enforced at compile time. Key selling points:",[33,34,35,42,48,54,60,66],"ul",{},[36,37,38,41],"li",{},[24,39,40],{},"Performance",": comparable to C\u002FC++; no runtime, no GC.",[36,43,44,47],{},[24,45,46],{},"Memory safety",": no null pointers, no dangling pointers, no buffer overflows.",[36,49,50,53],{},[24,51,52],{},"Fearless concurrency",": the compiler prevents data races.",[36,55,56,59],{},[24,57,58],{},"Zero-cost abstractions",": iterators, traits, generics compile down to the same machine code you'd write by hand.",[36,61,62,65],{},[24,63,64],{},"Strong type system",": algebraic data types (enums), pattern matching, traits.",[36,67,68,71,72,76,77,80,81,84,85,88,89,92],{},[24,69,70],{},"Great tooling",": ",[73,74,75],"code",{},"cargo"," (build\u002Fpackage), ",[73,78,79],{},"rustfmt"," (formatting), ",[73,82,83],{},"clippy"," (lints), ",[73,86,87],{},"rustdoc"," (docs), ",[73,90,91],{},"rust-analyzer"," (IDE).",[15,94,96],{"id":95},"the-compile-time-vs-runtime-tradeoff","The Compile-Time vs Runtime Tradeoff",[20,98,99],{},"Rust moves correctness checks to compile time. A program that compiles is far more likely to \"just work\" than in most languages. The cost: longer compile times and a steeper learning curve (especially ownership\u002Flifetimes).",[15,101,103],{"id":102},"installing-rust-rustup","Installing Rust (rustup)",[20,105,106,109],{},[73,107,108],{},"rustup"," is the official toolchain manager.",[111,112,118],"pre",{"className":113,"code":115,"language":116,"meta":117},[114],"language-bash","# macOS \u002F Linux\ncurl --proto '=https' --tlsv1.2 -sSf https:\u002F\u002Fsh.rustup.rs | sh\n\n# Windows: download rustup-init.exe from https:\u002F\u002Frustup.rs\n","bash","",[73,119,115],{"__ignoreMap":117},[20,121,122],{},"Verify:",[111,124,127],{"className":125,"code":126,"language":116,"meta":117},[114],"rustc --version\ncargo --version\nrustup --version\n",[73,128,126],{"__ignoreMap":117},[130,131,133],"h3",{"id":132},"toolchain-components","Toolchain Components",[33,135,136,142,148],{},[36,137,138,141],{},[73,139,140],{},"stable"," — default, released every 6 weeks.",[36,143,144,147],{},[73,145,146],{},"beta"," — next stable candidate.",[36,149,150,153],{},[73,151,152],{},"nightly"," — unstable features (e.g., some macros, inline assembly).",[111,155,158],{"className":156,"code":157,"language":116,"meta":117},[114],"rustup install stable\nrustup install nightly\nrustup default stable\nrustup component add rustfmt clippy rust-src rust-analyzer\nrustup target add wasm32-unknown-unknown   # cross-compile to WebAssembly\n",[73,159,157],{"__ignoreMap":117},[130,161,163],{"id":162},"editions","Editions",[20,165,166,167,170],{},"Editions (2015, 2018, 2021, 2024) are opt-in language evolutions. Set in ",[73,168,169],{},"Cargo.toml",":",[111,172,177],{"className":173,"code":175,"language":176,"meta":117},[174],"language-toml","[package]\nedition = \"2021\"\n","toml",[73,178,175],{"__ignoreMap":117},[20,180,181,182,186,187,190,191,194,195,198,199,202],{},"Code from older editions keeps compiling; editions are about how the ",[183,184,185],"em",{},"parser"," sees your code, not the runtime behavior. Key 2021 changes: ",[73,188,189],{},"IntoIterator"," for arrays, disjoint closure captures, ",[73,192,193],{},"panic"," macros consistency. Edition 2024 adds ",[73,196,197],{},"unsafe"," attributes on extern blocks, ",[73,200,201],{},"gen"," keyword reservation, etc.",[15,204,206],{"id":205},"the-cargo-build-pipeline","The Cargo Build Pipeline",[111,208,213],{"className":209,"code":211,"language":212},[210],"language-text","cargo new my_project      # scaffolds a binary crate\ncargo new my_lib --lib    # scaffolds a library crate\ncargo build               # debug build -> target\u002Fdebug\ncargo build --release     # optimized build -> target\u002Frelease (O3-ish)\ncargo run                 # build + run binary\ncargo check               # type-check without codegen (fast feedback)\ncargo test                # run tests\ncargo doc --open          # generate & open docs\ncargo fmt                 # format code\ncargo clippy              # run lints\ncargo update             # update deps in Cargo.lock\ncargo tree               # print dependency tree\ncargo bench              # run benchmarks (requires nightly or criterion)\n","text",[73,214,211],{"__ignoreMap":117},[130,216,218],{"id":217},"profile-customization","Profile customization",[111,220,223],{"className":221,"code":222,"language":176,"meta":117},[174],"# Cargo.toml\n[profile.release]\nopt-level = 3\nlto = \"fat\"          # link-time optimization across crates\ncodegen-units = 1    # better optimization, slower compile\nstrip = true         # strip debug symbols\npanic = \"abort\"      # smaller binary, no unwinding\n",[73,224,222],{"__ignoreMap":117},[15,226,228],{"id":227},"project-layout-conventions","Project Layout Conventions",[111,230,233],{"className":231,"code":232,"language":212},[210],"my_project\u002F\n├── Cargo.toml\n├── Cargo.lock          # binary: commit it; library: usually commit too\n├── src\u002F\n│   ├── main.rs         # binary crate root\n│   ├── lib.rs          # library crate root\n│   └── bin\u002F\n│       └── extra.rs    # additional binary target\n├── tests\u002F              # integration tests\n│   └── integration_test.rs\n├── benches\u002F\n│   └── my_bench.rs\n└── examples\u002F\n    └── example.rs\n",[73,234,232],{"__ignoreMap":117},[15,236,238],{"id":237},"edge-cases-gotchas","Edge Cases & Gotchas",[33,240,241,247,260,274,283,296,306],{},[36,242,243,246],{},[24,244,245],{},"Cargo.lock",": commit it for binaries to ensure reproducible builds. For libraries it's debated; the official guidance is to commit it too, but it's not required.",[36,248,249,255,256,259],{},[24,250,251,254],{},[73,252,253],{},"cargo check"," is your friend",": during development it's 10x faster than ",[73,257,258],{},"build",".",[36,261,262,266,267,270,271,259],{},[24,263,264],{},[73,265,91],{}," needs ",[73,268,269],{},"rust-src"," to show stdlib source — install it via ",[73,272,273],{},"rustup component add rust-src",[36,275,276,279,280,259],{},[24,277,278],{},"macOS linkers",": if you hit linker errors, install Xcode Command Line Tools: ",[73,281,282],{},"xcode-select --install",[36,284,285,288,289,292,293,295],{},[24,286,287],{},"MSRV"," (Minimum Supported Rust Version): set with ",[73,290,291],{},"rust-version"," in ",[73,294,169],{},"; CI should pin to that version.",[36,297,298,301,302,305],{},[24,299,300],{},"Incremental compilation",": on by default in dev; can occasionally produce stale errors — ",[73,303,304],{},"cargo clean"," fixes it.",[36,307,308,313,314,317],{},[24,309,310],{},[73,311,312],{},"~\u002F.cargo\u002Fbin"," must be on your ",[73,315,316],{},"PATH"," (rustup installer adds it to your shell profile).",[15,319,321],{"id":320},"recommended-environment-vs-code","Recommended Environment (VS Code)",[20,323,324,325,327],{},"Install the ",[24,326,91],{}," extension (NOT the legacy \"Rust\" extension). Enable:",[33,329,330,335,341],{},[36,331,332],{},[73,333,334],{},"rust-analyzer.check.command = \"clippy\"",[36,336,337,340],{},[73,338,339],{},"rust-analyzer.inlayHints"," for type\u002Fchaining hints",[36,342,343,344],{},"Format on save with ",[73,345,79],{},[15,347,349],{"id":348},"summary","Summary",[20,351,352,353,355],{},"You now have the toolchain and understand the build lifecycle. Next: writing your first program and reading ",[73,354,169],{}," semantics.",{"title":117,"searchDepth":357,"depth":357,"links":358},2,[359,360,361,366,369,370,371,372],{"id":17,"depth":357,"text":18},{"id":95,"depth":357,"text":96},{"id":102,"depth":357,"text":103,"children":362},[363,365],{"id":132,"depth":364,"text":133},3,{"id":162,"depth":364,"text":163},{"id":205,"depth":357,"text":206,"children":367},[368],{"id":217,"depth":364,"text":218},{"id":227,"depth":357,"text":228},{"id":237,"depth":357,"text":238},{"id":320,"depth":357,"text":321},{"id":348,"depth":357,"text":349},"md",{},true,"\u002Frust\u002F01-introduction-and-setup",{"title":6,"description":117},"rust\u002F01-introduction-and-setup","trdDtMzOE8JA1hCUd-rJJaWY6MIg_8cW0--Bb1DZX6w",{"id":381,"title":382,"body":383,"description":117,"extension":373,"meta":850,"navigation":375,"path":851,"seo":852,"stem":853,"__hash__":854},"content\u002Frust\u002F02-hello-world-and-cargo.md","02 — Hello World & Cargo Deep Dive",{"type":8,"value":384,"toc":828},[385,388,392,400,406,412,423,430,443,449,453,503,510,516,536,542,552,563,569,575,581,585,627,631,637,641,648,654,659,685,689,696,702,708,712,812,816,823,825],[11,386,382],{"id":387},"_02-hello-world-cargo-deep-dive",[15,389,391],{"id":390},"the-minimal-program","The Minimal Program",[111,393,398],{"className":394,"code":396,"language":397,"meta":117},[395],"language-rust","\u002F\u002F src\u002Fmain.rs\nfn main() {\n    println!(\"Hello, world!\");\n}\n","rust",[73,399,396],{"__ignoreMap":117},[20,401,402,403,170],{},"Compile and run directly with ",[73,404,405],{},"rustc",[111,407,410],{"className":408,"code":409,"language":116,"meta":117},[114],"rustc src\u002Fmain.rs && .\u002Fmain      # produces .\u002Fmain (or main.exe)\n",[73,411,409],{"__ignoreMap":117},[20,413,414,416,417,419,420,422],{},[73,415,405],{}," is the compiler. In practice you use ",[73,418,75],{}," instead, but understanding ",[73,421,405],{}," helps you read compiler errors.",[15,424,426,429],{"id":425},"println-is-a-macro-not-a-function",[73,427,428],{},"println!"," is a Macro, Not a Function",[20,431,432,434,435,438,439,442],{},[73,433,428],{}," ends with ",[73,436,437],{},"!"," because it's a ",[24,440,441],{},"macro",". It can't be a function because it validates format strings at compile time and takes a variadic number of arguments.",[111,444,447],{"className":445,"code":446,"language":397,"meta":117},[395],"let name = \"Ada\";\nlet age = 36;\nprintln!(\"{name} is {age}\");            \u002F\u002F implicit named args (edition 2021+)\nprintln!(\"{0} is {1}\", name, age);      \u002F\u002F positional\nprintln!(\"{name} is {age}\", name=name, age=age); \u002F\u002F explicit named\nprintln!(\"{name:>10}\");                  \u002F\u002F right-align width 10\nprintln!(\"{name:^10}\");                  \u002F\u002F center\nprintln!(\"{age:0>5}\");                   \u002F\u002F zero-padded: 00036\nprintln!(\"{age:#x}\", 255u32);            \u002F\u002F hex with 0x prefix -> 0xff\nprintln!(\"{:b}\", 10u8);                  \u002F\u002F binary -> 1010\nprintln!(\"{:e}\", 12345.678f64);          \u002F\u002F scientific\nprintln!(\"{:#?}\", some_struct);          \u002F\u002F pretty-print debug\nprintln!(\"{:>10.2}\", 3.14159);           \u002F\u002F width 10, 2 decimals\n",[73,448,446],{"__ignoreMap":117},[130,450,452],{"id":451},"format-trait-hierarchy","Format Trait Hierarchy",[20,454,455,458,459,462,463,466,467,470,471,474,475,470,477,480,481,480,484,480,487,480,490,480,493,496,497,499,500,502],{},[73,456,457],{},"{}"," uses the ",[73,460,461],{},"Display"," trait; ",[73,464,465],{},"{:?}"," uses ",[73,468,469],{},"Debug","; ",[73,472,473],{},"{:#?}"," is pretty ",[73,476,469],{},[73,478,479],{},"{o}",", ",[73,482,483],{},"{x}",[73,485,486],{},"{X}",[73,488,489],{},"{b}",[73,491,492],{},"{e}",[73,494,495],{},"{E}"," select integer\u002Ffloat formatting. You implement ",[73,498,461],{}," manually for user-facing output; ",[73,501,469],{}," can be derived.",[15,504,506,507],{"id":505},"anatomy-of-main","Anatomy of ",[73,508,509],{},"main",[111,511,514],{"className":512,"code":513,"language":397,"meta":117},[395],"fn main() {\n    \u002F\u002F program entry point\n}\n",[73,515,513],{"__ignoreMap":117},[33,517,518,527],{},[36,519,520,522,523,526],{},[73,521,509],{}," never takes arguments and never returns a value (returns unit ",[73,524,525],{},"()",").",[36,528,529,530,533,534,170],{},"To exit with a code, use ",[73,531,532],{},"std::process::exit(code)"," (skips destructors!) or return from ",[73,535,509],{},[111,537,540],{"className":538,"code":539,"language":397,"meta":117},[395],"fn main() -> std::process::ExitCode {\n    std::process::ExitCode::SUCCESS\n}\n",[73,541,539],{"__ignoreMap":117},[20,543,544,545,27,548,551],{},"(Stable ",[73,546,547],{},"ExitCode",[73,549,550],{},"Termination"," trait are available since 1.61.)",[15,553,555,556,559,560],{"id":554},"cargo-new-vs-init","Cargo: ",[73,557,558],{},"new"," vs ",[73,561,562],{},"init",[111,564,567],{"className":565,"code":566,"language":116,"meta":117},[114],"cargo new my_app          # creates new directory with a binary project\ncargo new my_lib --lib    # library project (lib.rs, no main)\ncargo init                # scaffolds in the current directory (existing git repo preserved)\ncargo init --name custom_name\n",[73,568,566],{"__ignoreMap":117},[15,570,572,574],{"id":571},"cargotoml-anatomy",[73,573,169],{}," Anatomy",[111,576,579],{"className":577,"code":578,"language":176,"meta":117},[174],"[package]\nname = \"my_app\"\nversion = \"0.1.0\"\nedition = \"2021\"\nauthors = [\"You \u003Cyou@example.com>\"]\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"...\"\nrust-version = \"1.75\"          # MSRV\npublish = false                 # don't accidentally publish to crates.io\n\n[dependencies]\nserde = { version = \"1.0\", features = [\"derive\"] }\ntokio = { version = \"1\", features = [\"full\"] }\nrand = \"0.8\"\n\n[dev-dependencies]\npretty_assertions = \"1\"        # only for tests\u002Fbenches\n\n[build-dependencies]\nanyhow = \"1\"                   # for build.rs\n\n[[bin]]\nname = \"my_app\"\npath = \"src\u002Fmain.rs\"\n\n[features]\ndefault = [\"json\"]\njson = [\"serde\"]\n",[73,580,578],{"__ignoreMap":117},[130,582,584],{"id":583},"version-requirement-syntax","Version Requirement Syntax",[33,586,587,601,607,615,621],{},[36,588,589,592,593,596,597,600],{},[73,590,591],{},"\"1.0\""," → ",[73,594,595],{},"^1.0"," → compatible up to ",[73,598,599],{},"\u003C2.0.0"," (caret, default)",[36,602,603,606],{},[73,604,605],{},"\"=1.0.0\""," → exact",[36,608,609,592,612],{},[73,610,611],{},"\"~1.0.0\"",[73,613,614],{},">=1.0.0, \u003C1.1.0",[36,616,617,620],{},[73,618,619],{},"\">=1.0, \u003C2.0\""," → explicit range",[36,622,623,626],{},[73,624,625],{},"\"*\""," → any (avoid)",[15,628,630],{"id":629},"dependency-sources","Dependency Sources",[111,632,635],{"className":633,"code":634,"language":176,"meta":117},[174],"[dependencies]\n# crates.io\nserde = \"1.0\"\n\n# git\nmy_crate = { git = \"https:\u002F\u002Fgithub.com\u002Fuser\u002Fcrate\", branch = \"dev\" }\nmy_crate2 = { git = \"...\", tag = \"v1.2.3\" }\nmy_crate3 = { git = \"...\", rev = \"abc123\" }\n\n# path (local)\nmy_local = { path = \"..\u002Fmy_local\" }\n\n# optional dependency behind a feature\nextra = { version = \"1.0\", optional = true }\n",[73,636,634],{"__ignoreMap":117},[15,638,640],{"id":639},"features","Features",[20,642,643,644,647],{},"Features enable ",[24,645,646],{},"conditional compilation",". Avoid exposing features of dependencies (this causes \"feature unification\" surprises). Use direct deps + optional features instead.",[111,649,652],{"className":650,"code":651,"language":397,"meta":117},[395],"#[cfg(feature = \"json\")]\nmod json;\n",[73,653,651],{"__ignoreMap":117},[15,655,657],{"id":656},"cargolock",[73,658,245],{},[33,660,661,664,675],{},[36,662,663],{},"Pin exact versions resolved for your dependency graph.",[36,665,666,667,670,671,674],{},"Always commit for ",[24,668,669],{},"binaries",". For ",[24,672,673],{},"libraries"," the official recommendation is also to commit it, but it's commonly gitignored.",[36,676,677,680,681,684],{},[73,678,679],{},"cargo update"," bumps within semver-compatible range; ",[73,682,683],{},"cargo update -p serde --precise 1.0.150"," pins a single crate.",[15,686,688],{"id":687},"workspaces","Workspaces",[20,690,691,692,695],{},"When multiple crates share a workspace, dependency versions unify and ",[73,693,694],{},"target\u002F"," is shared:",[111,697,700],{"className":698,"code":699,"language":176,"meta":117},[174],"# Cargo.toml (workspace root)\n[workspace]\nmembers = [\"crates\u002F*\", \"app\"]\n\n[workspace.dependencies]\nserde = \"1.0\"\n",[73,701,699],{"__ignoreMap":117},[20,703,704,705,259],{},"Members then reference with ",[73,706,707],{},"serde.workspace = true",[15,709,711],{"id":710},"edge-cases","Edge Cases",[33,713,714,734,752,770,785,794],{},[36,715,716,722,723,726,727,730,731,259],{},[24,717,718,719],{},"Binaries from ",[73,720,721],{},"src\u002Fbin\u002F*.rs",": each ",[73,724,725],{},".rs"," file in ",[73,728,729],{},"src\u002Fbin\u002F"," becomes a separate binary target automatically. Run with ",[73,732,733],{},"cargo run --bin extra",[36,735,736,71,745,748,749,259],{},[24,737,738,741,742],{},[73,739,740],{},"cargo run"," passes args after ",[73,743,744],{},"--",[73,746,747],{},"cargo run -- --flag"," runs your binary with ",[73,750,751],{},"--flag",[36,753,754,761,762,765,766,769],{},[24,755,756,757,760],{},"Multiple ",[73,758,759],{},"[[bin]]"," targets"," can share a ",[73,763,764],{},"src\u002Flib.rs"," for logic and have thin ",[73,767,768],{},"src\u002Fbin\u002F*"," shells.",[36,771,772,71,777,780,781,784],{},[24,773,774,776],{},[73,775,405],{}," error codes",[73,778,779],{},"E0382"," etc. Search ",[73,782,783],{},"rustc --explain E0382"," or online for detailed explanations.",[36,786,787,71,790,793],{},[24,788,789],{},"Build scripts",[73,791,792],{},"build.rs"," runs before compilation; use for linking C libs, generating code at build time.",[36,795,796,71,802,480,805,808,809,259],{},[24,797,798,801],{},[73,799,800],{},"CARGO_*"," env vars",[73,803,804],{},"CARGO_PKG_VERSION",[73,806,807],{},"CARGO_MANIFEST_DIR",", etc., useful in build scripts and via ",[73,810,811],{},"env!",[15,813,815],{"id":814},"reading-compiler-errors","Reading Compiler Errors",[20,817,818,819,822],{},"Rust errors are structured: the message, an ",[73,820,821],{},"-->"," pointing at the code, and often a help\u002Fnote. Multi-error cascades are common — fix the first error, then re-run; later ones often vanish.",[15,824,349],{"id":348},[20,826,827],{},"You can scaffold, build, run, format, lint, and document a project. Next: the type system starts with variables and mutability.",{"title":117,"searchDepth":357,"depth":357,"links":829},[830,831,835,837,839,843,844,845,846,847,848,849],{"id":390,"depth":357,"text":391},{"id":425,"depth":357,"text":832,"children":833},"println! is a Macro, Not a Function",[834],{"id":451,"depth":364,"text":452},{"id":505,"depth":357,"text":836},"Anatomy of main",{"id":554,"depth":357,"text":838},"Cargo: new vs init",{"id":571,"depth":357,"text":840,"children":841},"Cargo.toml Anatomy",[842],{"id":583,"depth":364,"text":584},{"id":629,"depth":357,"text":630},{"id":639,"depth":357,"text":640},{"id":656,"depth":357,"text":245},{"id":687,"depth":357,"text":688},{"id":710,"depth":357,"text":711},{"id":814,"depth":357,"text":815},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F02-hello-world-and-cargo",{"title":382,"description":117},"rust\u002F02-hello-world-and-cargo","zotiGtLP8a-v0d1SMGEp30WWyNqvlIM-3_lWO6aH_4M",{"id":856,"title":857,"body":858,"description":117,"extension":373,"meta":1311,"navigation":375,"path":1312,"seo":1313,"stem":1314,"__hash__":1315},"content\u002Frust\u002F03-variables-and-mutability.md","03 — Variables & Mutability",{"type":8,"value":859,"toc":1293},[860,863,870,876,887,891,904,910,916,970,976,980,986,1020,1026,1032,1036,1042,1066,1070,1076,1079,1085,1091,1096,1102,1106,1112,1122,1126,1265,1271,1274,1280,1282],[11,861,857],{"id":862},"_03-variables-mutability",[15,864,866,869],{"id":865},"let-and-immutability-by-default",[73,867,868],{},"let"," and Immutability by Default",[111,871,874],{"className":872,"code":873,"language":397,"meta":117},[395],"let x = 5;          \u002F\u002F immutable\nlet mut y = 5;      \u002F\u002F mutable\ny += 1;\n\u002F\u002F x = 6;           \u002F\u002F ERROR: cannot assign twice to immutable variable\n",[73,875,873],{"__ignoreMap":117},[20,877,878,879,882,883,886],{},"Rust variables are ",[24,880,881],{},"immutable by default",". You must opt into mutation with ",[73,884,885],{},"mut",". This isn't a philosophical stance — it lets the compiler reason about aliasing for ownership and concurrency guarantees.",[15,888,890],{"id":889},"shadowing","Shadowing",[20,892,893,894,896,897,900,901,903],{},"A new ",[73,895,868],{}," with the same name ",[183,898,899],{},"shadows"," the previous binding. The old value still gets dropped at end of scope; shadowing creates a ",[24,902,558],{}," binding (possibly a new type).",[111,905,908],{"className":906,"code":907,"language":397,"meta":117},[395],"let x = 5;\nlet x = x + 1;          \u002F\u002F shadows, same type\nlet x = x.to_string();  \u002F\u002F shadows with new type — totally fine\n\n{\n    let x = x * 2;      \u002F\u002F shadows inside block\n    println!(\"{x}\");    \u002F\u002F 12\n}\nprintln!(\"{x}\");        \u002F\u002F 6 (block shadow gone)\n",[73,909,907],{"__ignoreMap":117},[130,911,913,914],{"id":912},"shadowing-vs-mut","Shadowing vs ",[73,915,885],{},[917,918,919,935],"table",{},[920,921,922],"thead",{},[923,924,925,929,931],"tr",{},[926,927,928],"th",{},"Feature",[926,930,890],{},[926,932,933],{},[73,934,885],{},[936,937,938,950,959],"tbody",{},[923,939,940,944,947],{},[941,942,943],"td",{},"New binding?",[941,945,946],{},"Yes",[941,948,949],{},"No",[923,951,952,955,957],{},[941,953,954],{},"Can change type?",[941,956,946],{},[941,958,949],{},[923,960,961,964,967],{},[941,962,963],{},"Requires initialization at declaration?",[941,965,966],{},"No (can be uninit then assign)",[941,968,969],{},"Yes (must assign)",[20,971,972,973,975],{},"Use shadowing to transform a value into a different type\u002Fshape; use ",[73,974,885],{}," to evolve one value.",[15,977,979],{"id":978},"constants","Constants",[111,981,984],{"className":982,"code":983,"language":397,"meta":117},[395],"const MAX_POINTS: u32 = 100_000;\n",[73,985,983],{"__ignoreMap":117},[33,987,988,994,997,1003,1006,1009],{},[36,989,990,993],{},[73,991,992],{},"const"," is evaluated at compile time (must be a constant expression).",[36,995,996],{},"Always annotated with a type.",[36,998,999,1000,259],{},"Conventionally ",[73,1001,1002],{},"SCREAMING_SNAKE_CASE",[36,1004,1005],{},"Inlined everywhere; no fixed memory address.",[36,1007,1008],{},"Can be declared in any scope, including module\u002Fglobal.",[36,1010,1011,1012,1014,1015,1017,1018,259],{},"Cannot shadow ",[73,1013,885],{}," (they're always immutable); a ",[73,1016,992],{}," cannot be ",[73,1019,885],{},[111,1021,1024],{"className":1022,"code":1023,"language":397,"meta":117},[395],"const FACTOR: f64 = 1.5;\nconst fn double(x: i32) -> i32 { x * 2 }   \u002F\u002F const fn: callable in const context\nconst ANSWER: i32 = double(21);\n",[73,1025,1023],{"__ignoreMap":117},[20,1027,1028,1031],{},[73,1029,1030],{},"const fn"," allows a restricted subset of Rust at compile time (no heap, limited control flow historically; improving each release).",[15,1033,1035],{"id":1034},"statics","Statics",[111,1037,1040],{"className":1038,"code":1039,"language":397,"meta":117},[395],"static LANGUAGE: &str = \"Rust\";\nstatic mut COUNTER: u32 = 0;   \u002F\u002F mutable static — unsafe to read\u002Fwrite\n",[73,1041,1039],{"__ignoreMap":117},[33,1043,1044,1047,1056],{},[36,1045,1046],{},"Have a fixed memory address for the program's lifetime.",[36,1048,1049,1052,1053,1055],{},[73,1050,1051],{},"static mut"," requires ",[73,1054,197],{}," to access (no synchronization).",[36,1057,1058,1059,1062,1063,1065],{},"Use atomics (",[73,1060,1061],{},"std::sync::atomic",") instead of ",[73,1064,1051],{}," for counters.",[15,1067,1069],{"id":1068},"type-inference","Type Inference",[111,1071,1074],{"className":1072,"code":1073,"language":397,"meta":117},[395],"let v = vec![1, 2, 3];      \u002F\u002F Vec\u003Ci32>\nlet s = \"hi\";               \u002F\u002F &str\nlet n = 1.0;                \u002F\u002F f64 (default float)\nlet i = 1;                  \u002F\u002F i32 (default integer)\nlet b = true;\n",[73,1075,1073],{"__ignoreMap":117},[20,1077,1078],{},"When the type can't be inferred, add an annotation:",[111,1080,1083],{"className":1081,"code":1082,"language":397,"meta":117},[395],"let mut v: Vec\u003Cu8> = Vec::new();\nlet n: u64 = 42;\nlet parsed = \"42\".parse::\u003Ci32>().unwrap();\n",[73,1084,1082],{"__ignoreMap":117},[15,1086,1088,1090],{"id":1087},"let-patterns-destructuring",[73,1089,868],{}," Patterns (Destructuring)",[20,1092,1093,1095],{},[73,1094,868],{}," is a pattern, not just a binding:",[111,1097,1100],{"className":1098,"code":1099,"language":397,"meta":117},[395],"let (a, b, c) = (1, 2, 3);\nlet [first, ..] = [1, 2, 3];        \u002F\u002F slice pattern (limited on stable)\nlet (x, ..) = (1, 2, 3, 4);         \u002F\u002F ignore rest\nlet Point { x, y } = point;          \u002F\u002F struct destructuring\nlet (Ok(v) | Err(v)) = result.map(|n| n + 1).map_err(|e| 0); \u002F\u002F or-pattern binding\n",[73,1101,1099],{"__ignoreMap":117},[15,1103,1105],{"id":1104},"mutable-references-vs-mutable-variables","Mutable References vs Mutable Variables",[111,1107,1110],{"className":1108,"code":1109,"language":397,"meta":117},[395],"let mut v = vec![1, 2, 3];\nv.push(4);\n\nlet r = &mut v;     \u002F\u002F mutable reference (covered in Borrowing chapter)\nr.push(5);\n",[73,1111,1109],{"__ignoreMap":117},[20,1113,1114,1115,1118,1119,1121],{},"A ",[73,1116,1117],{},"&mut T"," requires the underlying binding to be ",[73,1120,885],{}," too (you can't take a mutable borrow of an immutable binding).",[15,1123,1125],{"id":1124},"edge-cases-pitfalls","Edge Cases & Pitfalls",[33,1127,1128,1146,1159,1171,1186,1219,1235,1245,1256],{},[36,1129,1130,71,1135,1138,1139,1141,1142,1145],{},[24,1131,1132,1133],{},"Unused ",[73,1134,885],{},[73,1136,1137],{},"warning: variable does not need to be mutable",". Fix by removing ",[73,1140,885],{}," or prefix ",[73,1143,1144],{},"_mut"," if intentional.",[36,1147,1148,71,1151,1154,1155,1158],{},[24,1149,1150],{},"Unused variables",[73,1152,1153],{},"let _x = 5;"," (leading underscore) suppresses the warning; ",[73,1156,1157],{},"_"," itself drops the value immediately.",[36,1160,1161,1166,1167,1170],{},[24,1162,1163],{},[73,1164,1165],{},"let _ = expr;"," evaluates ",[73,1168,1169],{},"expr"," then immediately drops the result — useful for side effects.",[36,1172,1173,1176,1177,1180,1181,1183,1184,259],{},[24,1174,1175],{},"Capture in closures",": a closure capturing ",[73,1178,1179],{},"x"," immutably borrows; capturing mutably requires the variable to be ",[73,1182,885],{}," and the closure itself ",[73,1185,885],{},[36,1187,1188,1191,1192,480,1195,480,1198,1201,1202,1204,1205,1208,1209,1212,1213,1212,1216,259],{},[24,1189,1190],{},"Const generics \u002F types in const",": types like ",[73,1193,1194],{},"Vec",[73,1196,1197],{},"String",[73,1199,1200],{},"Box"," can't live in ",[73,1203,992],{}," context (no heap at compile time), but they can in ",[73,1206,1207],{},"static"," only via ",[73,1210,1211],{},"lazy_static","\u002F",[73,1214,1215],{},"once_cell",[73,1217,1218],{},"std::sync::OnceLock",[36,1220,1221,71,1224,1227,1228,1231,1232,1234],{},[24,1222,1223],{},"Shadowing footgun",[73,1225,1226],{},"let x = something_that_panic();"," after ",[73,1229,1230],{},"let x = 5;"," — the first ",[73,1233,1179],{}," is shadowed and dropped at scope end, but the panic happens during init of the new binding.",[36,1236,1237,1240,1241,1244],{},[24,1238,1239],{},"Initialization required",": Rust has no \"uninitialized variable\" UB like C. ",[73,1242,1243],{},"let x: i32;"," followed by a read before any assignment is a compile error.",[36,1246,1247,71,1252,1255],{},[24,1248,1249,1251],{},[73,1250,868],{}," chains (unstable)",[73,1253,1254],{},"let Some(x) = opt && x > 0"," — not stable; use explicit checks.",[36,1257,1258,71,1261,1264],{},[24,1259,1260],{},"Tuples and unit",[73,1262,1263],{},"let () = some_fn();"," pattern-matches that the function returns unit; useful for \"I expect this to return nothing.\"",[15,1266,1268,1270],{"id":1267},"let-else-165",[73,1269,868],{},"-else (1.65+)",[20,1272,1273],{},"Diverge if a pattern doesn't match:",[111,1275,1278],{"className":1276,"code":1277,"language":397,"meta":117},[395],"let Some(x) = maybe_value else {\n    return; \u002F\u002F or panic!, break, continue, etc.\n};\n\u002F\u002F x is bound and in scope here\n",[73,1279,1277],{"__ignoreMap":117},[15,1281,349],{"id":348},[20,1283,1284,1285,1287,1288,1212,1290,1292],{},"Variables are immutable by default; use ",[73,1286,885],{}," for evolution, shadowing for transformation, ",[73,1289,992],{},[73,1291,1207],{}," for compile-time\u002Fglobal values. Next: the full type system.",{"title":117,"searchDepth":357,"depth":357,"links":1294},[1295,1297,1301,1302,1303,1304,1306,1307,1308,1310],{"id":865,"depth":357,"text":1296},"let and Immutability by Default",{"id":889,"depth":357,"text":890,"children":1298},[1299],{"id":912,"depth":364,"text":1300},"Shadowing vs mut",{"id":978,"depth":357,"text":979},{"id":1034,"depth":357,"text":1035},{"id":1068,"depth":357,"text":1069},{"id":1087,"depth":357,"text":1305},"let Patterns (Destructuring)",{"id":1104,"depth":357,"text":1105},{"id":1124,"depth":357,"text":1125},{"id":1267,"depth":357,"text":1309},"let-else (1.65+)",{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F03-variables-and-mutability",{"title":857,"description":117},"rust\u002F03-variables-and-mutability","KscGt0BSYz2a7uaQXIXky7CqEk6ZZyWcInWytmaopPs",{"id":1317,"title":1318,"body":1319,"description":117,"extension":373,"meta":2060,"navigation":375,"path":2061,"seo":2062,"stem":2063,"__hash__":2064},"content\u002Frust\u002F04-data-types.md","04 — Data Types",{"type":8,"value":1320,"toc":2035},[1321,1324,1328,1332,1443,1449,1454,1492,1498,1502,1518,1524,1551,1555,1580,1588,1597,1603,1639,1643,1647,1650,1656,1689,1693,1696,1702,1733,1743,1746,1750,1770,1776,1780,1786,1801,1807,1825,1831,1835,1841,1844,1848,1854,1857,1863,1868,1874,1890,1892,2027,2029],[11,1322,1318],{"id":1323},"_04-data-types",[15,1325,1327],{"id":1326},"scalar-types","Scalar Types",[130,1329,1331],{"id":1330},"integers","Integers",[917,1333,1334,1347],{},[920,1335,1336],{},[923,1337,1338,1341,1344],{},[926,1339,1340],{},"Type",[926,1342,1343],{},"Bits",[926,1345,1346],{},"Signed\u002FUnsigned",[936,1348,1349,1366,1381,1397,1412,1427],{},[923,1350,1351,1360,1363],{},[941,1352,1353,1356,1357],{},[73,1354,1355],{},"i8"," ",[73,1358,1359],{},"u8",[941,1361,1362],{},"8",[941,1364,1365],{},"signed\u002Funsigned",[923,1367,1368,1376,1379],{},[941,1369,1370,1356,1373],{},[73,1371,1372],{},"i16",[73,1374,1375],{},"u16",[941,1377,1378],{},"16",[941,1380],{},[923,1382,1383,1391,1394],{},[941,1384,1385,1356,1388],{},[73,1386,1387],{},"i32",[73,1389,1390],{},"u32",[941,1392,1393],{},"32",[941,1395,1396],{},"(default integer)",[923,1398,1399,1407,1410],{},[941,1400,1401,1356,1404],{},[73,1402,1403],{},"i64",[73,1405,1406],{},"u64",[941,1408,1409],{},"64",[941,1411],{},[923,1413,1414,1422,1425],{},[941,1415,1416,1356,1419],{},[73,1417,1418],{},"i128",[73,1420,1421],{},"u128",[941,1423,1424],{},"128",[941,1426],{},[923,1428,1429,1437,1440],{},[941,1430,1431,1356,1434],{},[73,1432,1433],{},"isize",[73,1435,1436],{},"usize",[941,1438,1439],{},"ptr-width (platform)",[941,1441,1442],{},"index\u002Fsizes",[111,1444,1447],{"className":1445,"code":1446,"language":397,"meta":117},[395],"let a: i32 = -5;\nlet b: u8 = 255;\nlet hex = 0xff;\nlet oct = 0o17;\nlet bin = 0b1010;\nlet byte = b'A';        \u002F\u002F u8 from byte literal -> 65\nlet big = 1_000_000;    \u002F\u002F underscores for readability\n",[73,1448,1446],{"__ignoreMap":117},[1450,1451,1453],"h4",{"id":1452},"integer-overflow","Integer Overflow",[33,1455,1456,1463,1469],{},[36,1457,1458,1459,1462],{},"In ",[24,1460,1461],{},"debug"," builds: overflow panics.",[36,1464,1458,1465,1468],{},[24,1466,1467],{},"release"," builds: wraps silently (two's complement).",[36,1470,1471,1472,480,1475,1478,1479,1482,1483,1478,1486,1482,1489,259],{},"Explicit methods: ",[73,1473,1474],{},"wrapping_add",[73,1476,1477],{},"checked_add"," (returns ",[73,1480,1481],{},"Option","), ",[73,1484,1485],{},"overflowing_add",[73,1487,1488],{},"(value, overflowed)",[73,1490,1491],{},"saturating_add",[111,1493,1496],{"className":1494,"code":1495,"language":397,"meta":117},[395],"let (val, ovf) = 255u8.overflowing_add(1); \u002F\u002F (0, true)\nlet safe = 255u8.checked_add(1);          \u002F\u002F None\nlet sat = 255u8.saturating_add(1);        \u002F\u002F 255\nlet wrap = 255u8.wrapping_add(1);         \u002F\u002F 0\n",[73,1497,1495],{"__ignoreMap":117},[130,1499,1501],{"id":1500},"floats","Floats",[20,1503,1504,480,1507,1510,1511,1212,1514,1517],{},[73,1505,1506],{},"f32",[73,1508,1509],{},"f64"," (default). IEEE 754. No ",[73,1512,1513],{},"f16",[73,1515,1516],{},"f128"," in std.",[111,1519,1522],{"className":1520,"code":1521,"language":397,"meta":117},[395],"let f = 2.0;        \u002F\u002F f64\nlet g: f32 = 3.0;\nlet inf = f64::INFINITY;\nlet nan = f64::NAN;\nnan == nan          \u002F\u002F false! NaN never equals itself\nnan.is_nan()        \u002F\u002F true\n",[73,1523,1521],{"__ignoreMap":117},[20,1525,1526,1527,1530,1531,1534,1535,1538,1539,1542,1543,1546,1547,1550],{},"Floats implement ",[73,1528,1529],{},"PartialOrd"," (not ",[73,1532,1533],{},"Ord",") because NaN has no total ordering. ",[73,1536,1537],{},"f64::NAN.partial_cmp(&f64::NAN)"," returns ",[73,1540,1541],{},"None",". Sorting floats requires ",[73,1544,1545],{},"sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))"," or ",[73,1548,1549],{},"total_cmp"," (1.62+, gives total ordering).",[130,1552,1554],{"id":1553},"booleans","Booleans",[20,1556,1557,1560,1561,1212,1564,1567,1568,1571,1572,1575,1576,1579],{},[73,1558,1559],{},"bool",", values ",[73,1562,1563],{},"true",[73,1565,1566],{},"false",", one byte. Cast with ",[73,1569,1570],{},"as"," to integer: ",[73,1573,1574],{},"true as u8 == 1",". Booleans are ",[183,1577,1578],{},"not"," integers (no implicit conversion in conditions or arithmetic).",[130,1581,1583,1584,1587],{"id":1582},"characters-char","Characters (",[73,1585,1586],{},"char",")",[20,1589,1590,1592,1593,1596],{},[73,1591,1586],{}," is a ",[24,1594,1595],{},"4-byte Unicode scalar value"," (not UTF-8 bytes, not a byte):",[111,1598,1601],{"className":1599,"code":1600,"language":397,"meta":117},[395],"let c = 'z';\nlet emoji = '🦀';\nlet heart = '\\u{2764}';\n",[73,1602,1600],{"__ignoreMap":117},[33,1604,1605,1619,1625],{},[36,1606,1607,559,1610,1613,1614,1616,1617,259],{},[73,1608,1609],{},"'A'",[73,1611,1612],{},"b'A'",": the first is ",[73,1615,1586],{}," (4 bytes), the second is ",[73,1618,1359],{},[36,1620,1621,1622,1624],{},"Surrogates (D800–DFFF) are not valid ",[73,1623,1586],{},"s.",[36,1626,1627,1628,1631,1632,1634,1635,1638],{},"Iterating ",[73,1629,1630],{},"&str"," yields ",[73,1633,1586],{},"s (decodes UTF-8); indexing ",[73,1636,1637],{},"s[0]"," panics (UTF-8 bytes don't align with chars).",[15,1640,1642],{"id":1641},"compound-types","Compound Types",[130,1644,1646],{"id":1645},"tuples","Tuples",[20,1648,1649],{},"Fixed-length, heterogeneous:",[111,1651,1654],{"className":1652,"code":1653,"language":397,"meta":117},[395],"let t: (i32, f64, &str) = (1, 2.0, \"three\");\nlet (a, b, c) = t;          \u002F\u002F destructuring\nlet first = t.0;\nlet unit: () = ();           \u002F\u002F unit type, zero-sized\n",[73,1655,1653],{"__ignoreMap":117},[33,1657,1658,1664,1673],{},[36,1659,1660,1661,259],{},"Single-element tuple: ",[73,1662,1663],{},"(x,)",[36,1665,1666,1667,1669,1670,1672],{},"The empty tuple ",[73,1668,525],{}," is the unit type (represents \"no meaningful value\", e.g., ",[73,1671,509],{},"'s return type).",[36,1674,1675,1678,1679,1681,1682,1684,1685,1688],{},[73,1676,1677],{},"0","-tuple ",[73,1680,525],{}," is inhabited by exactly one value ",[73,1683,525],{},". Useful as a ",[73,1686,1687],{},"HashMap"," value when you want a set.",[130,1690,1692],{"id":1691},"arrays","Arrays",[20,1694,1695],{},"Fixed length, same type, stack-allocated:",[111,1697,1700],{"className":1698,"code":1699,"language":397,"meta":117},[395],"let arr: [i32; 3] = [1, 2, 3];\nlet zeros = [0; 100];        \u002F\u002F 100 zeros\nlet first = arr[0];\nlet slice = &arr[1..3];\n",[73,1701,1699],{"__ignoreMap":117},[33,1703,1704,1714,1721,1727],{},[36,1705,1706,1707,1710,1711,259],{},"Length is part of the type: ",[73,1708,1709],{},"[i32; 3]"," != ",[73,1712,1713],{},"[i32; 4]",[36,1715,1716,1717,1720],{},"Out-of-bounds indexing ",[24,1718,1719],{},"panics"," at runtime with bounds checking.",[36,1722,1723,1726],{},[73,1724,1725],{},"arr.len()"," is a compile-time constant for arrays.",[36,1728,1729,1730,1732],{},"Arrays implement ",[73,1731,189],{}," since edition 2021 (by value).",[130,1734,1736,1737,480,1740,1587],{"id":1735},"slices-t-mut-t","Slices (",[73,1738,1739],{},"&[T]",[73,1741,1742],{},"&mut [T]",[20,1744,1745],{},"Dynamically-sized view into a contiguous sequence (covered in the Slices chapter). The fat-pointer representation: (pointer, length).",[15,1747,1749],{"id":1748},"strings-preview","Strings (preview)",[33,1751,1752,1757,1762],{},[36,1753,1754,1756],{},[73,1755,1630],{}," — borrowed string slice, UTF-8, immutable view, fat pointer (ptr+len).",[36,1758,1759,1761],{},[73,1760,1197],{}," — owned, growable UTF-8 string (heap).",[36,1763,1764,559,1767,1769],{},[73,1765,1766],{},"&[u8]",[73,1768,1630],{},": bytes vs decoded text.",[111,1771,1774],{"className":1772,"code":1773,"language":397,"meta":117},[395],"let s: &str = \"hello\";\nlet owned: String = String::from(\"hello\");\nlet bytes: &[u8] = b\"hello\";        \u002F\u002F &[u8; 5] \u002F &[u8]\n",[73,1775,1773],{"__ignoreMap":117},[15,1777,1779],{"id":1778},"function-types","Function Types",[111,1781,1784],{"className":1782,"code":1783,"language":397,"meta":117},[395],"fn add(a: i32, b: i32) -> i32 { a + b }\nlet f: fn(i32, i32) -> i32 = add;\n",[73,1785,1783],{"__ignoreMap":117},[20,1787,1788,1789,1792,1793,1796,1797,1800],{},"Function pointers (",[73,1790,1791],{},"fn(...) -> ...",") are zero-sized, ",[73,1794,1795],{},"Copy",", and implement ",[73,1798,1799],{},"Fn",". Closures have unnameable types (see Closures chapter).",[15,1802,1804,1805,1587],{"id":1803},"never-type","Never Type (",[73,1806,437],{},[20,1808,1809,1811,1812,480,1815,480,1818,1821,1822,1824],{},[73,1810,437],{}," is the never type (diverges). Functions like ",[73,1813,1814],{},"panic!",[73,1816,1817],{},"loop {}",[73,1819,1820],{},"std::process::exit"," return ",[73,1823,437],{},". It coerces to any type:",[111,1826,1829],{"className":1827,"code":1828,"language":397,"meta":117},[395],"let x: i32 = match opt {\n    Some(v) => v,\n    None => panic!(\"missing\"),   \u002F\u002F ! coerces to i32\n};\n",[73,1830,1828],{"__ignoreMap":117},[15,1832,1834],{"id":1833},"type-aliases","Type Aliases",[111,1836,1839],{"className":1837,"code":1838,"language":397,"meta":117},[395],"type Kilometers = i32;\ntype IntPair = (i32, i32);\n",[73,1840,1838],{"__ignoreMap":117},[20,1842,1843],{},"Aliases are purely nominal — no new type, no methods, just a shorthand.",[15,1845,1847],{"id":1846},"newtype-pattern-real-distinct-type","Newtype Pattern (real distinct type)",[111,1849,1852],{"className":1850,"code":1851,"language":397,"meta":117},[395],"struct Kilometers(i32);\nstruct Miles(i32);\n\u002F\u002F Kilometers and Miles are different types — no accidental mixing\n",[73,1853,1851],{"__ignoreMap":117},[20,1855,1856],{},"This is the idiomatic way to prevent unit confusion.",[15,1858,1860,1861,1587],{"id":1859},"casting-as","Casting (",[73,1862,1570],{},[20,1864,1865,1867],{},[73,1866,1570],{}," is a coarse numeric conversion (truncates, may wrap):",[111,1869,1872],{"className":1870,"code":1871,"language":397,"meta":117},[395],"let a = 1_000_000_000u32 as u8;     \u002F\u002F truncates -> 192 (low byte)\nlet f = 3.9_f32 as i32;             \u002F\u002F truncates toward zero -> 3\nlet b = true as u8;                 \u002F\u002F 1\nlet p = 42 as *const i32;\n",[73,1873,1871],{"__ignoreMap":117},[20,1875,1876,1877,1212,1880,1212,1883,1212,1886,1889],{},"Use ",[73,1878,1879],{},"From",[73,1881,1882],{},"Into",[73,1884,1885],{},"TryFrom",[73,1887,1888],{},"TryInto"," for safe, explicit conversions.",[15,1891,711],{"id":710},[33,1893,1894,1909,1919,1939,1961,1978,1992,2005,2016],{},[36,1895,1896,71,1899,592,1902,1904,1905,1908],{},[24,1897,1898],{},"Default int",[73,1900,1901],{},"let x = 1;",[73,1903,1387],{},". In a ",[73,1906,1907],{},"match"," arm that returns an integer, the inferred type can leak across arms.",[36,1910,1911,71,1914,592,1917,259],{},[24,1912,1913],{},"Default float",[73,1915,1916],{},"let x = 1.0;",[73,1918,1509],{},[36,1920,1921,71,1924,1927,1928,1930,1931,266,1933,1478,1936,1938],{},[24,1922,1923],{},"Char to int",[73,1925,1926],{},"'A' as u32"," → 65 (Unicode code point). ",[73,1929,1390],{}," to ",[73,1932,1586],{},[73,1934,1935],{},"char::from_u32",[73,1937,1481],{},", since not all u32 are valid chars).",[36,1940,1941,71,1946,1949,1950,1052,1953,1955,1956,1958,1959,526],{},[24,1942,1943,1945],{},[73,1944,1194],{}," of arrays",[73,1947,1948],{},"vec![[0; 3]; 4]"," works; ",[73,1951,1952],{},"vec![[1,2,3]; 4]",[73,1954,1795],{}," (arrays of ",[73,1957,1795],{}," are ",[73,1960,1795],{},[36,1962,1963,71,1966,480,1968,480,1971,1973,1974,1977],{},[24,1964,1965],{},"Zero-sized types (ZSTs)",[73,1967,525],{},[73,1969,1970],{},"struct Empty;",[73,1972,1970],{}," occupy 0 bytes; ",[73,1975,1976],{},"Vec\u003C()>"," is effectively a counter.",[36,1979,1980,1986,1987,1212,1989,1991],{},[24,1981,1982,1212,1984],{},[73,1983,1433],{},[73,1985,1436],{}," change with platform — don't rely on width in serialized data; use ",[73,1988,1403],{},[73,1990,1406],{}," explicitly.",[36,1993,1994,71,1997,2000,2001,2004],{},[24,1995,1996],{},"Integer literals overflow in source",[73,1998,1999],{},"let x: u8 = 255;"," is fine, but ",[73,2002,2003],{},"let x: u8 = 256;"," is a compile error.",[36,2006,2007,2012,2013,2015],{},[24,2008,2009,2011],{},[73,2010,1586],{}," size",": always 4 bytes even for ASCII; for ASCII use ",[73,2014,1359],{}," if memory matters.",[36,2017,2018,2026],{},[24,2019,2020,2022,2023],{},[73,2021,1570],{}," with ",[73,2024,2025],{},"f64::NAN as i32"," → 0 (platform-defined, not reliable).",[15,2028,349],{"id":348},[20,2030,2031,2032,2034],{},"You know the primitives, integers\u002Foverflow, floats\u002FNaN, tuples, arrays, and the distinction between ",[73,2033,1586],{}," and bytes. Next: functions.",{"title":117,"searchDepth":357,"depth":357,"links":2036},[2037,2044,2050,2051,2052,2054,2055,2056,2058,2059],{"id":1326,"depth":357,"text":1327,"children":2038},[2039,2040,2041,2042],{"id":1330,"depth":364,"text":1331},{"id":1500,"depth":364,"text":1501},{"id":1553,"depth":364,"text":1554},{"id":1582,"depth":364,"text":2043},"Characters (char)",{"id":1641,"depth":357,"text":1642,"children":2045},[2046,2047,2048],{"id":1645,"depth":364,"text":1646},{"id":1691,"depth":364,"text":1692},{"id":1735,"depth":364,"text":2049},"Slices (&[T], &mut [T])",{"id":1748,"depth":357,"text":1749},{"id":1778,"depth":357,"text":1779},{"id":1803,"depth":357,"text":2053},"Never Type (!)",{"id":1833,"depth":357,"text":1834},{"id":1846,"depth":357,"text":1847},{"id":1859,"depth":357,"text":2057},"Casting (as)",{"id":710,"depth":357,"text":711},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F04-data-types",{"title":1318,"description":117},"rust\u002F04-data-types","RAixSW-_DOpeDb7gA4oLO5bqvFNJYSj5hMhBHw3Unxs",{"id":2066,"title":2067,"body":2068,"description":117,"extension":373,"meta":2486,"navigation":375,"path":2487,"seo":2488,"stem":2489,"__hash__":2490},"content\u002Frust\u002F05-functions.md","05 — Functions",{"type":8,"value":2069,"toc":2466},[2070,2073,2077,2083,2106,2110,2116,2132,2136,2139,2145,2152,2158,2163,2169,2173,2180,2200,2204,2210,2217,2223,2259,2268,2286,2290,2297,2303,2315,2319,2325,2337,2341,2344,2350,2355,2358,2364,2370,2374,2380,2383,2385,2458,2460],[11,2071,2067],{"id":2072},"_05-functions",[15,2074,2076],{"id":2075},"basics","Basics",[111,2078,2081],{"className":2079,"code":2080,"language":397,"meta":117},[395],"fn add(a: i32, b: i32) -> i32 {\n    a + b            \u002F\u002F last expression — no semicolon — is the return value\n}\n\nfn no_return() {\n    println!(\"returns ()\");\n}\n",[73,2082,2080],{"__ignoreMap":117},[33,2084,2085,2092,2100],{},[36,2086,2087,2088,2091],{},"The last expression (without ",[73,2089,2090],{},";",") is the return value.",[36,2093,2094,2095,2097,2098,259],{},"A trailing ",[73,2096,2090],{}," makes it a statement returning ",[73,2099,525],{},[36,2101,2102,2105],{},[73,2103,2104],{},"return x;"," is for early returns; the implicit last-expression form is idiomatic for the common case.",[15,2107,2109],{"id":2108},"statements-vs-expressions","Statements vs Expressions",[111,2111,2114],{"className":2112,"code":2113,"language":397,"meta":117},[395],"let x = (let y = 5;);   \u002F\u002F ERROR: statements don't produce values\nlet y = {\n    let z = 5;\n    z + 1                \u002F\u002F expression — block evaluates to 6\n};\n",[73,2115,2113],{"__ignoreMap":117},[20,2117,2118,2119,2122,2123,480,2126,480,2128,2131],{},"Blocks ",[73,2120,2121],{},"{ ... }"," are expressions. ",[73,2124,2125],{},"if",[73,2127,1907],{},[73,2129,2130],{},"loop"," are also expressions.",[15,2133,2135],{"id":2134},"parameters-patterns","Parameters & Patterns",[20,2137,2138],{},"Parameters can be patterns:",[111,2140,2143],{"className":2141,"code":2142,"language":397,"meta":117},[395],"fn print_pair((a, b): (i32, i32)) { println!(\"{a} {b}\"); }\nfn first((a, _): (i32, i32)) -> i32 { a }\n",[73,2144,2142],{"__ignoreMap":117},[15,2146,2148,2149,1587],{"id":2147},"diverging-functions","Diverging Functions (",[73,2150,2151],{},"-> !",[111,2153,2156],{"className":2154,"code":2155,"language":397,"meta":117},[395],"fn forever() -> ! {\n    loop {}\n}\nfn die() -> ! {\n    panic!(\"bye\");\n}\n",[73,2157,2155],{"__ignoreMap":117},[20,2159,2160,2162],{},[73,2161,437],{}," coerces to any type, allowing it anywhere a value is expected:",[111,2164,2167],{"className":2165,"code":2166,"language":397,"meta":117},[395],"let v: i32 = match opt {\n    Some(x) => x,\n    None => die(),    \u002F\u002F ! coerces to i32\n};\n",[73,2168,2166],{"__ignoreMap":117},[15,2170,2172],{"id":2171},"default-optional-parameters","Default & Optional Parameters?",[20,2174,2175,2176,2179],{},"Rust has ",[24,2177,2178],{},"no function overloading or default parameters",". Use:",[33,2181,2182,2185,2193],{},[36,2183,2184],{},"Builder pattern",[36,2186,2187,2188,480,2190,1587],{},"Multiple methods (",[73,2189,558],{},[73,2191,2192],{},"with_capacity",[36,2194,2195,2196,1212,2198,1587],{},"Traits for \"overloading\" semantics (e.g., ",[73,2197,1879],{},[73,2199,1882],{},[15,2201,2203],{"id":2202},"generic-functions-preview","Generic Functions (preview)",[111,2205,2208],{"className":2206,"code":2207,"language":397,"meta":117},[395],"fn first\u003CT>(v: &[T]) -> Option\u003C&T> {\n    v.first()\n}\n\nfn max\u003CT: PartialOrd + Copy>(a: T, b: T) -> T {\n    if a > b { a } else { b }\n}\n",[73,2209,2207],{"__ignoreMap":117},[15,2211,2213,2216],{"id":2212},"impl-blocks-methods",[73,2214,2215],{},"impl"," Blocks (Methods)",[111,2218,2221],{"className":2219,"code":2220,"language":397,"meta":117},[395],"struct Rect { w: u32, h: u32 }\n\nimpl Rect {\n    fn area(&self) -> u32 { self.w * self.h }          \u002F\u002F method\n    fn new(w: u32, h: u32) -> Self { Rect { w, h } }    \u002F\u002F associated fn\n    fn set(&mut self, w: u32) { self.w = w; }           \u002F\u002F mut borrow\n}\n",[73,2222,2220],{"__ignoreMap":117},[33,2224,2225,2235,2241,2249],{},[36,2226,2227,2230,2231,2234],{},[73,2228,2229],{},"&self"," = ",[73,2232,2233],{},"self: &Self"," (immutable borrow).",[36,2236,2237,2240],{},[73,2238,2239],{},"&mut self"," = mutable borrow.",[36,2242,2243,2246,2247,259],{},[73,2244,2245],{},"self"," (by value) = consumes ",[73,2248,2245],{},[36,2250,2251,2252,2254,2255,2258],{},"Associated functions (no ",[73,2253,2245],{},") called as ",[73,2256,2257],{},"Rect::new(...)"," (like static methods).",[15,2260,2262,27,2265,2267],{"id":2261},"self-and-self-keywords",[73,2263,2264],{},"Self",[73,2266,2245],{}," Keywords",[20,2269,2270,2272,2273,2275,2276,2278,2279,2281,2282,2285],{},[73,2271,2264],{}," is the type the ",[73,2274,2215],{}," is for. ",[73,2277,2245],{}," is the receiver shorthand. ",[73,2280,2264],{}," in a ",[73,2283,2284],{},"trait"," body refers to the implementing type.",[15,2287,2289],{"id":2288},"variadic-functions","Variadic Functions",[20,2291,2292,2293,2296],{},"Only ",[73,2294,2295],{},"extern \"C\""," FFI functions can be C-style variadic:",[111,2298,2301],{"className":2299,"code":2300,"language":397,"meta":117},[395],"extern \"C\" {\n    fn printf(fmt: *const u8, ...) -> i32;\n}\n",[73,2302,2300],{"__ignoreMap":117},[20,2304,2305,2306,480,2308,2311,2312,526],{},"Idiomatic variadic-ness comes from macros (",[73,2307,428],{},[73,2309,2310],{},"vec!",") or slices (",[73,2313,2314],{},"fn sum(nums: &[i32])",[15,2316,2318],{"id":2317},"function-pointers-vs-closures","Function Pointers vs Closures",[111,2320,2323],{"className":2321,"code":2322,"language":397,"meta":117},[395],"fn add(a: i32, b: i32) -> i32 { a + b }\nlet fp: fn(i32, i32) -> i32 = add;       \u002F\u002F function pointer, Copy, Sized\nlet cl = |a, b| a + b;                    \u002F\u002F closure, captures env, !Sized\n",[73,2324,2322],{"__ignoreMap":117},[20,2326,2327,2328,1212,2330,1212,2333,2336],{},"See the Closures chapter for ",[73,2329,1799],{},[73,2331,2332],{},"FnMut",[73,2334,2335],{},"FnOnce"," distinctions.",[15,2338,2340],{"id":2339},"recursion","Recursion",[20,2342,2343],{},"Rust doesn't guarantee tail-call optimization. Deep recursion can overflow the stack. For deep\u002Fiterative algorithms, convert to an explicit loop with a stack.",[111,2345,2348],{"className":2346,"code":2347,"language":397,"meta":117},[395],"fn fact(n: u64) -> u64 {\n    if n == 0 { 1 } else { n * fact(n - 1) }\n}\n",[73,2349,2347],{"__ignoreMap":117},[15,2351,2353],{"id":2352},"const-fn",[73,2354,1030],{},[20,2356,2357],{},"Compile-time-callable functions with a restricted feature set:",[111,2359,2362],{"className":2360,"code":2361,"language":397,"meta":117},[395],"const fn square(x: i32) -> i32 { x * x }\nconst N: i32 = square(5);   \u002F\u002F evaluated at compile time\n",[73,2363,2361],{"__ignoreMap":117},[20,2365,2366,2367,2369],{},"Each release expands what's allowed in ",[73,2368,1030],{}," (loops, mutable locals, etc.).",[15,2371,2373],{"id":2372},"calling-conventions-abi","Calling Conventions & ABI",[111,2375,2378],{"className":2376,"code":2377,"language":397,"meta":117},[395],"extern \"C\" fn c_fn(x: i32) -> i32 { x + 1 }\nextern \"Rust\" fn rust_fn(x: i32) -> i32 { x + 1 }   \u002F\u002F default\nextern \"C\" { fn imported(x: i32) -> i32; }\n",[73,2379,2377],{"__ignoreMap":117},[20,2381,2382],{},"Useful for FFI and callbacks passed to C libraries.",[15,2384,711],{"id":710},[33,2386,2387,2406,2418,2431,2440,2446],{},[36,2388,2389,71,2395,2397,2398,2401,2402,2405],{},[24,2390,2391,2394],{},[73,2392,2393],{},"return"," in a closure",[73,2396,2393],{}," inside a closure returns from the ",[183,2399,2400],{},"closure",", not the enclosing function (unlike some languages). Use labeled loops\u002Fbreaks or ",[73,2403,2404],{},"?"," carefully.",[36,2407,2408,2411,2412,2414,2415,2417],{},[24,2409,2410],{},"Block-as-expression footgun",": forgetting the trailing ",[73,2413,2090],{}," returns the value; adding it silently changes the return type to ",[73,2416,525],{},". The compiler catches this.",[36,2419,2420,2428,2429,259],{},[24,2421,2422,2425,2426],{},[73,2423,2424],{},"fn"," types are ",[73,2427,1795],{},": you can copy function pointers freely; closures are not necessarily ",[73,2430,1795],{},[36,2432,2433,71,2436,2439],{},[24,2434,2435],{},"Lifetime elision in fn signatures",[73,2437,2438],{},"fn first(s: &str) -> &str"," has elided lifetimes; the compiler infers one input lifetime → output lifetime.",[36,2441,2442,2445],{},[24,2443,2444],{},"Recursion + generics",": monomorphized per type — code bloat risk.",[36,2447,2448,2453,2454,2457],{},[24,2449,2450],{},[73,2451,2452],{},"#[inline]",": a hint; ",[73,2455,2456],{},"#[inline(always)]"," can bloat code; usually trust the compiler.",[15,2459,349],{"id":348},[20,2461,2462,2463,2465],{},"Functions are expressions, support patterns in parameters, can diverge, have no overloading, and methods live in ",[73,2464,2215],{}," blocks. Next: control flow.",{"title":117,"searchDepth":357,"depth":357,"links":2467},[2468,2469,2470,2471,2473,2474,2475,2477,2479,2480,2481,2482,2483,2484,2485],{"id":2075,"depth":357,"text":2076},{"id":2108,"depth":357,"text":2109},{"id":2134,"depth":357,"text":2135},{"id":2147,"depth":357,"text":2472},"Diverging Functions (-> !)",{"id":2171,"depth":357,"text":2172},{"id":2202,"depth":357,"text":2203},{"id":2212,"depth":357,"text":2476},"impl Blocks (Methods)",{"id":2261,"depth":357,"text":2478},"Self and self Keywords",{"id":2288,"depth":357,"text":2289},{"id":2317,"depth":357,"text":2318},{"id":2339,"depth":357,"text":2340},{"id":2352,"depth":357,"text":1030},{"id":2372,"depth":357,"text":2373},{"id":710,"depth":357,"text":711},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F05-functions",{"title":2067,"description":117},"rust\u002F05-functions","vqYAeWaYt-UYkTMrcZVaixykCAb7NqmRQbxcbqlp0EE",{"id":2492,"title":2493,"body":2494,"description":117,"extension":373,"meta":3012,"navigation":375,"path":3013,"seo":3014,"stem":3015,"__hash__":3016},"content\u002Frust\u002F06-control-flow.md","06 — Control Flow",{"type":8,"value":2495,"toc":2987},[2496,2499,2505,2511,2543,2549,2553,2563,2569,2573,2579,2592,2597,2603,2609,2616,2622,2633,2637,2640,2646,2695,2699,2705,2712,2718,2724,2730,2736,2744,2759,2765,2771,2794,2798,2941,2950,2956,2966,2968],[11,2497,2493],{"id":2498},"_06-control-flow",[15,2500,2502,2504],{"id":2501},"if-expressions",[73,2503,2125],{}," Expressions",[111,2506,2509],{"className":2507,"code":2508,"language":397,"meta":117},[395],"let n = 5;\nif n > 0 {\n    println!(\"positive\");\n} else if n \u003C 0 {\n    println!(\"negative\");\n} else {\n    println!(\"zero\");\n}\n\nlet sign = if n > 0 { 1 } else { -1 };   \u002F\u002F if is an expression\n",[73,2510,2508],{"__ignoreMap":117},[33,2512,2513,2522,2537],{},[36,2514,2515,2516,2519,2520,526],{},"Branches must return the ",[24,2517,2518],{},"same type"," (or ",[73,2521,437],{},[36,2523,2524,2525,2527,2528,2531,2532,2534,2535,259],{},"The condition must be a ",[73,2526,1559],{}," — no truthy integers, no ",[73,2529,2530],{},"if x { }"," where ",[73,2533,1179],{}," is ",[73,2536,1387],{},[36,2538,2539,2542],{},[73,2540,2541],{},"if let"," combines pattern match + branch.",[111,2544,2547],{"className":2545,"code":2546,"language":397,"meta":117},[395],"if let Some(v) = opt {\n    println!(\"{v}\");\n}\n",[73,2548,2546],{"__ignoreMap":117},[15,2550,2551],{"id":2130},[73,2552,2130],{},[20,2554,2555,2556,2559,2560,2562],{},"Infinite loop until ",[73,2557,2558],{},"break",". ",[73,2561,2558],{}," can return a value:",[111,2564,2567],{"className":2565,"code":2566,"language":397,"meta":117},[395],"let mut i = 0;\nlet result = loop {\n    if i == 10 { break i * 2; }\n    i += 1;\n};\n",[73,2568,2566],{"__ignoreMap":117},[130,2570,2572],{"id":2571},"labeled-loops","Labeled Loops",[111,2574,2577],{"className":2575,"code":2576,"language":397,"meta":117},[395],"'outer: for i in 0..3 {\n    for j in 0..3 {\n        if i == j { continue 'outer; }\n        if i + j > 3 { break 'outer; }\n        println!(\"{i},{j}\");\n    }\n}\n",[73,2578,2576],{"__ignoreMap":117},[20,2580,2581,2582,2559,2585,27,2588,2591],{},"Labels start with ",[73,2583,2584],{},"'",[73,2586,2587],{},"break 'label",[73,2589,2590],{},"continue 'label"," control the outer loop.",[15,2593,2595],{"id":2594},"while",[73,2596,2594],{},[111,2598,2601],{"className":2599,"code":2600,"language":397,"meta":117},[395],"let mut n = 5;\nwhile n > 0 {\n    n -= 1;\n}\n\nwhile let Some(x) = stack.pop() {\n    println!(\"{x}\");\n}\n",[73,2602,2600],{"__ignoreMap":117},[20,2604,2605,2608],{},[73,2606,2607],{},"while let"," repeatedly matches; exits when pattern fails.",[15,2610,2612,2615],{"id":2611},"for-iterator-based",[73,2613,2614],{},"for"," (Iterator Based)",[111,2617,2620],{"className":2618,"code":2619,"language":397,"meta":117},[395],"for i in 0..5 { print!(\"{i} \"); }        \u002F\u002F 0 1 2 3 4\nfor i in 0..=5 { print!(\"{i} \"); }       \u002F\u002F inclusive 0..5\nfor c in \"abc\".chars() { print!(\"{c}\"); }\nfor b in &[1, 2, 3] { print!(\"{b} \"); }   \u002F\u002F borrows\nfor v in vec![1, 2, 3] { print!(\"{v} \"); } \u002F\u002F consumes\n",[73,2621,2619],{"__ignoreMap":117},[20,2623,2624,2626,2627,2629,2630,2632],{},[73,2625,2614],{}," consumes an ",[73,2628,189],{},". Arrays implement ",[73,2631,189],{}," (by value) since edition 2021.",[15,2634,2635],{"id":1907},[73,2636,1907],{},[20,2638,2639],{},"Exhaustive pattern matching. Powerful and central to Rust:",[111,2641,2644],{"className":2642,"code":2643,"language":397,"meta":117},[395],"match x {\n    0 => \"zero\",\n    1 | 2 => \"small\",\n    3..=9 => \"medium\",\n    n if n \u003C 100 => \"big\",     \u002F\u002F match guard\n    _ => \"huge\",\n};\n",[73,2645,2643],{"__ignoreMap":117},[33,2647,2648,2654,2657,2660,2666,2676,2686],{},[36,2649,2650,2651,2653],{},"Must be exhaustive; ",[73,2652,1157],{}," is the wildcard.",[36,2655,2656],{},"Arms evaluate to a single common type.",[36,2658,2659],{},"Order matters; first matching arm wins.",[36,2661,2662,2663,259],{},"Multiple patterns with ",[73,2664,2665],{},"|",[36,2667,2668,2669,2672,2673,2675],{},"Ranges with ",[73,2670,2671],{},"..="," (only for ",[73,2674,1586],{}," and numeric types).",[36,2677,2678,2681,2682,2685],{},[24,2679,2680],{},"Match guards"," (",[73,2683,2684],{},"if cond",") enable extra conditions but can prevent exhaustiveness analysis.",[36,2687,2688,2689,71,2692,259],{},"Binding with ",[73,2690,2691],{},"@",[73,2693,2694],{},"Some(n @ 1..=10) => n",[130,2696,2698],{"id":2697},"binding-modes-2021-edition","Binding Modes (2021 edition)",[111,2700,2703],{"className":2701,"code":2702,"language":397,"meta":117},[395],"match &opt {\n    Some(x) => println!(\"{x}\"),   \u002F\u002F x: &i32 — auto-ref\n    None => {}\n}\n",[73,2704,2702],{"__ignoreMap":117},[20,2706,2707,2708,2711],{},"The 2021 edition \"default binding modes\" let you avoid writing ",[73,2709,2710],{},"&"," everywhere; the compiler inserts references as needed. This can be subtle — see the Patterns chapter.",[15,2713,2715,2717],{"id":2714},"match-on-references",[73,2716,1907],{}," on References",[111,2719,2722],{"className":2720,"code":2721,"language":397,"meta":117},[395],"match &s {\n    &\"yes\" => 1,\n    _ => 0,\n}\n\u002F\u002F or pattern-match by value of &str (Copy):\nmatch s.as_str() {\n    \"yes\" => 1,\n    _ => 0,\n}\n",[73,2723,2721],{"__ignoreMap":117},[15,2725,2727,2728],{"id":2726},"destructuring-in-match","Destructuring in ",[73,2729,1907],{},[111,2731,2734],{"className":2732,"code":2733,"language":397,"meta":117},[395],"enum Shape { Circle(f64), Square(f64), Rect(f64, f64) }\nmatch shape {\n    Shape::Circle(r) => 3.14 * r * r,\n    Shape::Square(s) => s * s,\n    Shape::Rect(a, b) if a == b => a * a,    \u002F\u002F guard: catch squares\n    Shape::Rect(a, b) => a * b,\n}\n",[73,2735,2733],{"__ignoreMap":117},[15,2737,2739,2740,559,2742],{"id":2738},"returning-from-match-vs-break","Returning from ",[73,2741,1907],{},[73,2743,2558],{},[20,2745,2746,2748,2749,480,2751,480,2753,2755,2756,259],{},[73,2747,1907],{}," is an expression. To short-circuit, use ",[73,2750,2393],{},[73,2752,2558],{},[73,2754,2404],{},", or ",[73,2757,2758],{},"continue",[15,2760,2762,2764],{"id":2761},"operator-error-propagation",[73,2763,2404],{}," Operator (Error Propagation)",[111,2766,2769],{"className":2767,"code":2768,"language":397,"meta":117},[395],"fn parse_and_double(s: &str) -> Result\u003Ci32, ParseIntError> {\n    let n: i32 = s.parse()?;\n    Ok(n * 2)\n}\n",[73,2770,2768],{"__ignoreMap":117},[20,2772,2773,2775,2776,2519,2779,2022,2781,2783,2784,2787,2788,1212,2790,2793],{},[73,2774,2404],{}," returns early from the function on ",[73,2777,2778],{},"Err",[73,2780,1541],{},[73,2782,1481],{},"). Works on anything implementing ",[73,2785,2786],{},"Try"," (stabilized for ",[73,2789,1481],{},[73,2791,2792],{},"Result","). See Error Handling chapter.",[15,2795,2797],{"id":2796},"control-flow-edge-cases","Control-Flow Edge Cases",[33,2799,2800,2814,2828,2842,2853,2867,2875,2892,2902,2919],{},[36,2801,2802,2810,2811,2813],{},[24,2803,2804,2806,2807,2809],{},[73,2805,2125],{}," returning ",[73,2808,525],{}," vs value",": forgetting the trailing expr in one arm gives ",[73,2812,525],{}," and a type mismatch error.",[36,2815,2816,2821,2822,2824,2825,2827],{},[24,2817,2818,2820],{},[73,2819,2558],{}," value type",": every ",[73,2823,2558],{}," in the same ",[73,2826,2130],{}," must return the same type.",[36,2829,2830,2836,2837,2839,2840,259],{},[24,2831,2832,2534,2834],{},[73,2833,2758],{},[73,2835,525],{},": can't use ",[73,2838,2758],{}," to return a value from a ",[73,2841,2130],{},[36,2843,2844,2849,2850,259],{},[24,2845,2846,2848],{},[73,2847,2614],{}," consumes the iterator",": can't easily get the index — use ",[73,2851,2852],{},".enumerate()",[36,2854,2855,71,2861,2863,2864,2866],{},[24,2856,2857,559,2859],{},[73,2858,2607],{},[73,2860,2541],{},[73,2862,2594],{}," loops; ",[73,2865,2125],{}," runs once.",[36,2868,2869,2874],{},[24,2870,2871,2873],{},[73,2872,1907],{}," arm trailing comma",": optional but idiomatic.",[36,2876,2877,2883,2884,2887,2888,2891],{},[24,2878,2879,2880,2882],{},"Empty ",[73,2881,1907],{}," on a non-exhaustive enum"," across crates requires ",[73,2885,2886],{},"_ => unreachable!()"," because adding variants is a non-breaking change for the upstream crate (unless ",[73,2889,2890],{},"#[non_exhaustive]"," rules apply).",[36,2893,2894,2898,2899,2901],{},[24,2895,2896],{},[73,2897,2890],{}," on an enum forces downstream code to include a ",[73,2900,1157],{}," arm even if all current variants are matched (future-proofing).",[36,2903,2904,71,2907,480,2910,2913,2914,27,2916,2918],{},[24,2905,2906],{},"Short-circuit evaluation",[73,2908,2909],{},"&&",[73,2911,2912],{},"||"," short-circuit. ",[73,2915,2710],{},[73,2917,2665],{}," are bitwise and don't.",[36,2920,2921,2927,2928,1212,2930,2933,2934,1212,2937,2940],{},[24,2922,2923,2924],{},"No ternary ",[73,2925,2926],{},"?:",": use ",[73,2929,2125],{},[73,2931,2932],{},"else"," expressions, or ",[73,2935,2936],{},".then()",[73,2938,2939],{},".unwrap_or()"," on bools.",[15,2942,2944,2946,2947],{"id":2943},"if-let-chains-unstable-let-else",[73,2945,2541],{}," chains (unstable) \u002F ",[73,2948,2949],{},"let-else",[111,2951,2954],{"className":2952,"code":2953,"language":397,"meta":117},[395],"let Some(x) = opt else { return; };\n",[73,2955,2953],{"__ignoreMap":117},[20,2957,2958,2960,2961,2963,2964,259],{},[73,2959,2949],{}," is the idiomatic early-return form. For multiple conditions, use nested ",[73,2962,2949],{}," or a ",[73,2965,1907],{},[15,2967,349],{"id":348},[20,2969,2970,1212,2972,1212,2974,1212,2976,1212,2978,2980,2981,2983,2984,2986],{},[73,2971,2125],{},[73,2973,2594],{},[73,2975,2614],{},[73,2977,2130],{},[73,2979,1907],{}," are all expressions. ",[73,2982,1907],{}," is exhaustive and central. ",[73,2985,2404],{}," propagates errors. Labels disambiguate nested loops. Next: the famous Ownership model.",{"title":117,"searchDepth":357,"depth":357,"links":2988},[2989,2991,2994,2995,2997,3000,3002,3004,3006,3008,3009,3011],{"id":2501,"depth":357,"text":2990},"if Expressions",{"id":2130,"depth":357,"text":2130,"children":2992},[2993],{"id":2571,"depth":364,"text":2572},{"id":2594,"depth":357,"text":2594},{"id":2611,"depth":357,"text":2996},"for (Iterator Based)",{"id":1907,"depth":357,"text":1907,"children":2998},[2999],{"id":2697,"depth":364,"text":2698},{"id":2714,"depth":357,"text":3001},"match on References",{"id":2726,"depth":357,"text":3003},"Destructuring in match",{"id":2738,"depth":357,"text":3005},"Returning from match vs break",{"id":2761,"depth":357,"text":3007},"? Operator (Error Propagation)",{"id":2796,"depth":357,"text":2797},{"id":2943,"depth":357,"text":3010},"if let chains (unstable) \u002F let-else",{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F06-control-flow",{"title":2493,"description":117},"rust\u002F06-control-flow","antzhfGBbVo8k2NuHFO5cD9ZFBwuSC4tGz1VdBAIlmE",{"id":3018,"title":3019,"body":3020,"description":3503,"extension":373,"meta":3504,"navigation":375,"path":3505,"seo":3506,"stem":3507,"__hash__":3508},"content\u002Frust\u002F07-ownership.md","07 — Ownership (The Heart of Rust)",{"type":8,"value":3021,"toc":3480},[3022,3025,3032,3036,3063,3067,3085,3091,3103,3110,3115,3140,3155,3161,3165,3171,3177,3183,3187,3193,3196,3200,3207,3213,3227,3235,3249,3253,3259,3262,3266,3323,3330,3337,3343,3346,3355,3366,3370,3373,3393,3400,3406,3425,3434,3440,3443,3445,3473],[11,3023,3019],{"id":3024},"_07-ownership-the-heart-of-rust",[20,3026,3027,3028,3031],{},"Ownership is ",[24,3029,3030],{},"the"," defining feature of Rust. Every other memory-safety guarantee flows from these rules.",[15,3033,3035],{"id":3034},"the-three-rules","The Three Rules",[3037,3038,3039,3046,3053],"ol",{},[36,3040,3041,3042,3045],{},"Each value has exactly one ",[24,3043,3044],{},"owner"," (a variable).",[36,3047,3048,3049,3052],{},"When the owner goes out of scope, the value is ",[24,3050,3051],{},"dropped"," (its destructor runs).",[36,3054,3055,3056,3059,3060,3062],{},"Assigning or passing a value ",[24,3057,3058],{},"moves"," it (for non-",[73,3061,1795],{}," types) — the old binding becomes invalid.",[15,3064,3066],{"id":3065},"stack-vs-heap","Stack vs Heap",[33,3068,3069,3075],{},[36,3070,3071,3072,3074],{},"Stack: fast, LIFO, fixed-size values (integers, ",[73,3073,1559],{},", fixed arrays, pointers).",[36,3076,3077,3078,480,3080,480,3082,3084],{},"Heap: dynamic, slower, runtime-allocated (",[73,3079,1200],{},[73,3081,1194],{},[73,3083,1197],{},"). Ownership primarily concerns heap data.",[111,3086,3089],{"className":3087,"code":3088,"language":397,"meta":117},[395],"let s1 = String::from(\"hi\");   \u002F\u002F heap allocation\nlet s2 = s1;                    \u002F\u002F MOVE — s1 is now invalid\n\u002F\u002F println!(\"{s1}\");            \u002F\u002F ERROR: borrow of moved value\n",[73,3090,3088],{"__ignoreMap":117},[20,3092,3093,2534,3095,3098,3099,3102],{},[73,3094,1197],{},[73,3096,3097],{},"{ ptr, len, capacity }"," (stack) pointing to heap bytes. A move copies the stack header and ",[24,3100,3101],{},"invalidates the old binding"," so you can't have two owners trying to free the same heap memory.",[15,3104,3106,3107,3109],{"id":3105},"the-copy-trait","The ",[73,3108,1795],{}," Trait",[20,3111,3112,3113,170],{},"Types whose bits can be trivially copied without invalidating the source are ",[73,3114,1795],{},[33,3116,3117,3120,3126,3134],{},[36,3118,3119],{},"All integer\u002Ffloat\u002Fbool\u002Fchar types.",[36,3121,3122,3123,3125],{},"Tuples\u002Farrays of ",[73,3124,1795],{}," types.",[36,3127,3128,3131,3132,526],{},[73,3129,3130],{},"&T"," (shared references are ",[73,3133,1795],{},[36,3135,3136,3137,259],{},"Function pointers ",[73,3138,3139],{},"fn(...)",[20,3141,3142,3143,3145,3146,480,3148,480,3150,480,3152,3154],{},"Non-",[73,3144,1795],{}," types (heap-ish): ",[73,3147,1197],{},[73,3149,1194],{},[73,3151,1200],{},[73,3153,1687],{},", any type with a destructor or that owns a resource.",[111,3156,3159],{"className":3157,"code":3158,"language":397,"meta":117},[395],"let a = 5;\nlet b = a;            \u002F\u002F i32 is Copy — a is still valid\nprintln!(\"{a} {b}\");  \u002F\u002F fine\n",[73,3160,3158],{"__ignoreMap":117},[15,3162,3164],{"id":3163},"move-semantics-in-functions","Move Semantics in Functions",[111,3166,3169],{"className":3167,"code":3168,"language":397,"meta":117},[395],"fn take(s: String) { println!(\"{s}\"); }\n\nlet s = String::from(\"hi\");\ntake(s);\n\u002F\u002F s is now invalid — moved into the function\n",[73,3170,3168],{"__ignoreMap":117},[20,3172,3173,3174,170],{},"To keep ownership, pass by reference or ",[73,3175,3176],{},"clone()",[111,3178,3181],{"className":3179,"code":3180,"language":397,"meta":117},[395],"take(s.clone());      \u002F\u002F s still owned here\ntake(&s);             \u002F\u002F pass reference (covered in References chapter)\n",[73,3182,3180],{"__ignoreMap":117},[15,3184,3186],{"id":3185},"returning-ownership","Returning Ownership",[111,3188,3191],{"className":3189,"code":3190,"language":397,"meta":117},[395],"fn make() -> String { String::from(\"hi\") }\nlet s = make();       \u002F\u002F ownership moves to caller\n",[73,3192,3190],{"__ignoreMap":117},[20,3194,3195],{},"Returning transfers ownership out without a copy. This is the Rust idiom for \"constructing\" data.",[15,3197,3199],{"id":3198},"drop-order","Drop Order",[20,3201,3202,3203,3206],{},"Destructors run in ",[24,3204,3205],{},"reverse declaration order"," within a scope:",[111,3208,3211],{"className":3209,"code":3210,"language":397,"meta":117},[395],"{\n    let a = String::from(\"a\");\n    let b = String::from(\"b\");\n    \u002F\u002F b drops, then a drops\n}\n",[73,3212,3210],{"__ignoreMap":117},[20,3214,3215,3218,3219,3222,3223,3226],{},[73,3216,3217],{},"Drop"," trait's ",[73,3220,3221],{},"drop(&mut self)"," is the destructor. You usually don't call it manually — use ",[73,3224,3225],{},"std::mem::drop(value)"," to drop early.",[15,3228,3230,27,3232,3234],{"id":3229},"drop-and-copy-are-mutually-exclusive",[73,3231,3217],{},[73,3233,1795],{}," are Mutually Exclusive",[20,3236,3237,3238,1017,3240,3242,3243,3245,3246,3248],{},"A type with a custom ",[73,3239,3217],{},[73,3241,1795],{}," (you can't derive both). ",[73,3244,1795],{}," means \"duplicate bits\"; ",[73,3247,3217],{}," means \"do something on cleanup\" — duplicating would risk double-cleanup.",[15,3250,3252],{"id":3251},"partial-moves","Partial Moves",[111,3254,3257],{"className":3255,"code":3256,"language":397,"meta":117},[395],"struct Person { name: String, age: u32 }\nlet p = Person { name: \"Ada\".into(), age: 36 };\nlet n = p.name;       \u002F\u002F partial move — p.name is moved, p.age still valid\n\u002F\u002F println!(\"{}\", p); \u002F\u002F ERROR: p partially moved\nprintln!(\"{}\", p.age); \u002F\u002F OK — only name was moved\n",[73,3258,3256],{"__ignoreMap":117},[20,3260,3261],{},"You can still access non-moved fields after a partial move.",[15,3263,3265],{"id":3264},"move-footguns","Move Footguns",[33,3267,3268,3284,3296,3309],{},[36,3269,3270,71,3273,3276,3277,3280,3281,3283],{},[24,3271,3272],{},"Closure captures",[73,3274,3275],{},"|| use_s(s)"," moves ",[73,3278,3279],{},"s"," into the closure if ",[73,3282,3279],{}," is consumed inside.",[36,3285,3286,3295],{},[24,3287,3288,1212,3290,292,3292,3294],{},[73,3289,1194],{},[73,3291,1197],{},[73,3293,1907],{}," arms",": moving a value out in one arm invalidates it in others; the compiler ensures all paths move or none do.",[36,3297,3298,3301,3302,3305,3306,3308],{},[24,3299,3300],{},"Field reorder \u002F re-init",": after a partial move, you can reassign the moved field (",[73,3303,3304],{},"p.name = \"Bob\".into();",") to make ",[73,3307,20],{}," whole again.",[36,3310,3311,71,3316,3319,3320,3322],{},[24,3312,3313,3315],{},[73,3314,885],{}," binding of a moved value",[73,3317,3318],{},"let mut s = String::new(); let t = s; s = String::from(\"x\");"," — re-binding is fine; ",[73,3321,3279],{}," was invalid between the move and reassignment.",[15,3324,3326,3329],{"id":3325},"drop-order-in-structs",[73,3327,3328],{},"drop"," Order in Structs",[20,3331,3332,3333,3336],{},"Struct fields drop in ",[24,3334,3335],{},"declaration order"," (NOT reverse), per RFC 1857. This is a common surprise:",[111,3338,3341],{"className":3339,"code":3340,"language":397,"meta":117},[395],"struct A { \u002F* ... *\u002F }\nimpl Drop for A { fn drop(&mut self) { println!(\"A dropped\"); } }\n\nstruct Pair { first: A, second: A }\n\u002F\u002F when a Pair is dropped: first drops, then second\n",[73,3342,3340],{"__ignoreMap":117},[20,3344,3345],{},"Tuple fields drop in order 0, 1, 2, ...",[15,3347,3349,27,3352],{"id":3348},"manuallydrop-and-maybeuninit",[73,3350,3351],{},"ManuallyDrop",[73,3353,3354],{},"MaybeUninit",[20,3356,3357,3358,3361,3362,3365],{},"For unsafe manual memory management, use ",[73,3359,3360],{},"std::mem::ManuallyDrop"," to prevent auto-drop, or ",[73,3363,3364],{},"std::mem::MaybeUninit"," for uninitialized memory. These are advanced; covered in the Unsafe chapter.",[15,3367,3369],{"id":3368},"why-ownership-is-unique","Why Ownership Is Unique",[20,3371,3372],{},"Languages choose between:",[33,3374,3375,3381,3387],{},[36,3376,3377,3380],{},[24,3378,3379],{},"GC"," (Java, Go, Python): runtime cost, pause times.",[36,3382,3383,3386],{},[24,3384,3385],{},"Manual management"," (C, C++): use-after-free, double-free, leaks.",[36,3388,3389,3392],{},[24,3390,3391],{},"Ownership"," (Rust): compile-time rules, zero runtime cost, but you learn the borrow checker.",[15,3394,3396,3397],{"id":3395},"common-error-cannot-move-out-of","Common Error: ",[73,3398,3399],{},"cannot move out of ...",[111,3401,3404],{"className":3402,"code":3403,"language":397,"meta":117},[395],"let v = vec![String::from(\"a\"), String::from(\"b\")];\nlet first = v[0];   \u002F\u002F ERROR: cannot move out of index of Vec\n",[73,3405,3403],{"__ignoreMap":117},[20,3407,3408,3409,3412,3413,3415,3416,1546,3419,1546,3422,259],{},"Indexing returns a reference (",[73,3410,3411],{},"&String","); moving out would leave the ",[73,3414,1194],{}," in an invalid state. Use ",[73,3417,3418],{},"v.into_iter().next()",[73,3420,3421],{},"mem::take(&mut v[0])",[73,3423,3424],{},"v.remove(0)",[15,3426,3428,27,3431],{"id":3427},"memtake-and-memreplace",[73,3429,3430],{},"mem::take",[73,3432,3433],{},"mem::replace",[111,3435,3438],{"className":3436,"code":3437,"language":397,"meta":117},[395],"use std::mem;\nlet mut s = String::from(\"hi\");\nlet taken = mem::take(&mut s);  \u002F\u002F s becomes default (empty String), taken gets \"hi\"\nlet prev = mem::replace(&mut s, \"bye\".into());  \u002F\u002F s = \"bye\", prev = \"\"\n",[73,3439,3437],{"__ignoreMap":117},[20,3441,3442],{},"These let you extract values from behind a mutable reference without invalidating the container.",[15,3444,349],{"id":348},[33,3446,3447,3450,3458,3465],{},[36,3448,3449],{},"Each value has one owner; scope-end drops it.",[36,3451,3142,3452,3454,3455,3457],{},[73,3453,1795],{}," types move on assignment\u002Fpass; ",[73,3456,1795],{}," types duplicate.",[36,3459,3460,3462,3463,259],{},[73,3461,3217],{}," is a destructor; can't be combined with ",[73,3464,1795],{},[36,3466,3467,3468,480,3470,3472],{},"Partial moves, ",[73,3469,3430],{},[73,3471,3433],{}," let you surgically move things around.",[20,3474,3475,3476,3479],{},"Next: References and Borrowing — ",[183,3477,3478],{},"using"," a value without owning it.",{"title":117,"searchDepth":357,"depth":357,"links":3481},[3482,3483,3484,3486,3487,3488,3489,3491,3492,3493,3495,3497,3498,3500,3502],{"id":3034,"depth":357,"text":3035},{"id":3065,"depth":357,"text":3066},{"id":3105,"depth":357,"text":3485},"The Copy Trait",{"id":3163,"depth":357,"text":3164},{"id":3185,"depth":357,"text":3186},{"id":3198,"depth":357,"text":3199},{"id":3229,"depth":357,"text":3490},"Drop and Copy are Mutually Exclusive",{"id":3251,"depth":357,"text":3252},{"id":3264,"depth":357,"text":3265},{"id":3325,"depth":357,"text":3494},"drop Order in Structs",{"id":3348,"depth":357,"text":3496},"ManuallyDrop and MaybeUninit",{"id":3368,"depth":357,"text":3369},{"id":3395,"depth":357,"text":3499},"Common Error: cannot move out of ...",{"id":3427,"depth":357,"text":3501},"mem::take and mem::replace",{"id":348,"depth":357,"text":349},"Ownership is the defining feature of Rust. Every other memory-safety guarantee flows from these rules.",{},"\u002Frust\u002F07-ownership",{"title":3019,"description":3503},"rust\u002F07-ownership","gl6RdZEKrYYujxEP4IDT4Uup1jke76xULSkcmWGJueM",{"id":3510,"title":3511,"body":3512,"description":3996,"extension":373,"meta":3997,"navigation":375,"path":3998,"seo":3999,"stem":4000,"__hash__":4001},"content\u002Frust\u002F08-references-and-borrowing.md","08 — References & Borrowing",{"type":8,"value":3513,"toc":3969},[3514,3517,3524,3530,3536,3563,3569,3575,3602,3606,3626,3633,3639,3643,3649,3655,3658,3662,3668,3679,3683,3689,3692,3698,3702,3705,3711,3714,3718,3724,3730,3734,3742,3748,3755,3771,3777,3784,3790,3796,3812,3822,3828,3837,3841,3896,3902,3908,3916,3922,3925,3935,3945,3947,3966],[11,3515,3511],{"id":3516},"_08-references-borrowing",[20,3518,3519,3520,3523],{},"Ownership is heavy. ",[24,3521,3522],{},"References"," let you use a value without taking ownership.",[15,3525,3527,3528],{"id":3526},"shared-references-t","Shared References ",[73,3529,3130],{},[111,3531,3534],{"className":3532,"code":3533,"language":397,"meta":117},[395],"fn len(s: &String) -> usize { s.len() }\n\nlet s = String::from(\"hi\");\nlet l = len(&s);     \u002F\u002F borrow — s still owned by caller\nprintln!(\"{s} {l}\"); \u002F\u002F OK\n",[73,3535,3533],{"__ignoreMap":117},[33,3537,3538,3546,3556],{},[36,3539,3540,1592,3542,3545],{},[73,3541,3130],{},[24,3543,3544],{},"shared, immutable"," borrow.",[36,3547,3548,3549,3552,3553,3555],{},"You can have ",[24,3550,3551],{},"any number"," of simultaneous ",[73,3554,3130],{}," to the same value.",[36,3557,3558,2534,3560,3562],{},[73,3559,3130],{},[73,3561,1795],{}," (the reference itself can be copied).",[15,3564,3566,3567],{"id":3565},"mutable-references-mut-t","Mutable References ",[73,3568,1117],{},[111,3570,3573],{"className":3571,"code":3572,"language":397,"meta":117},[395],"fn push(s: &mut String) { s.push('!'); }\n\nlet mut s = String::from(\"hi\");\npush(&mut s);\nprintln!(\"{s}\");   \u002F\u002F \"hi!\"\n",[73,3574,3572],{"__ignoreMap":117},[33,3576,3577,3585,3594],{},[36,3578,3579,3581,3582,3545],{},[73,3580,1117],{}," is an ",[24,3583,3584],{},"exclusive",[36,3586,3548,3587,3590,3591,3593],{},[24,3588,3589],{},"exactly one"," active ",[73,3592,1117],{}," at a time.",[36,3595,3596,3597,27,3599,3601],{},"You cannot mix ",[73,3598,3130],{},[73,3600,1117],{}," to the same data while both are alive.",[15,3603,3605],{"id":3604},"the-borrow-rules","The Borrow Rules",[3607,3608,3609,3615],"blockquote",{},[20,3610,3611,3612,170],{},"At any given time, you can have ",[24,3613,3614],{},"either",[33,3616,3617,3623],{},[36,3618,3619,3620],{},"One mutable reference, ",[24,3621,3622],{},"or",[36,3624,3625],{},"Any number of immutable references.",[20,3627,3628,3629,3632],{},"These rules are checked at compile time. Violations produce ",[73,3630,3631],{},"E0502"," (aliased mutable borrow) and similar.",[111,3634,3637],{"className":3635,"code":3636,"language":397,"meta":117},[395],"let mut v = vec![1, 2, 3];\nlet r = &v;\nlet r2 = &v;          \u002F\u002F OK — multiple shared\nprintln!(\"{r} {r2}\");\nlet m = &mut v;       \u002F\u002F OK — shared refs ended above (NLL)\nm.push(4);\n",[73,3638,3636],{"__ignoreMap":117},[15,3640,3642],{"id":3641},"non-lexical-lifetimes-nll","Non-Lexical Lifetimes (NLL)",[20,3644,3645,3646,170],{},"Pre-2018, references were valid until the end of their lexical scope. NLL shrinks a reference's lifetime to its ",[24,3647,3648],{},"last use",[111,3650,3653],{"className":3651,"code":3652,"language":397,"meta":117},[395],"let mut v = vec![1, 2, 3];\nlet r = &v;\nprintln!(\"{r}\");      \u002F\u002F last use of r\nv.push(4);           \u002F\u002F OK — r no longer used\n",[73,3654,3652],{"__ignoreMap":117},[20,3656,3657],{},"Without NLL this would error. With NLL it compiles.",[15,3659,3661],{"id":3660},"reference-scope-edge-cases","Reference Scope Edge Cases",[111,3663,3666],{"className":3664,"code":3665,"language":397,"meta":117},[395],"let mut v = vec![1, 2, 3];\nlet r = &v;\nlet r2 = &v;          \u002F\u002F multiple shared OK\nv.push(4);            \u002F\u002F ERROR: cannot borrow v as mutable because r\u002Fr2 alive\nprintln!(\"{r} {r2}\");\n",[73,3667,3665],{"__ignoreMap":117},[20,3669,3670,3671,3674,3675,3678],{},"Here the mutable borrow happens ",[183,3672,3673],{},"before"," the last use of ",[73,3676,3677],{},"r",", so it's rejected.",[15,3680,3682],{"id":3681},"reborrowing","Reborrowing",[111,3684,3687],{"className":3685,"code":3686,"language":397,"meta":117},[395],"let mut s = String::from(\"hi\");\nlet r1: &mut String = &mut s;\nlet r2: &mut String = &mut *r1;    \u002F\u002F reborrow — r1 temporarily inactive\nr2.push('!');\n\u002F\u002F r1 still inactive until r2 dies\nr1.push('!');                       \u002F\u002F OK now\n",[73,3688,3686],{"__ignoreMap":117},[20,3690,3691],{},"Reborrowing is the mechanism by which you can chain mutable references through function calls:",[111,3693,3696],{"className":3694,"code":3695,"language":397,"meta":117},[395],"fn push_all(dst: &mut Vec\u003Ci32>, src: &[i32]) {\n    for &x in src { dst.push(x); }   \u002F\u002F dst reborrows each call\n}\n",[73,3697,3695],{"__ignoreMap":117},[15,3699,3701],{"id":3700},"lifetimes-of-references-preview","Lifetimes of References (preview)",[20,3703,3704],{},"The compiler tracks lifetimes. A function returning a reference must tie its output lifetime to an input:",[111,3706,3709],{"className":3707,"code":3708,"language":397,"meta":117},[395],"fn longest\u003C'a>(x: &'a str, y: &'a str) -> &'a str {\n    if x.len() > y.len() { x } else { y }\n}\n",[73,3710,3708],{"__ignoreMap":117},[20,3712,3713],{},"See the Lifetimes chapter for full details.",[15,3715,3717],{"id":3716},"dangling-references-impossible","Dangling References — Impossible",[111,3719,3722],{"className":3720,"code":3721,"language":397,"meta":117},[395],"let r;\n{\n    let s = String::from(\"hi\");\n    r = &s;            \u002F\u002F ERROR: s does not live long enough\n}\nprintln!(\"{r}\");\n",[73,3723,3721],{"__ignoreMap":117},[20,3725,3726,3727,3729],{},"The borrow checker rejects code that could produce a dangling reference. This is ",[183,3728,3030],{}," guarantee that prevents use-after-free in safe Rust.",[15,3731,3733],{"id":3732},"reference-coercion","Reference Coercion",[20,3735,3736,3738,3739,3741],{},[73,3737,1117],{}," coerces to ",[73,3740,3130],{}," when needed:",[111,3743,3746],{"className":3744,"code":3745,"language":397,"meta":117},[395],"fn len(s: &String) -> usize { s.len() }\nlet mut s = String::from(\"hi\");\nlet l = len(&mut s);     \u002F\u002F &mut String coerces to &String\n",[73,3747,3745],{"__ignoreMap":117},[15,3749,3751,559,3753],{"id":3750},"str-vs-string",[73,3752,1630],{},[73,3754,3411],{},[20,3756,3757,3759,3760,3762,3763,3765,3766,480,3768,3770],{},[73,3758,3411],{}," auto-derefs to ",[73,3761,1630],{},". Idiomatic API: take ",[73,3764,1630],{}," (more general; accepts ",[73,3767,3411],{},[73,3769,1630],{},", string literals).",[111,3772,3775],{"className":3773,"code":3774,"language":397,"meta":117},[395],"fn greet(name: &str) { println!(\"hi {name}\"); }\ngreet(\"Ada\");              \u002F\u002F &str\ngreet(&String::from(\"Ada\"));   \u002F\u002F &String coerces to &str\n",[73,3776,3774],{"__ignoreMap":117},[15,3778,3780,3783],{"id":3779},"deref-coercion",[73,3781,3782],{},"Deref"," Coercion",[20,3785,3786,3787,3789],{},"Types implementing ",[73,3788,3782],{}," allow chained deref coercions:",[111,3791,3794],{"className":3792,"code":3793,"language":397,"meta":117},[395],"let s = String::from(\"hi\");\nlet r: &str = &s;          \u002F\u002F &String -> &str via Deref\nlet b = Box::new(String::from(\"hi\"));\nlet r: &str = &b;          \u002F\u002F &Box\u003CString> -> &String -> &str\n",[73,3795,3793],{"__ignoreMap":117},[20,3797,3798,3799,480,3801,480,3804,480,3806,3808,3809,3811],{},"This is how ",[73,3800,1200],{},[73,3802,3803],{},"Rc",[73,3805,1197],{},[73,3807,1194],{}," all play nicely with ",[73,3810,2710],{},"-APIs.",[15,3813,3815,3818,3819],{"id":3814},"as_ref-as_mut",[73,3816,3817],{},"as_ref"," \u002F ",[73,3820,3821],{},"as_mut",[111,3823,3826],{"className":3824,"code":3825,"language":397,"meta":117},[395],"let s = String::from(\"hi\");\nlet r: &str = s.as_ref();   \u002F\u002F explicit AsRef coercion\n",[73,3827,3825],{"__ignoreMap":117},[20,3829,3830,27,3833,3836],{},[73,3831,3832],{},"AsRef\u003CT>",[73,3834,3835],{},"AsMut\u003CT>"," allow flexible type-erased borrowing.",[15,3838,3840],{"id":3839},"mutable-reference-footguns","Mutable Reference Footguns",[33,3842,3843,3862,3871],{},[36,3844,3845,71,3852,2534,3855,3858,3859,2004],{},[24,3846,3847,3848,3851],{},"Two ",[73,3849,3850],{},"&mut"," to overlapping memory",[73,3853,3854],{},"let (a, b) = (&mut v[0], &mut v[1]);",[183,3856,3857],{},"OK"," (non-overlapping), but ",[73,3860,3861],{},"let (a, b) = (&mut v[0], &mut v[0]);",[36,3863,3864,71,3867,3870],{},[24,3865,3866],{},"Splitting borrows of a struct",[73,3868,3869],{},"let (a, b) = (&mut s.x, &mut s.y);"," is allowed (non-overlapping fields).",[36,3872,3873,3876,3877,3880,3881,3884,3885,3888,3889,3892,3893,259],{},[24,3874,3875],{},"Borrowing through a method",": if ",[73,3878,3879],{},"vec.push(x)"," mutably borrows ",[73,3882,3883],{},"vec",", you can't also ",[73,3886,3887],{},"&vec[0]"," simultaneously. The classic ",[73,3890,3891],{},"v.push(v[0])"," error — copy first: ",[73,3894,3895],{},"let x = v[0]; v.push(x);",[15,3897,3396,3899],{"id":3898},"common-error-cannot-borrow-as-mutable-as-it-is-not-declared-as-mut",[73,3900,3901],{},"cannot borrow ... as mutable, as it is not declared as mut",[111,3903,3906],{"className":3904,"code":3905,"language":397,"meta":117},[395],"let s = String::from(\"hi\");\nlet r = &mut s;    \u002F\u002F ERROR: s is not mut\n",[73,3907,3905],{"__ignoreMap":117},[20,3909,3910,3911,3913,3914,259],{},"The variable itself must be ",[73,3912,885],{}," to allow ",[73,3915,3850],{},[15,3917,3396,3919],{"id":3918},"common-error-cannot-borrow-as-mutable-because-it-is-also-borrowed-as-immutable",[73,3920,3921],{},"cannot borrow ... as mutable ... because it is also borrowed as immutable",[20,3923,3924],{},"Fix by reordering so the immutable borrow ends before the mutable borrow (NLL), or by cloning, or by restructuring.",[15,3926,3928,27,3931,3934],{"id":3927},"ref-and-refmut-interior-mutability",[73,3929,3930],{},"Ref",[73,3932,3933],{},"RefMut"," (Interior Mutability)",[20,3936,3937,3940,3941,3944],{},[73,3938,3939],{},"std::cell::RefCell"," provides ",[183,3942,3943],{},"runtime-checked"," borrow rules (single mutable xor multiple immutable), enabling interior mutability behind an immutable reference. Covered in Interior Mutability chapter.",[15,3946,349],{"id":348},[33,3948,3949,3957,3960,3963],{},[36,3950,3951,3953,3954,3956],{},[73,3952,3130],{}," = shared, many at once; ",[73,3955,1117],{}," = exclusive, one at a time.",[36,3958,3959],{},"NLL makes references live only as long as needed.",[36,3961,3962],{},"Borrowing lets you write APIs that don't steal ownership.",[36,3964,3965],{},"Dangling references are impossible in safe Rust.",[20,3967,3968],{},"Next: Slices — borrowed views into contiguous data.",{"title":117,"searchDepth":357,"depth":357,"links":3970},[3971,3973,3975,3976,3977,3978,3979,3980,3981,3982,3984,3986,3988,3989,3991,3993,3995],{"id":3526,"depth":357,"text":3972},"Shared References &T",{"id":3565,"depth":357,"text":3974},"Mutable References &mut T",{"id":3604,"depth":357,"text":3605},{"id":3641,"depth":357,"text":3642},{"id":3660,"depth":357,"text":3661},{"id":3681,"depth":357,"text":3682},{"id":3700,"depth":357,"text":3701},{"id":3716,"depth":357,"text":3717},{"id":3732,"depth":357,"text":3733},{"id":3750,"depth":357,"text":3983},"&str vs &String",{"id":3779,"depth":357,"text":3985},"Deref Coercion",{"id":3814,"depth":357,"text":3987},"as_ref \u002F as_mut",{"id":3839,"depth":357,"text":3840},{"id":3898,"depth":357,"text":3990},"Common Error: cannot borrow ... as mutable, as it is not declared as mut",{"id":3918,"depth":357,"text":3992},"Common Error: cannot borrow ... as mutable ... because it is also borrowed as immutable",{"id":3927,"depth":357,"text":3994},"Ref and RefMut (Interior Mutability)",{"id":348,"depth":357,"text":349},"Ownership is heavy. References let you use a value without taking ownership.",{},"\u002Frust\u002F08-references-and-borrowing",{"title":3511,"description":3996},"rust\u002F08-references-and-borrowing","0tSisRkmTTEG0ylQdgZ_URFKTDjMF4EbTQ1BFzbxQ7g",{"id":4003,"title":4004,"body":4005,"description":4429,"extension":373,"meta":4430,"navigation":375,"path":4431,"seo":4432,"stem":4433,"__hash__":4434},"content\u002Frust\u002F09-slices.md","09 — Slices",{"type":8,"value":4006,"toc":4411},[4007,4010,4017,4021,4027,4042,4048,4053,4059,4063,4106,4112,4121,4127,4138,4144,4148,4154,4162,4180,4184,4190,4193,4197,4203,4209,4213,4219,4233,4237,4246,4252,4257,4265,4271,4277,4279,4371,4375,4381,4383,4408],[11,4008,4004],{"id":4009},"_09-slices",[20,4011,4012,4013,4016],{},"A slice is a ",[24,4014,4015],{},"borrowed view"," into a contiguous sequence of elements. It's the most common way to pass \"a chunk of data\" without taking ownership.",[15,4018,4020],{"id":4019},"the-slice-type","The Slice Type",[111,4022,4025],{"className":4023,"code":4024,"language":397,"meta":117},[395],"let arr = [1, 2, 3, 4, 5];\nlet s: &[i32] = &arr;          \u002F\u002F slice of the whole array\nlet part: &[i32] = &arr[1..4]; \u002F\u002F [2, 3, 4]\n",[73,4026,4024],{"__ignoreMap":117},[20,4028,4029,4030,1592,4032,71,4035,4038,4039,4041],{},"A slice ",[73,4031,1739],{},[24,4033,4034],{},"fat pointer",[73,4036,4037],{},"(pointer, length)",". Two ",[73,4040,1436],{}," worth of data on the stack. No ownership of the underlying elements.",[15,4043,4045,4046],{"id":4044},"string-slices-str","String Slices ",[73,4047,1630],{},[20,4049,4050,4052],{},[73,4051,1630],{}," is a slice of UTF-8 bytes — same fat-pointer layout but with the UTF-8 invariant:",[111,4054,4057],{"className":4055,"code":4056,"language":397,"meta":117},[395],"let s = String::from(\"hello, world\");\nlet hello: &str = &s[0..5];     \u002F\u002F \"hello\"\nlet world: &s[7..12];            \u002F\u002F \"world\"\nlet whole: &str = &s[..];        \u002F\u002F whole string\n",[73,4058,4056],{"__ignoreMap":117},[15,4060,4062],{"id":4061},"indexing-rules","Indexing Rules",[33,4064,4065,4085,4091,4097],{},[36,4066,4067,2230,4070,470,4073,2230,4076,470,4079,2230,4082,259],{},[73,4068,4069],{},"..n",[73,4071,4072],{},"0..n",[73,4074,4075],{},"n..",[73,4077,4078],{},"n..len",[73,4080,4081],{},"..",[73,4083,4084],{},"0..len",[36,4086,4087,4090],{},[73,4088,4089],{},"..=k"," is inclusive.",[36,4092,4093,4094,4096],{},"Out-of-bounds slicing ",[24,4095,1719],{}," at runtime.",[36,4098,4099,4100,2281,4103,4105],{},"Slicing on a ",[24,4101,4102],{},"non-char boundary",[73,4104,1630],{}," panics:",[111,4107,4110],{"className":4108,"code":4109,"language":397,"meta":117},[395],"let s = \"hi🦀\";  \u002F\u002F '🦀' is 4 bytes\nlet bad = &s[2..4];   \u002F\u002F PANIC: byte index 2 is not a char boundary\n",[73,4111,4109],{"__ignoreMap":117},[20,4113,4114,4115,3818,4118,170],{},"To slice by char, use ",[73,4116,4117],{},".chars()",[73,4119,4120],{},".char_indices()",[111,4122,4125],{"className":4123,"code":4124,"language":397,"meta":117},[395],"let first_char_str: &str = s.split(0).next().unwrap();\n",[73,4126,4124],{"__ignoreMap":117},[20,4128,4129,4130,4133,4134,4137],{},"Wait — ",[73,4131,4132],{},"str::split"," splits on a pattern. Use ",[73,4135,4136],{},"char_indices"," for safety:",[111,4139,4142],{"className":4140,"code":4141,"language":397,"meta":117},[395],"let bytes_to = s.char_indices().nth(1).map(|(i, _)| i).unwrap();\nlet first: &str = &s[..bytes_to];\n",[73,4143,4141],{"__ignoreMap":117},[15,4145,4147],{"id":4146},"creating-slices","Creating Slices",[111,4149,4152],{"className":4150,"code":4151,"language":397,"meta":117},[395],"\u002F\u002F From Vec\nlet v = vec![1, 2, 3];\nlet s = &v[..];\nlet s = v.as_slice();\n\n\u002F\u002F From array\nlet a = [1, 2, 3];\nlet s = &a[..];\n\n\u002F\u002F From String\nlet st = String::from(\"hi\");\nlet s: &str = &st[..];\nlet s: &str = st.as_str();\n\n\u002F\u002F From raw parts (unsafe)\nlet s: &[u8] = unsafe { std::slice::from_raw_parts(ptr, len) };\n",[73,4153,4151],{"__ignoreMap":117},[15,4155,4157,559,4159],{"id":4156},"t-vs-t-n",[73,4158,1739],{},[73,4160,4161],{},"&[T; N]",[33,4163,4164,4169,4174],{},[36,4165,4166,4168],{},[73,4167,1739],{}," is the slice type (dynamically sized).",[36,4170,4171,4173],{},[73,4172,4161],{}," is a reference to a fixed-size array (size known at compile time).",[36,4175,4176,3738,4178,259],{},[73,4177,4161],{},[73,4179,1739],{},[15,4181,4183],{"id":4182},"mutable-slices","Mutable Slices",[111,4185,4188],{"className":4186,"code":4187,"language":397,"meta":117},[395],"let mut v = vec![1, 2, 3];\nlet s: &mut [i32] = &mut v[..];\ns[0] = 99;\n",[73,4189,4187],{"__ignoreMap":117},[20,4191,4192],{},"One mutable slice at a time (borrow rules still apply).",[15,4194,4196],{"id":4195},"splitting-slices","Splitting Slices",[111,4198,4201],{"className":4199,"code":4200,"language":397,"meta":117},[395],"let (left, right) = s.split_first();   \u002F\u002F Option\u003C(&T, &[T])>\nlet (mut_left, mut_right) = s.split_at_mut(2);   \u002F\u002F (&mut [T], &mut [T])\n",[73,4202,4200],{"__ignoreMap":117},[20,4204,4205,4208],{},[73,4206,4207],{},"split_at_mut"," is the canonical way to get two non-overlapping mutable subslices — the compiler can't otherwise prove disjointness.",[15,4210,4212],{"id":4211},"iteration","Iteration",[111,4214,4217],{"className":4215,"code":4216,"language":397,"meta":117},[395],"for x in &arr { \u002F* x: &i32 *\u002F }\nfor x in &mut arr { \u002F* x: &mut i32 *\u002F }\nfor x in arr { \u002F* x: i32 — consumes (Copy ok) *\u002F }\n",[73,4218,4216],{"__ignoreMap":117},[20,4220,4221,27,4223,4225,4226,4228,4229,3818,4231,259],{},[73,4222,1739],{},[73,4224,1742],{}," implement ",[73,4227,189],{}," yielding ",[73,4230,3130],{},[73,4232,1117],{},[15,4234,4236],{"id":4235},"why-slices-matter-for-apis","Why Slices Matter for APIs",[20,4238,4239,4240,4242,4243,170],{},"Take ",[73,4241,1739],{}," not ",[73,4244,4245],{},"&Vec\u003CT>",[111,4247,4250],{"className":4248,"code":4249,"language":397,"meta":117},[395],"fn sum(nums: &[i32]) -> i32 {\n    nums.iter().sum()\n}\nsum(&vec![1, 2, 3]);\nsum(&[1, 2, 3]);\nsum(&arr);            \u002F\u002F works for arrays too\n",[73,4251,4249],{"__ignoreMap":117},[20,4253,4254,4256],{},[73,4255,1739],{}," is the most general borrowed form.",[15,4258,4260,559,4262,4264],{"id":4259},"str-vs-string-api-choice",[73,4261,1630],{},[73,4263,1197],{}," API Choice",[111,4266,4269],{"className":4267,"code":4268,"language":397,"meta":117},[395],"fn print(s: &str) { println!(\"{s}\"); }\nprint(\"literal\");          \u002F\u002F &str\nprint(&owned_string);      \u002F\u002F &String -> &str\nprint(&substring);         \u002F\u002F &str\n",[73,4270,4268],{"__ignoreMap":117},[20,4272,4273,4274,4276],{},"Always prefer ",[73,4275,1630],{}," in function parameters unless you need to grow the string.",[15,4278,711],{"id":710},[33,4280,4281,4290,4299,4317,4328,4338,4348],{},[36,4282,4283,71,4286,4289],{},[24,4284,4285],{},"Empty slice",[73,4287,4288],{},"&arr[0..0]"," is valid, length 0; never panics.",[36,4291,4292,71,4295,4298],{},[24,4293,4294],{},"Slicing past end",[73,4296,4297],{},"&v[..v.len() + 1]"," panics.",[36,4300,4301,4309,4310,3818,4313,4316],{},[24,4302,4303,3818,4306],{},[73,4304,4305],{},"slice.get(i)",[73,4307,4308],{},"slice.get_mut(i)",": returns ",[73,4311,4312],{},"Option\u003C&T>",[73,4314,4315],{},"Option\u003C&mut T>"," — non-panicking indexing.",[36,4318,4319,4324,4325,526],{},[24,4320,4321],{},[73,4322,4323],{},"slice.get_unchecked(i)",": unsafe — skips bounds check (only correct when you've proven ",[73,4326,4327],{},"i \u003C len",[36,4329,4330,4333,4334,4337],{},[24,4331,4332],{},"Range patterns",": limited; stable Rust allows ",[73,4335,4336],{},"[a, b, ..]"," only in limited forms.",[36,4339,4340,4345,4346,259],{},[24,4341,4342],{},[73,4343,4344],{},"Vec::drain",": takes a range, removes and returns an iterator — modifies the ",[73,4347,1194],{},[36,4349,4350,4353,4354,4357,4358,1212,4360,4362,4363,1546,4366,1478,4369,526],{},[24,4351,4352],{},"String slicing pitfall",": indexing ",[73,4355,4356],{},"s[i]"," is intentionally not allowed for ",[73,4359,1197],{},[73,4361,1630],{}," because UTF-8 byte indexing is meaningless. Use ",[73,4364,4365],{},"s.chars().nth(i)",[73,4367,4368],{},"s.as_bytes()[i]",[73,4370,1359],{},[15,4372,4374],{"id":4373},"slice-methods-cheat-sheet","Slice Methods Cheat Sheet",[111,4376,4379],{"className":4377,"code":4378,"language":397,"meta":117},[395],"s.len();\ns.is_empty();\ns.first();             \u002F\u002F Option\u003C&T>\ns.last();\ns.split_first();       \u002F\u002F Option\u003C(&T, &[T])>\ns.iter() \u002F s.iter_mut();\ns.windows(2);          \u002F\u002F sliding windows\ns.chunks(3);           \u002F\u002F non-overlapping chunks\ns.chunks_exact(3);\ns.split(|x| *x == 0);\ns.splitn(2, |x| *x == 0);\ns.contains(&3);\ns.starts_with(&[1, 2]);\ns.ends_with(&[4, 5]);\ns.iter().position(|x| *x == 3);\ns.binary_search(&3);\ns.sort();\ns.sort_by(|a, b| b.cmp(a));\ns.sort_by_key(|x| x.abs());\ns.reverse();\ns.rotate_left(1);\ns.copy_within(0..3, 5);\ns.fill(0);\n",[73,4380,4378],{"__ignoreMap":117},[15,4382,349],{"id":348},[33,4384,4385,4388,4396,4403],{},[36,4386,4387],{},"Slices are borrowed, fat-pointer views into contiguous data.",[36,4389,4390,4392,4393,4395],{},[73,4391,1630],{}," is a UTF-8 slice; ",[73,4394,1739],{}," is a generic slice.",[36,4397,1876,4398,3818,4400,4402],{},[73,4399,1739],{},[73,4401,1630],{}," in APIs for maximum generality.",[36,4404,4405,4406,259],{},"Split at mutable boundaries with ",[73,4407,4207],{},[20,4409,4410],{},"Next: Lifetimes — the borrow checker's vocabulary.",{"title":117,"searchDepth":357,"depth":357,"links":4412},[4413,4414,4416,4417,4418,4420,4421,4422,4423,4424,4426,4427,4428],{"id":4019,"depth":357,"text":4020},{"id":4044,"depth":357,"text":4415},"String Slices &str",{"id":4061,"depth":357,"text":4062},{"id":4146,"depth":357,"text":4147},{"id":4156,"depth":357,"text":4419},"&[T] vs &[T; N]",{"id":4182,"depth":357,"text":4183},{"id":4195,"depth":357,"text":4196},{"id":4211,"depth":357,"text":4212},{"id":4235,"depth":357,"text":4236},{"id":4259,"depth":357,"text":4425},"&str vs String API Choice",{"id":710,"depth":357,"text":711},{"id":4373,"depth":357,"text":4374},{"id":348,"depth":357,"text":349},"A slice is a borrowed view into a contiguous sequence of elements. It's the most common way to pass \"a chunk of data\" without taking ownership.",{},"\u002Frust\u002F09-slices",{"title":4004,"description":4429},"rust\u002F09-slices","1R8fuiHDUpQ-fyd-6eSrHqc5lkTIAkwu-dlN3gKRYYE",{"id":4436,"title":4437,"body":4438,"description":5068,"extension":373,"meta":5069,"navigation":375,"path":5070,"seo":5071,"stem":5072,"__hash__":5073},"content\u002Frust\u002F10-lifetimes.md","10 — Lifetimes",{"type":8,"value":4439,"toc":5046},[4440,4443,4450,4454,4461,4467,4471,4478,4483,4509,4513,4516,4547,4550,4556,4561,4566,4587,4593,4605,4609,4612,4618,4625,4629,4635,4641,4645,4651,4670,4674,4691,4720,4729,4733,4739,4751,4758,4764,4773,4777,4780,4786,4790,4796,4800,4806,4818,4825,4831,4839,4843,4884,4888,4958,4960,5024,5026,5029,5043],[11,4441,4437],{"id":4442},"_10-lifetimes",[20,4444,4445,4446,4449],{},"Lifetimes are the borrow checker's way of tracking ",[24,4447,4448],{},"how long a reference is valid",". They're a compile-time concept; there's zero runtime cost.",[15,4451,4453],{"id":4452},"the-core-idea","The Core Idea",[20,4455,4456,4457,4460],{},"A reference's lifetime is the region of code where it's valid to use. The compiler ",[183,4458,4459],{},"rejects"," code where a reference could outlive the data it points to:",[111,4462,4465],{"className":4463,"code":4464,"language":397,"meta":117},[395],"let r;\n{\n    let x = 5;\n    r = &x;\n}                       \u002F\u002F x dropped here\nprintln!(\"{r}\");        \u002F\u002F ERROR: x does not live long enough\n",[73,4466,4464],{"__ignoreMap":117},[15,4468,4470],{"id":4469},"generic-lifetime-parameters","Generic Lifetime Parameters",[20,4472,4473,4474,4477],{},"When a function returns a reference, the compiler needs to know its lifetime is tied to ",[183,4475,4476],{},"some"," input:",[111,4479,4481],{"className":4480,"code":3708,"language":397,"meta":117},[395],[73,4482,3708],{"__ignoreMap":117},[20,4484,4485,4488,4489,4492,4493,4496,4497,4500,4501],{},[73,4486,4487],{},"\u003C'a>"," declares a generic lifetime. ",[73,4490,4491],{},"&'a str"," means \"a ",[73,4494,4495],{},"str"," reference valid for at least ",[73,4498,4499],{},"'a","\". The signature says: ",[183,4502,4503,4504,27,4506,259],{},"the returned reference is valid for at least as long as the shorter of ",[73,4505,1179],{},[73,4507,4508],{},"y",[15,4510,4512],{"id":4511},"lifetime-elision-rules","Lifetime Elision Rules",[20,4514,4515],{},"To reduce boilerplate, the compiler applies three elision rules:",[3037,4517,4518,4527,4536],{},[36,4519,4520,4521,592,4524,259],{},"Each input reference gets its own lifetime: ",[73,4522,4523],{},"fn f(x: &str, y: &str)",[73,4525,4526],{},"fn f\u003C'a, 'b>(x: &'a str, y: &'b str)",[36,4528,4529,4530,592,4533,259],{},"If there's exactly one input lifetime, all output references get that lifetime: ",[73,4531,4532],{},"fn f(x: &str) -> &str",[73,4534,4535],{},"fn f\u003C'a>(x: &'a str) -> &'a str",[36,4537,4538,4539,1212,4541,4543,4544,4546],{},"If there are multiple inputs but one is ",[73,4540,2229],{},[73,4542,2239],{},", all output lifetimes get ",[73,4545,2245],{},"'s lifetime (method elision).",[20,4548,4549],{},"If after these rules the output lifetime is ambiguous, you must write it explicitly:",[111,4551,4554],{"className":4552,"code":4553,"language":397,"meta":117},[395],"fn longest\u003C'a>(x: &'a str, y: &'a str) -> &'a str { ... }\n",[73,4555,4553],{"__ignoreMap":117},[15,4557,4558],{"id":1207},[73,4559,4560],{},"'static",[20,4562,3106,4563,4565],{},[73,4564,4560],{}," lifetime lasts the entire program. Examples:",[33,4567,4568,4579,4584],{},[36,4569,4570,4571,4574,4575,4578],{},"All string literals: ",[73,4572,4573],{},"\"hello\""," has type ",[73,4576,4577],{},"&'static str"," (stored in the binary).",[36,4580,4581,4583],{},[73,4582,992],{}," values that are references.",[36,4585,4586],{},"Global statics.",[111,4588,4591],{"className":4589,"code":4590,"language":397,"meta":117},[395],"let s: &'static str = \"I live forever\";\n",[73,4592,4590],{"__ignoreMap":117},[20,4594,4595,4596,4598,4599,4601,4602,4604],{},"Don't reach for ",[73,4597,4560],{}," to silence lifetime errors — it usually means a design problem. Common accidental ",[73,4600,4560],{},": spawning threads that capture references requires ",[73,4603,4560],{}," (see Concurrency chapter).",[15,4606,4608],{"id":4607},"structs-holding-references","Structs Holding References",[20,4610,4611],{},"If a struct holds a reference, it must declare a lifetime:",[111,4613,4616],{"className":4614,"code":4615,"language":397,"meta":117},[395],"struct Excerpt\u003C'a> { part: &'a str }\n\nlet novel = String::from(\"a long novel...\");\nlet first = novel.split(' ').next().unwrap();\nlet e = Excerpt { part: first };\n",[73,4617,4615],{"__ignoreMap":117},[20,4619,4620,4621,4624],{},"The struct can't outlive the data ",[73,4622,4623],{},"part"," borrows. The compiler enforces this.",[15,4626,4628],{"id":4627},"lifetimes-in-method-signatures","Lifetimes in Method Signatures",[111,4630,4633],{"className":4631,"code":4632,"language":397,"meta":117},[395],"impl\u003C'a> Excerpt\u003C'a> {\n    fn announce(&self, msg: &str) -> &str {\n        println!(\"{msg}{}\", self.part);\n        self.part            \u002F\u002F elided: returns &'a str (rule 3)\n    }\n}\n",[73,4634,4632],{"__ignoreMap":117},[20,4636,4637,4638,4640],{},"Output references are tied to ",[73,4639,2229],{}," automatically when there's no other choice.",[15,4642,4644],{"id":4643},"multiple-lifetimes","Multiple Lifetimes",[111,4646,4649],{"className":4647,"code":4648,"language":397,"meta":117},[395],"struct Parser\u003C'src, 'arena> {\n    source: &'src str,\n    arena: &'arena Arena,\n}\n",[73,4650,4648],{"__ignoreMap":117},[20,4652,4653,4654,4657,4658,4661,4662,4665,4666,4669],{},"Two distinct lifetimes express \"the source lives at least ",[73,4655,4656],{},"'src",", the arena lives at least ",[73,4659,4660],{},"'arena","\". If the struct never mixes them (e.g., never puts ",[73,4663,4664],{},"source"," into ",[73,4667,4668],{},"arena","), this is more flexible than collapsing to one lifetime.",[15,4671,4673],{"id":4672},"lifetime-variance-advanced","Lifetime Variance (advanced)",[20,4675,1114,4676,2534,4679,292,4682,4684,4685,4687,4688,4690],{},[73,4677,4678],{},"&'a T",[24,4680,4681],{},"covariant",[73,4683,4499],{},": you can use a longer-lived reference where a shorter-lived one is expected. ",[73,4686,4577],{}," fits anywhere ",[73,4689,4491],{}," is needed.",[20,4692,4693,4696,4697,4699,4700,292,4703,4706,4707,4709,4710,480,4713,480,4716,4719],{},[73,4694,4695],{},"&'a mut T"," is covariant in ",[73,4698,4499],{}," but ",[24,4701,4702],{},"invariant",[73,4704,4705],{},"T"," (you can't shorten the borrow of ",[73,4708,4705],{}," because mutation could write back). ",[73,4711,4712],{},"Cell\u003CT>",[73,4714,4715],{},"RefCell\u003CT>",[73,4717,4718],{},"UnsafeCell\u003CT>"," are invariant.",[20,4721,4722,4725,4726,4728],{},[73,4723,4724],{},"fn(&'a T)"," is contravariant in ",[73,4727,4499],{},". Most code doesn't think about variance, but it explains why some seemingly valid code compiles or doesn't.",[15,4730,4732],{"id":4731},"higher-rank-trait-bounds-hrtb","Higher-Rank Trait Bounds (HRTB)",[111,4734,4737],{"className":4735,"code":4736,"language":397,"meta":117},[395],"fn foo\u003CF>(f: F) where F: for\u003C'a> Fn(&'a str) { ... }\n",[73,4738,4736],{"__ignoreMap":117},[20,4740,4741,4744,4745,4747,4748,4750],{},[73,4742,4743],{},"for\u003C'a>"," means \"for all possible lifetimes ",[73,4746,4499],{},"\". Closures that work with any borrowed input need this. The ",[73,4749,1799],{}," traits implicitly have HRTB on their arguments.",[15,4752,4754,4755],{"id":4753},"anonymous-lifetime-_","Anonymous Lifetime ",[73,4756,4757],{},"'_",[111,4759,4762],{"className":4760,"code":4761,"language":397,"meta":117},[395],"fn longest(x: &str, y: &str) -> &'_ str { ... }\n",[73,4763,4761],{"__ignoreMap":117},[20,4765,4766,4768,4769,4772],{},[73,4767,4757],{}," is \"use elision here\". Useful in ",[73,4770,4771],{},"impl Trait"," positions and to silence \"elided lifetime in path\" warnings. Don't sprinkle it; only when you specifically want elision.",[15,4774,4776],{"id":4775},"lifetime-in-enums","Lifetime in Enums",[20,4778,4779],{},"Same rules as structs:",[111,4781,4784],{"className":4782,"code":4783,"language":397,"meta":117},[395],"enum Node\u003C'a> { Leaf(&'a str), Branch(&'a [Node\u003C'a>]) }\n",[73,4785,4783],{"__ignoreMap":117},[15,4787,4789],{"id":4788},"static-vs-stack-lifetimes","Static vs Stack Lifetimes",[111,4791,4794],{"className":4792,"code":4793,"language":397,"meta":117},[395],"fn returns_str() -> &'static str { \"literal\" }  \u002F\u002F OK\nfn returns_stack() -> &str {\n    let local = String::from(\"hi\");\n    &local                  \u002F\u002F ERROR: local does not live long enough\n}\n",[73,4795,4793],{"__ignoreMap":117},[15,4797,4799],{"id":4798},"lifetime-bounds-on-generics","Lifetime Bounds on Generics",[111,4801,4804],{"className":4802,"code":4803,"language":397,"meta":117},[395],"fn parse\u003CT>(s: &str) -> T where T: FromStr, T::Err: Debug { ... }\nfn longest_anon\u003C'a, T: 'a>(x: &'a T) -> &'a T { x }\n",[73,4805,4803],{"__ignoreMap":117},[20,4807,4808,4811,4812,4814,4815,4817],{},[73,4809,4810],{},"T: 'a"," means \"T's owned references (if any) outlive ",[73,4813,4499],{},"\". Often implicit, but needed when ",[73,4816,4705],{}," itself contains references.",[15,4819,4821,4822],{"id":4820},"lifetime-extension-via-boxleak","Lifetime Extension via ",[73,4823,4824],{},"Box::leak",[111,4826,4829],{"className":4827,"code":4828,"language":397,"meta":117},[395],"let leaked: &'static mut [u8] = Box::leak(vec![1, 2, 3].into_boxed_slice());\n",[73,4830,4828],{"__ignoreMap":117},[20,4832,4833,4835,4836,4838],{},[73,4834,4824],{}," turns an owned heap value into a ",[73,4837,4560],{}," reference (memory is never reclaimed). Useful for long-lived configs but a memory leak by design.",[15,4840,4842],{"id":4841},"common-lifetime-errors-fixes","Common Lifetime Errors & Fixes",[33,4844,4845,4851,4861,4872],{},[36,4846,4847,4850],{},[24,4848,4849],{},"\"borrowed value does not live long enough\"",": the referent's scope is too short. Restructure so it outlives the borrow, or clone\u002Fown.",[36,4852,4853,4856,4857,4860],{},[24,4854,4855],{},"\"lifetime may not live long enough\"",": explicit lifetimes where the relationship is wrong; usually you need to express that the output is bound to ",[183,4858,4859],{},"one specific"," input.",[36,4862,4863,4866,4867,4242,4869,4871],{},[24,4864,4865],{},"\"returns a value referencing data owned by the current function\"",": returning a reference to a local. You must return an owned value (e.g., ",[73,4868,1197],{},[73,4870,1630],{},"), or take the data as input.",[36,4873,4874,4880,4881,4883],{},[24,4875,4876,4877,4879],{},"Adding ",[73,4878,4560],{}," to silence",": usually wrong. Thread spawns require ",[73,4882,4560],{}," for closure captures; you need owned data there.",[15,4885,4887],{"id":4886},"lifetime-patterns-cheat-sheet","Lifetime Patterns Cheat Sheet",[917,4889,4890,4900],{},[920,4891,4892],{},[923,4893,4894,4897],{},[926,4895,4896],{},"Function returns…",[926,4898,4899],{},"What you do",[936,4901,4902,4910,4918,4934,4948],{},[923,4903,4904,4907],{},[941,4905,4906],{},"A reference clearly tied to one input",[941,4908,4909],{},"Rely on elision",[923,4911,4912,4915],{},[941,4913,4914],{},"A reference derived from multiple inputs",[941,4916,4917],{},"Pick the relevant input lifetime and annotate",[923,4919,4920,4923],{},[941,4921,4922],{},"A reference to newly created data",[941,4924,4925,4926,480,4928,4931,4932],{},"Return owned (",[73,4927,1197],{},[73,4929,4930],{},"Vec\u003CT>","), not ",[73,4933,2710],{},[923,4935,4936,4941],{},[941,4937,1114,4938,4940],{},[73,4939,4560],{}," literal or constant",[941,4942,4943,4944,4947],{},"Write ",[73,4945,4946],{},"&'static"," explicitly",[923,4949,4950,4953],{},[941,4951,4952],{},"Data tied to a self-borrow",[941,4954,1876,4955,4957],{},[73,4956,2245],{},"-elision",[15,4959,711],{"id":710},[33,4961,4962,4985,4991,5002,5014],{},[36,4963,4964,71,4973,4976,4977,4980,4981,27,4983,259],{},[24,4965,4966,4968,4969,4972],{},[73,4967,4499],{}," ties output to the ",[183,4970,4971],{},"shortest"," input",[73,4974,4975],{},"longest\u003C'a>(x: &'a, y: &'a)"," means the result lives at most as long as the ",[183,4978,4979],{},"shorter"," of ",[73,4982,1179],{},[73,4984,4508],{},[36,4986,4987,4990],{},[24,4988,4989],{},"Closures capturing references",": the closure's lifetime must include the captured references' lifetimes.",[36,4992,4993,71,4996,4999,5000,259],{},[24,4994,4995],{},"Iterators holding references",[73,4997,4998],{},"std::slice::Iter\u003C'a, T>"," borrows the slice for ",[73,5001,4499],{},[36,5003,5004,5009,5010,5013],{},[24,5005,5006,5008],{},[73,5007,2245],{},"-referential structs"," are famously hard in safe Rust; use ",[73,5011,5012],{},"ouroboros"," crate or restructure. The borrow checker can't express \"this field borrows from that field of the same struct\".",[36,5015,5016,5019,5020,5023],{},[24,5017,5018],{},"Async functions"," desugar to state machines that hold references across ",[73,5021,5022],{},".await"," points — lifetimes get complex; usually you must own the data instead of borrowing.",[15,5025,349],{"id":348},[20,5027,5028],{},"Lifetimes are how Rust makes references safe. They:",[33,5030,5031,5034,5037,5040],{},[36,5032,5033],{},"Annotate relationships between inputs and outputs.",[36,5035,5036],{},"Get elided in common cases.",[36,5038,5039],{},"Enforce that no reference outlives its referent.",[36,5041,5042],{},"Sometimes need explicit annotation when multiple inputs feed an output.",[20,5044,5045],{},"Next: Structs and enums — the algebraic data types at the heart of Rust modeling.",{"title":117,"searchDepth":357,"depth":357,"links":5047},[5048,5049,5050,5051,5052,5053,5054,5055,5056,5057,5059,5060,5061,5062,5064,5065,5066,5067],{"id":4452,"depth":357,"text":4453},{"id":4469,"depth":357,"text":4470},{"id":4511,"depth":357,"text":4512},{"id":1207,"depth":357,"text":4560},{"id":4607,"depth":357,"text":4608},{"id":4627,"depth":357,"text":4628},{"id":4643,"depth":357,"text":4644},{"id":4672,"depth":357,"text":4673},{"id":4731,"depth":357,"text":4732},{"id":4753,"depth":357,"text":5058},"Anonymous Lifetime '_",{"id":4775,"depth":357,"text":4776},{"id":4788,"depth":357,"text":4789},{"id":4798,"depth":357,"text":4799},{"id":4820,"depth":357,"text":5063},"Lifetime Extension via Box::leak",{"id":4841,"depth":357,"text":4842},{"id":4886,"depth":357,"text":4887},{"id":710,"depth":357,"text":711},{"id":348,"depth":357,"text":349},"Lifetimes are the borrow checker's way of tracking how long a reference is valid. They're a compile-time concept; there's zero runtime cost.",{},"\u002Frust\u002F10-lifetimes",{"title":4437,"description":5068},"rust\u002F10-lifetimes","J3_HqRIBjlk8Vjw_N5H-E0DhwQzVZ4RssZB7bxyo3Sk",{"id":5075,"title":5076,"body":5077,"description":5084,"extension":373,"meta":5606,"navigation":375,"path":5607,"seo":5608,"stem":5609,"__hash__":5610},"content\u002Frust\u002F11-structs.md","11 — Structs",{"type":8,"value":5078,"toc":5582},[5079,5082,5085,5089,5095,5099,5105,5111,5115,5121,5155,5159,5165,5185,5189,5195,5198,5204,5210,5216,5220,5253,5257,5263,5267,5273,5278,5282,5288,5291,5295,5301,5304,5351,5368,5373,5379,5382,5389,5406,5412,5416,5422,5428,5430,5514,5520,5546,5550,5567,5569,5579],[11,5080,5076],{"id":5081},"_11-structs",[20,5083,5084],{},"Structs group related data. Rust structs come in three flavors.",[15,5086,5088],{"id":5087},"named-field-structs","Named-Field Structs",[111,5090,5093],{"className":5091,"code":5092,"language":397,"meta":117},[395],"struct User {\n    username: String,\n    email: String,\n    sign_in_count: u64,\n    active: bool,\n}\n\nlet u = User {\n    username: String::from(\"ada\"),\n    email: String::from(\"ada@example.com\"),\n    sign_in_count: 1,\n    active: true,\n};\n",[73,5094,5092],{"__ignoreMap":117},[130,5096,5098],{"id":5097},"field-init-shorthand","Field-Init Shorthand",[111,5100,5103],{"className":5101,"code":5102,"language":397,"meta":117},[395],"fn new(email: String, username: String) -> User {\n    User { email, username, active: true, sign_in_count: 0 }\n}\n",[73,5104,5102],{"__ignoreMap":117},[20,5106,5107,5108,259],{},"When a variable name matches the field, omit ",[73,5109,5110],{},": value",[130,5112,5114],{"id":5113},"struct-update-syntax","Struct Update Syntax",[111,5116,5119],{"className":5117,"code":5118,"language":397,"meta":117},[395],"let u2 = User { email: String::from(\"ada2@x.com\"), ..u };\n",[73,5120,5118],{"__ignoreMap":117},[33,5122,5123,5132,5144],{},[36,5124,5125,5128,5129,259],{},[73,5126,5127],{},"..u"," copies\u002Fmoves the remaining fields from ",[73,5130,5131],{},"u",[36,5133,5134,5135,5138,5139,5141,5142,526],{},"Like a partial move — ",[73,5136,5137],{},"u.username"," is now invalid if ",[73,5140,1197],{}," was moved (non-",[73,5143,1795],{},[36,5145,5146,5147,5149,5150,5152,5153,259],{},"For ",[73,5148,1795],{}," fields, they're copied; for non-",[73,5151,1795],{},", they're moved out of ",[73,5154,5131],{},[15,5156,5158],{"id":5157},"tuple-structs","Tuple Structs",[111,5160,5163],{"className":5161,"code":5162,"language":397,"meta":117},[395],"struct Color(i32, i32, i32);\nlet c = Color(255, 128, 0);\nlet r = c.0;\n",[73,5164,5162],{"__ignoreMap":117},[33,5166,5167,5170,5179],{},[36,5168,5169],{},"Look like tuples but are distinct types.",[36,5171,5172,5173,5176,5177,259],{},"Useful for newtype pattern: ",[73,5174,5175],{},"struct Meters(f64);"," prevents mixing with other ",[73,5178,1509],{},[36,5180,5181,5182,259],{},"Pattern match: ",[73,5183,5184],{},"let Color(r, g, b) = c;",[15,5186,5188],{"id":5187},"unit-structs","Unit Structs",[111,5190,5193],{"className":5191,"code":5192,"language":397,"meta":117},[395],"struct AlwaysEqual;\nlet _a = AlwaysEqual;\n",[73,5194,5192],{"__ignoreMap":117},[20,5196,5197],{},"Zero-sized; useful for trait implementations with no data (e.g., marker traits, type-state).",[15,5199,5201,5203],{"id":5200},"impl-blocks",[73,5202,2215],{}," Blocks",[111,5205,5208],{"className":5206,"code":5207,"language":397,"meta":117},[395],"impl User {\n    fn new(email: String, username: String) -> Self {\n        User { email, username, active: true, sign_in_count: 0 }\n    }\n    fn is_active(&self) -> bool { self.active }\n    fn sign_in(&mut self) { self.sign_in_count += 1; }\n    fn deactivate(self) -> User { User { active: false, ..self } }\n}\n",[73,5209,5207],{"__ignoreMap":117},[20,5211,5212,5213,5215],{},"You can split ",[73,5214,2215],{}," across multiple blocks (common in real codebases: one for methods, one for trait impls).",[15,5217,5219],{"id":5218},"methods-vs-associated-functions","Methods vs Associated Functions",[33,5221,5222,5235,5243],{},[36,5223,5224,5225,1212,5227,1212,5229,5231,5232,259],{},"Methods take ",[73,5226,2229],{},[73,5228,2239],{},[73,5230,2245],{}," and are called on instances: ",[73,5233,5234],{},"u.is_active()",[36,5236,2251,5237,5239,5240,259],{},[73,5238,2245],{},") are constructors: ",[73,5241,5242],{},"User::new(...)",[36,5244,5245,5246,5248,5249,5252],{},"Convention: ",[73,5247,558],{}," for the canonical constructor, ",[73,5250,5251],{},"with_x"," for variant constructors.",[15,5254,5256],{"id":5255},"lifetime-on-structs-recap","Lifetime on Structs (recap)",[111,5258,5261],{"className":5259,"code":5260,"language":397,"meta":117},[395],"struct Excerpt\u003C'a> { part: &'a str }\nimpl\u003C'a> Excerpt\u003C'a> { fn part(&self) -> &'a str { self.part } }\n",[73,5262,5260],{"__ignoreMap":117},[15,5264,5266],{"id":5265},"generic-structs","Generic Structs",[111,5268,5271],{"className":5269,"code":5270,"language":397,"meta":117},[395],"struct Point\u003CT> { x: T, y: T }\n\nimpl\u003CT> Point\u003CT> {\n    fn x(&self) -> &T { &self.x }\n}\n\nimpl Point\u003Cf64> {            \u002F\u002F specialized impl for f64\n    fn distance(&self, other: &Self) -> f64 {\n        ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()\n    }\n}\n",[73,5272,5270],{"__ignoreMap":117},[20,5274,5275,5276,259],{},"Type params can be specialized: methods exist only for a specific ",[73,5277,4705],{},[15,5279,5281],{"id":5280},"constants-in-structs","Constants in Structs",[111,5283,5286],{"className":5284,"code":5285,"language":397,"meta":117},[395],"struct Grid\u003Cconst W: usize, const H: usize> {\n    cells: [[u8; W]; H],\n}\nlet g: Grid\u003C10, 20> = Grid { cells: [[0; 10]; 20] };\n",[73,5287,5285],{"__ignoreMap":117},[20,5289,5290],{},"Const generics (1.51+) allow parametrizing by compile-time constants. Limited to integers\u002Fbool\u002Fchar for now (full generic constants are unstable).",[15,5292,5294],{"id":5293},"derive-macros","Derive Macros",[111,5296,5299],{"className":5297,"code":5298,"language":397,"meta":117},[395],"#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]\nstruct Pos { x: i32, y: i32 }\n",[73,5300,5298],{"__ignoreMap":117},[20,5302,5303],{},"Common derives:",[33,5305,5306,5312,5318,5326,5332,5343],{},[36,5307,5308,592,5310],{},[73,5309,469],{},[73,5311,465],{},[36,5313,5314,5317],{},[73,5315,5316],{},"Clone, Copy"," → value duplication",[36,5319,5320,592,5323],{},[73,5321,5322],{},"PartialEq, Eq",[73,5324,5325],{},"==",[36,5327,5328,5331],{},[73,5329,5330],{},"PartialOrd, Ord"," → comparison and sorting",[36,5333,5334,5337,5338,1212,5341],{},[73,5335,5336],{},"Hash"," → usable in ",[73,5339,5340],{},"HashSet",[73,5342,1687],{},[36,5344,5345,592,5348],{},[73,5346,5347],{},"Default",[73,5349,5350],{},"Pos::default()",[20,5352,5353,1212,5356,5358,5359,5362,5363,1212,5366,259],{},[73,5354,5355],{},"Eq",[73,5357,1533],{}," require no ",[73,5360,5361],{},"NaN","-like values — floats only get ",[73,5364,5365],{},"PartialEq",[73,5367,1529],{},[15,5369,5371],{"id":5370},"default",[73,5372,5347],{},[111,5374,5377],{"className":5375,"code":5376,"language":397,"meta":117},[395],"#[derive(Default)]\nstruct Config { host: String, port: u16 }\nlet c = Config { host: \"localhost\".into(), ..Default::default() };\n",[73,5378,5376],{"__ignoreMap":117},[20,5380,5381],{},"Idiomatic way to provide \"default with overrides\".",[15,5383,5385,559,5387],{"id":5384},"debug-vs-display",[73,5386,469],{},[73,5388,461],{},[33,5390,5391,5401],{},[36,5392,5393,5395,5396,5398,5399,526],{},[73,5394,469],{}," is derived, machine-readable-ish (",[73,5397,465],{}," \u002F pretty ",[73,5400,473],{},[36,5402,5403,5405],{},[73,5404,461],{}," is user-facing; you must write it manually.",[111,5407,5410],{"className":5408,"code":5409,"language":397,"meta":117},[395],"impl std::fmt::Display for User {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        write!(f, \"{} \u003C{}>\", self.username, self.email)\n    }\n}\n",[73,5411,5409],{"__ignoreMap":117},[15,5413,5415],{"id":5414},"struct-updates-and-moves","Struct Updates and Moves",[111,5417,5420],{"className":5418,"code":5419,"language":397,"meta":117},[395],"let u = User { \u002F* filled *\u002F };\nlet email = u.email;             \u002F\u002F partial move\n\u002F\u002F u is partially moved; can still read other fields, but not u as a whole\n",[73,5421,5419],{"__ignoreMap":117},[20,5423,5424,5425,5427],{},"Reconstruct with ",[73,5426,4081],{}," if needed.",[15,5429,1125],{"id":1124},[33,5431,5432,5438,5454,5460,5470,5478,5485,5494,5503],{},[36,5433,5434,5437],{},[24,5435,5436],{},"Out-of-order field initialization"," is allowed — order doesn't matter in struct literals.",[36,5439,5440,5443,5444,5446,5447,1212,5450,5453],{},[24,5441,5442],{},"Mutability is per-binding, not per-field",": there's no ",[73,5445,885],{}," field modifier. Use ",[73,5448,5449],{},"Cell",[73,5451,5452],{},"RefCell"," for interior mutability of single fields.",[36,5455,5456,5459],{},[24,5457,5458],{},"No inheritance",": Rust has no class inheritance. Use composition + traits.",[36,5461,5462,5465,5466,5469],{},[24,5463,5464],{},"Private fields",": by default, fields are private to the module. Use ",[73,5467,5468],{},"pub"," to expose.",[36,5471,5472,5477],{},[24,5473,5474],{},[73,5475,5476],{},"pub(crate)",": visible within the same crate only.",[36,5479,5480,5484],{},[24,5481,5482],{},[73,5483,2890],{}," prevents external crates from constructing the struct with literal syntax — forces them to use a constructor (future-proofing).",[36,5486,5487,5490,5491,5493],{},[24,5488,5489],{},"Self-referential structs",": not expressible directly in safe Rust (the borrow checker can't describe the relationship); use crates like ",[73,5492,5012],{}," or own the data.",[36,5495,5496,71,5499,5502],{},[24,5497,5498],{},"ZST struct",[73,5500,5501],{},"struct Marker;"," has size 0.",[36,5504,5505,5510,5511,5513],{},[24,5506,5507,5508],{},"Field order and ",[73,5509,3217],{},": struct fields drop in ",[24,5512,3335],{}," (RFC 1857), unlike locals which drop in reverse order. This can matter for field destructors that depend on each other.",[15,5515,5517,5519],{"id":5516},"impl-method-dispatch",[73,5518,2215],{}," Method Dispatch",[33,5521,5522,5528,5540],{},[36,5523,5524,5525,5527],{},"Methods taking ",[73,5526,2245],{}," by value consume the receiver.",[36,5529,5530,5531,480,5533,480,5536,5539],{},"Method resolution finds methods on ",[73,5532,2264],{},[73,5534,5535],{},"&Self",[73,5537,5538],{},"&mut Self"," automatically based on call syntax.",[36,5541,5542,5543,5545],{},"Auto-ref\u002Fderef lets you call ",[73,5544,2229],{}," methods on owned values and vice versa.",[15,5547,5549],{"id":5548},"memory-layout","Memory Layout",[33,5551,5552],{},[36,5553,5554,5555,5558,5559,5562,5563,5566],{},"Reorder fields for minimal padding — the compiler does this by default (repr optimization). Use ",[73,5556,5557],{},"#[repr(C)]"," to force C-compatible layout (FFI). Use ",[73,5560,5561],{},"#[repr(transparent)]"," for newtype wrappers (same layout as inner). Use ",[73,5564,5565],{},"#[repr(packed)]"," to disable padding (careful with alignment → unaligned reads are UB).",[15,5568,349],{"id":348},[20,5570,5571,5572,5574,5575,5578],{},"Structs come in named, tuple, and unit forms. Methods live in ",[73,5573,2215],{}," blocks. Derive macros give you common traits for free. Const generics, generics, and lifetimes parametrize them. Memory layout can be controlled with ",[73,5576,5577],{},"repr"," attributes.",[20,5580,5581],{},"Next: Enums — Rust's algebraic data types.",{"title":117,"searchDepth":357,"depth":357,"links":5583},[5584,5588,5589,5590,5592,5593,5594,5595,5596,5597,5598,5600,5601,5602,5604,5605],{"id":5087,"depth":357,"text":5088,"children":5585},[5586,5587],{"id":5097,"depth":364,"text":5098},{"id":5113,"depth":364,"text":5114},{"id":5157,"depth":357,"text":5158},{"id":5187,"depth":357,"text":5188},{"id":5200,"depth":357,"text":5591},"impl Blocks",{"id":5218,"depth":357,"text":5219},{"id":5255,"depth":357,"text":5256},{"id":5265,"depth":357,"text":5266},{"id":5280,"depth":357,"text":5281},{"id":5293,"depth":357,"text":5294},{"id":5370,"depth":357,"text":5347},{"id":5384,"depth":357,"text":5599},"Debug vs Display",{"id":5414,"depth":357,"text":5415},{"id":1124,"depth":357,"text":1125},{"id":5516,"depth":357,"text":5603},"impl Method Dispatch",{"id":5548,"depth":357,"text":5549},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F11-structs",{"title":5076,"description":5084},"rust\u002F11-structs","PLpOPKSGYcUDKaHZ7Z634UO0FaFo20SrP2x3VsOJWKs",{"id":5612,"title":5613,"body":5614,"description":5621,"extension":373,"meta":6124,"navigation":375,"path":6125,"seo":6126,"stem":6127,"__hash__":6128},"content\u002Frust\u002F12-enums.md","12 — Enums (Algebraic Data Types)",{"type":8,"value":5615,"toc":6099},[5616,5619,5622,5626,5632,5638,5642,5648,5674,5677,5683,5690,5696,5708,5714,5721,5727,5730,5734,5740,5743,5747,5753,5759,5766,5772,5786,5790,5807,5813,5817,5823,5834,5838,5844,5851,5857,5860,5864,5867,5873,5888,5899,5903,5906,5912,5918,5922,5928,5931,5933,6035,6041,6047,6054,6058,6061,6067,6077,6079,6096],[11,5617,5613],{"id":5618},"_12-enums-algebraic-data-types",[20,5620,5621],{},"Enums are Rust's killer feature for modeling domain choices. Each variant can carry data of a different shape — they're algebraic data types (ADTs), more like F# discriminated unions than C enums.",[15,5623,5625],{"id":5624},"basic-enum","Basic Enum",[111,5627,5630],{"className":5628,"code":5629,"language":397,"meta":117},[395],"enum IpAddr {\n    V4(u8, u8, u8, u8),\n    V6(String),\n}\n\nlet v4 = IpAddr::V4(127, 0, 0, 1);\nlet v6 = IpAddr::V6(String::from(\"::1\"));\n",[73,5631,5629],{"__ignoreMap":117},[20,5633,5634,5635,5637],{},"Each variant is a constructor; the enum value is ",[183,5636,3589],{}," of them.",[15,5639,5641],{"id":5640},"variants-with-named-fields","Variants with Named Fields",[111,5643,5646],{"className":5644,"code":5645,"language":397,"meta":117},[395],"enum Message {\n    Quit,\n    Move { x: i32, y: i32 },\n    Write(String),\n    ChangeColor(i32, i32, i32),\n}\n",[73,5647,5645],{"__ignoreMap":117},[33,5649,5650,5656,5662,5668],{},[36,5651,5652,5655],{},[73,5653,5654],{},"Quit"," — unit variant (no data).",[36,5657,5658,5661],{},[73,5659,5660],{},"Move"," — struct-like variant.",[36,5663,5664,5667],{},[73,5665,5666],{},"Write"," — tuple-like variant.",[36,5669,5670,5673],{},[73,5671,5672],{},"ChangeColor"," — tuple-like with multiple fields.",[20,5675,5676],{},"Pattern matching destructures them:",[111,5678,5681],{"className":5679,"code":5680,"language":397,"meta":117},[395],"match msg {\n    Message::Quit => {},\n    Message::Move { x, y } => println!(\"{x},{y}\"),\n    Message::Write(s) => println!(\"{s}\"),\n    Message::ChangeColor(r, g, b) => println!(\"{r},{g},{b}\"),\n}\n",[73,5682,5680],{"__ignoreMap":117},[15,5684,5686,5689],{"id":5685},"optiont-the-null-replacement",[73,5687,5688],{},"Option\u003CT>"," — The Null Replacement",[111,5691,5694],{"className":5692,"code":5693,"language":397,"meta":117},[395],"enum Option\u003CT> {\n    Some(T),\n    None,\n}\n",[73,5695,5693],{"__ignoreMap":117},[20,5697,5698,5699,5702,5703,5705,5706,259],{},"There is ",[24,5700,5701],{},"no null"," in Rust. Use ",[73,5704,5688],{}," when a value may be absent. The compiler forces you to handle ",[73,5707,1541],{},[111,5709,5712],{"className":5710,"code":5711,"language":397,"meta":117},[395],"let v: Option\u003Ci32> = Some(5);\nlet none: Option\u003Ci32> = None;\nmatch v { Some(x) => println!(\"{x}\"), None => println!(\"none\") }\nlet unwrapped = v.unwrap_or(0);\n",[73,5713,5711],{"__ignoreMap":117},[15,5715,5717,5720],{"id":5716},"resultt-e-error-handling-primitive",[73,5718,5719],{},"Result\u003CT, E>"," — Error Handling Primitive",[111,5722,5725],{"className":5723,"code":5724,"language":397,"meta":117},[395],"enum Result\u003CT, E> {\n    Ok(T),\n    Err(E),\n}\n",[73,5726,5724],{"__ignoreMap":117},[20,5728,5729],{},"The basis of Rust error handling. See Error Handling chapter.",[15,5731,5733],{"id":5732},"methods-on-enums","Methods on Enums",[111,5735,5738],{"className":5736,"code":5737,"language":397,"meta":117},[395],"impl Message {\n    fn call(&self) {\n        \u002F\u002F dispatch on self\n    }\n}\n",[73,5739,5737],{"__ignoreMap":117},[20,5741,5742],{},"Enums can have methods, just like structs.",[15,5744,5746],{"id":5745},"enums-with-generic-parameters","Enums with Generic Parameters",[111,5748,5751],{"className":5749,"code":5750,"language":397,"meta":117},[395],"enum Either\u003CL, R> {\n    Left(L),\n    Right(R),\n}\n\nenum Tree\u003CT> {\n    Leaf,\n    Node(Box\u003CTree\u003CT>>, T, Box\u003CTree\u003CT>>),\n}\n",[73,5752,5750],{"__ignoreMap":117},[20,5754,5755,5756,5758],{},"Recursive enums need indirection (",[73,5757,1200],{},") because the compiler needs to know the size — direct self-recursion would be infinitely sized.",[15,5760,5762,5765],{"id":5761},"derive-for-enums",[73,5763,5764],{},"#[derive]"," for Enums",[111,5767,5770],{"className":5768,"code":5769,"language":397,"meta":117},[395],"#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\nenum Color { Red, Green, Blue }\n",[73,5771,5769],{"__ignoreMap":117},[20,5773,5774,5776,5777,5780,5781,5783,5784,526],{},[73,5775,1795],{}," only works if ",[24,5778,5779],{},"every"," variant's data is ",[73,5782,1795],{}," (e.g., no ",[73,5785,1197],{},[15,5787,5789],{"id":5788},"match-exhaustiveness","Match Exhaustiveness",[20,5791,5792,5794,5795,5797,5798,5800,5801,2534,5803,5806],{},[73,5793,1907],{}," must cover every variant. Use ",[73,5796,1157],{}," for \"everything else\". For ",[73,5799,2890],{}," enums from external crates, ",[73,5802,1157],{},[183,5804,5805],{},"required"," even if you cover all current variants (upstream may add more).",[111,5808,5811],{"className":5809,"code":5810,"language":397,"meta":117},[395],"#[non_exhaustive]\npub enum Event { Login, Logout }\n\u002F\u002F External crate must write `_ => ...` arm.\n",[73,5812,5810],{"__ignoreMap":117},[15,5814,5816],{"id":5815},"field-access-on-tuple-variants","Field-Access on Tuple Variants",[111,5818,5821],{"className":5819,"code":5820,"language":397,"meta":117},[395],"let m = Message::Write(\"hi\".into());\nlet s = m.0;  \u002F\u002F ERROR: cannot access field — must pattern match\n",[73,5822,5820],{"__ignoreMap":117},[20,5824,5825,5826,5829,5830,5833],{},"Tuple-variant fields aren't accessible via ",[73,5827,5828],{},".0"," — you must destructure with ",[73,5831,5832],{},"let Message::Write(s) = m;",". (Some newer nightly features relax this.)",[15,5835,5837],{"id":5836},"pattern-matching-patterns","Pattern Matching Patterns",[111,5839,5842],{"className":5840,"code":5841,"language":397,"meta":117},[395],"match opt {\n    Some(0) => \"zero\",\n    Some(1..=9) => \"small\",\n    Some(n) if n > 1000 => \"big\",     \u002F\u002F guard\n    Some(_) => \"other\",\n    None => \"none\",\n}\n\n\u002F\u002F binding with @\nmatch n {\n    0..=9 => \"digit\",\n    x @ 10..=99 => \"two digits: {x}\",\n    _ => \"big\",\n}\n\n\u002F\u002F or-patterns\nmatch c {\n    'a' | 'e' | 'i' | 'o' | 'u' => \"vowel\",\n    _ => \"consonant\",\n}\n",[73,5843,5841],{"__ignoreMap":117},[15,5845,5847,27,5849],{"id":5846},"if-let-and-while-let",[73,5848,2541],{},[73,5850,2607],{},[111,5852,5855],{"className":5853,"code":5854,"language":397,"meta":117},[395],"if let Some(x) = opt { println!(\"{x}\"); }\nwhile let Some(x) = iter.next() { \u002F* ... *\u002F }\n",[73,5856,5854],{"__ignoreMap":117},[20,5858,5859],{},"Short for \"match one pattern and ignore the rest\". Use when you only care about one case.",[15,5861,5863],{"id":5862},"enum-memory-layout","Enum Memory Layout",[20,5865,5866],{},"Enums store a discriminant (tag) plus enough space for the largest variant's payload:",[111,5868,5871],{"className":5869,"code":5870,"language":397,"meta":117},[395],"enum E {\n    A,\n    B(i64),\n    C([u8; 16]),\n}\n\u002F\u002F size = max(payload size) + discriminant (often optimized)\n",[73,5872,5870],{"__ignoreMap":117},[20,5874,5875,5876,5879,5880,5882,5883,5885,5886,526],{},"The compiler performs ",[24,5877,5878],{},"niche optimization",": if a variant is impossible to overlap with another, it can drop the discriminant. Classic case: ",[73,5881,4312],{}," is the same size as ",[73,5884,3130],{}," (null pointer is reserved for ",[73,5887,1541],{},[20,5889,5890,480,5893,480,5896,5898],{},[73,5891,5892],{},"Option\u003CNonNull\u003CT>>",[73,5894,5895],{},"Option\u003CBox\u003CT>>",[73,5897,4315],{}," are all pointer-sized.",[15,5900,5902],{"id":5901},"state-machines","State Machines",[20,5904,5905],{},"Enums are perfect for state machines:",[111,5907,5910],{"className":5908,"code":5909,"language":397,"meta":117},[395],"enum Conn {\n    Idle,\n    Connecting(std::time::Instant),\n    Connected { addr: String, since: std::time::Instant },\n    Error(String),\n}\n",[73,5911,5909],{"__ignoreMap":117},[20,5913,5914,5915,259],{},"Each state carries the data relevant to it. Transitions are explicit functions returning a new ",[73,5916,5917],{},"Conn",[15,5919,5921],{"id":5920},"variants-as-constructors","Variants as Constructors",[111,5923,5926],{"className":5924,"code":5925,"language":397,"meta":117},[395],"let f: fn(String) -> Message = Message::Write;\n",[73,5927,5925],{"__ignoreMap":117},[20,5929,5930],{},"Each variant acts as a function. Useful for higher-order code.",[15,5932,1125],{"id":1124},[33,5934,5935,5949,5961,5971,5986,5997,6010,6023],{},[36,5936,5937,71,5942,5945,5946,259],{},[24,5938,5939,5940],{},"Recursive enums without ",[73,5941,1200],{},[73,5943,5944],{},"enum Bad { Node(Bad) }"," — infinite size, compile error. Use ",[73,5947,5948],{},"Box\u003CBad>",[36,5950,5951,71,5954,5957,5958,259],{},[24,5952,5953],{},"Variant equality",[73,5955,5956],{},"Option::Some(5) == Option::Some(5)"," works only if ",[73,5959,5960],{},"T: PartialEq",[36,5962,5963,5968,5969,259],{},[24,5964,5965,5967],{},[73,5966,1795],{}," enums",": only if all payloads are ",[73,5970,1795],{},[36,5972,5973,5978,5979,5982,5983,526],{},[24,5974,5975,5977],{},[73,5976,5347],{}," for enums",": not derivable for enums (no obvious default). You can ",[73,5980,5981],{},"impl Default"," manually — convention is \"smallest\u002Fzero\" variant (e.g., ",[73,5984,5985],{},"Option::None",[36,5987,5988,5992,5993,5996],{},[24,5989,5990],{},[73,5991,5557],{},": gives C-style layout with explicit discriminant (size depends on largest discriminant). Use ",[73,5994,5995],{},"#[repr(C, u8)]"," etc. to fix discriminant width.",[36,5998,5999,71,6002,1212,6004,6006,6007,6009],{},[24,6000,6001],{},"Comparing variants",[73,6003,1529],{},[73,6005,1533],{}," compares by ",[24,6008,3335],{}," of variants, then by payload.",[36,6011,6012,6018,6019,6022],{},[24,6013,6014,6017],{},[73,6015,6016],{},"is_x()"," methods",": idiom is to write ",[73,6020,6021],{},"matches!(self, Self::X)"," or a helper method rather than exposing internal representation.",[36,6024,6025,71,6031,6034],{},[24,6026,6027,6030],{},[73,6028,6029],{},"matches!"," macro",[73,6032,6033],{},"if matches!(opt, Some(0)) { }"," — concise single-pattern check.",[15,6036,6038,6040],{"id":6037},"matches-macro",[73,6039,6029],{}," Macro",[111,6042,6045],{"className":6043,"code":6044,"language":397,"meta":117},[395],"let ok = matches!(result, Ok(_));\nlet small = matches!(n, 0..=9);\n",[73,6046,6044],{"__ignoreMap":117},[20,6048,6049,6050,2806,6052,259],{},"Like a tiny ",[73,6051,1907],{},[73,6053,1559],{},[15,6055,6057],{"id":6056},"typestate-pattern-advanced","Typestate Pattern (advanced)",[20,6059,6060],{},"Use type params to encode states:",[111,6062,6065],{"className":6063,"code":6064,"language":397,"meta":117},[395],"struct Builder\u003CT>(PhantomData\u003CT>);\nstruct Unconfigured;\nstruct Configured;\nimpl Builder\u003CUnconfigured> {\n    fn configure(self) -> Builder\u003CConfigured> { Builder(PhantomData) }\n}\nimpl Builder\u003CConfigured> {\n    fn build(self) -> Product { \u002F* ... *\u002F }\n}\n",[73,6066,6064],{"__ignoreMap":117},[20,6068,6069,6070,6072,6073,6076],{},"Calling ",[73,6071,258],{}," on an ",[73,6074,6075],{},"Unconfigured"," builder is a compile-time error. Encode invariants in the type system.",[15,6078,349],{"id":348},[20,6080,6081,6082,6084,6085,1212,6087,6089,6090,6092,6093,6095],{},"Enums are sum types: each value is one variant (with optional data). Combined with ",[73,6083,1907],{},", they form Rust's modeling backbone. ",[73,6086,1481],{},[73,6088,2792],{}," are the canonical examples. Niche optimization makes them memory-efficient. Pattern matching with guards, or-patterns, ",[73,6091,2691],{},"-bindings, and ",[73,6094,6029],{}," give you expressive dispatch.",[20,6097,6098],{},"Next: Pattern Matching — a deep dive.",{"title":117,"searchDepth":357,"depth":357,"links":6100},[6101,6102,6103,6105,6107,6108,6109,6111,6112,6113,6114,6116,6117,6118,6119,6120,6122,6123],{"id":5624,"depth":357,"text":5625},{"id":5640,"depth":357,"text":5641},{"id":5685,"depth":357,"text":6104},"Option\u003CT> — The Null Replacement",{"id":5716,"depth":357,"text":6106},"Result\u003CT, E> — Error Handling Primitive",{"id":5732,"depth":357,"text":5733},{"id":5745,"depth":357,"text":5746},{"id":5761,"depth":357,"text":6110},"#[derive] for Enums",{"id":5788,"depth":357,"text":5789},{"id":5815,"depth":357,"text":5816},{"id":5836,"depth":357,"text":5837},{"id":5846,"depth":357,"text":6115},"if let and while let",{"id":5862,"depth":357,"text":5863},{"id":5901,"depth":357,"text":5902},{"id":5920,"depth":357,"text":5921},{"id":1124,"depth":357,"text":1125},{"id":6037,"depth":357,"text":6121},"matches! Macro",{"id":6056,"depth":357,"text":6057},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F12-enums",{"title":5613,"description":5621},"rust\u002F12-enums","wKdHYaa1OJW7DGrDx5ZXwNa5zQWk--STxVp_U38sGX8",{"id":6130,"title":6131,"body":6132,"description":6139,"extension":373,"meta":6677,"navigation":375,"path":6678,"seo":6679,"stem":6680,"__hash__":6681},"content\u002Frust\u002F13-pattern-matching.md","13 — Pattern Matching (Deep Dive)",{"type":8,"value":6133,"toc":6644},[6134,6137,6140,6144,6175,6181,6185,6189,6195,6201,6204,6208,6214,6221,6227,6233,6236,6242,6248,6254,6263,6267,6273,6277,6283,6287,6293,6297,6303,6312,6316,6322,6326,6332,6340,6346,6352,6357,6361,6367,6378,6387,6390,6396,6399,6405,6410,6416,6424,6428,6435,6439,6445,6451,6455,6461,6466,6470,6577,6583,6618,6620,6634],[11,6135,6131],{"id":6136},"_13-pattern-matching-deep-dive",[20,6138,6139],{},"Pattern matching is Rust's most expressive control-flow construct. This chapter covers every pattern form, the binding rules, and the gotchas.",[15,6141,6143],{"id":6142},"where-patterns-appear","Where Patterns Appear",[33,6145,6146,6150,6156,6161,6165,6168],{},[36,6147,6148,3294],{},[73,6149,1907],{},[36,6151,6152,3818,6154],{},[73,6153,2541],{},[73,6155,2607],{},[36,6157,6158,6160],{},[73,6159,868],{}," declarations (destructure)",[36,6162,6163],{},[73,6164,2949],{},[36,6166,6167],{},"Function parameters (limited)",[36,6169,6170,1212,6172,6174],{},[73,6171,2614],{},[73,6173,2594],{}," loops (destructure each item)",[111,6176,6179],{"className":6177,"code":6178,"language":397,"meta":117},[395],"let (a, b) = (1, 2);\nfn first((a, _): (i32, i32)) -> i32 { a }\nfor (i, v) in vec.iter().enumerate() { \u002F* ... *\u002F }\n",[73,6180,6178],{"__ignoreMap":117},[15,6182,6184],{"id":6183},"pattern-forms","Pattern Forms",[130,6186,6188],{"id":6187},"literals","Literals",[111,6190,6193],{"className":6191,"code":6192,"language":397,"meta":117},[395],"match x { 0 => \"zero\", 1 => \"one\", _ => \"many\" }\nmatch c { 'a'..='z' | 'A'..='Z' => \"letter\", _ => \"other\" }\n",[73,6194,6192],{"__ignoreMap":117},[130,6196,6198,6199],{"id":6197},"wildcards-_","Wildcards ",[73,6200,1157],{},[20,6202,6203],{},"Matches anything, doesn't bind. Use to ignore.",[130,6205,6207],{"id":6206},"variables","Variables",[111,6209,6212],{"className":6210,"code":6211,"language":397,"meta":117},[395],"match opt {\n    Some(x) => println!(\"{x}\"),   \u002F\u002F binds x\n    None => {},\n}\n",[73,6213,6211],{"__ignoreMap":117},[20,6215,6216,6217,6220],{},"A bare identifier binds the value. ",[73,6218,6219],{},"_x"," also binds but signals \"intentionally unused\" (suppresses warnings).",[130,6222,6224,6225],{"id":6223},"or-patterns","Or-Patterns ",[73,6226,2665],{},[111,6228,6231],{"className":6229,"code":6230,"language":397,"meta":117},[395],"match x {\n    1 | 2 | 3 => \"small\",\n    4 | 5 | 6 => \"medium\",\n    _ => \"big\",\n}\n",[73,6232,6230],{"__ignoreMap":117},[20,6234,6235],{},"Can bind in all alternatives with the same name (or-pattern binding, edition 2021+):",[111,6237,6240],{"className":6238,"code":6239,"language":397,"meta":117},[395],"let (Ok(n) | Err(n)) = result.map(|n| n + 1).map_err(|e| 0);\n",[73,6241,6239],{"__ignoreMap":117},[130,6243,6245,6246],{"id":6244},"ranges","Ranges ",[73,6247,2671],{},[111,6249,6252],{"className":6250,"code":6251,"language":397,"meta":117},[395],"match x {\n    0..=9 => \"digit\",\n    10..=99 => \"tens\",\n    100.. => \"big\",     \u002F\u002F open-ended (unstable on stable for match arms in some forms)\n}\n",[73,6253,6251],{"__ignoreMap":117},[20,6255,6256,6257,6259,6260,6262],{},"Ranges work for ",[73,6258,1586],{}," and numeric types. Use ",[73,6261,4081],{}," for exclusive range in slice patterns.",[130,6264,6266],{"id":6265},"destructuring-structs","Destructuring Structs",[111,6268,6271],{"className":6269,"code":6270,"language":397,"meta":117},[395],"struct P { x: i32, y: i32 }\nmatch p {\n    P { x, y } => println!(\"{x},{y}\"),     \u002F\u002F shorthand\n    P { x: a, y: b } => println!(\"{a},{b}\"),\n    P { x, .. } => println!(\"only x\"),     \u002F\u002F ignore rest\n}\n",[73,6272,6270],{"__ignoreMap":117},[130,6274,6276],{"id":6275},"destructuring-tuples","Destructuring Tuples",[111,6278,6281],{"className":6279,"code":6280,"language":397,"meta":117},[395],"match t {\n    (0, _) => \"first zero\",\n    (a, b) if a \u003C b => \"ascending\",\n    _ => \"other\",\n}\nlet (x, ..) = (1, 2, 3);    \u002F\u002F first element only\nlet (.., z) = (1, 2, 3);    \u002F\u002F last element only\n",[73,6282,6280],{"__ignoreMap":117},[130,6284,6286],{"id":6285},"destructuring-enums","Destructuring Enums",[111,6288,6291],{"className":6289,"code":6290,"language":397,"meta":117},[395],"match e {\n    Message::Quit => {},\n    Message::Move { x: 0, y } => println!(\"zero-x, y={y}\"),\n    Message::Move { x, y } => println!(\"{x},{y}\"),\n    Message::Write(s) if s.is_empty() => \"empty\",\n    Message::Write(s) => s,\n    Message::ChangeColor(r, g, b) => println!(\"{r},{g},{b}\"),\n}\n",[73,6292,6290],{"__ignoreMap":117},[130,6294,6296],{"id":6295},"slice-patterns","Slice Patterns",[111,6298,6301],{"className":6299,"code":6300,"language":397,"meta":117},[395],"match slice {\n    [] => \"empty\",\n    [a] => \"one: {a}\",\n    [a, b] => \"two: {a},{b}\",\n    [first, .., last] => \"first={first} last={last}\",   \u002F\u002F subslice pattern\n    [a, b, c @ ..] => println!(\"{a}, {b}, rest={:?}\", c),\n}\n",[73,6302,6300],{"__ignoreMap":117},[20,6304,6305,6307,6308,6311],{},[73,6306,4081],{}," in slice patterns matches the middle (any length). Limited stable support; ",[73,6309,6310],{},"c @ .."," binds the subslice.",[130,6313,6315],{"id":6314},"reference-patterns","Reference Patterns",[111,6317,6320],{"className":6318,"code":6319,"language":397,"meta":117},[395],"match &x {\n    &0 => \"ref to zero\",      \u002F\u002F matches &0\n    0 => \"deref zero\",         \u002F\u002F auto-deref (binding mode)\n}\nlet &y = &5;                  \u002F\u002F matches the reference, y is i32 (Copy)\nlet ref r = x;               \u002F\u002F r: &i32 — borrow pattern\nlet mut z = 0;\nmatch z {\n    ref mut r => *r += 1,    \u002F\u002F r: &mut i32\n}\n",[73,6321,6319],{"__ignoreMap":117},[130,6323,6325],{"id":6324},"binding-modes-2021","Binding Modes (2021)",[111,6327,6330],{"className":6328,"code":6329,"language":397,"meta":117},[395],"match &opt {\n    Some(x) => println!(\"{x}\"),   \u002F\u002F x: &i32 — auto-ref\n    None => {}\n}\nmatch &mut opt {\n    Some(x) => *x += 1,           \u002F\u002F x: &mut i32\n    None => {}\n}\n",[73,6331,6329],{"__ignoreMap":117},[20,6333,6334,6335,1212,6337,6339],{},"The 2021 edition simplified this — you no longer sprinkle ",[73,6336,2710],{},[73,6338,3850],{}," everywhere. The compiler inserts references as needed based on what you match against.",[130,6341,6343,6345],{"id":6342},"bindings",[73,6344,2691],{}," Bindings",[111,6347,6350],{"className":6348,"code":6349,"language":397,"meta":117},[395],"match n {\n    x @ 0..=9 => \"small: {x}\",\n    x @ (10..=99) => \"medium: {x}\",\n    _ => \"big\",\n}\n",[73,6351,6349],{"__ignoreMap":117},[20,6353,6354,6356],{},[73,6355,2691],{}," binds the value while also constraining it with a pattern.",[130,6358,6360],{"id":6359},"match-guards","Match Guards",[111,6362,6365],{"className":6363,"code":6364,"language":397,"meta":117},[395],"match opt {\n    Some(x) if x > 0 => \"positive\",\n    Some(_) => \"non-positive\",\n    None => \"none\",\n}\n",[73,6366,6364],{"__ignoreMap":117},[20,6368,6369,6370,6373,6374,6377],{},"Guards let you add boolean conditions. They ",[183,6371,6372],{},"can"," prevent exhaustiveness checking — the compiler considers guards potentially false even for matched patterns, so you often need ",[73,6375,6376],{},"_ =>"," arms.",[15,6379,6381,27,6384],{"id":6380},"ref-and-ref-mut",[73,6382,6383],{},"ref",[73,6385,6386],{},"ref mut",[20,6388,6389],{},"Old-school (pre-2021) way to borrow in patterns:",[111,6391,6394],{"className":6392,"code":6393,"language":397,"meta":117},[395],"match opt {\n    Some(ref x) => ...,    \u002F\u002F x: &i32\n    None => ...,\n}\n",[73,6395,6393],{"__ignoreMap":117},[20,6397,6398],{},"Still useful when the default binding mode doesn't fit (e.g., matching by value where you want a ref to one field). Modern Rust mostly auto-borrows.",[15,6400,6402,6403],{"id":6401},"destructuring-with","Destructuring with ",[73,6404,4081],{},[20,6406,6407,6409],{},[73,6408,4081],{}," ignores remaining fields\u002Felements:",[111,6411,6414],{"className":6412,"code":6413,"language":397,"meta":117},[395],"let P { x, .. } = p;     \u002F\u002F ignore y\nlet (a, .., z) = tuple;  \u002F\u002F ignore middle\n",[73,6415,6413],{"__ignoreMap":117},[20,6417,6418,6420,6421,6423],{},[73,6419,4081],{}," can appear once in a struct pattern and once in a tuple\u002Fslice pattern. Multiple ",[73,6422,4081],{}," is an error.",[15,6425,6427],{"id":6426},"patterns-dont-allow-expressions","Patterns Don't Allow Expressions",[20,6429,6430,6431,6434],{},"You can't write ",[73,6432,6433],{},"Some(x + 1)"," as a pattern. Guards exist for that. Patterns are structural; conditions go in guards.",[15,6436,6438],{"id":6437},"exhaustiveness","Exhaustiveness",[111,6440,6443],{"className":6441,"code":6442,"language":397,"meta":117},[395],"fn classify(c: Color) -> &'static str {\n    match c {\n        Color::Red => \"red\",\n        \u002F\u002F ERROR if missing Green\u002FBlue\n    }\n}\n",[73,6444,6442],{"__ignoreMap":117},[20,6446,6447,6448,6450],{},"The compiler lists the missing patterns. Add ",[73,6449,1157],{}," if you genuinely don't care, but be explicit when you can — exhaustiveness is a feature.",[15,6452,6453,6040],{"id":6037},[73,6454,6029],{},[111,6456,6459],{"className":6457,"code":6458,"language":397,"meta":117},[395],"let is_some = matches!(opt, Some(_));\nlet in_range = matches!(n, 0..=9 | 100..=199);\n",[73,6460,6458],{"__ignoreMap":117},[20,6462,6463,6464,259],{},"Concise one-arm matcher returning ",[73,6465,1559],{},[15,6467,6469],{"id":6468},"common-pitfalls","Common Pitfalls",[33,6471,6472,6484,6498,6512,6521,6541,6555,6564],{},[36,6473,6474,71,6477,6480,6481,6483],{},[24,6475,6476],{},"Variable shadowing in pattern",[73,6478,6479],{},"match x { Some(x) => x, None => 0 }"," — the inner ",[73,6482,1179],{}," shadows the outer; usually what you want, but easy to misread.",[36,6485,6486,71,6492,6494,6495,6497],{},[24,6487,6488,559,6490],{},[73,6489,1157],{},[73,6491,6219],{},[73,6493,1157],{}," doesn't bind (drops the value), ",[73,6496,6219],{}," binds (must be used or it warns).",[36,6499,6500,6503,6504,6507,6508,6511],{},[24,6501,6502],{},"Binding mode surprises",": when matching ",[73,6505,6506],{},"&Option\u003Ci32>",", the bound variable is ",[73,6509,6510],{},"&i32",". The compiler prints the inferred type — read the error carefully.",[36,6513,6514,6517,6518,6520],{},[24,6515,6516],{},"Match guard + exhaustiveness",": guards make the compiler treat arms as non-exhaustive. Always have a final ",[73,6519,1157],{}," or cover every variant.",[36,6522,6523,6526,6527,6530,6531,6533,6534,6536,6537,6540],{},[24,6524,6525],{},"Move-out in pattern",": matching ",[73,6528,6529],{},"Some(s)"," on a ",[73,6532,1197],{},"-carrying enum by value moves the ",[73,6535,1197],{},"; matching ",[73,6538,6539],{},"&Some(s)"," borrows it.",[36,6542,6543,71,6546,6548,6549,6551,6552,6554],{},[24,6544,6545],{},"Range patterns need contiguous types",[73,6547,2671],{}," works for ",[73,6550,1586],{}," and integers; ",[73,6553,1197],{}," can't be range-matched.",[36,6556,6557,71,6560,6563],{},[24,6558,6559],{},"Nested patterns",[73,6561,6562],{},"Some((Ok(x), _))"," is valid; patterns nest arbitrarily.",[36,6565,6566,71,6569,6572,6573,6576],{},[24,6567,6568],{},"Tuple struct variants",[73,6570,6571],{},"Message::Move { x, y }"," (struct form) vs ",[73,6574,6575],{},"Message::Write(s)"," (tuple form) — must use the form matching the variant.",[15,6578,6580,6582],{"id":6579},"let-patterns-and-refutability",[73,6581,868],{}," Patterns and Refutability",[33,6584,6585,6603,6610],{},[36,6586,6587,6590,6591,6594,6595,6598,6599,6602],{},[73,6588,6589],{},"let PATTERN = expr"," requires PATTERN to be ",[24,6592,6593],{},"irrefutable"," (always matches): ",[73,6596,6597],{},"let (a, b) = tuple"," is fine; ",[73,6600,6601],{},"let Some(x) = opt"," is an error (refutable).",[36,6604,6605,27,6607,6609],{},[73,6606,2541],{},[73,6608,2607],{}," accept refutable patterns.",[36,6611,6612,6614,6615,259],{},[73,6613,2949],{}," bridges: ",[73,6616,6617],{},"let Some(x) = opt else { return; };",[15,6619,349],{"id":348},[20,6621,6622,6623,6625,6626,1212,6628,6630,6631,6633],{},"Patterns are structural, support literals, ranges, or-patterns, destructuring, ",[73,6624,2691],{}," bindings, and guards. The 2021 binding modes reduced noise. Exhaustiveness is enforced. ",[73,6627,6383],{},[73,6629,6386],{}," are escape hatches for older patterns. ",[73,6632,6029],{}," is a tiny match for booleans.",[20,6635,6636,6637,480,6639,480,6641,6643],{},"Next: Collections (",[73,6638,1194],{},[73,6640,1197],{},[73,6642,1687],{},", etc.).",{"title":117,"searchDepth":357,"depth":357,"links":6645},[6646,6647,6666,6668,6670,6671,6672,6673,6674,6676],{"id":6142,"depth":357,"text":6143},{"id":6183,"depth":357,"text":6184,"children":6648},[6649,6650,6652,6653,6655,6657,6658,6659,6660,6661,6662,6663,6665],{"id":6187,"depth":364,"text":6188},{"id":6197,"depth":364,"text":6651},"Wildcards _",{"id":6206,"depth":364,"text":6207},{"id":6223,"depth":364,"text":6654},"Or-Patterns |",{"id":6244,"depth":364,"text":6656},"Ranges ..=",{"id":6265,"depth":364,"text":6266},{"id":6275,"depth":364,"text":6276},{"id":6285,"depth":364,"text":6286},{"id":6295,"depth":364,"text":6296},{"id":6314,"depth":364,"text":6315},{"id":6324,"depth":364,"text":6325},{"id":6342,"depth":364,"text":6664},"@ Bindings",{"id":6359,"depth":364,"text":6360},{"id":6380,"depth":357,"text":6667},"ref and ref mut",{"id":6401,"depth":357,"text":6669},"Destructuring with ..",{"id":6426,"depth":357,"text":6427},{"id":6437,"depth":357,"text":6438},{"id":6037,"depth":357,"text":6121},{"id":6468,"depth":357,"text":6469},{"id":6579,"depth":357,"text":6675},"let Patterns and Refutability",{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F13-pattern-matching",{"title":6131,"description":6139},"rust\u002F13-pattern-matching","vv3yEEFlzJhmqyI6UYVKF4B9c8cnPUPBHam7w97aHRw",{"id":6683,"title":6684,"body":6685,"description":117,"extension":373,"meta":7512,"navigation":375,"path":7513,"seo":7514,"stem":7515,"__hash__":7516},"content\u002Frust\u002F14-collections.md","14 — Collections (Vec, String, HashMap, and more)",{"type":8,"value":6686,"toc":7472},[6687,6698,6704,6710,6714,6721,6741,6747,6751,6757,6759,6765,6769,6775,6779,6785,6789,6795,6797,6882,6888,6894,6898,6906,6912,6919,6975,6979,6985,6989,6995,6999,7005,7008,7081,7090,7096,7116,7123,7126,7132,7136,7142,7153,7156,7209,7217,7223,7230,7236,7239,7246,7263,7270,7276,7285,7291,7295,7356,7360,7451,7453,7469],[11,6688,6690,6691,480,6693,480,6695,6697],{"id":6689},"_14-collections-vec-string-hashmap-and-more","14 — Collections (",[73,6692,1194],{},[73,6694,1197],{},[73,6696,1687],{},", and more)",[15,6699,6701,6703],{"id":6700},"vect-the-growable-array",[73,6702,4930],{}," — The Growable Array",[111,6705,6708],{"className":6706,"code":6707,"language":397,"meta":117},[395],"let mut v: Vec\u003Ci32> = Vec::new();\nv.push(1);\nv.push(2);\nlet v2 = vec![1, 2, 3];          \u002F\u002F macro shortcut\nlet v3 = vec![0; 5];              \u002F\u002F [0, 0, 0, 0, 0]\n",[73,6709,6707],{"__ignoreMap":117},[130,6711,6713],{"id":6712},"memory-model","Memory Model",[20,6715,6716,2534,6718,170],{},[73,6717,1194],{},[73,6719,6720],{},"(ptr, len, capacity)",[33,6722,6723,6729,6735],{},[36,6724,6725,6728],{},[73,6726,6727],{},"ptr"," → heap allocation",[36,6730,6731,6734],{},[73,6732,6733],{},"len"," → number of elements currently stored",[36,6736,6737,6740],{},[73,6738,6739],{},"capacity"," → allocated space; pushing beyond doubles capacity (amortized O(1))",[111,6742,6745],{"className":6743,"code":6744,"language":397,"meta":117},[395],"let mut v = Vec::with_capacity(100);   \u002F\u002F pre-allocate for perf\nv.push(1);\nprintln!(\"{:?}\", v.capacity());\n",[73,6746,6744],{"__ignoreMap":117},[130,6748,6750],{"id":6749},"indexing-slicing","Indexing & Slicing",[111,6752,6755],{"className":6753,"code":6754,"language":397,"meta":117},[395],"v[0];            \u002F\u002F panics on OOB\nv.get(0);        \u002F\u002F Option\u003C&T> — safe\n&v[1..3];        \u002F\u002F slice, panics on OOB\nv.get(1..3);     \u002F\u002F Option\u003C&[T]>\n",[73,6756,6754],{"__ignoreMap":117},[130,6758,4212],{"id":4211},[111,6760,6763],{"className":6761,"code":6762,"language":397,"meta":117},[395],"for x in &v { \u002F* x: &i32 *\u002F }\nfor x in &mut v { \u002F* x: &mut i32 *\u002F }\nfor x in v { \u002F* consumes v, x: i32 *\u002F }   \u002F\u002F ownership move\nv.iter();        \u002F\u002F iterator over &T\nv.iter_mut();    \u002F\u002F over &mut T\nv.into_iter();   \u002F\u002F consumes v, yields T (or &T for &Vec)\n",[73,6764,6762],{"__ignoreMap":117},[130,6766,6768],{"id":6767},"insert-remove-swap","Insert \u002F Remove \u002F Swap",[111,6770,6773],{"className":6771,"code":6772,"language":397,"meta":117},[395],"v.insert(0, 99);   \u002F\u002F O(n) — shifts\nv.remove(0);       \u002F\u002F O(n)\nv.swap_remove(0);  \u002F\u002F O(1) — reorders\nv.pop();           \u002F\u002F Option\u003CT>\nv.clear();\nv.truncate(2);\nv.retain(|x| *x > 0);   \u002F\u002F filter in place\nv.drain(1..3);          \u002F\u002F removes range, returns iterator\n",[73,6774,6772],{"__ignoreMap":117},[130,6776,6778],{"id":6777},"capacity-management","Capacity Management",[111,6780,6783],{"className":6781,"code":6782,"language":397,"meta":117},[395],"v.shrink_to_fit();\nv.reserve(10);\nv.reserve_exact(10);\n",[73,6784,6782],{"__ignoreMap":117},[130,6786,6788],{"id":6787},"sorting-searching","Sorting & Searching",[111,6790,6793],{"className":6791,"code":6792,"language":397,"meta":117},[395],"v.sort();\nv.sort_by(|a, b| b.cmp(a));\nv.sort_by_key(|x| x.abs());\nv.sort_unstable_by_key(|x| *x);    \u002F\u002F faster, non-stable\nv.dedup();                        \u002F\u002F remove consecutive duplicates (after sort)\nv.binary_search(&5);              \u002F\u002F Option\u003Cusize> on sorted vec\n",[73,6794,6792],{"__ignoreMap":117},[130,6796,711],{"id":710},[33,6798,6799,6808,6816,6827,6835,6848,6863,6874],{},[36,6800,6801,6805,6806,259],{},[24,6802,6803],{},[73,6804,3891],{},": borrow error (mutable borrow while reading). Copy first: ",[73,6807,3895],{},[36,6809,6810,6815],{},[24,6811,6812],{},[73,6813,6814],{},"Vec::with_capacity(0)",": valid; first push triggers alloc.",[36,6817,6818,71,6823,6826],{},[24,6819,2879,6820,6822],{},[73,6821,1194],{}," is non-null",[73,6824,6825],{},"Vec::new()"," doesn't allocate; capacity 0.",[36,6828,6829,6834],{},[24,6830,6831],{},[73,6832,6833],{},"Vec\u003COption\u003CT>>",": not niche-optimized; uses full space.",[36,6836,6837,6840,6841,6843,6844,6847],{},[24,6838,6839],{},"ZST elements",": a ",[73,6842,1976],{}," has ",[73,6845,6846],{},"capacity = usize::MAX","; never actually allocates.",[36,6849,6850,6855,6856,6859,6860,259],{},[24,6851,6852,6030],{},[73,6853,6854],{},"vec![]"," is hygienic — supports any ",[73,6857,6858],{},"T: Clone"," for ",[73,6861,6862],{},"vec![v; n]",[36,6864,6865,6870,6871,259],{},[24,6866,6867],{},[73,6868,6869],{},"Vec::leak",": leak into ",[73,6872,6873],{},"&'static mut [T]",[36,6875,6876,6881],{},[24,6877,6878],{},[73,6879,6880],{},"Vec::spare_capacity_mut"," for unsafe manual writes.",[15,6883,6885,6887],{"id":6884},"string-owned-utf-8-string",[73,6886,1197],{}," — Owned UTF-8 String",[111,6889,6892],{"className":6890,"code":6891,"language":397,"meta":117},[395],"let s = String::new();\nlet s = String::from(\"hi\");\nlet s: String = \"hi\".to_string();\nlet s: String = \"hi\".to_owned();\ns.push_str(\" world\");\ns.push('!');\ns.replace(\"hi\", \"bye\");\ns.to_lowercase();\ns.split(' ').collect::\u003CVec\u003C_>>();\n",[73,6893,6891],{"__ignoreMap":117},[130,6895,6897],{"id":6896},"utf-8-invariant","UTF-8 Invariant",[20,6899,6900,1592,6902,6905],{},[73,6901,1197],{},[73,6903,6904],{},"Vec\u003Cu8>"," with the invariant that bytes are valid UTF-8. You can't push arbitrary bytes:",[111,6907,6910],{"className":6908,"code":6909,"language":397,"meta":117},[395],"let bytes = vec![0xffu8];\n\u002F\u002F String::from_utf8(bytes).unwrap();  \u002F\u002F ERROR: invalid UTF-8\nString::from_utf8(bytes).unwrap_err(); \u002F\u002F ok\nString::from_utf8_lossy(&[0xffu8, b'h']);  \u002F\u002F \"�h\"\n",[73,6911,6909],{"__ignoreMap":117},[130,6913,6915,559,6917],{"id":6914},"string-vs-str",[73,6916,1197],{},[73,6918,1630],{},[917,6920,6921,6933],{},[920,6922,6923],{},[923,6924,6925,6929],{},[926,6926,6927],{},[73,6928,1197],{},[926,6930,6931],{},[73,6932,1630],{},[936,6934,6935,6943,6951,6961],{},[923,6936,6937,6940],{},[941,6938,6939],{},"Owned, growable",[941,6941,6942],{},"Borrowed view",[923,6944,6945,6948],{},[941,6946,6947],{},"Heap",[941,6949,6950],{},"Heap\u002Fstack\u002Fstatic",[923,6952,6953,6958],{},[941,6954,6955,6957],{},[73,6956,885],{}," to grow",[941,6959,6960],{},"Read-only",[923,6962,6963,6969],{},[941,6964,6965,6966,6968],{},"Can convert to ",[73,6967,1630],{}," freely",[941,6970,6971,6972,6974],{},"Can be obtained from ",[73,6973,1197],{}," cheaply",[130,6976,6978],{"id":6977},"common-conversions","Common Conversions",[111,6980,6983],{"className":6981,"code":6982,"language":397,"meta":117},[395],"let s: String = String::from(\"hi\");\nlet r: &str = &s;\nlet r: &str = s.as_str();\nlet owned: String = r.to_string();\nlet bytes: Vec\u003Cu8> = s.into_bytes();\nlet s: String = String::from_utf8(bytes).unwrap();\nlet s: String = unsafe { String::from_utf8_unchecked(bytes) };   \u002F\u002F fast, dangerous\n",[73,6984,6982],{"__ignoreMap":117},[130,6986,6988],{"id":6987},"byte-oriented-operations","Byte-Oriented Operations",[111,6990,6993],{"className":6991,"code":6992,"language":397,"meta":117},[395],"let s = \"hello\";\nfor b in s.bytes() { \u002F* u8 *\u002F }\nfor c in s.chars() { \u002F* char *\u002F }\nfor (i, c) in s.char_indices() { \u002F* (byte_offset, char) *\u002F }\ns.as_bytes();      \u002F\u002F &[u8]\n",[73,6994,6992],{"__ignoreMap":117},[130,6996,6998],{"id":6997},"concatenation","Concatenation",[111,7000,7003],{"className":7001,"code":7002,"language":397,"meta":117},[395],"let s = String::from(\"a\") + \"b\" + \"c\";   \u002F\u002F + takes String by value, &str args\nlet s = [\"a\", \"b\", \"c\"].concat();\nlet s = format!(\"{}-{}\", \"a\", \"b\");\nlet s: String = \"a\".to_string() + \"b\";\n",[73,7004,7002],{"__ignoreMap":117},[130,7006,711],{"id":7007},"edge-cases-1",[33,7009,7010,7029,7037,7045,7060],{},[36,7011,7012,7017,7018,7020,7021,7024,7025,7028],{},[24,7013,7014,7016],{},[73,7015,1637],{}," panics",": there's no byte indexing of ",[73,7019,1197],{},". Use ",[73,7022,7023],{},"s.as_bytes()[0]"," for bytes, ",[73,7026,7027],{},"s.chars().nth(0)"," for chars.",[36,7030,7031,7036],{},[24,7032,7033,7016],{},[73,7034,7035],{},"s[1..4]"," if not on char boundary.",[36,7038,7039,7044],{},[24,7040,7041],{},[73,7042,7043],{},"String::remove(i)",": byte-indexed; must be on char boundary.",[36,7046,7047,2927,7053,1212,7056,7059],{},[24,7048,7049,7052],{},[73,7050,7051],{},"split('\\n')"," doesn't include trailing empty",[73,7054,7055],{},"split_terminator",[73,7057,7058],{},"split_inclusive"," for variations.",[36,7061,7062,7074,7075,7077,7078,259],{},[24,7063,7064,7067,7068,27,7071],{},[73,7065,7066],{},"lines()"," splits on ",[73,7069,7070],{},"\\n",[73,7072,7073],{},"\\r\\n",", but ",[73,7076,7051],{}," doesn't strip ",[73,7079,7080],{},"\\r",[15,7082,7084,27,7087],{"id":7083},"hashmapk-v-and-btreemapk-v",[73,7085,7086],{},"HashMap\u003CK, V>",[73,7088,7089],{},"BTreeMap\u003CK, V>",[111,7091,7094],{"className":7092,"code":7093,"language":397,"meta":117},[395],"use std::collections::HashMap;\nlet mut m: HashMap\u003CString, i32> = HashMap::new();\nm.insert(\"a\".into(), 1);\nm.entry(\"b\".into()).or_insert(2);\nm.entry(\"a\".into()).and_modify(|v| *v += 1).or_insert(0);\nlet v = m.get(\"a\");          \u002F\u002F Option\u003C&i32>\nlet v = m.get_key_value(\"a\");\nm.remove(\"a\");\nfor (k, v) in &m { \u002F* ... *\u002F }\n",[73,7095,7093],{"__ignoreMap":117},[33,7097,7098,7110],{},[36,7099,7100,7102,7103,1212,7106,7109],{},[73,7101,1687],{}," uses hashing (SipHash by default, secure but slower; use ",[73,7104,7105],{},"FxHashMap",[73,7107,7108],{},"AHashMap"," for perf).",[36,7111,7112,7115],{},[73,7113,7114],{},"BTreeMap"," keeps keys sorted (binary tree), iteration is ordered, lookups are O(log n) vs HashMap's amortized O(1).",[130,7117,7119,7122],{"id":7118},"entry-api",[73,7120,7121],{},"entry"," API",[20,7124,7125],{},"The idiomatic way to \"insert if absent, else modify\":",[111,7127,7130],{"className":7128,"code":7129,"language":397,"meta":117},[395],"m.entry(key).or_insert_with(|| expensive_default());\n*m.entry(key).or_insert(0) += 1;\n",[73,7131,7129],{"__ignoreMap":117},[130,7133,7135],{"id":7134},"custom-keys","Custom Keys",[111,7137,7140],{"className":7138,"code":7139,"language":397,"meta":117},[395],"#[derive(Hash, Eq, PartialEq)]\nstruct MyKey { \u002F* ... *\u002F }\n",[73,7141,7139],{"__ignoreMap":117},[20,7143,7144,1052,7146,2559,7149,1052,7151,259],{},[73,7145,1687],{},[73,7147,7148],{},"Hash + Eq",[73,7150,7114],{},[73,7152,1533],{},[130,7154,711],{"id":7155},"edge-cases-2",[33,7157,7158,7171,7179,7190,7199],{},[36,7159,7160,7163,7164,7166,7167,1212,7169,259],{},[24,7161,7162],{},"Float keys",": not ",[73,7165,7148],{}," (NaN), so can't be in ",[73,7168,1687],{},[73,7170,7114],{},[36,7172,7173,7178],{},[24,7174,7175,7177],{},[73,7176,1687],{}," iteration order is random"," per run (uses random seed). Don't rely on order.",[36,7180,7181,7186,7187,526],{},[24,7182,7183],{},[73,7184,7185],{},"BTreeMap::range",": efficient range queries (",[73,7188,7189],{},"m.range('a'..='z')",[36,7191,7192,71,7195,7198],{},[24,7193,7194],{},"Capacity",[73,7196,7197],{},"HashMap::with_capacity"," pre-allocates.",[36,7200,7201,7206,7207,259],{},[24,7202,7203],{},[73,7204,7205],{},"mem::take(&mut map[key])",": extract a value, replacing with ",[73,7208,5347],{},[15,7210,7212,27,7214],{"id":7211},"hashset-and-btreeset",[73,7213,5340],{},[73,7215,7216],{},"BTreeSet",[111,7218,7221],{"className":7219,"code":7220,"language":397,"meta":117},[395],"use std::collections::HashSet;\nlet mut s: HashSet\u003Ci32> = HashSet::new();\ns.insert(1);\ns.contains(&1);\ns.remove(&1);\ns.intersection(&other).collect::\u003CHashSet\u003C_>>();\ns.union(&other);\ns.difference(&other);\ns.symmetric_difference(&other);\ns.is_subset(&other);\ns.is_disjoint(&other);\n",[73,7222,7220],{"__ignoreMap":117},[15,7224,7226,7229],{"id":7225},"vecdequet-double-ended-queue",[73,7227,7228],{},"VecDeque\u003CT>"," — Double-Ended Queue",[111,7231,7234],{"className":7232,"code":7233,"language":397,"meta":117},[395],"use std::collections::VecDeque;\nlet mut dq: VecDeque\u003Ci32> = VecDeque::new();\ndq.push_back(1); dq.push_front(0);\ndq.pop_back(); dq.pop_front();\n",[73,7235,7233],{"__ignoreMap":117},[20,7237,7238],{},"Ring buffer; O(1) push\u002Fpop on both ends.",[15,7240,7242,7245],{"id":7241},"linkedlistt-rarely-needed",[73,7243,7244],{},"LinkedList\u003CT>"," — Rarely Needed",[20,7247,7248,7251,7252,7255,7256,7259,7260,259],{},[73,7249,7250],{},"std::collections::LinkedList"," is a doubly-linked list. Almost always the wrong choice — use ",[73,7253,7254],{},"VecDeque"," instead. Linked lists are cache-unfriendly; their only advantage is O(1) splicing, which Rust's ",[73,7257,7258],{},"LinkedList"," supports via ",[73,7261,7262],{},"append",[15,7264,7266,7269],{"id":7265},"binaryheapt-max-heap",[73,7267,7268],{},"BinaryHeap\u003CT>"," — Max-Heap",[111,7271,7274],{"className":7272,"code":7273,"language":397,"meta":117},[395],"use std::collections::BinaryHeap;\nlet mut h = BinaryHeap::new();\nh.push(5); h.push(1); h.push(10);\nh.pop();     \u002F\u002F 10\n",[73,7275,7273],{"__ignoreMap":117},[20,7277,7278,7279,1546,7282,170],{},"For a min-heap, wrap with ",[73,7280,7281],{},"Reverse",[73,7283,7284],{},"std::cmp::Reverse",[111,7286,7289],{"className":7287,"code":7288,"language":397,"meta":117},[395],"let mut h: BinaryHeap\u003Cstd::cmp::Reverse\u003Ci32>> = BinaryHeap::new();\nh.push(std::cmp::Reverse(5));\n",[73,7290,7288],{"__ignoreMap":117},[15,7292,7294],{"id":7293},"other-useful-collections-std-crates","Other Useful Collections (std + crates)",[33,7296,7297,7305,7314,7326,7332,7338,7344,7350],{},[36,7298,7299,480,7301,480,7303],{},[73,7300,7254],{},[73,7302,5340],{},[73,7304,7216],{},[36,7306,7307,7310,7311],{},[73,7308,7309],{},"IndexMap"," (preserves insertion order) — crate ",[73,7312,7313],{},"indexmap",[36,7315,7316,1212,7318,7321,7322,7325],{},[73,7317,7105],{},[73,7319,7320],{},"FxHashSet"," — crate ",[73,7323,7324],{},"fxhash",", fast non-crypto hash",[36,7327,7328,7331],{},[73,7329,7330],{},"ahash::AHashMap"," — fast non-crypto hash",[36,7333,7334,7337],{},[73,7335,7336],{},"smallvec::SmallVec"," — inline storage, avoids heap for small sizes",[36,7339,7340,7343],{},[73,7341,7342],{},"arrayvec::ArrayVec"," — stack-only, fixed capacity",[36,7345,7346,7349],{},[73,7347,7348],{},"bytes::Bytes"," — cheap clone byte buffers (network\u002FIO)",[36,7351,7352,7355],{},[73,7353,7354],{},"ropey"," — large text manipulation (Rope data structure)",[15,7357,7359],{"id":7358},"choosing-a-collection","Choosing a Collection",[917,7361,7362,7372],{},[920,7363,7364],{},[923,7365,7366,7369],{},[926,7367,7368],{},"You want",[926,7370,7371],{},"Use",[936,7373,7374,7383,7392,7401,7410,7419,7429,7442],{},[923,7375,7376,7379],{},[941,7377,7378],{},"Sequence, push\u002Fpop back",[941,7380,7381],{},[73,7382,1194],{},[923,7384,7385,7388],{},[941,7386,7387],{},"Sequence, both ends",[941,7389,7390],{},[73,7391,7254],{},[923,7393,7394,7397],{},[941,7395,7396],{},"Map with arbitrary keys",[941,7398,7399],{},[73,7400,1687],{},[923,7402,7403,7406],{},[941,7404,7405],{},"Map with sorted keys \u002F ranges",[941,7407,7408],{},[73,7409,7114],{},[923,7411,7412,7415],{},[941,7413,7414],{},"Unique elements",[941,7416,7417],{},[73,7418,5340],{},[923,7420,7421,7424],{},[941,7422,7423],{},"Priority queue",[941,7425,7426],{},[73,7427,7428],{},"BinaryHeap",[923,7430,7431,7434],{},[941,7432,7433],{},"Small, known max size",[941,7435,7436,3818,7439],{},[73,7437,7438],{},"ArrayVec",[73,7440,7441],{},"SmallVec",[923,7443,7444,7447],{},[941,7445,7446],{},"Often-cloned byte buffer",[941,7448,7449],{},[73,7450,7348],{},[15,7452,349],{"id":348},[20,7454,7455,7457,7458,7460,7461,1212,7463,7465,7466,7468],{},[73,7456,1194],{}," is the workhorse — understand its memory model (capacity doubling, amortized O(1)). ",[73,7459,1197],{}," is UTF-8-aware; never confuse bytes with chars. ",[73,7462,1687],{},[73,7464,7114],{}," are the map workhorses; the ",[73,7467,7121],{}," API is idiomatic. Pick the right collection for the access pattern.",[20,7470,7471],{},"Next: Iterators and combinators — the functional side of Rust.",{"title":117,"searchDepth":357,"depth":357,"links":7473},[7474,7484,7494,7501,7503,7505,7507,7509,7510,7511],{"id":6700,"depth":357,"text":7475,"children":7476},"Vec\u003CT> — The Growable Array",[7477,7478,7479,7480,7481,7482,7483],{"id":6712,"depth":364,"text":6713},{"id":6749,"depth":364,"text":6750},{"id":4211,"depth":364,"text":4212},{"id":6767,"depth":364,"text":6768},{"id":6777,"depth":364,"text":6778},{"id":6787,"depth":364,"text":6788},{"id":710,"depth":364,"text":711},{"id":6884,"depth":357,"text":7485,"children":7486},"String — Owned UTF-8 String",[7487,7488,7490,7491,7492,7493],{"id":6896,"depth":364,"text":6897},{"id":6914,"depth":364,"text":7489},"String vs &str",{"id":6977,"depth":364,"text":6978},{"id":6987,"depth":364,"text":6988},{"id":6997,"depth":364,"text":6998},{"id":7007,"depth":364,"text":711},{"id":7083,"depth":357,"text":7495,"children":7496},"HashMap\u003CK, V> and BTreeMap\u003CK, V>",[7497,7499,7500],{"id":7118,"depth":364,"text":7498},"entry API",{"id":7134,"depth":364,"text":7135},{"id":7155,"depth":364,"text":711},{"id":7211,"depth":357,"text":7502},"HashSet and BTreeSet",{"id":7225,"depth":357,"text":7504},"VecDeque\u003CT> — Double-Ended Queue",{"id":7241,"depth":357,"text":7506},"LinkedList\u003CT> — Rarely Needed",{"id":7265,"depth":357,"text":7508},"BinaryHeap\u003CT> — Max-Heap",{"id":7293,"depth":357,"text":7294},{"id":7358,"depth":357,"text":7359},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F14-collections",{"title":6684,"description":117},"rust\u002F14-collections","lraKW6YZ1Bx4GcHZDRngFhkkx-xnIFyNJqi7SKZPqHI",{"id":7518,"title":7519,"body":7520,"description":8137,"extension":373,"meta":8138,"navigation":375,"path":8139,"seo":8140,"stem":8141,"__hash__":8142},"content\u002Frust\u002F15-iterators.md","15 — Iterators & Combinators",{"type":8,"value":7521,"toc":8105},[7522,7525,7535,7541,7547,7567,7571,7577,7590,7595,7603,7609,7625,7629,7676,7680,7686,7690,7696,7700,7706,7714,7720,7731,7737,7743,7747,7753,7759,7765,7773,7779,7786,7792,7798,7802,7808,7821,7826,7832,7841,7846,7862,7868,7873,7876,7882,7886,7892,7896,7900,7906,7910,7916,7920,7926,7930,7936,7940,7946,7950,7956,7958,8089,8091,8102],[11,7523,7519],{"id":7524},"_15-iterators-combinators",[20,7526,7527,7528,480,7531,7534],{},"Rust's iterators are ",[24,7529,7530],{},"lazy",[24,7532,7533],{},"zero-cost",", and compose into chains that compile down to tight loops. Mastering them is the difference between \"writing Rust\" and \"writing idiomatic Rust\".",[15,7536,3106,7538,3109],{"id":7537},"the-iterator-trait",[73,7539,7540],{},"Iterator",[111,7542,7545],{"className":7543,"code":7544,"language":397,"meta":117},[395],"pub trait Iterator {\n    type Item;\n    fn next(&mut self) -> Option\u003CSelf::Item>;\n    \u002F\u002F ... dozens of provided methods\n}\n",[73,7546,7544],{"__ignoreMap":117},[20,7548,7549,7550,7553,7554,480,7557,480,7560,480,7563,7566],{},"Implement ",[73,7551,7552],{},"next()"," and you get ",[73,7555,7556],{},"map",[73,7558,7559],{},"filter",[73,7561,7562],{},"fold",[73,7564,7565],{},"collect",", etc. for free.",[15,7568,7570],{"id":7569},"laziness","Laziness",[111,7572,7575],{"className":7573,"code":7574,"language":397,"meta":117},[395],"let v = vec![1, 2, 3];\nlet it = v.iter().map(|x| x * 2);   \u002F\u002F no work yet\nfor y in it { println!(\"{y}\"); }    \u002F\u002F work happens here\n",[73,7576,7574],{"__ignoreMap":117},[20,7578,7579,7580,480,7582,480,7584,480,7587,6643],{},"Iterator chains don't run until consumed (by ",[73,7581,2614],{},[73,7583,7565],{},[73,7585,7586],{},"sum",[73,7588,7589],{},"count",[15,7591,7593],{"id":7592},"intoiterator",[73,7594,189],{},[20,7596,7597,7598,7600,7601,170],{},"Anything implementing ",[73,7599,189],{}," can be used in ",[73,7602,2614],{},[111,7604,7607],{"className":7605,"code":7606,"language":397,"meta":117},[395],"for x in &vec { }       \u002F\u002F &Vec\u003CT>  -> Iterator\u003CItem = &T>\nfor x in &mut vec { }  \u002F\u002F &mut Vec\u003CT> -> Iterator\u003CItem = &mut T>\nfor x in vec { }        \u002F\u002F Vec\u003CT> -> consumes, yields T\n",[73,7608,7606],{"__ignoreMap":117},[20,7610,7611,7614,7615,7618,7619,1546,7622,259],{},[73,7612,7613],{},"Vec\u003CT>: IntoIterator\u003CItem = T>"," since edition 2021. Pre-2021, arrays only borrowed-by-default — ",[73,7616,7617],{},"for x in [1,2,3]"," errored unless you wrote ",[73,7620,7621],{},"for x in &[1,2,3]",[73,7623,7624],{},"into_iter()",[15,7626,7628],{"id":7627},"consuming-vs-borrowing-iterators","Consuming vs Borrowing Iterators",[917,7630,7631,7641],{},[920,7632,7633],{},[923,7634,7635,7638],{},[926,7636,7637],{},"Method",[926,7639,7640],{},"Yields",[936,7642,7643,7654,7665],{},[923,7644,7645,7650],{},[941,7646,7647],{},[73,7648,7649],{},"iter()",[941,7651,7652],{},[73,7653,3130],{},[923,7655,7656,7661],{},[941,7657,7658],{},[73,7659,7660],{},"iter_mut()",[941,7662,7663],{},[73,7664,1117],{},[923,7666,7667,7671],{},[941,7668,7669],{},[73,7670,7624],{},[941,7672,7673,7675],{},[73,7674,4705],{}," (consumes the collection)",[15,7677,7679],{"id":7678},"common-adapters-producers","Common Adapters (Producers)",[111,7681,7684],{"className":7682,"code":7683,"language":397,"meta":117},[395],"0..10                       \u002F\u002F Range\n(1..=5).rev()\n\"abc\".chars()\n\"abc\".bytes()\nvec.iter()\nvec.iter_mut()\nvec.into_iter()\nslice.chunks(3)\nslice.chunks_exact(3)\nslice.rchunks(3)\nslice.windows(2)             \u002F\u002F sliding window, overlapping\nslice.split(|c| *c == b',')\nslice.splitn(3, |c| *c == b',')\nstr.lines()\nstr.split_whitespace()\nstr.split_ascii_whitespace()\nstd::iter::repeat(5)         \u002F\u002F infinite\nstd::iter::repeat_with(|| rand::random())\nstd::iter::once(5)\nstd::iter::empty::\u003Ci32>()\nstd::iter::successors(Some(1), |n| Some(n * 2))   \u002F\u002F unfold\nstd::iter::from_fn(|| Some(1))\nstd::iter::zip(a, b)         \u002F\u002F zip two iterables\n",[73,7685,7683],{"__ignoreMap":117},[15,7687,7689],{"id":7688},"common-transformers","Common Transformers",[111,7691,7694],{"className":7692,"code":7693,"language":397,"meta":117},[395],"it.map(|x| x * 2)\nit.filter(|x| *x > 0)\nit.filter_map(|x| if *x > 0 { Some(*x) } else { None })\nit.enumerate()               \u002F\u002F (index, item)\nit.zip(other_iter)           \u002F\u002F pair up\nit.flat_map(|x| x.iter())     \u002F\u002F flatten one level\nit.flatten()                  \u002F\u002F for Iterator\u003CItem = Iterator>\nit.take(3)                    \u002F\u002F first 3\nit.skip(3)\nit.take_while(|x| *x \u003C 10)\nit.skip_while(|x| *x \u003C 10)\nit.step_by(2)\nit.chain(other)\nit.rev()                       \u002F\u002F requires DoubleEndedIterator\nit.peekable()                  \u002F\u002F Peekable — see next without consuming\nit.cycle()                     \u002F\u002F infinite repeat (Clone-able items)\nit.scan(init, |state, x| ...)  \u002F\u002F stateful map, returns Option\nit.dedup()\nit.unzip()                     \u002F\u002F (Vec\u003CA>, Vec\u003CB>)\nit.collect()\nit.copied()                    \u002F\u002F Iterator\u003CItem=&T where T:Copy> -> Item=T\nit.cloned()                    \u002F\u002F Iterator\u003CItem=&T> -> Item=T (T: Clone)\nit.by_ref()                    \u002F\u002F borrow iterator for partial consumption\n",[73,7695,7693],{"__ignoreMap":117},[15,7697,7699],{"id":7698},"common-consumers","Common Consumers",[111,7701,7704],{"className":7702,"code":7703,"language":397,"meta":117},[395],"it.collect::\u003CVec\u003C_>>()\nit.collect::\u003CHashMap\u003CK, V>>()\nit.sum::\u003Ci32>()\nit.product::\u003Ci32>()\nit.count()\nit.last()              \u002F\u002F Option\u003CT>\nit.nth(5)\nit.all(|x| *x > 0)\nit.any(|x| *x > 0)\nit.find(|x| *x > 0)    \u002F\u002F first matching\nit.position(|x| *x > 0) \u002F\u002F Option\u003Cusize>\nit.fold(init, |acc, x| acc + x)\nit.try_fold(init, |acc, x| Ok(acc + x))   \u002F\u002F bails on Err\nit.for_each(|x| println!(\"{x}\"))\nit.max() \u002F it.min()\nit.max_by_key(|x| *x)\nit.min_by(|a, b| a.cmp(b))\nit.eq(other)\nit.ne(other)\nit.lt(other)\nit.cmp(other)\nit.partition(|x| *x > 0)   \u002F\u002F (Vec\u003CT>, Vec\u003CT>)\nit.unzip()\n",[73,7705,7703],{"__ignoreMap":117},[15,7707,7709,27,7711],{"id":7708},"collect-and-fromiterator",[73,7710,7565],{},[73,7712,7713],{},"FromIterator",[111,7715,7718],{"className":7716,"code":7717,"language":397,"meta":117},[395],"let v: Vec\u003Ci32> = (0..5).collect();\nlet s: String = \"abc\".chars().collect();\nlet m: HashMap\u003C&str, i32> = [(\"a\", 1), (\"b\", 2)].into_iter().collect();\nlet (evens, odds): (Vec\u003Ci32>, Vec\u003Ci32>) = (0..10).partition(|x| x % 2 == 0);\n",[73,7719,7717],{"__ignoreMap":117},[20,7721,7722,7724,7725,1356,7728,7730],{},[73,7723,7565],{}," can build ",[183,7726,7727],{},"any",[73,7729,7713],{}," type — the turbofish or type annotation tells it which.",[15,7732,7734,7735,1587],{"id":7733},"custom-iterator-manual-impl","Custom Iterator (Manual ",[73,7736,2215],{},[111,7738,7741],{"className":7739,"code":7740,"language":397,"meta":117},[395],"struct Counter { count: u32 }\nimpl Counter {\n    fn new() -> Self { Counter { count: 0 } }\n}\nimpl Iterator for Counter {\n    type Item = u32;\n    fn next(&mut self) -> Option\u003CSelf::Item> {\n        self.count += 1;\n        if self.count \u003C= 5 { Some(self.count) } else { None }\n    }\n}\n\nfor n in Counter::new().map(|x| x * 2) {\n    println!(\"{n}\");   \u002F\u002F 2, 4, 6, 8, 10\n}\n",[73,7742,7740],{"__ignoreMap":117},[15,7744,7746],{"id":7745},"performance-iterators-compile-to-tight-loops","Performance: Iterators Compile to Tight Loops",[111,7748,7751],{"className":7749,"code":7750,"language":397,"meta":117},[395],"let v: Vec\u003Ci32> = (0..1_000_000).collect();\nlet sum: i32 = v.iter().map(|x| x + 1).filter(|x| x % 2 == 0).sum();\n",[73,7752,7750],{"__ignoreMap":117},[20,7754,7755,7756,7758],{},"This compiles to essentially the same machine code as a hand-written ",[73,7757,2614],{}," loop. No allocations, no closures dispatched at runtime — everything inlines.",[15,7760,7762],{"id":7761},"doubleendediterator",[73,7763,7764],{},"DoubleEndedIterator",[20,7766,7767,1052,7770,7772],{},[73,7768,7769],{},".rev()",[73,7771,7764],{}," (can pull from the back):",[111,7774,7777],{"className":7775,"code":7776,"language":397,"meta":117},[395],"for x in (0..5).rev() { print!(\"{x} \"); }   \u002F\u002F 4 3 2 1 0\n",[73,7778,7776],{"__ignoreMap":117},[20,7780,7781,7782,7785],{},"Not all iterators are double-ended (",[73,7783,7784],{},"std::io::Lines"," reading a file isn't).",[15,7787,7789],{"id":7788},"exactsizeiterator",[73,7790,7791],{},"ExactSizeIterator",[20,7793,7794,7797],{},[73,7795,7796],{},".len()"," works if the iterator knows its exact remaining length.",[15,7799,7801],{"id":7800},"infinite-iterators","Infinite Iterators",[111,7803,7806],{"className":7804,"code":7805,"language":397,"meta":117},[395],"let ones = std::iter::repeat(1);\nlet natural = (0..).map(|x| x * 2);\nlet mut evens = (0..).step_by(2);\n",[73,7807,7805],{"__ignoreMap":117},[20,7809,1876,7810,1546,7813,7816,7817,7820],{},[73,7811,7812],{},"take(n)",[73,7814,7815],{},"take_while"," to bound them. Don't ",[73,7818,7819],{},".collect()"," an infinite iterator!",[15,7822,7824],{"id":7823},"peekable",[73,7825,7823],{},[111,7827,7830],{"className":7828,"code":7829,"language":397,"meta":117},[395],"let mut it = vec.iter().peekable();\nlet first = it.peek();\nif let Some(&&3) = first { \u002F* ... *\u002F }\nlet actual = it.next();\n",[73,7831,7829],{"__ignoreMap":117},[20,7833,7834,1538,7837,7840],{},[73,7835,7836],{},"peek",[73,7838,7839],{},"Option\u003C&Item>"," without advancing.",[15,7842,7844],{"id":7843},"fuse",[73,7845,7843],{},[20,7847,7848,7849,7851,7852,7855,7856,7858,7859,7861],{},"After an iterator returns ",[73,7850,1541],{}," once, calling ",[73,7853,7854],{},"next"," again is unspecified — ",[73,7857,7843],{}," makes it always return ",[73,7860,1541],{}," after the first:",[111,7863,7866],{"className":7864,"code":7865,"language":397,"meta":117},[395],"let mut it = some_iter.fuse();\nwhile let Some(x) = it.next() { \u002F* ... *\u002F }\nit.next();   \u002F\u002F guaranteed None\n",[73,7867,7865],{"__ignoreMap":117},[15,7869,7871],{"id":7870},"inspect",[73,7872,7870],{},[20,7874,7875],{},"For debugging chains without breaking them:",[111,7877,7880],{"className":7878,"code":7879,"language":397,"meta":117},[395],"(0..5)\n    .inspect(|x| println!(\"before: {x}\"))\n    .map(|x| x * 2)\n    .inspect(|x| println!(\"after:  {x}\"))\n    .collect::\u003CVec\u003C_>>();\n",[73,7881,7879],{"__ignoreMap":117},[15,7883,7885],{"id":7884},"iterators-and-ownership","Iterators and Ownership",[111,7887,7890],{"className":7888,"code":7889,"language":397,"meta":117},[395],"let v = vec![String::from(\"a\"), String::from(\"b\")];\n\n\u002F\u002F Borrow (keep v alive):\nfor s in &v { \u002F* s: &String *\u002F }\n\n\u002F\u002F Consume (v gone after):\nfor s in v { \u002F* s: String *\u002F }\n\n\u002F\u002F Partial consume then use rest:\nlet mut it = v.into_iter();\nlet first = it.next();\nlet rest: Vec\u003C_> = it.collect();\n",[73,7891,7889],{"__ignoreMap":117},[15,7893,7895],{"id":7894},"common-patterns","Common Patterns",[130,7897,7899],{"id":7898},"group-consecutive-equal-elements","Group consecutive equal elements",[111,7901,7904],{"className":7902,"code":7903,"language":397,"meta":117},[395],"let v = vec![1, 1, 2, 2, 2, 3];\nfor (key, group) in v.into_iter().group_by(|a, b| a == b) { \u002F* unstable API *\u002F }\n\u002F\u002F Use `itertools` crate for `group_by` on stable.\n",[73,7905,7903],{"__ignoreMap":117},[130,7907,7909],{"id":7908},"chunked-iterator","Chunked iterator",[111,7911,7914],{"className":7912,"code":7913,"language":397,"meta":117},[395],"for chunk in v.chunks(10) { \u002F* process *\u002F }\n",[73,7915,7913],{"__ignoreMap":117},[130,7917,7919],{"id":7918},"build-a-map-from-a-vec","Build a map from a vec",[111,7921,7924],{"className":7922,"code":7923,"language":397,"meta":117},[395],"let m: HashMap\u003Ci32, &str> = vec.iter().map(|x| (*x, \"x\")).collect();\n",[73,7925,7923],{"__ignoreMap":117},[130,7927,7929],{"id":7928},"sum-of-squares-of-evens","Sum of squares of evens",[111,7931,7934],{"className":7932,"code":7933,"language":397,"meta":117},[395],"let sum: i32 = (1..=100).filter(|x| x % 2 == 0).map(|x| x * x).sum();\n",[73,7935,7933],{"__ignoreMap":117},[130,7937,7939],{"id":7938},"flatten-nested-options","Flatten nested options",[111,7941,7944],{"className":7942,"code":7943,"language":397,"meta":117},[395],"let v: Vec\u003Ci32> = vec![Some(1), None, Some(2)].into_iter().flatten().collect();\n",[73,7945,7943],{"__ignoreMap":117},[130,7947,7949],{"id":7948},"find-max-by-key","Find max by key",[111,7951,7954],{"className":7952,"code":7953,"language":397,"meta":117},[395],"let max = v.iter().max_by_key(|x| x.score);\n",[73,7955,7953],{"__ignoreMap":117},[15,7957,1125],{"id":1124},[33,7959,7960,7972,7982,7998,8008,8029,8042,8054,8078],{},[36,7961,7962,7967,7968,7971],{},[24,7963,7964,7966],{},[73,7965,7565],{}," ambiguity",": if you write ",[73,7969,7970],{},"let v = it.collect();"," without a type annotation, you'll get an error. Always annotate.",[36,7973,7974,7977,7978,7981],{},[24,7975,7976],{},"Iterator invalidation",": you can't mutate the underlying collection while iterating via a borrowed iterator. ",[73,7979,7980],{},"Vec::retain"," is the safe way to filter in place.",[36,7983,7984,7990,7991,7993,7994,7997],{},[24,7985,7986,7989],{},[73,7987,7988],{},"for x in vec"," consumes",": easy mistake — ",[73,7992,3883],{}," is gone after. Use ",[73,7995,7996],{},"&vec"," to keep it.",[36,7999,8000,8007],{},[24,8001,8002,8003,1212,8005],{},"Infinite iterator + ",[73,8004,7589],{},[73,8006,7586],{},": hangs forever.",[36,8009,8010,71,8022,8025,8026,8028],{},[24,8011,8012,8014,8015,8018,8019],{},[73,8013,7769],{}," on ",[73,8016,8017],{},"Range"," from ",[73,8020,8021],{},"0..",[73,8023,8024],{},"RangeFrom"," isn't ",[73,8027,7764],{}," (no end to reverse to).",[36,8030,8031,8037,8038,8041],{},[24,8032,8033,8036],{},[73,8034,8035],{},".zip"," stops at shorter",": zipping a 3-element with a 5-element yields 3 pairs. Use ",[73,8039,8040],{},"itertools::zip_longest"," for the padded form.",[36,8043,8044,71,8046,8049,8050,8053],{},[24,8045,3272],{},[73,8047,8048],{},"it.map(|x| x + offset)"," borrows ",[73,8051,8052],{},"offset"," for the iterator's lifetime; can surprise you with borrow errors.",[36,8055,8056,8064,8065,8067,8068,8070,8071,8073,8074,8077],{},[24,8057,8058,8014,8061],{},[73,8059,8060],{},"flatten",[73,8062,8063],{},"Iterator\u003CItem = Option\u003CT>>",": this is a special impl — ",[73,8066,1481],{}," impls ",[73,8069,189],{},". Same for ",[73,8072,5719],{}," (only the ",[73,8075,8076],{},"Ok"," cases flatten).",[36,8079,8080,4309,8085,8088],{},[24,8081,8082],{},[73,8083,8084],{},"Iterator::size_hint",[73,8086,8087],{},"(lower, Option\u003Cupper>)","; useful for algorithms that need a size estimate.",[15,8090,349],{"id":348},[20,8092,8093,8094,8096,8097,8099,8100,259],{},"Iterators are lazy, zero-cost, and compose beautifully. Pick the right adapter for the job. ",[73,8095,7565],{}," is a swiss-army knife driven by type inference. Avoid infinite iterator pitfalls. Manual ",[73,8098,7540],{}," impl is straightforward — implement ",[73,8101,7552],{},[20,8103,8104],{},"Next: Traits and generics — the type system's reuse mechanism.",{"title":117,"searchDepth":357,"depth":357,"links":8106},[8107,8109,8110,8111,8112,8113,8114,8115,8117,8119,8120,8121,8122,8123,8124,8125,8126,8127,8135,8136],{"id":7537,"depth":357,"text":8108},"The Iterator Trait",{"id":7569,"depth":357,"text":7570},{"id":7592,"depth":357,"text":189},{"id":7627,"depth":357,"text":7628},{"id":7678,"depth":357,"text":7679},{"id":7688,"depth":357,"text":7689},{"id":7698,"depth":357,"text":7699},{"id":7708,"depth":357,"text":8116},"collect and FromIterator",{"id":7733,"depth":357,"text":8118},"Custom Iterator (Manual impl)",{"id":7745,"depth":357,"text":7746},{"id":7761,"depth":357,"text":7764},{"id":7788,"depth":357,"text":7791},{"id":7800,"depth":357,"text":7801},{"id":7823,"depth":357,"text":7823},{"id":7843,"depth":357,"text":7843},{"id":7870,"depth":357,"text":7870},{"id":7884,"depth":357,"text":7885},{"id":7894,"depth":357,"text":7895,"children":8128},[8129,8130,8131,8132,8133,8134],{"id":7898,"depth":364,"text":7899},{"id":7908,"depth":364,"text":7909},{"id":7918,"depth":364,"text":7919},{"id":7928,"depth":364,"text":7929},{"id":7938,"depth":364,"text":7939},{"id":7948,"depth":364,"text":7949},{"id":1124,"depth":357,"text":1125},{"id":348,"depth":357,"text":349},"Rust's iterators are lazy, zero-cost, and compose into chains that compile down to tight loops. Mastering them is the difference between \"writing Rust\" and \"writing idiomatic Rust\".",{},"\u002Frust\u002F15-iterators",{"title":7519,"description":8137},"rust\u002F15-iterators","r1khpSlRDbho8dejjpIIBx7fXtK3SVvNlA3--u9uZlw",{"id":8144,"title":8145,"body":8146,"description":9011,"extension":373,"meta":9012,"navigation":375,"path":9013,"seo":9014,"stem":9015,"__hash__":9016},"content\u002Frust\u002F16-traits-and-generics.md","16 — Traits and Generics",{"type":8,"value":8147,"toc":8981},[8148,8151,8158,8162,8168,8187,8191,8197,8215,8219,8222,8248,8266,8272,8276,8280,8286,8300,8304,8310,8314,8320,8326,8331,8335,8341,8345,8351,8375,8379,8596,8603,8609,8624,8634,8641,8647,8664,8668,8674,8715,8719,8725,8740,8749,8753,8759,8764,8768,8774,8777,8781,8787,8790,8794,8800,8803,8807,8813,8817,8831,8835,8838,8844,8855,8857,8954,8956,8978],[11,8149,8145],{"id":8150},"_16-traits-and-generics",[20,8152,8153,8154,8157],{},"Traits are Rust's answer to interfaces\u002Ftypeclasses — they define ",[24,8155,8156],{},"shared behavior",". Generics parametrize code over types. Together they're the foundation of Rust's abstraction.",[15,8159,8161],{"id":8160},"defining-and-implementing-traits","Defining and Implementing Traits",[111,8163,8166],{"className":8164,"code":8165,"language":397,"meta":117},[395],"trait Greet {\n    fn say_hi(&self) -> String;\n    fn say_loud(&self) -> String {\n        format!(\"{}!!!\", self.say_hi())    \u002F\u002F default method body\n    }\n}\n\nstruct User { name: String }\nimpl Greet for User {\n    fn say_hi(&self) -> String { format!(\"hi {}\", self.name) }\n}\n",[73,8167,8165],{"__ignoreMap":117},[33,8169,8170,8173,8176],{},[36,8171,8172],{},"Default methods can be overridden.",[36,8174,8175],{},"Implementations are explicit (no automatic interface implementation like Java).",[36,8177,8178,8179,8182,8183,8186],{},"You can implement a trait for a type only if either the trait or the type is ",[24,8180,8181],{},"local"," to your crate (the ",[24,8184,8185],{},"orphan rule",") — prevents conflicting impls across crates.",[15,8188,8190],{"id":8189},"trait-objects-vs-static-dispatch","Trait Objects vs Static Dispatch",[111,8192,8195],{"className":8193,"code":8194,"language":397,"meta":117},[395],"fn print_all\u003CT: Greet>(items: &[T]) { \u002F* monomorphized per T *\u002F }\nfn print_dyn(items: &[Box\u003Cdyn Greet>]) { \u002F* dynamic dispatch *\u002F }\n",[73,8196,8194],{"__ignoreMap":117},[33,8198,8199,8206],{},[36,8200,8201,8202,8205],{},"Generics + trait bounds = ",[24,8203,8204],{},"static dispatch"," (inlined, zero-cost, code duplication per type).",[36,8207,8208,2230,8211,8214],{},[73,8209,8210],{},"dyn Trait",[24,8212,8213],{},"dynamic dispatch"," via vtable (one copy, indirect call, slightly slower, enables heterogeneous collections).",[15,8216,8218],{"id":8217},"trait-object-requirements-object-safety","Trait Object Requirements (Object Safety)",[20,8220,8221],{},"A trait is object-safe iff:",[33,8223,8224,8230,8233,8242],{},[36,8225,8226,8227,8229],{},"No associated functions \u002F methods returning ",[73,8228,2264],{}," (by value).",[36,8231,8232],{},"No generics in methods.",[36,8234,8235,8236,8238,8239,526],{},"All methods take ",[73,8237,2245],{}," by reference (or have ",[73,8240,8241],{},"where Self: Sized",[36,8243,8244,8247],{},[73,8245,8246],{},"Self: Sized"," super-bound disqualifies.",[20,8249,8250,1212,8253,1212,8255,8257,8258,480,8261,480,8263,8265],{},[73,8251,8252],{},"Clone",[73,8254,7540],{},[73,8256,5365],{}," aren't object-safe. ",[73,8259,8260],{},"Greet",[73,8262,461],{},[73,8264,469],{}," are.",[111,8267,8270],{"className":8268,"code":8269,"language":397,"meta":117},[395],"let v: Vec\u003CBox\u003Cdyn Greet>> = vec![Box::new(User { name: \"a\".into() })];\n",[73,8271,8269],{"__ignoreMap":117},[15,8273,8275],{"id":8274},"default-type-parameters-and-associated-types","Default Type Parameters and Associated Types",[130,8277,8279],{"id":8278},"generics-vs-associated-types","Generics vs Associated Types",[111,8281,8284],{"className":8282,"code":8283,"language":397,"meta":117},[395],"\u002F\u002F Generic trait — caller picks T:\ntrait Container\u003CT> { fn item(&self) -> &T; }\n\n\u002F\u002F Associated type — impl picks the type:\ntrait Container { type Item; fn item(&self) -> &Self::Item; }\n",[73,8285,8283],{"__ignoreMap":117},[20,8287,8288,8289,8292,8293,8296,8297,526],{},"Use associated types when each type has ",[24,8290,8291],{},"one"," natural inner type (e.g., ",[73,8294,8295],{},"Iterator::Item","). Use generics when the type can carry multiple variants (e.g., ",[73,8298,8299],{},"From\u003CT>",[130,8301,8303],{"id":8302},"default-associated-type","Default Associated Type",[111,8305,8308],{"className":8306,"code":8307,"language":397,"meta":117},[395],"trait Rng { type Output = u64; fn next(&self) -> Self::Output; }\n",[73,8309,8307],{"__ignoreMap":117},[15,8311,8313],{"id":8312},"trait-bounds","Trait Bounds",[111,8315,8318],{"className":8316,"code":8317,"language":397,"meta":117},[395],"fn max\u003CT: PartialOrd + Copy>(a: T, b: T) -> T { if a > b { a } else { b } }\n\nfn sum_all\u003CT>(items: &[T]) -> T\nwhere\n    T: Sum + Copy,\n{\n    items.iter().copied().sum()\n}\n",[73,8319,8317],{"__ignoreMap":117},[20,8321,8322,8325],{},[73,8323,8324],{},"where"," clauses are more readable for long bounds and enable more expressiveness (bounds on associated types, lifetimes).",[15,8327,8329],{"id":8328},"impl-trait",[73,8330,4771],{},[130,8332,8334],{"id":8333},"in-argument-position","In argument position",[111,8336,8339],{"className":8337,"code":8338,"language":397,"meta":117},[395],"fn print(it: impl Iterator\u003CItem = i32>) { \u002F* ... *\u002F }\n\u002F\u002F equivalent to:\nfn print\u003CT: Iterator\u003CItem = i32>>(it: T) { \u002F* ... *\u002F }\n",[73,8340,8338],{"__ignoreMap":117},[130,8342,8344],{"id":8343},"in-return-position","In return position",[111,8346,8349],{"className":8347,"code":8348,"language":397,"meta":117},[395],"fn counter() -> impl Iterator\u003CItem = u32> {\n    (0..5).map(|x| x * 2)\n}\n",[73,8350,8348],{"__ignoreMap":117},[33,8352,8353,8359,8365,8368],{},[36,8354,8355,8356,8358],{},"Returns ",[183,8357,4476],{}," concrete type that implements the trait — the actual type is hidden from the caller.",[36,8360,8361,8362,526],{},"Cannot be conditional (no ",[73,8363,8364],{},"if cond { type A } else { type B }",[36,8366,8367],{},"Each return-site must use a single concrete type.",[36,8369,8370,8371,8374],{},"For returning different types, use ",[73,8372,8373],{},"Box\u003Cdyn Trait>"," or trait objects.",[15,8376,8378],{"id":8377},"common-standard-traits","Common Standard Traits",[917,8380,8381,8391],{},[920,8382,8383],{},[923,8384,8385,8388],{},[926,8386,8387],{},"Trait",[926,8389,8390],{},"Purpose",[936,8392,8393,8404,8415,8426,8437,8448,8457,8468,8483,8496,8504,8523,8538,8547,8557,8570,8583],{},[923,8394,8395,8399],{},[941,8396,8397],{},[73,8398,461],{},[941,8400,8401,8402,1587],{},"User-facing string (",[73,8403,457],{},[923,8405,8406,8410],{},[941,8407,8408],{},[73,8409,469],{},[941,8411,8412,8413,1587],{},"Developer string (",[73,8414,465],{},[923,8416,8417,8423],{},[941,8418,8419,480,8421],{},[73,8420,8252],{},[73,8422,1795],{},[941,8424,8425],{},"Duplication",[923,8427,8428,8434],{},[941,8429,8430,480,8432],{},[73,8431,5365],{},[73,8433,5355],{},[941,8435,8436],{},"Equality",[923,8438,8439,8445],{},[941,8440,8441,480,8443],{},[73,8442,1529],{},[73,8444,1533],{},[941,8446,8447],{},"Ordering",[923,8449,8450,8454],{},[941,8451,8452],{},[73,8453,5336],{},[941,8455,8456],{},"Hashing",[923,8458,8459,8463],{},[941,8460,8461],{},[73,8462,5347],{},[941,8464,8465],{},[73,8466,8467],{},"Default::default()",[923,8469,8470,8480],{},[941,8471,8472,480,8474,480,8476,480,8478],{},[73,8473,1879],{},[73,8475,1882],{},[73,8477,1885],{},[73,8479,1888],{},[941,8481,8482],{},"Conversions",[923,8484,8485,8493],{},[941,8486,8487,480,8490],{},[73,8488,8489],{},"AsRef",[73,8491,8492],{},"AsMut",[941,8494,8495],{},"Cheap borrows",[923,8497,8498,8502],{},[941,8499,8500],{},[73,8501,7540],{},[941,8503,4212],{},[923,8505,8506,8520],{},[941,8507,8508,480,8511,480,8514,480,8517],{},[73,8509,8510],{},"Add",[73,8512,8513],{},"Sub",[73,8515,8516],{},"Mul",[73,8518,8519],{},"Div",[941,8521,8522],{},"Operator overloading",[923,8524,8525,8533],{},[941,8526,8527,480,8530],{},[73,8528,8529],{},"Index",[73,8531,8532],{},"IndexMut",[941,8534,8535],{},[73,8536,8537],{},"[]",[923,8539,8540,8544],{},[941,8541,8542],{},[73,8543,3217],{},[941,8545,8546],{},"Destructor",[923,8548,8549,8554],{},[941,8550,8551],{},[73,8552,8553],{},"Sized",[941,8555,8556],{},"Has a known size",[923,8558,8559,8567],{},[941,8560,8561,480,8564],{},[73,8562,8563],{},"Send",[73,8565,8566],{},"Sync",[941,8568,8569],{},"Thread safety (auto)",[923,8571,8572,8580],{},[941,8573,8574,480,8577],{},[73,8575,8576],{},"Unpin",[73,8578,8579],{},"Pin",[941,8581,8582],{},"Async\u002Fpinning",[923,8584,8585,8593],{},[941,8586,8587,480,8589,480,8591],{},[73,8588,1799],{},[73,8590,2332],{},[73,8592,2335],{},[941,8594,8595],{},"Closures",[15,8597,8599,27,8601],{"id":8598},"from-and-into",[73,8600,1879],{},[73,8602,1882],{},[111,8604,8607],{"className":8605,"code":8606,"language":397,"meta":117},[395],"impl From\u003Ci32> for My { fn from(x: i32) -> Self { \u002F* ... *\u002F } }\nlet m: My = 5i32.into();\n",[73,8608,8606],{"__ignoreMap":117},[20,8610,8611,8612,8614,8615,8617,8618,8620,8621,8623],{},"Implementing ",[73,8613,1879],{}," automatically gives you ",[73,8616,1882],{},". Idiomatic: implement ",[73,8619,1879],{},", never ",[73,8622,1882],{}," directly.",[20,8625,8626,8629,8630,8633],{},[73,8627,8628],{},"FromStr"," is the parsing version (",[73,8631,8632],{},"str::parse()"," uses it).",[15,8635,8637,27,8639],{"id":8636},"asref-and-asmut",[73,8638,8489],{},[73,8640,8492],{},[111,8642,8645],{"className":8643,"code":8644,"language":397,"meta":117},[395],"fn open\u003CP: AsRef\u003CPath>>(path: P) { let p = path.as_ref(); \u002F* p: &Path *\u002F }\nopen(\"file.txt\");           \u002F\u002F &str: AsRef\u003CPath>\nopen(Path::new(\"f\"));       \u002F\u002F &Path: AsRef\u003CPath>\nopen(String::from(\"f\"));    \u002F\u002F String: AsRef\u003CPath>\n",[73,8646,8644],{"__ignoreMap":117},[20,8648,8649,8650,8652,8653,480,8655,480,8657,480,8660,8663],{},"Multi-source APIs use ",[73,8651,3832],{}," to accept ",[73,8654,1630],{},[73,8656,1197],{},[73,8658,8659],{},"&Path",[73,8661,8662],{},"&OsStr",", etc.",[15,8665,8667],{"id":8666},"operator-overloading","Operator Overloading",[111,8669,8672],{"className":8670,"code":8671,"language":397,"meta":117},[395],"use std::ops::Add;\nstruct Vec2 { x: f64, y: f64 }\nimpl Add for Vec2 {\n    type Output = Vec2;\n    fn add(self, rhs: Vec2) -> Vec2 { Vec2 { x: self.x + rhs.x, y: self.y + rhs.y } }\n}\nlet v = Vec2 { x: 1.0, y: 0.0 } + Vec2 { x: 0.0, y: 1.0 };\n",[73,8673,8671],{"__ignoreMap":117},[20,8675,8676,8677,480,8679,480,8681,480,8683,480,8685,480,8688,480,8691,480,8693,480,8695,480,8697,480,8700,480,8703,480,8706,480,8709,480,8712,8663],{},"You can overload ",[73,8678,8510],{},[73,8680,8513],{},[73,8682,8516],{},[73,8684,8519],{},[73,8686,8687],{},"Rem",[73,8689,8690],{},"Neg",[73,8692,8529],{},[73,8694,8532],{},[73,8696,3782],{},[73,8698,8699],{},"DerefMut",[73,8701,8702],{},"BitAnd",[73,8704,8705],{},"BitOr",[73,8707,8708],{},"Shl",[73,8710,8711],{},"Shr",[73,8713,8714],{},"Fn*",[15,8716,8717,3783],{"id":3779},[73,8718,3782],{},[111,8720,8723],{"className":8721,"code":8722,"language":397,"meta":117},[395],"impl Deref for My { type Target = Inner; fn deref(&self) -> &Inner { &self.inner } }\nlet m = My { inner: Inner { x: 5 } };\nlet x = m.x;     \u002F\u002F m.x works via Deref coercion\n",[73,8724,8722],{"__ignoreMap":117},[20,8726,8727,480,8730,480,8733,8736,8737,8739],{},[73,8728,8729],{},"String: Deref\u003CTarget = str>",[73,8731,8732],{},"Vec\u003CT>: Deref\u003CTarget = [T]>",[73,8734,8735],{},"Box\u003CT>: Deref\u003CTarget = T>",". This enables method\u002Ffield forwarding and ",[73,8738,2710],{},"-coercions.",[20,8741,8742,8745,8746,8748],{},[24,8743,8744],{},"Don't"," abuse ",[73,8747,3782],{}," for inheritance — it's a memory-layout mechanism, not a modeling tool.",[15,8750,8751],{"id":3328},[73,8752,3217],{},[111,8754,8757],{"className":8755,"code":8756,"language":397,"meta":117},[395],"impl Drop for File {\n    fn drop(&mut self) {\n        \u002F\u002F close file, free resources\n    }\n}\n",[73,8758,8756],{"__ignoreMap":117},[20,8760,8761,8762,3226],{},"Runs automatically at scope end. Don't call directly — use ",[73,8763,3225],{},[15,8765,8767],{"id":8766},"supertraits","Supertraits",[111,8769,8772],{"className":8770,"code":8771,"language":397,"meta":117},[395],"trait Pretty: Debug { fn pretty(&self) { \u002F* can use {:?} *\u002F } }\n",[73,8773,8771],{"__ignoreMap":117},[20,8775,8776],{},"A supertrait bound means \"any type implementing Pretty must also implement Debug\".",[15,8778,8780],{"id":8779},"trait-composition","Trait Composition",[111,8782,8785],{"className":8783,"code":8784,"language":397,"meta":117},[395],"trait Read: io::Read + BufRead {}\nimpl\u003CT: io::Read + BufRead> Read for T {}\n",[73,8786,8784],{"__ignoreMap":117},[20,8788,8789],{},"Blanket impl gives any type with both underlying traits the composite trait.",[15,8791,8793],{"id":8792},"blanket-implementations","Blanket Implementations",[111,8795,8798],{"className":8796,"code":8797,"language":397,"meta":117},[395],"impl\u003CT: Display> ToString for T {\n    fn to_string(&self) -> String { \u002F* ... *\u002F }\n}\n",[73,8799,8797],{"__ignoreMap":117},[20,8801,8802],{},"A blanket impl covers all matching types. Powerful but can lock out other impls (orphan-rule implications).",[15,8804,8806],{"id":8805},"traits-with-const-generics","Traits with Const Generics",[111,8808,8811],{"className":8809,"code":8810,"language":397,"meta":117},[395],"trait Bytes\u003Cconst N: usize> { fn data(&self) -> [u8; N]; }\n",[73,8812,8810],{"__ignoreMap":117},[15,8814,8816],{"id":8815},"marker-traits","Marker Traits",[20,8818,8819,8820,480,8822,480,8824,480,8826,480,8828,8830],{},"Zero-method traits that tag types: ",[73,8821,8553],{},[73,8823,8563],{},[73,8825,8566],{},[73,8827,8576],{},[73,8829,1795],{},". Some are auto-traits (compiler-implemented when possible).",[15,8832,8834],{"id":8833},"sealed-traits","Sealed Traits",[20,8836,8837],{},"To prevent downstream impls while still exposing a stable API:",[111,8839,8842],{"className":8840,"code":8841,"language":397,"meta":117},[395],"mod private { pub trait Sealed {} }\npub trait Public: private::Sealed { \u002F* ... *\u002F }\n",[73,8843,8841],{"__ignoreMap":117},[20,8845,8846,8847,8850,8851,8854],{},"Downstream types can't implement ",[73,8848,8849],{},"Sealed",", so they can't implement ",[73,8852,8853],{},"Public",". Used by std and many crates for forward compatibility.",[15,8856,1125],{"id":1124},[33,8858,8859,8869,8882,8892,8898,8911,8923,8931,8939,8945],{},[36,8860,8861,8864,8865,8868],{},[24,8862,8863],{},"Orphan rule",": can't implement external trait for external type. Use the ",[24,8866,8867],{},"newtype pattern"," to wrap and implement.",[36,8870,8871,8876,8877,8879,8880,259],{},[24,8872,8873,8875],{},[73,8874,2264],{}," returns break object safety",": traits returning ",[73,8878,2264],{}," can't be made into ",[73,8881,8210],{},[36,8883,8884,8887,8888,8891],{},[24,8885,8886],{},"Method resolution",": when multiple traits provide the same method name, you must write ",[73,8889,8890],{},"Trait::method(&self)"," or use UFCS.",[36,8893,8894,8897],{},[24,8895,8896],{},"Conflicting impls",": blanket impls can cause \"conflicting implementations\" errors; design carefully.",[36,8899,8900,71,8906,8908,8909,259],{},[24,8901,8902,559,8904],{},[73,8903,5365],{},[73,8905,5355],{},[73,8907,5355],{}," is a marker requiring reflexivity; floats lack ",[73,8910,5355],{},[36,8912,8913,8916,8917,8920,8921,259],{},[24,8914,8915],{},"Trait objects can't have generic methods"," at runtime: ",[73,8918,8919],{},"fn dyn_call\u003CT>(&self, x: T)"," is forbidden on ",[73,8922,8210],{},[36,8924,8925,8930],{},[24,8926,8927,8929],{},[73,8928,4771],{}," in argument position"," is sugar for a generic — not a way to accept trait objects.",[36,8932,8933,8938],{},[24,8934,8935,8937],{},[73,8936,8246],{}," bound on a method"," excludes it from the vtable — useful for \"static-only\" methods on an object-safe trait.",[36,8940,8941,8944],{},[24,8942,8943],{},"Generic method on trait object"," is impossible — workaround is to expose concrete variants.",[36,8946,8947,71,8950,8953],{},[24,8948,8949],{},"Lifetime bounds on traits",[73,8951,8952],{},"trait Foo\u003C'a>"," requires the impl to specify a lifetime; used when methods borrow from inputs.",[15,8955,349],{"id":348},[20,8957,8958,8959,8961,8962,8964,8965,480,8967,1212,8969,8971,8972,8974,8975,8977],{},"Traits define behavior; generics parametrize code; ",[73,8960,4771],{}," is sugar for both. Use trait bounds to require capabilities. Object safety decides whether you can use ",[73,8963,8210],{},". Implement ",[73,8966,1879],{},[73,8968,461],{},[73,8970,469],{},", and ",[73,8973,5347],{}," for ergonomics. Avoid abusing ",[73,8976,3782],{},". Sealed traits give you stable APIs.",[20,8979,8980],{},"Next: Lifetimes in generics + the deeper type-system chapter.",{"title":117,"searchDepth":357,"depth":357,"links":8982},[8983,8984,8985,8986,8990,8991,8995,8996,8998,9000,9001,9002,9003,9004,9005,9006,9007,9008,9009,9010],{"id":8160,"depth":357,"text":8161},{"id":8189,"depth":357,"text":8190},{"id":8217,"depth":357,"text":8218},{"id":8274,"depth":357,"text":8275,"children":8987},[8988,8989],{"id":8278,"depth":364,"text":8279},{"id":8302,"depth":364,"text":8303},{"id":8312,"depth":357,"text":8313},{"id":8328,"depth":357,"text":4771,"children":8992},[8993,8994],{"id":8333,"depth":364,"text":8334},{"id":8343,"depth":364,"text":8344},{"id":8377,"depth":357,"text":8378},{"id":8598,"depth":357,"text":8997},"From and Into",{"id":8636,"depth":357,"text":8999},"AsRef and AsMut",{"id":8666,"depth":357,"text":8667},{"id":3779,"depth":357,"text":3985},{"id":3328,"depth":357,"text":3217},{"id":8766,"depth":357,"text":8767},{"id":8779,"depth":357,"text":8780},{"id":8792,"depth":357,"text":8793},{"id":8805,"depth":357,"text":8806},{"id":8815,"depth":357,"text":8816},{"id":8833,"depth":357,"text":8834},{"id":1124,"depth":357,"text":1125},{"id":348,"depth":357,"text":349},"Traits are Rust's answer to interfaces\u002Ftypeclasses — they define shared behavior. Generics parametrize code over types. Together they're the foundation of Rust's abstraction.",{},"\u002Frust\u002F16-traits-and-generics",{"title":8145,"description":9011},"rust\u002F16-traits-and-generics","UVqqF3XNgqTcN2hdSik9REMj818mAdK5myin4goBihE",{"id":9018,"title":9019,"body":9020,"description":9665,"extension":373,"meta":9666,"navigation":375,"path":9667,"seo":9668,"stem":9669,"__hash__":9670},"content\u002Frust\u002F17-closures.md","17 — Closures",{"type":8,"value":9021,"toc":9636},[9022,9025,9032,9036,9042,9060,9064,9067,9073,9077,9117,9131,9137,9144,9147,9153,9160,9164,9170,9177,9181,9187,9193,9199,9203,9209,9214,9220,9226,9230,9236,9240,9246,9259,9269,9275,9281,9285,9288,9294,9300,9306,9316,9320,9323,9343,9349,9355,9380,9384,9390,9402,9406,9412,9415,9422,9428,9434,9436,9566,9574,9580,9597,9599,9627],[11,9023,9019],{"id":9024},"_17-closures",[20,9026,9027,9028,9031],{},"Closures are anonymous functions that ",[24,9029,9030],{},"capture"," their environment. They bridge the gap between functions and objects.",[15,9033,9035],{"id":9034},"syntax","Syntax",[111,9037,9040],{"className":9038,"code":9039,"language":397,"meta":117},[395],"let add = |a, b| a + b;\nlet square = |x: i32| x * x;\nlet greet = |name: &str| format!(\"hi {name}\");\nlet block = |x| { let y = x + 1; y * 2 };\nlet no_args = || 42;\nlet void_closure = || { \u002F* do something *\u002F };\n",[73,9041,9039],{"__ignoreMap":117},[33,9043,9044,9047,9054],{},[36,9045,9046],{},"Parameter types often inferred (especially when called immediately).",[36,9048,9049,9050,9053],{},"Single expression OR ",[73,9051,9052],{},"{ }"," block.",[36,9055,9056,9057,526],{},"Can't have generic parameters (no ",[73,9058,9059],{},"|\u003CT> x| ...",[15,9061,9063],{"id":9062},"capturing-the-environment","Capturing the Environment",[20,9065,9066],{},"Closures capture variables from the enclosing scope. The capture mode determines how:",[111,9068,9071],{"className":9069,"code":9070,"language":397,"meta":117},[395],"let n = 5;\nlet add_n = |x| x + n;            \u002F\u002F borrows n by reference\nlet n_mut = String::from(\"a\");\nlet consume = move || n_mut;       \u002F\u002F moves n_mut into closure\n",[73,9072,9070],{"__ignoreMap":117},[130,9074,9076],{"id":9075},"capture-modes-trait-hierarchy","Capture Modes (Trait Hierarchy)",[917,9078,9079,9088],{},[920,9080,9081],{},[923,9082,9083,9085],{},[926,9084,8387],{},[926,9086,9087],{},"How it captures",[936,9089,9090,9099,9108],{},[923,9091,9092,9096],{},[941,9093,9094],{},[73,9095,2335],{},[941,9097,9098],{},"May consume captures (move out) — callable once",[923,9100,9101,9105],{},[941,9102,9103],{},[73,9104,2332],{},[941,9106,9107],{},"May mutate captures — callable multiple times (mutably)",[923,9109,9110,9114],{},[941,9111,9112],{},[73,9113,1799],{},[941,9115,9116],{},"Only immutable borrows — callable any number of times",[20,9118,9119,9120,9123,9124,9126,9127,27,9129,259],{},"Each is a supertrait of the next: ",[73,9121,9122],{},"Fn: FnMut: FnOnce",". So every ",[73,9125,1799],{}," is also ",[73,9128,2332],{},[73,9130,2335],{},[111,9132,9135],{"className":9133,"code":9134,"language":397,"meta":117},[395],"let mut s = String::from(\"hi\");\nlet push_closure = || s.push('!');       \u002F\u002F FnMut — needs mut capture\nlet consume_closure = || drop(s);         \u002F\u002F FnOnce — consumes s\n",[73,9136,9134],{"__ignoreMap":117},[130,9138,9140,9143],{"id":9139},"move-keyword",[73,9141,9142],{},"move"," Keyword",[20,9145,9146],{},"Forces capture by value (move) regardless of how the closure uses them:",[111,9148,9151],{"className":9149,"code":9150,"language":397,"meta":117},[395],"let s = String::from(\"hi\");\nlet f = move || println!(\"{s}\");   \u002F\u002F s moved into f; original invalid\n",[73,9152,9150],{"__ignoreMap":117},[20,9154,9155,9157,9158,526],{},[73,9156,9142],{}," is essential for thread spawning — closures sent to other threads must own their captures (",[73,9159,4560],{},[130,9161,9163],{"id":9162},"disjoint-closure-captures-edition-2021","Disjoint Closure Captures (Edition 2021)",[111,9165,9168],{"className":9166,"code":9167,"language":397,"meta":117},[395],"let a = String::from(\"a\");\nlet b = String::from(\"b\");\nlet f = || println!(\"{a} {b}\");   \u002F\u002F edition 2018: borrows BOTH a and b\n                                   \u002F\u002F edition 2021: borrows only what's used in each branch\n",[73,9169,9167],{"__ignoreMap":117},[20,9171,9172,9173,9176],{},"The 2021 edition captures only the ",[183,9174,9175],{},"used"," fields of disjoint structs, enabling more code to compile.",[15,9178,9180],{"id":9179},"closures-as-arguments","Closures as Arguments",[111,9182,9185],{"className":9183,"code":9184,"language":397,"meta":117},[395],"fn apply\u003CF: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }\nfn apply_mut\u003CF: FnMut(i32) -> i32>(mut f: F, x: i32) -> i32 { f(x) }\nfn apply_once\u003CF: FnOnce() -> i32>(f: F) -> i32 { f() }\n\napply(|x| x + 1, 5);\n",[73,9186,9184],{"__ignoreMap":117},[20,9188,1876,9189,9192],{},[73,9190,9191],{},"impl Fn(...)"," for shorthand:",[111,9194,9197],{"className":9195,"code":9196,"language":397,"meta":117},[395],"fn apply(f: impl Fn(i32) -> i32, x: i32) -> i32 { f(x) }\n",[73,9198,9196],{"__ignoreMap":117},[15,9200,9202],{"id":9201},"returning-closures","Returning Closures",[111,9204,9207],{"className":9205,"code":9206,"language":397,"meta":117},[395],"fn make_adder(n: i32) -> impl Fn(i32) -> i32 {\n    move |x| x + n\n}\n\nlet add5 = make_adder(5);\nadd5(10);   \u002F\u002F 15\n",[73,9208,9206],{"__ignoreMap":117},[20,9210,1876,9211,9213],{},[73,9212,9142],{}," so the closure owns its captures (otherwise it'd borrow a stack variable that's gone).",[20,9215,9216,9217,170],{},"For multiple closure types in different branches, use ",[73,9218,9219],{},"Box\u003Cdyn Fn()>",[111,9221,9224],{"className":9222,"code":9223,"language":397,"meta":117},[395],"fn make(pred: bool) -> Box\u003Cdyn Fn(i32) -> i32> {\n    if pred { Box::new(move |x| x + 1) }\n    else    { Box::new(move |x| x - 1) }\n}\n",[73,9225,9223],{"__ignoreMap":117},[15,9227,9229],{"id":9228},"closure-type-is-unique","Closure Type is Unique",[20,9231,9232,9233,9235],{},"Each closure has an anonymous, unnameable type generated by the compiler. Two structurally identical closures have different types. This is why ",[73,9234,9219],{}," exists for heterogeneous collections.",[15,9237,9239],{"id":9238},"closures-implement-traits","Closures Implement Traits",[111,9241,9244],{"className":9242,"code":9243,"language":397,"meta":117},[395],"impl Fn(i32) -> i32 for SomeClosureType { ... }\n",[73,9245,9243],{"__ignoreMap":117},[20,9247,3136,9248,9251,9252,9254,9255,9258],{},[73,9249,9250],{},"fn(A) -> B"," also implement ",[73,9253,1799],{},", so you can pass plain functions where ",[73,9256,9257],{},"impl Fn"," is expected.",[15,9260,9262,1212,9264,1212,9266,9268],{"id":9261},"fnfnmutfnonce-object-safety",[73,9263,1799],{},[73,9265,2332],{},[73,9267,2335],{}," Object Safety",[111,9270,9273],{"className":9271,"code":9272,"language":397,"meta":117},[395],"let f: Box\u003Cdyn Fn(i32) -> i32> = Box::new(|x| x + 1);\nlet fm: Box\u003Cdyn FnMut(i32) -> i32> = Box::new(|x| x + 1);\nlet fo: Box\u003Cdyn FnOnce(i32) -> i32> = Box::new(|x| x + 1);\n",[73,9274,9272],{"__ignoreMap":117},[20,9276,9277,9280],{},[73,9278,9279],{},"Box\u003Cdyn FnOnce>"," is callable since Rust 1.35 (special support).",[15,9282,9284],{"id":9283},"closures-and-iterators","Closures and Iterators",[20,9286,9287],{},"Closures are the currency of iterator adapters:",[111,9289,9292],{"className":9290,"code":9291,"language":397,"meta":117},[395],"v.iter().map(|x| x * 2).filter(|x| *x > 5).for_each(|x| println!(\"{x}\"));\n",[73,9293,9291],{"__ignoreMap":117},[15,9295,9297,9299],{"id":9296},"in-closures",[73,9298,2404],{}," in Closures",[111,9301,9304],{"className":9302,"code":9303,"language":397,"meta":117},[395],"let parse = |s: &str| s.parse::\u003Ci32>()?;   \u002F\u002F closure can use ?\n",[73,9305,9303],{"__ignoreMap":117},[20,9307,9308,9309,9311,9312,1546,9314,526],{},"Closures can use ",[73,9310,2404],{}," if their return type supports it (",[73,9313,2792],{},[73,9315,1481],{},[15,9317,9319],{"id":9318},"recursion-in-closures","Recursion in Closures",[20,9321,9322],{},"Closures can't easily recurse — they don't have a name. Workarounds:",[33,9324,9325,9330,9337],{},[36,9326,9327,9328,259],{},"Use a regular ",[73,9329,2424],{},[36,9331,9332,9333,9336],{},"Use a ",[73,9334,9335],{},"Box\u003Cdyn Fn>"," and pass it to itself.",[36,9338,1876,9339,9342],{},[73,9340,9341],{},"Y"," combinator (academic).",[15,9344,9346,9348],{"id":9345},"move-closure-captures-and-lifetime",[73,9347,9142],{}," Closure Captures and Lifetime",[111,9350,9353],{"className":9351,"code":9352,"language":397,"meta":117},[395],"let s = String::from(\"hi\");\nlet f = move || println!(\"{s}\");\n\u002F\u002F s moved into f; f now owns the String\n",[73,9354,9352],{"__ignoreMap":117},[20,9356,9357,9358,480,9360,9363,9364,9366,9367,9369,9370,9372,9373,480,9375,9377,9378,259],{},"Without ",[73,9359,9142],{},[73,9361,9362],{},"f"," would borrow ",[73,9365,3279],{},", which means ",[73,9368,3279],{}," must outlive ",[73,9371,9362],{},". With ",[73,9374,9142],{},[73,9376,9362],{}," owns its captures and can be ",[73,9379,4560],{},[15,9381,9383],{"id":9382},"capture-by-reference-by-mut-reference-by-value","Capture By Reference, By Mut Reference, By Value",[111,9385,9388],{"className":9386,"code":9387,"language":397,"meta":117},[395],"let n = 5;\nlet f1 = || println!(\"{n}\");        \u002F\u002F &n\nlet mut m = 0;\nlet mut f2 = || m += 1;             \u002F\u002F &mut m\nlet s = String::from(\"x\");\nlet f3 = || drop(s);                 \u002F\u002F moves s out — FnOnce\nlet f4 = move || println!(\"{n}\");    \u002F\u002F copies n (i32 is Copy)\n",[73,9389,9387],{"__ignoreMap":117},[20,9391,9392,9393,9396,9397,9399,9400,526],{},"The compiler picks the ",[183,9394,9395],{},"least restrictive"," capture mode by default. ",[73,9398,9142],{}," forces everything to move (or copy if ",[73,9401,1795],{},[15,9403,9405],{"id":9404},"higher-order-closures","Higher-Order Closures",[111,9407,9410],{"className":9408,"code":9409,"language":397,"meta":117},[395],"fn make\u003CF: Fn(i32) -> i32>(f: F) -> impl Fn(i32) -> i32 {\n    move |x| f(x) + 1\n}\n",[73,9411,9409],{"__ignoreMap":117},[20,9413,9414],{},"Functions returning closures returning closures — typical in functional pipelines.",[15,9416,9418,9421],{"id":9417},"partial-currying",[73,9419,9420],{},"partial"," \u002F Currying",[20,9423,9424,9425,9427],{},"Rust doesn't have built-in currying, but ",[73,9426,9142],{}," closures make it easy:",[111,9429,9432],{"className":9430,"code":9431,"language":397,"meta":117},[395],"let add = |a: i32| move |b: i32| a + b;\nlet add5 = add(5);\nadd5(3);   \u002F\u002F 8\n",[73,9433,9431],{"__ignoreMap":117},[15,9435,1125],{"id":1124},[33,9437,9438,9450,9465,9486,9495,9509,9523,9550],{},[36,9439,9440,9443,9444,9446,9447,9449],{},[24,9441,9442],{},"Capture lifetime",": a closure borrowing from local vars can't escape the local's scope. Use ",[73,9445,9142],{}," (often with ",[73,9448,4560],{}," requirement).",[36,9451,9452,71,9459,9461,9462,9464],{},[24,9453,9454,27,9456],{},[73,9455,2335],{},[73,9457,9458],{},"Vec::map",[73,9460,7556],{}," consumes the iterator but only requires ",[73,9463,2332],{},"; if you consume captures inside, you might need a different signature.",[36,9466,9467,9476,9477,9479,9480,9482,9483,9485],{},[24,9468,9469,559,9471,559,9473,9475],{},[73,9470,1799],{},[73,9472,2332],{},[73,9474,2335],{}," matching",": passing a ",[73,9478,2335],{}," closure to a function expecting ",[73,9481,1799],{}," won't compile. Pass ",[73,9484,1799],{}," when possible.",[36,9487,9488,9491,9492,9494],{},[24,9489,9490],{},"Recursive closure",": not directly possible; use a ",[73,9493,2424],{}," instead.",[36,9496,9497,9502,9503,8049,9506,9508],{},[24,9498,9499,9500],{},"Closures with ",[73,9501,2239],{},": a method that takes a closure that ",[183,9504,9505],{},"also",[73,9507,2245],{}," mutably conflicts. Restructure (e.g., extract a value first).",[36,9510,9511,9516,9517,9519,9520,9522],{},[24,9512,9513,9514],{},"Capturing by ",[73,9515,5452],{},": if you need to mutate through a closure called multiple times behind an ",[73,9518,2710],{},"-reference, use ",[73,9521,5452],{}," for interior mutability.",[36,9524,9525,71,9530,6859,9533,9536,9537,9539,9540,9543,9544,9546,9547,9549],{},[24,9526,9527,9529],{},[73,9528,9142],{}," doesn't always move",[73,9531,9532],{},"move || println!(\"{x}\")",[73,9534,9535],{},"x: i32"," copies; ",[73,9538,9142],{}," only forces ",[183,9541,9542],{},"by-value"," capture (which is ",[73,9545,1795],{},"-duplicating for ",[73,9548,1795],{}," types).",[36,9551,9552,9558,9559,9562,9563,259],{},[24,9553,9554,9557],{},[73,9555,9556],{},"move ||"," in threads",": required for ",[73,9560,9561],{},"std::thread::spawn"," since the closure must be ",[73,9564,9565],{},"'static + Send",[15,9567,9569,27,9572],{"id":9568},"threadspawn-and-static-send",[73,9570,9571],{},"thread::spawn",[73,9573,9565],{},[111,9575,9578],{"className":9576,"code":9577,"language":397,"meta":117},[395],"let data = vec![1, 2, 3];\nstd::thread::spawn(move || {\n    println!(\"{:?}\", data);   \u002F\u002F OK — data moved in\n});\n",[73,9579,9577],{"__ignoreMap":117},[20,9581,9357,9582,9584,9585,9588,9589,2559,9591,9593,9594,9596],{},[73,9583,9142],{},", you'd borrow ",[73,9586,9587],{},"data",", which doesn't satisfy ",[73,9590,4560],{},[73,9592,9142],{}," + ",[73,9595,9565],{}," is the recipe for thread closures.",[15,9598,349],{"id":348},[20,9600,9601,9602,9604,9605,9607,9608,9610,9611,9613,9614,1212,9616,1212,9618,9620,9621,9623,9624,9626],{},"Closures capture by ref (",[73,9603,1799],{},"), mut ref (",[73,9606,2332],{},"), or value (",[73,9609,2335],{},"). ",[73,9612,9142],{}," forces by-value. Use ",[73,9615,9191],{},[73,9617,2332],{},[73,9619,2335],{}," for generic APIs. Closures are essential for iterator combinators and async. Returning closures requires ",[73,9622,9257],{}," (single type) or ",[73,9625,9335],{}," (heterogeneous).",[20,9628,9629,9630,480,9632,8971,9634,259],{},"Next: Error handling — ",[73,9631,2792],{},[73,9633,1481],{},[73,9635,2404],{},{"title":117,"searchDepth":357,"depth":357,"links":9637},[9638,9639,9645,9646,9647,9648,9649,9651,9652,9654,9655,9657,9658,9659,9661,9662,9664],{"id":9034,"depth":357,"text":9035},{"id":9062,"depth":357,"text":9063,"children":9640},[9641,9642,9644],{"id":9075,"depth":364,"text":9076},{"id":9139,"depth":364,"text":9643},"move Keyword",{"id":9162,"depth":364,"text":9163},{"id":9179,"depth":357,"text":9180},{"id":9201,"depth":357,"text":9202},{"id":9228,"depth":357,"text":9229},{"id":9238,"depth":357,"text":9239},{"id":9261,"depth":357,"text":9650},"Fn\u002FFnMut\u002FFnOnce Object Safety",{"id":9283,"depth":357,"text":9284},{"id":9296,"depth":357,"text":9653},"? in Closures",{"id":9318,"depth":357,"text":9319},{"id":9345,"depth":357,"text":9656},"move Closure Captures and Lifetime",{"id":9382,"depth":357,"text":9383},{"id":9404,"depth":357,"text":9405},{"id":9417,"depth":357,"text":9660},"partial \u002F Currying",{"id":1124,"depth":357,"text":1125},{"id":9568,"depth":357,"text":9663},"thread::spawn and 'static + Send",{"id":348,"depth":357,"text":349},"Closures are anonymous functions that capture their environment. They bridge the gap between functions and objects.",{},"\u002Frust\u002F17-closures",{"title":9019,"description":9665},"rust\u002F17-closures","1ngdjkh5xMEyhtAY7fpeGpSXSfefWaQgDYyI-2lszcA",{"id":9672,"title":9673,"body":9674,"description":10439,"extension":373,"meta":10440,"navigation":375,"path":10441,"seo":10442,"stem":10443,"__hash__":10444},"content\u002Frust\u002F18-error-handling.md","18 — Error Handling",{"type":8,"value":9675,"toc":10399},[9676,9679,9694,9700,9706,9711,9717,9723,9729,9735,9741,9746,9752,9757,9763,9771,9778,9784,9791,9798,9806,9812,9820,9824,9830,9844,9850,9856,9881,9885,9889,9895,9902,9908,9926,9933,9939,9945,9951,9957,9966,9970,9991,9998,10002,10031,10039,10045,10051,10057,10061,10067,10074,10080,10089,10096,10102,10110,10114,10120,10129,10135,10141,10143,10320,10324,10367,10369,10396],[11,9677,9673],{"id":9678},"_18-error-handling",[20,9680,9681,9682,9685,9686,2681,9689,1212,9691,9693],{},"Rust's error handling is a defining strength. There's no exceptions, no ",[73,9683,9684],{},"null",". Errors are ",[24,9687,9688],{},"values",[73,9690,2792],{},[73,9692,1481],{},") and the type system forces you to handle them.",[15,9695,9697,9699],{"id":9696},"optiont-absence",[73,9698,5688],{}," — Absence",[111,9701,9704],{"className":9702,"code":9703,"language":397,"meta":117},[395],"enum Option\u003CT> { Some(T), None }\n",[73,9705,9703],{"__ignoreMap":117},[20,9707,9708,9709,170],{},"Use when a value is logically absent. The compiler forces you to handle ",[73,9710,1541],{},[111,9712,9715],{"className":9713,"code":9714,"language":397,"meta":117},[395],"let v: Option\u003Ci32> = Some(5);\nlet s = match v { Some(x) => x.to_string(), None => String::from(\"none\") };\n",[73,9716,9714],{"__ignoreMap":117},[15,9718,9720,9722],{"id":9719},"resultt-e-recoverable-errors",[73,9721,5719],{}," — Recoverable Errors",[111,9724,9727],{"className":9725,"code":9726,"language":397,"meta":117},[395],"enum Result\u003CT, E> { Ok(T), Err(E) }\n",[73,9728,9726],{"__ignoreMap":117},[111,9730,9733],{"className":9731,"code":9732,"language":397,"meta":117},[395],"fn parse(s: &str) -> Result\u003Ci32, std::num::ParseIntError> {\n    s.parse()\n}\nmatch parse(\"42\") {\n    Ok(n) => println!(\"{n}\"),\n    Err(e) => println!(\"err: {e}\"),\n}\n",[73,9734,9732],{"__ignoreMap":117},[15,9736,3106,9738,9740],{"id":9737},"the-operator",[73,9739,2404],{}," Operator",[20,9742,9743,9744,170],{},"Short-circuits on error, propagating ",[73,9745,2778],{},[111,9747,9750],{"className":9748,"code":9749,"language":397,"meta":117},[395],"fn parse_and_double(s: &str) -> Result\u003Ci32, std::num::ParseIntError> {\n    let n: i32 = s.parse()?;     \u002F\u002F returns Err on failure\n    Ok(n * 2)\n}\n",[73,9751,9749],{"__ignoreMap":117},[20,9753,9754,9756],{},[73,9755,2404],{}," desugars roughly to:",[111,9758,9761],{"className":9759,"code":9760,"language":397,"meta":117},[395],"match expr {\n    Ok(v) => v,\n    Err(e) => return Err(e.into()),\n}\n",[73,9762,9760],{"__ignoreMap":117},[20,9764,9765,9766,9768,9769,259],{},"It uses ",[73,9767,1879],{}," to convert errors, so you can mix error types if they implement ",[73,9770,1879],{},[130,9772,9774,8014,9776],{"id":9773},"on-option",[73,9775,2404],{},[73,9777,1481],{},[111,9779,9782],{"className":9780,"code":9781,"language":397,"meta":117},[395],"fn first_char(s: &str) -> Option\u003Cchar> {\n    s.chars().next()?\n}\n",[73,9783,9781],{"__ignoreMap":117},[20,9785,8355,9786,9788,9789,259],{},[73,9787,1541],{}," if the inner is ",[73,9790,1541],{},[130,9792,9794,292,9796],{"id":9793},"in-main",[73,9795,2404],{},[73,9797,509],{},[20,9799,9800,9801,9803,9804,170],{},"Since Rust 1.56, ",[73,9802,509],{}," can return ",[73,9805,2792],{},[111,9807,9810],{"className":9808,"code":9809,"language":397,"meta":117},[395],"fn main() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n    let n: i32 = std::env::args().nth(1).unwrap().parse()?;\n    println!(\"{n}\");\n    Ok(())\n}\n",[73,9811,9809],{"__ignoreMap":117},[20,9813,9814,9815,1538,9817,9819],{},"If ",[73,9816,509],{},[73,9818,2778],{},", the program exits with code 1 and prints the error.",[15,9821,9823],{"id":9822},"recovering-values","Recovering Values",[111,9825,9828],{"className":9826,"code":9827,"language":397,"meta":117},[395],"let v = opt.unwrap();          \u002F\u002F panics on None\nlet v = opt.expect(\"msg\");      \u002F\u002F panics with custom msg\nlet v = opt.unwrap_or(default);\nlet v = opt.unwrap_or_default();\nlet v = opt.unwrap_or_else(|| expensive());\nlet v = opt.map(|x| x + 1);   \u002F\u002F Option\u003COption\u003C...>> sometimes\nlet v = opt.and_then(|x| Some(x + 1));   \u002F\u002F flatten\nlet v = opt.or(Some(0));\nlet v = opt.or_else(|| Some(0));\nlet v = opt.get_or_insert(0);\nlet v = opt.take();             \u002F\u002F leaves None in opt\n",[73,9829,9827],{"__ignoreMap":117},[20,9831,9832,9833,9835,9836,480,9839,480,9841,6643],{},"Same combinator suite exists for ",[73,9834,2792],{}," (with ",[73,9837,9838],{},"map_err",[73,9840,7556],{},[73,9842,9843],{},"and_then",[15,9845,3106,9847,3109],{"id":9846},"the-stderrorerror-trait",[73,9848,9849],{},"std::error::Error",[111,9851,9854],{"className":9852,"code":9853,"language":397,"meta":117},[395],"pub trait Error: Debug + Display {\n    fn source(&self) -> Option\u003C&(dyn Error + 'static)> { None }\n}\n",[73,9855,9853],{"__ignoreMap":117},[20,9857,9858,9859,9862,9863,9866,9867,9869,9870,9872,9873,1212,9875,9877,9878,9880],{},"A type implementing ",[73,9860,9861],{},"Error"," can be used with ",[73,9864,9865],{},"Result\u003C_, MyError>",", chained with ",[73,9868,2404],{}," (via ",[73,9871,1879],{},"), and printed with ",[73,9874,465],{},[73,9876,457],{},". The ",[73,9879,4664],{}," method gives an error chain.",[15,9882,9884],{"id":9883},"defining-your-own-error-type","Defining Your Own Error Type",[130,9886,9888],{"id":9887},"the-manual-way","The Manual Way",[111,9890,9893],{"className":9891,"code":9892,"language":397,"meta":117},[395],"#[derive(Debug)]\nenum AppError {\n    Io(std::io::Error),\n    Parse(std::num::ParseIntError),\n    Custom(String),\n}\n\nimpl std::fmt::Display for AppError {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        match self {\n            AppError::Io(e) => write!(f, \"io: {e}\"),\n            AppError::Parse(e) => write!(f, \"parse: {e}\"),\n            AppError::Custom(s) => write!(f, \"{s}\"),\n        }\n    }\n}\n\nimpl std::error::Error for AppError {\n    fn source(&self) -> Option\u003C&(dyn std::error::Error + 'static)> {\n        match self {\n            AppError::Io(e) => Some(e),\n            AppError::Parse(e) => Some(e),\n            AppError::Custom(_) => None,\n        }\n    }\n}\n\nimpl From\u003Cstd::io::Error> for AppError { fn from(e: std::io::Error) -> Self { AppError::Io(e) } }\nimpl From\u003Cstd::num::ParseIntError> for AppError { fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) } }\n",[73,9894,9892],{"__ignoreMap":117},[130,9896,3106,9898,9901],{"id":9897},"the-thiserror-crate-idiomatic",[73,9899,9900],{},"thiserror"," Crate (idiomatic)",[111,9903,9906],{"className":9904,"code":9905,"language":397,"meta":117},[395],"use thiserror::Error;\n\n#[derive(Debug, Error)]\nenum AppError {\n    #[error(\"io: {0}\")]\n    Io(#[from] std::io::Error),\n    #[error(\"parse: {0}\")]\n    Parse(#[from] std::num::ParseIntError),\n    #[error(\"{0}\")]\n    Custom(String),\n}\n",[73,9907,9905],{"__ignoreMap":117},[20,9909,9910,9913,9914,9916,9917,9920,9921,7020,9923,9925],{},[73,9911,9912],{},"#[from]"," generates the ",[73,9915,1879],{}," impl. ",[73,9918,9919],{},"#[error]"," generates ",[73,9922,461],{},[73,9924,9900],{}," for libraries.",[130,9927,3106,9929,9932],{"id":9928},"the-anyhow-crate-applications",[73,9930,9931],{},"anyhow"," Crate (applications)",[111,9934,9937],{"className":9935,"code":9936,"language":397,"meta":117},[395],"use anyhow::{Context, Result};\n\nfn read_config(path: &str) -> Result\u003CConfig> {\n    let s = std::fs::read_to_string(path).with_context(|| format!(\"read {path}\"))?;\n    Ok(parse(&s)?)\n}\n",[73,9938,9936],{"__ignoreMap":117},[20,9940,9941,9944],{},[73,9942,9943],{},"anyhow::Error"," is a boxed trait object with backtraces and context. Perfect for application code where you just want errors to bubble up with context.",[15,9946,9948,9950],{"id":9947},"panic-unrecoverable",[73,9949,1814],{}," — Unrecoverable",[111,9952,9955],{"className":9953,"code":9954,"language":397,"meta":117},[395],"panic!(\"cannot continue\");\nunreachable!(\"documented unreachable\");\nunimplemented!(\"todo\");\ntodo!(\"later\");\nassert!(x > 0);\nassert_eq!(a, b);\nassert_ne!(a, b);\ndebug_assert!(x > 0);   \u002F\u002F only in debug builds\n",[73,9956,9954],{"__ignoreMap":117},[20,9958,9959,9961,9962,9965],{},[73,9960,1814],{}," is for invariants: \"this state should never happen.\" It unwinds the stack (calling destructors) unless ",[73,9963,9964],{},"panic = \"abort\""," is set in the profile.",[15,9967,9969],{"id":9968},"unwinding-vs-aborting","Unwinding vs Aborting",[33,9971,9972,9981],{},[36,9973,9974,9977,9978,9980],{},[24,9975,9976],{},"Unwind"," (default): cleans up via ",[73,9979,3217],{},", then exits the thread\u002Fprocess.",[36,9982,9983,2681,9986,292,9988,9990],{},[24,9984,9985],{},"Abort",[73,9987,9964],{},[73,9989,169],{},"): immediate process exit, smaller binary, faster panic, but no cleanup.",[20,9992,9993,9994,9997],{},"Catch a panic with ",[73,9995,9996],{},"std::panic::catch_unwind"," (rare; mostly for FFI).",[15,9999,10001],{"id":10000},"result-vs-panic-heuristics","Result vs panic — Heuristics",[33,10003,10004,10012,10020,10026],{},[36,10005,10006,6859,10008,10011],{},[73,10007,2792],{},[24,10009,10010],{},"expected"," failure (file not found, parse error).",[36,10013,10014,6859,10016,10019],{},[73,10015,193],{},[24,10017,10018],{},"violated invariants"," (index out of bounds, unreachable code, internal corruption).",[36,10021,10022,10023,10025],{},"Returning ",[73,10024,1481],{}," for \"logically absent\" (looking up a key).",[36,10027,10022,10028,10030],{},[73,10029,2792],{}," for \"operation failed\".",[15,10032,10034,27,10036,10038],{"id":10033},"option-and-result-conversion",[73,10035,1481],{},[73,10037,2792],{}," Conversion",[111,10040,10043],{"className":10041,"code":10042,"language":397,"meta":117},[395],"opt.ok_or(ErrorKind::Missing)?;\nres.ok()?;                    \u002F\u002F discards Err, returns None on Err\nres.err()?;                   \u002F\u002F discards Ok\nres.ok().filter(|x| *x > 0);\nopt.ok_or_else(|| ErrorKind::Missing)?;\n",[73,10044,10042],{"__ignoreMap":117},[15,10046,10048,10050],{"id":10047},"result-combinators",[73,10049,2792],{}," Combinators",[111,10052,10055],{"className":10053,"code":10054,"language":397,"meta":117},[395],"let r: Result\u003Ci32, E> = Ok(5);\nr.map(|x| x + 1);\nr.map_err(|e| OtherError(e));\nr.and_then(|x| Ok(x + 1));\nr.or(Ok(0));\nr.or_else(|_| Ok(0));\nr.unwrap_or(0);\nr.unwrap_or_default();\nr.unwrap_or_else(|_| 0);\nr.is_ok();\nr.is_err();\nr.ok();          \u002F\u002F Option\u003CT>\nr.err();          \u002F\u002F Option\u003CE>\nr.as_ref();\nr.as_mut();\nr.transpose();    \u002F\u002F Option\u003CResult\u003CT, E>> -> Result\u003COption\u003CT>, E>\n",[73,10056,10054],{"__ignoreMap":117},[15,10058,10060],{"id":10059},"multiple-errors","Multiple Errors",[111,10062,10065],{"className":10063,"code":10064,"language":397,"meta":117},[395],"fn parse_two(s1: &str, s2: &str) -> Result\u003C(i32, i32), ParseIntError> {\n    let a: i32 = s1.parse()?;\n    let b: i32 = s2.parse()?;\n    Ok((a, b))\n}\n",[73,10066,10064],{"__ignoreMap":117},[20,10068,10069,10070,10073],{},"For independent errors you want to accumulate (not short-circuit), use ",[73,10071,10072],{},"itertools::process_results"," or roll your own.",[15,10075,10077,10079],{"id":10076},"result-with-multiple-variants",[73,10078,2792],{}," with Multiple Variants",[20,10081,1876,10082,10085,10086,10088],{},[73,10083,10084],{},"Result\u003CT, MyErrorEnum>"," and a custom error enum (see ",[73,10087,9900],{}," above).",[15,10090,10092,10095],{"id":10091},"boxdyn-error-as-catchall",[73,10093,10094],{},"Box\u003Cdyn Error>"," as Catchall",[111,10097,10100],{"className":10098,"code":10099,"language":397,"meta":117},[395],"fn foo() -> Result\u003Ci32, Box\u003Cdyn std::error::Error>> {\n    let n: i32 = \"x\".parse()?;     \u002F\u002F works for any Error type\n    let f = std::fs::File::open(\"x\")?;\n    Ok(n)\n}\n",[73,10101,10099],{"__ignoreMap":117},[20,10103,10104,10106,10107,10109],{},[73,10105,10094],{}," accepts any error via ",[73,10108,2404],{},". Loses static type info; ok for prototypes.",[15,10111,10113],{"id":10112},"error-chaining","Error Chaining",[111,10115,10118],{"className":10116,"code":10117,"language":397,"meta":117},[395],"return Err(MyError::New).context(\"while processing X\"));\n",[73,10119,10117],{"__ignoreMap":117},[20,10121,10122,10124,10125,10128],{},[73,10123,9931],{},"'s ",[73,10126,10127],{},"Context"," trait adds messages:",[111,10130,10133],{"className":10131,"code":10132,"language":397,"meta":117},[395],"std::fs::read_to_string(path).context(\"read config\")?;\n",[73,10134,10132],{"__ignoreMap":117},[20,10136,10137,10138,259],{},"The error chain shows: \"read config\" → original ",[73,10139,10140],{},"io::Error",[15,10142,1125],{"id":1124},[33,10144,10145,10158,10170,10180,10197,10205,10222,10236,10248,10259,10280,10296,10309],{},[36,10146,10147,10153,10154,1546,10156,9494],{},[24,10148,10149,10152],{},[73,10150,10151],{},"unwrap()"," in production",": panic on bad input. Use ",[73,10155,2404],{},[73,10157,1907],{},[36,10159,10160,10169],{},[24,10161,10162,10165,10166],{},[73,10163,10164],{},"expect()"," is better than ",[73,10167,10168],{},"unwrap",": a custom message helps debugging.",[36,10171,10172,10175,10176,10179],{},[24,10173,10174],{},"Panic across FFI",": undefined behavior — use ",[73,10177,10178],{},"catch_unwind"," at the FFI boundary.",[36,10181,10182,10188,10189,10191,10192,10124,10194,10196],{},[24,10183,10184,27,10186],{},[73,10185,2404],{},[73,10187,1879],{},": when mixing error types, ensure ",[73,10190,1879],{}," impls exist; ",[73,10193,9900],{},[73,10195,9912],{}," is the easy way.",[36,10198,10199,10202,10203,259],{},[24,10200,10201],{},"Panic in destructors",": aborts; avoid panicking in ",[73,10204,3217],{},[36,10206,10207,71,10217,1052,10219,10221],{},[24,10208,10209,8014,10211,10213,10214,10216],{},[73,10210,2404],{},[73,10212,1481],{}," returns from ",[73,10215,1481],{},"-returning functions only",[73,10218,2404],{},[73,10220,2786],{},", and the return type must match.",[36,10223,10224,10235],{},[24,10225,10226,1052,10228,10231,10232],{},[73,10227,9849],{},[73,10229,10230],{},"Send + Sync"," to box as ",[73,10233,10234],{},"Box\u003Cdyn Error + Send + Sync>"," — useful for thread-safe error storage.",[36,10237,10238,71,10241,10244,10245,10247],{},[24,10239,10240],{},"Backtraces",[73,10242,10243],{},"std::backtrace::Backtrace"," (1.65+) gives you a backtrace at error construction; ",[73,10246,9931],{}," integrates with it.",[36,10249,10250,10258],{},[24,10251,10252,1212,10255],{},[73,10253,10254],{},"Result::into_ok",[73,10256,10257],{},"into_err"," consume without checking — useful only when you're sure.",[36,10260,10261,71,10268,480,10271,480,10274,480,10277,259],{},[24,10262,10263,559,10265,10267],{},[73,10264,2792],{},[73,10266,1481],{}," interop",[73,10269,10270],{},"Option::ok_or",[73,10272,10273],{},"Option::ok_or_else",[73,10275,10276],{},"Result::ok",[73,10278,10279],{},"Result::err",[36,10281,10282,10285,10286,10289,10290,8018,10293,259],{},[24,10283,10284],{},"Panicking in a thread",": kills the thread but not the process. Use ",[73,10287,10288],{},"JoinHandle"," to detect; the panic becomes ",[73,10291,10292],{},"Box\u003Cdyn Any + Send>",[73,10294,10295],{},"join",[36,10297,10298,10305,10306,259],{},[24,10299,10300,2531,10302],{},[73,10301,5719],{},[73,10303,10304],{},"T == E",": the compiler can't infer which arm you mean — annotate or use ",[73,10307,10308],{},".map_err",[36,10310,10311,10316,10317,10319],{},[24,10312,10313,10314],{},"Custom error type without ",[73,10315,469],{},": required by ",[73,10318,9861],{}," trait; derive it.",[15,10321,10323],{"id":10322},"idioms-cheat-sheet","Idioms Cheat Sheet",[33,10325,10326,10331,10336,10342,10347,10352,10357],{},[36,10327,1876,10328,10330],{},[73,10329,2404],{}," to propagate.",[36,10332,10333,10334,259],{},"Define one error enum per crate with ",[73,10335,9900],{},[36,10337,1876,10338,10341],{},[73,10339,10340],{},"anyhow::Result"," in application code (binary crates).",[36,10343,1876,10344,10346],{},[73,10345,2792],{}," in library APIs.",[36,10348,1876,10349,10351],{},[73,10350,1481],{}," only when absence is normal, not \"operation failed\".",[36,10353,10354,10356],{},[73,10355,1814],{}," for invariants, never for input validation in public APIs.",[36,10358,10359,10362,10363,10366],{},[73,10360,10361],{},"assert!"," for tests; ",[73,10364,10365],{},"debug_assert!"," for invariants you don't want in release.",[15,10368,349],{"id":348},[20,10370,10371,10372,480,10374,10376,10377,10379,10380,10382,10383,10385,10386,10388,10389,10391,10392,9803,10394,259],{},"Errors are values, handled via ",[73,10373,2404],{},[73,10375,1907],{},", and combinators. ",[73,10378,1481],{}," = absence, ",[73,10381,2792],{}," = failure. ",[73,10384,9900],{}," for libraries, ",[73,10387,9931],{}," for apps. ",[73,10390,1814],{}," for invariants only. ",[73,10393,509],{},[73,10395,2792],{},[20,10397,10398],{},"Next: Memory management — smart pointers and interior mutability.",{"title":117,"searchDepth":357,"depth":357,"links":10400},[10401,10403,10405,10412,10413,10415,10422,10424,10425,10426,10428,10430,10431,10433,10435,10436,10437,10438],{"id":9696,"depth":357,"text":10402},"Option\u003CT> — Absence",{"id":9719,"depth":357,"text":10404},"Result\u003CT, E> — Recoverable Errors",{"id":9737,"depth":357,"text":10406,"children":10407},"The ? Operator",[10408,10410],{"id":9773,"depth":364,"text":10409},"? on Option",{"id":9793,"depth":364,"text":10411},"? in main",{"id":9822,"depth":357,"text":9823},{"id":9846,"depth":357,"text":10414},"The std::error::Error Trait",{"id":9883,"depth":357,"text":9884,"children":10416},[10417,10418,10420],{"id":9887,"depth":364,"text":9888},{"id":9897,"depth":364,"text":10419},"The thiserror Crate (idiomatic)",{"id":9928,"depth":364,"text":10421},"The anyhow Crate (applications)",{"id":9947,"depth":357,"text":10423},"panic! — Unrecoverable",{"id":9968,"depth":357,"text":9969},{"id":10000,"depth":357,"text":10001},{"id":10033,"depth":357,"text":10427},"Option and Result Conversion",{"id":10047,"depth":357,"text":10429},"Result Combinators",{"id":10059,"depth":357,"text":10060},{"id":10076,"depth":357,"text":10432},"Result with Multiple Variants",{"id":10091,"depth":357,"text":10434},"Box\u003Cdyn Error> as Catchall",{"id":10112,"depth":357,"text":10113},{"id":1124,"depth":357,"text":1125},{"id":10322,"depth":357,"text":10323},{"id":348,"depth":357,"text":349},"Rust's error handling is a defining strength. There's no exceptions, no null. Errors are values (Result\u002FOption) and the type system forces you to handle them.",{},"\u002Frust\u002F18-error-handling",{"title":9673,"description":10439},"rust\u002F18-error-handling","ArAxbTcKknbKzyOEnwqcpSw6DEqonq9RGs68LZtHOAo",{"id":10446,"title":10447,"body":10448,"description":10455,"extension":373,"meta":11430,"navigation":375,"path":11431,"seo":11432,"stem":11433,"__hash__":11434},"content\u002Frust\u002F19-smart-pointers.md","19 — Smart Pointers & Memory Management",{"type":8,"value":10449,"toc":11383},[10450,10453,10456,10463,10469,10483,10489,10512,10518,10524,10529,10532,10539,10545,10580,10586,10593,10599,10603,10609,10615,10622,10628,10649,10657,10678,10682,10688,10698,10702,10714,10720,10726,10732,10759,10765,10771,10800,10809,10815,10824,10830,10857,10861,10878,10891,10897,10908,10915,10921,10927,10933,10940,10948,10954,10960,10973,10985,10989,11184,11188,11199,11205,11209,11229,11231,11308,11312,11346,11348,11380],[11,10451,10447],{"id":10452},"_19-smart-pointers-memory-management",[20,10454,10455],{},"Smart pointers own data and provide extra behavior beyond references. They're the bridge between Rust's ownership model and dynamic data structures.",[15,10457,10459,10462],{"id":10458},"boxt-heap-allocation",[73,10460,10461],{},"Box\u003CT>"," — Heap Allocation",[111,10464,10467],{"className":10465,"code":10466,"language":397,"meta":117},[395],"let b = Box::new(5);\nlet s = Box::new(String::from(\"hi\"));\n",[73,10468,10466],{"__ignoreMap":117},[33,10470,10471,10474,10477],{},[36,10472,10473],{},"Allocates on the heap; owned.",[36,10475,10476],{},"Single owner; dropped when out of scope.",[36,10478,10479,10480,10482],{},"Sized: ",[73,10481,10461],{}," has the size of a pointer.",[130,10484,10486,10487],{"id":10485},"when-you-need-box","When You Need ",[73,10488,1200],{},[33,10490,10491,10494,10497,10503],{},[36,10492,10493],{},"Recursive types (linked structures need indirection to have a finite size).",[36,10495,10496],{},"Large data you don't want to copy on the stack.",[36,10498,10499,10500,10502],{},"Trait objects (",[73,10501,8373],{},") — unsized types need a wide pointer.",[36,10504,10505,10506,10509,10510,526],{},"Sending owned data to a thread (",[73,10507,10508],{},"Box::new"," makes it ",[73,10511,4560],{},[111,10513,10516],{"className":10514,"code":10515,"language":397,"meta":117},[395],"enum List {\n    Cons(i32, Box\u003CList>),   \u002F\u002F recursive — needs Box\n    Nil,\n}\n",[73,10517,10515],{"__ignoreMap":117},[130,10519,10521,10523],{"id":10520},"boxleak-permanent-reference",[73,10522,4824],{}," — Permanent Reference",[111,10525,10527],{"className":10526,"code":4828,"language":397,"meta":117},[395],[73,10528,4828],{"__ignoreMap":117},[20,10530,10531],{},"Leaks forever; useful for one-time configs but a real memory leak.",[15,10533,10535,10538],{"id":10534},"rct-reference-counted-single-threaded",[73,10536,10537],{},"Rc\u003CT>"," — Reference Counted (single-threaded)",[111,10540,10543],{"className":10541,"code":10542,"language":397,"meta":117},[395],"use std::rc::Rc;\nlet a = Rc::new(String::from(\"hi\"));\nlet b = Rc::clone(&a);    \u002F\u002F increments refcount, doesn't copy\nlet c = a.clone();         \u002F\u002F same\n\u002F\u002F a, b, c all share the same String\n",[73,10544,10542],{"__ignoreMap":117},[33,10546,10547,10553,10556,10567,10570],{},[36,10548,10549,10550,259],{},"Multiple owners in a ",[24,10551,10552],{},"single thread",[36,10554,10555],{},"Atomic increment\u002Fdecrement of a refcount.",[36,10557,10558,10559,1212,10561,10563,10564,526],{},"Not ",[73,10560,8563],{},[73,10562,8566],{}," (uses non-atomic counters; cheaper than ",[73,10565,10566],{},"Arc",[36,10568,10569],{},"When the count hits 0, the value is dropped.",[36,10571,1876,10572,10575,10576,10579],{},[73,10573,10574],{},"Rc::clone(&rc)"," (idiomatic) — don't use ",[73,10577,10578],{},"rc.clone()"," (looks like a deep clone).",[130,10581,10583,10585],{"id":10582},"rc-doesnt-allow-mutation",[73,10584,3803],{}," Doesn't Allow Mutation",[20,10587,10588,10590,10591,170],{},[73,10589,10537],{}," gives shared read access. To mutate shared state, wrap in ",[73,10592,5452],{},[111,10594,10597],{"className":10595,"code":10596,"language":397,"meta":117},[395],"let shared = Rc::new(RefCell::new(vec![1, 2, 3]));\nshared.borrow_mut().push(4);\n",[73,10598,10596],{"__ignoreMap":117},[130,10600,10602],{"id":10601},"weak-references","Weak References",[111,10604,10607],{"className":10605,"code":10606,"language":397,"meta":117},[395],"use std::rc::{Rc, Weak};\nlet strong = Rc::new(5);\nlet weak: Weak\u003Ci32> = Rc::downgrade(&strong);\nif let Some(v) = weak.upgrade() { \u002F* ... *\u002F }\n",[73,10608,10606],{"__ignoreMap":117},[20,10610,10611,10614],{},[73,10612,10613],{},"Weak"," doesn't count toward ownership; avoids cycles. Crucial for parent\u002Fchild links (e.g., GUI trees, linked structures).",[15,10616,10618,10621],{"id":10617},"arct-atomic-reference-counted-thread-safe",[73,10619,10620],{},"Arc\u003CT>"," — Atomic Reference Counted (thread-safe)",[111,10623,10626],{"className":10624,"code":10625,"language":397,"meta":117},[395],"use std::sync::Arc;\nlet a = Arc::new(vec![1, 2, 3]);\nlet b = Arc::clone(&a);\nstd::thread::spawn(move || println!(\"{:?}\", b));\n",[73,10627,10625],{"__ignoreMap":117},[33,10629,10630,10636,10646],{},[36,10631,10632,10633,10635],{},"Thread-safe version of ",[73,10634,3803],{}," (atomic ops, slower).",[36,10637,10638,27,10640,10642,10643,259],{},[73,10639,8563],{},[73,10641,8566],{}," if ",[73,10644,10645],{},"T: Send + Sync",[36,10647,10648],{},"Idiomatic for sharing across threads.",[130,10650,10652,10653,559,10655],{"id":10651},"when-rc-vs-arc","When ",[73,10654,3803],{},[73,10656,10566],{},[33,10658,10659,10665,10670],{},[36,10660,10661,10662,10664],{},"Single-threaded: ",[73,10663,3803],{}," (faster, simpler).",[36,10666,10667,10668,259],{},"Multi-threaded: ",[73,10669,10566],{},[36,10671,10672,10673,10675,10676,259],{},"Never use ",[73,10674,3803],{}," across threads — the compiler forbids it via ",[73,10677,8563],{},[15,10679,10681],{"id":10680},"cycles-and-memory-leaks","Cycles and Memory Leaks",[111,10683,10686],{"className":10684,"code":10685,"language":397,"meta":117},[395],"let a = Rc::new(RefCell::new(None));\nlet b = Rc::new(RefCell::new(None));\n*a.borrow_mut() = Some(Rc::clone(&b));\n*b.borrow_mut() = Some(Rc::clone(&a));    \u002F\u002F CYCLE: refcount never hits 0\n",[73,10687,10685],{"__ignoreMap":117},[20,10689,10690,1212,10692,10694,10695,10697],{},[73,10691,3803],{},[73,10693,10566],{}," cycles leak. Use ",[73,10696,10613],{}," for back-references. Rust can't prevent this; design matters.",[15,10699,10701],{"id":10700},"interior-mutability-pattern","Interior Mutability Pattern",[20,10703,10704,1212,10706,10708,10709,1212,10711,170],{},[73,10705,3803],{},[73,10707,10566],{}," give shared ownership but no mutation. Wrap the inner in ",[73,10710,5452],{},[73,10712,10713],{},"Mutex",[111,10715,10718],{"className":10716,"code":10717,"language":397,"meta":117},[395],"\u002F\u002F single-threaded\nlet shared = Rc::new(RefCell::new(0));\n*shared.borrow_mut() += 1;\n\n\u002F\u002F multi-threaded\nlet shared = Arc::new(Mutex::new(0));\n*shared.lock().unwrap() += 1;\n",[73,10719,10717],{"__ignoreMap":117},[15,10721,10723,10725],{"id":10722},"cellt-copy-type-interior-mutability",[73,10724,4712],{}," — Copy-Type Interior Mutability",[111,10727,10730],{"className":10728,"code":10729,"language":397,"meta":117},[395],"use std::cell::Cell;\nlet c = Cell::new(5);\nc.set(10);\nlet v = c.get();          \u002F\u002F requires T: Copy\n",[73,10731,10729],{"__ignoreMap":117},[33,10733,10734,10739,10742,10754],{},[36,10735,10736,10737,3125],{},"Zero-cost interior mutability for ",[73,10738,1795],{},[36,10740,10741],{},"No borrow checking (just stores the value).",[36,10743,10744,10745,10747,10748,1212,10751,526],{},"Cannot get a ",[73,10746,3130],{}," out (only ",[73,10749,10750],{},"get",[73,10752,10753],{},"set",[36,10755,10756,10757,3125],{},"Use for simple flags, counters, small ",[73,10758,1795],{},[15,10760,10762,10764],{"id":10761},"refcellt-borrow-checked-interior-mutability",[73,10763,4715],{}," — Borrow-Checked Interior Mutability",[111,10766,10769],{"className":10767,"code":10768,"language":397,"meta":117},[395],"use std::cell::RefCell;\nlet c = RefCell::new(vec![1, 2, 3]);\nc.borrow_mut().push(4);\nlet r = c.borrow();      \u002F\u002F immutable borrow\nprintln!(\"{:?}\", r);\n",[73,10770,10768],{"__ignoreMap":117},[33,10772,10773,10786,10794],{},[36,10774,10775,10776,71,10779,27,10782,10785],{},"Moves borrow checking to ",[24,10777,10778],{},"runtime",[73,10780,10781],{},"borrow()",[73,10783,10784],{},"borrow_mut()"," track active borrows.",[36,10787,756,10788,10790,10791,10793],{},[73,10789,10781],{}," OK; one ",[73,10792,10784],{}," exclusive.",[36,10795,10796,10799],{},[24,10797,10798],{},"Panics"," on borrow violation: \"already borrowed\" \u002F \"already mutably borrowed\".",[130,10801,10803,3818,10806],{"id":10802},"try_borrow-try_borrow_mut",[73,10804,10805],{},"try_borrow",[73,10807,10808],{},"try_borrow_mut",[20,10810,10811,10812,10814],{},"Non-panicking variants returning ",[73,10813,2792],{},". Useful when you might encounter a borrow conflict gracefully.",[15,10816,10818,27,10821],{"id":10817},"mutext-and-rwlockt",[73,10819,10820],{},"Mutex\u003CT>",[73,10822,10823],{},"RwLock\u003CT>",[111,10825,10828],{"className":10826,"code":10827,"language":397,"meta":117},[395],"use std::sync::Mutex;\nlet m = Mutex::new(0);\nlet guard = m.lock().unwrap();\n*guard += 1;\n\u002F\u002F guard drops here, unlocking\n\nuse std::sync::RwLock;\nlet rw = RwLock::new(0);\n{\n    let r1 = rw.read().unwrap();\n    let r2 = rw.read().unwrap();   \u002F\u002F multiple readers OK\n}\n{\n    let mut w = rw.write().unwrap();  \u002F\u002F exclusive writer\n    *w += 1;\n}\n",[73,10829,10827],{"__ignoreMap":117},[33,10831,10832,10837,10843,10851],{},[36,10833,10834,10836],{},[73,10835,10713],{},": one accessor at a time.",[36,10838,10839,10842],{},[73,10840,10841],{},"RwLock",": many readers or one writer.",[36,10844,10845,10846,10848,10849,259],{},"Locks return ",[73,10847,2792],{}," because a poisoned lock (holder panicked) returns ",[73,10850,2778],{},[36,10852,10853,10856],{},[73,10854,10855],{},"Lock"," guards auto-unlock on drop (RAII).",[130,10858,10860],{"id":10859},"poison","Poison",[20,10862,10863,10864,1538,10867,10869,10870,10873,10874,10877],{},"If a thread panics while holding a lock, the lock becomes \"poisoned\"; subsequent ",[73,10865,10866],{},".lock()",[73,10868,2778],{},". This signals possibly-corrupted state. Recover with ",[73,10871,10872],{},"into_inner()"," if you're sure, or use ",[73,10875,10876],{},"lock().unwrap()"," to propagate the panic.",[15,10879,10881,480,10884,480,10887,10890],{"id":10880},"once-oncelock-lazylock-initialization",[73,10882,10883],{},"Once",[73,10885,10886],{},"OnceLock",[73,10888,10889],{},"LazyLock"," — Initialization",[111,10892,10895],{"className":10893,"code":10894,"language":397,"meta":117},[395],"use std::sync::OnceLock;\nstatic CONFIG: OnceLock\u003CConfig> = OnceLock::new();\nlet c = CONFIG.get_or_init(|| Config::load());\n\n\u002F\u002F 1.80+: LazyLock\nuse std::sync::LazyLock;\nstatic DB: LazyLock\u003CDb> = LazyLock::new(|| Db::open());\nlet _ = &*DB;     \u002F\u002F initialized on first access\n",[73,10896,10894],{"__ignoreMap":117},[20,10898,10899,10900,10902,10903,1546,10905,10907],{},"Pre-",[73,10901,10889],{}," you'd use the ",[73,10904,1215],{},[73,10906,1211],{}," crates. Modern std has you covered.",[15,10909,10911,10914],{"id":10910},"cowt-clone-on-write",[73,10912,10913],{},"Cow\u003CT>"," — Clone-on-Write",[111,10916,10919],{"className":10917,"code":10918,"language":397,"meta":117},[395],"use std::borrow::Cow;\nfn greet(name: Cow\u003Cstr>) {\n    println!(\"{name}\");\n}\ngreet(\"literal\".into());           \u002F\u002F borrowed\ngreet(String::from(\"owned\").into()); \u002F\u002F owned\n",[73,10920,10918],{"__ignoreMap":117},[20,10922,10923,10926],{},[73,10924,10925],{},"Cow\u003C'a, B>"," is either borrowed or owned — lets you write APIs that accept either, deferring the clone until mutation.",[111,10928,10931],{"className":10929,"code":10930,"language":397,"meta":117},[395],"let mut c: Cow\u003Cstr> = Cow::Borrowed(\"hi\");\nc.to_mut().push('!');     \u002F\u002F clones once, now owned\n",[73,10932,10930],{"__ignoreMap":117},[15,10934,10936,10939],{"id":10935},"pint-pinned-pointers",[73,10937,10938],{},"Pin\u003CT>"," — Pinned Pointers",[20,10941,10942,10944,10945,10947],{},[73,10943,8579],{}," guarantees a value won't be moved in memory after pinning. Essential for self-referential data (e.g., async futures holding references across ",[73,10946,5022],{}," points):",[111,10949,10952],{"className":10950,"code":10951,"language":397,"meta":117},[395],"let mut fut = async { 5 };\nlet pinned = Pin::new(&mut fut);\n",[73,10953,10951],{"__ignoreMap":117},[20,10955,10956,10957,10959],{},"You usually don't write ",[73,10958,8579],{}," by hand — async\u002Fawait generates it. The Pin chapter (Async) covers the details.",[15,10961,10963,480,10966,480,10969,10972],{"id":10962},"nonnullt-mut-t-const-t-unsafe",[73,10964,10965],{},"NonNull\u003CT>",[73,10967,10968],{},"*mut T",[73,10970,10971],{},"*const T"," (Unsafe)",[20,10974,10975,10976,10978,10979,10981,10982,10984],{},"Raw pointers, no automatic lifetime tracking; only usable in ",[73,10977,197],{}," blocks. ",[73,10980,10965],{}," is non-null ",[73,10983,10968],{}," and is covariant. Used in collections\u002FFFI. See Unsafe chapter.",[15,10986,10988],{"id":10987},"smart-pointer-cheat-sheet","Smart Pointer Cheat Sheet",[917,10990,10991,11007],{},[920,10992,10993],{},[923,10994,10995,10997,10999,11002,11005],{},[926,10996,1340],{},[926,10998,3391],{},[926,11000,11001],{},"Mutability",[926,11003,11004],{},"Thread-safe",[926,11006,7371],{},[936,11008,11009,11032,11052,11072,11092,11108,11124,11140,11162],{},[923,11010,11011,11015,11018,11023,11029],{},[941,11012,11013],{},[73,11014,10461],{},[941,11016,11017],{},"Single",[941,11019,11020,11021,1587],{},"direct (",[73,11022,885],{},[941,11024,11025,11026],{},"if ",[73,11027,11028],{},"T: Send",[941,11030,11031],{},"Heap, recursion",[923,11033,11034,11038,11041,11046,11049],{},[941,11035,11036],{},[73,11037,10537],{},[941,11039,11040],{},"Shared",[941,11042,11043,11044],{},"via ",[73,11045,5452],{},[941,11047,11048],{},"NO",[941,11050,11051],{},"Graphs, trees",[923,11053,11054,11058,11060,11066,11069],{},[941,11055,11056],{},[73,11057,10620],{},[941,11059,11040],{},[941,11061,11043,11062,1212,11064],{},[73,11063,10713],{},[73,11065,10841],{},[941,11067,11068],{},"YES",[941,11070,11071],{},"Cross-thread share",[923,11073,11074,11078,11080,11085,11087],{},[941,11075,11076],{},[73,11077,4712],{},[941,11079,11017],{},[941,11081,11082],{},[73,11083,11084],{},"set\u002Fget",[941,11086,11048],{},[941,11088,11089,11091],{},[73,11090,1795],{}," flags",[923,11093,11094,11098,11100,11103,11105],{},[941,11095,11096],{},[73,11097,4715],{},[941,11099,11017],{},[941,11101,11102],{},"runtime borrow",[941,11104,11048],{},[941,11106,11107],{},"Single-thread mut share",[923,11109,11110,11114,11116,11119,11121],{},[941,11111,11112],{},[73,11113,10820],{},[941,11115,11017],{},[941,11117,11118],{},"lock",[941,11120,11068],{},[941,11122,11123],{},"Cross-thread mut share",[923,11125,11126,11130,11132,11135,11137],{},[941,11127,11128],{},[73,11129,10823],{},[941,11131,11017],{},[941,11133,11134],{},"read\u002Fwrite lock",[941,11136,11068],{},[941,11138,11139],{},"Read-heavy share",[923,11141,11142,11146,11149,11154,11159],{},[941,11143,11144],{},[73,11145,10925],{},[941,11147,11148],{},"Either",[941,11150,11151],{},[73,11152,11153],{},"to_mut",[941,11155,11025,11156],{},[73,11157,11158],{},"B: Send",[941,11160,11161],{},"Borrowed-or-owned",[923,11163,11164,11169,11172,11176,11181],{},[941,11165,11166],{},[73,11167,11168],{},"Pin\u003CP>",[941,11170,11171],{},"(wrapper)",[941,11173,11043,11174],{},[73,11175,8699],{},[941,11177,11025,11178],{},[73,11179,11180],{},"P: Send",[941,11182,11183],{},"Self-referential",[15,11185,11187],{"id":11186},"deref-and-derefmut","Deref and DerefMut",[20,11189,11190,11191,1212,11193,11195,11196,11198],{},"Smart pointers implement ",[73,11192,3782],{},[73,11194,8699],{}," to enable ",[73,11197,2710],{},"-coercions and method forwarding:",[111,11200,11203],{"className":11201,"code":11202,"language":397,"meta":117},[395],"let b = Box::new(String::from(\"hi\"));\nb.push('!');              \u002F\u002F Box\u003CString> derefs to String, which derefs to str\nlet s: &str = &b;          \u002F\u002F &Box\u003CString> -> &String -> &str\n",[73,11204,11202],{"__ignoreMap":117},[15,11206,11208],{"id":11207},"drop-order-for-smart-pointers","Drop Order for Smart Pointers",[33,11210,11211,11220],{},[36,11212,11213,1212,11215,1212,11217,11219],{},[73,11214,1200],{},[73,11216,3803],{},[73,11218,10566],{}," drop their contents when refcount hits 0.",[36,11221,11222,1212,11225,11228],{},[73,11223,11224],{},"MutexGuard",[73,11226,11227],{},"RwLockReadGuard"," release the lock on drop — keep guards short-scoped.",[15,11230,6469],{"id":6468},[33,11232,11233,11244,11255,11261,11267,11281,11291,11300],{},[36,11234,11235,71,11240,11243],{},[24,11236,11237,11239],{},[73,11238,3803],{}," across threads",[73,11241,11242],{},"Rc: !Send",", compile error.",[36,11245,11246,11254],{},[24,11247,11248,559,11251],{},[73,11249,11250],{},"Arc\u003CMutex\u003CT>>",[73,11252,11253],{},"Mutex\u003CArc\u003CT>>",": the former mutates shared data; the latter replaces the entire shared pointer atomically.",[36,11256,11257,11260],{},[24,11258,11259],{},"Lock granularity",": too coarse = contention; too fine = deadlocks.",[36,11262,11263,11266],{},[24,11264,11265],{},"Deadlock",": lock ordering must be consistent across threads. Acquire locks in a fixed order.",[36,11268,11269,11277,11278,11280],{},[24,11270,11271,559,11274],{},[73,11272,11273],{},"Rc::clone",[73,11275,11276],{},"Clone::clone",": same; ",[73,11279,10574],{}," makes it obvious it's cheap.",[36,11282,11283,11290],{},[24,11284,11285,1538,11288],{},[73,11286,11287],{},"Weak::upgrade",[73,11289,1481],{},": handle the case where the value was dropped.",[36,11292,11293,11299],{},[24,11294,11295,11298],{},[73,11296,11297],{},"RefCell::borrow_mut"," panic",": can happen in complex call graphs; structure borrows to release before re-borrowing.",[36,11301,11302,11307],{},[24,11303,11304],{},[73,11305,11306],{},"Mutex::lock().unwrap()",": panics on poison. Consider graceful recovery.",[15,11309,11311],{"id":11310},"memory-layout-of-smart-pointers","Memory Layout of Smart Pointers",[33,11313,11314,11319,11329,11339],{},[36,11315,11316,11318],{},[73,11317,10461],{},": a single pointer.",[36,11320,11321,1212,11323,11325,11326,9053],{},[73,11322,10537],{},[73,11324,10620],{},": pointer to a heap-allocated ",[73,11327,11328],{},"{ strong_count, weak_count, value }",[36,11330,11331,1212,11333,11335,11336,11338],{},[73,11332,4712],{},[73,11334,4715],{},": in-place storage; ",[73,11337,5452],{}," adds a borrow-state field.",[36,11340,11341,1212,11343,11345],{},[73,11342,10820],{},[73,11344,10823],{},": in-place storage + OS synchronization primitives.",[15,11347,349],{"id":348},[20,11349,11350,11352,11353,1212,11355,11357,11358,1212,11360,1212,11362,1212,11364,11366,11367,11370,11371,11373,11374,11376,11377,11379],{},[73,11351,1200],{}," = single-owner heap. ",[73,11354,3803],{},[73,11356,10566],{}," = shared ownership. ",[73,11359,5449],{},[73,11361,5452],{},[73,11363,10713],{},[73,11365,10841],{}," = interior mutability. ",[73,11368,11369],{},"Cow"," = borrowed-or-owned. ",[73,11372,8579],{}," = no-move guarantee for async. ",[73,11375,10613],{}," avoids cycles. Memory leaks via reference cycles are possible in safe Rust — design with ",[73,11378,10613],{}," back-references.",[20,11381,11382],{},"Next: Modules and crates — organizing code.",{"title":117,"searchDepth":357,"depth":357,"links":11384},[11385,11392,11398,11403,11404,11405,11407,11412,11416,11418,11420,11422,11424,11425,11426,11427,11428,11429],{"id":10458,"depth":357,"text":11386,"children":11387},"Box\u003CT> — Heap Allocation",[11388,11390],{"id":10485,"depth":364,"text":11389},"When You Need Box",{"id":10520,"depth":364,"text":11391},"Box::leak — Permanent Reference",{"id":10534,"depth":357,"text":11393,"children":11394},"Rc\u003CT> — Reference Counted (single-threaded)",[11395,11397],{"id":10582,"depth":364,"text":11396},"Rc Doesn't Allow Mutation",{"id":10601,"depth":364,"text":10602},{"id":10617,"depth":357,"text":11399,"children":11400},"Arc\u003CT> — Atomic Reference Counted (thread-safe)",[11401],{"id":10651,"depth":364,"text":11402},"When Rc vs Arc",{"id":10680,"depth":357,"text":10681},{"id":10700,"depth":357,"text":10701},{"id":10722,"depth":357,"text":11406},"Cell\u003CT> — Copy-Type Interior Mutability",{"id":10761,"depth":357,"text":11408,"children":11409},"RefCell\u003CT> — Borrow-Checked Interior Mutability",[11410],{"id":10802,"depth":364,"text":11411},"try_borrow \u002F try_borrow_mut",{"id":10817,"depth":357,"text":11413,"children":11414},"Mutex\u003CT> and RwLock\u003CT>",[11415],{"id":10859,"depth":364,"text":10860},{"id":10880,"depth":357,"text":11417},"Once, OnceLock, LazyLock — Initialization",{"id":10910,"depth":357,"text":11419},"Cow\u003CT> — Clone-on-Write",{"id":10935,"depth":357,"text":11421},"Pin\u003CT> — Pinned Pointers",{"id":10962,"depth":357,"text":11423},"NonNull\u003CT>, *mut T, *const T (Unsafe)",{"id":10987,"depth":357,"text":10988},{"id":11186,"depth":357,"text":11187},{"id":11207,"depth":357,"text":11208},{"id":6468,"depth":357,"text":6469},{"id":11310,"depth":357,"text":11311},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F19-smart-pointers",{"title":10447,"description":10455},"rust\u002F19-smart-pointers","5ZiPjJZsDtMZySrb6JNF_tfgkQNCBYOm_qcWhlutFLY",{"id":11436,"title":11437,"body":11438,"description":11445,"extension":373,"meta":12009,"navigation":375,"path":12010,"seo":12011,"stem":12012,"__hash__":12013},"content\u002Frust\u002F20-modules-and-crates.md","20 — Modules, Crates, Packages & Paths",{"type":8,"value":11439,"toc":11978},[11440,11443,11446,11450,11488,11492,11498,11511,11515,11521,11530,11537,11543,11547,11553,11556,11563,11569,11572,11584,11604,11610,11614,11620,11623,11629,11632,11636,11642,11646,11673,11677,11683,11692,11700,11704,11710,11716,11720,11726,11732,11742,11744,11750,11762,11766,11775,11781,11788,11792,11796,11802,11806,11812,11816,11822,11829,11835,11837,11922,11926,11954,11956,11975],[11,11441,11437],{"id":11442},"_20-modules-crates-packages-paths",[20,11444,11445],{},"Rust's module system controls visibility, organization, and namespacing.",[15,11447,11449],{"id":11448},"definitions","Definitions",[33,11451,11452,11461,11473,11479],{},[36,11453,11454,11457,11458,11460],{},[24,11455,11456],{},"Package",": a Cargo project (a ",[73,11459,169],{}," + one or more crates).",[36,11462,11463,11466,11467,1212,11470,526],{},[24,11464,11465],{},"Crate",": a compilation unit (binary or library). Root file (",[73,11468,11469],{},"main.rs",[73,11471,11472],{},"lib.rs",[36,11474,11475,11478],{},[24,11476,11477],{},"Module",": a named scope inside a crate. Controls visibility.",[36,11480,11481,11484,11485,526],{},[24,11482,11483],{},"Path",": how you reference an item (",[73,11486,11487],{},"crate::foo::bar",[15,11489,11491],{"id":11490},"module-declaration","Module Declaration",[111,11493,11496],{"className":11494,"code":11495,"language":397,"meta":117},[395],"\u002F\u002F src\u002Flib.rs\nmod network;\nmod ui {\n    pub mod window;\n    pub mod button;\n}\n",[73,11497,11495],{"__ignoreMap":117},[20,11499,11500,11503,11504,1546,11507,11510],{},[73,11501,11502],{},"mod network;"," looks for ",[73,11505,11506],{},"network.rs",[73,11508,11509],{},"network\u002Fmod.rs"," (legacy) and includes it as a submodule.",[15,11512,11514],{"id":11513},"file-layout-conventions-2018","File Layout Conventions (2018+)",[111,11516,11519],{"className":11517,"code":11518,"language":212},[210],"src\u002F\n├── lib.rs            \u002F\u002F crate root: `pub mod ...`\n├── main.rs\n├── network.rs        \u002F\u002F corresponds to `mod network;`\n└── network\u002F\n    └── server.rs     \u002F\u002F corresponds to `mod server;` *inside* network.rs\n",[73,11520,11518],{"__ignoreMap":117},[20,11522,11523,11524,11526,11527,11529],{},"The 2018 edition prefers ",[73,11525,11506],{}," over ",[73,11528,11509],{},". Don't mix the two for the same module.",[15,11531,11533,11536],{"id":11532},"use-importing",[73,11534,11535],{},"use"," — Importing",[111,11538,11541],{"className":11539,"code":11540,"language":397,"meta":117},[395],"use std::collections::HashMap;\nuse std::io::{self, Read, Write};   \u002F\u002F bring multiple items\nuse std::io::Read as IoRead;        \u002F\u002F alias\nuse crate::network::server;          \u002F\u002F absolute path from crate root\nuse super::sibling;                  \u002F\u002F one module up\nuse self::inner;                     \u002F\u002F current module\n",[73,11542,11540],{"__ignoreMap":117},[130,11544,11546],{"id":11545},"glob-imports","Glob Imports",[111,11548,11551],{"className":11549,"code":11550,"language":397,"meta":117},[395],"use std::io::prelude::*;            \u002F\u002F rare; usually too broad\nuse crate::network::*;               \u002F\u002F bring all public items\n",[73,11552,11550],{"__ignoreMap":117},[20,11554,11555],{},"Avoid glob imports except for preludes.",[15,11557,11559,11562],{"id":11558},"pub-use-re-exports",[73,11560,11561],{},"pub use"," — Re-exports",[111,11564,11567],{"className":11565,"code":11566,"language":397,"meta":117},[395],"\u002F\u002F lib.rs\npub mod api;\npub use api::Client;   \u002F\u002F re-export so users can `use my_crate::Client`\n",[73,11568,11566],{"__ignoreMap":117},[20,11570,11571],{},"Re-export is the standard way to flatten the public API and hide internal structure.",[15,11573,11575,11576,480,11579,480,11581],{"id":11574},"paths-and-crate-self-super","Paths and ",[73,11577,11578],{},"crate",[73,11580,2245],{},[73,11582,11583],{},"super",[33,11585,11586,11592,11598],{},[36,11587,11588,11591],{},[73,11589,11590],{},"crate::"," — absolute from crate root.",[36,11593,11594,11597],{},[73,11595,11596],{},"self::"," — current module.",[36,11599,11600,11603],{},[73,11601,11602],{},"super::"," — parent module.",[111,11605,11608],{"className":11606,"code":11607,"language":397,"meta":117},[395],"\u002F\u002F in src\u002Fnetwork\u002Fserver.rs\nuse super::connection;     \u002F\u002F src\u002Fnetwork\u002Fconnection.rs\nuse crate::network::connection;   \u002F\u002F same, explicit\n",[73,11609,11607],{"__ignoreMap":117},[15,11611,11613],{"id":11612},"visibility","Visibility",[111,11615,11618],{"className":11616,"code":11617,"language":397,"meta":117},[395],"pub fn public_fn() {}            \u002F\u002F visible everywhere\nfn private_fn() {}                \u002F\u002F visible only in this module\npub(crate) fn internal() {}       \u002F\u002F visible within this crate only\npub(super) fn for_parent() {}     \u002F\u002F visible in parent module\npub(in path) fn scoped() {}       \u002F\u002F visible in a specific module path\n",[73,11619,11617],{"__ignoreMap":117},[20,11621,11622],{},"Fields and variants have their own visibility:",[111,11624,11627],{"className":11625,"code":11626,"language":397,"meta":117},[395],"pub struct User {\n    pub name: String,\n    email: String,       \u002F\u002F private — only this module can construct\u002Fmodify\n}\n",[73,11628,11626],{"__ignoreMap":117},[20,11630,11631],{},"Enums' variants inherit the enum's visibility by default; you can override per-variant.",[15,11633,11635],{"id":11634},"struct-visibility","Struct Visibility",[20,11637,11638,11639,11641],{},"A struct can be ",[73,11640,5468],{}," but have private fields — external code can't construct it with literal syntax or access private fields, but can use it via methods. This is how newtypes preserve invariants.",[15,11643,11645],{"id":11644},"module-path-items","Module Path Items",[33,11647,11648,11651,11654,11657,11660,11665],{},[36,11649,11650],{},"Modules",[36,11652,11653],{},"Functions",[36,11655,11656],{},"Structs\u002FEnums\u002FTypes",[36,11658,11659],{},"Constants\u002FStatics",[36,11661,11662,11664],{},[73,11663,11535],{}," statements",[36,11666,11667,11668,27,11671,1587],{},"Macros (via ",[73,11669,11670],{},"macro_rules!",[73,11672,11561],{},[15,11674,11676],{"id":11675},"submodules-and-privacy","Submodules and Privacy",[20,11678,11679,11680,11682],{},"A child module can access anything in its parent (privacy is per-module-tree, with ",[73,11681,5468],{}," opening it up). Children can use private items of parents and ancestors.",[15,11684,11686,11688,11689],{"id":11685},"pub-items-and-dochidden",[73,11687,5468],{}," Items and ",[73,11690,11691],{},"#[doc(hidden)]",[20,11693,11694,11696,11697,11699],{},[73,11695,11691],{}," hides an item from docs while keeping it ",[73,11698,5468],{}," (used for internal macros or re-exports you don't want users to call directly).",[15,11701,11703],{"id":11702},"crates-within-a-package","Crates Within a Package",[111,11705,11708],{"className":11706,"code":11707,"language":176,"meta":117},[174],"# Cargo.toml\n[lib]\nname = \"my_lib\"\npath = \"src\u002Flib.rs\"\n\n[[bin]]\nname = \"my_app\"\npath = \"src\u002Fmain.rs\"\n",[73,11709,11707],{"__ignoreMap":117},[20,11711,11712,11713,259],{},"A package can have many binaries and at most one library. Binaries can use the library via ",[73,11714,11715],{},"use my_lib::...",[15,11717,11719],{"id":11718},"external-crates","External Crates",[111,11721,11724],{"className":11722,"code":11723,"language":176,"meta":117},[174],"# Cargo.toml\n[dependencies]\nserde = \"1.0\"\n",[73,11725,11723],{"__ignoreMap":117},[111,11727,11730],{"className":11728,"code":11729,"language":397,"meta":117},[395],"use serde::Serialize;     \u002F\u002F external crates are in the extern prelude\n",[73,11731,11729],{"__ignoreMap":117},[20,11733,11734,11735,11738,11739,11741],{},"In edition 2018+, you don't need ",[73,11736,11737],{},"extern crate serde;"," — ",[73,11740,11535],{}," finds it.",[15,11743,688],{"id":687},[111,11745,11748],{"className":11746,"code":11747,"language":176,"meta":117},[174],"# Cargo.toml\n[workspace]\nmembers = [\"crates\u002Fapi\", \"crates\u002Fcli\", \"crates\u002Fcore\"]\n",[73,11749,11747],{"__ignoreMap":117},[20,11751,11752,11753,11756,11757,27,11759,11761],{},"Members can depend on each other via ",[73,11754,11755],{},"path = \"..\u002Fcore\"",". Shared ",[73,11758,245],{},[73,11760,694],{}," directory.",[15,11763,11765],{"id":11764},"macros-across-modules","Macros Across Modules",[20,11767,11768,11770,11771,11774],{},[73,11769,11670],{}," macros need ",[73,11772,11773],{},"#[macro_export]"," to be used outside their defining module:",[111,11776,11779],{"className":11777,"code":11778,"language":397,"meta":117},[395],"#[macro_export]\nmacro_rules! my_macro { \u002F* ... *\u002F }\n",[73,11780,11778],{"__ignoreMap":117},[20,11782,11783,11784,11787],{},"They're exported at the crate root. Use ",[73,11785,11786],{},"pub use my_macro;"," to re-export.",[15,11789,11791],{"id":11790},"module-organization-patterns","Module Organization Patterns",[130,11793,11795],{"id":11794},"library-binaries","Library + Binaries",[111,11797,11800],{"className":11798,"code":11799,"language":212},[210],"my_project\u002F\n├── Cargo.toml\n├── src\u002F\n│   ├── lib.rs       # public API\n│   └── bin\u002F\n│       ├── server.rs\n│       └── client.rs\n",[73,11801,11799],{"__ignoreMap":117},[130,11803,11805],{"id":11804},"feature-gated-modules","Feature-Gated Modules",[111,11807,11810],{"className":11808,"code":11809,"language":397,"meta":117},[395],"#[cfg(feature = \"json\")]\npub mod json;\n",[73,11811,11809],{"__ignoreMap":117},[130,11813,11815],{"id":11814},"tests-inline-and-separate","Tests Inline and Separate",[111,11817,11820],{"className":11818,"code":11819,"language":397,"meta":117},[395],"\u002F\u002F src\u002Flib.rs\npub fn add(a: i32, b: i32) -> i32 { a + b }\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    #[test]\n    fn test_add() { assert_eq!(add(1, 2), 3); }\n}\n",[73,11821,11819],{"__ignoreMap":117},[20,11823,11824,11825,11828],{},"Integration tests live in ",[73,11826,11827],{},"tests\u002F"," as separate crate:",[111,11830,11833],{"className":11831,"code":11832,"language":212},[210],"tests\u002F\n└── integration.rs\n",[73,11834,11832],{"__ignoreMap":117},[15,11836,711],{"id":710},[33,11838,11839,11854,11861,11873,11880,11893,11901,11910],{},[36,11840,11841,6840,11846,11849,11850,11853],{},[24,11842,11843,11845],{},[73,11844,5468],{}," doesn't propagate to ancestors",[73,11847,11848],{},"pub mod"," is public ",[183,11851,11852],{},"if its parent is also accessible",". Privacy is layered.",[36,11855,11856,11860],{},[24,11857,11858,7966],{},[73,11859,11561],{},": re-exporting two items with the same name into the same scope is an error.",[36,11862,11863,71,11866,27,11869,11872],{},[24,11864,11865],{},"Module path and item name conflicts",[73,11867,11868],{},"mod foo;",[73,11870,11871],{},"use crate::foo;"," are different things.",[36,11874,11875,11879],{},[24,11876,11877],{},[73,11878,2890],{}," prevents exhaustive construction outside the crate.",[36,11881,11882,11888,11889,11892],{},[24,11883,11884,11885,11887],{},"Private items in ",[73,11886,5468],{}," functions",": a public function can't have private types in its signature (e.g., ",[73,11890,11891],{},"pub fn get() -> PrivateType"," is an error — leaks private type).",[36,11894,11895,11900],{},[24,11896,11897],{},[73,11898,11899],{},"extern crate self as foo;",": lets you refer to your own crate by name (rare).",[36,11902,11903,11909],{},[24,11904,11905,11906],{},"Hidden ",[73,11907,11908],{},"mod.rs",": still works but is discouraged; the new layout is cleaner.",[36,11911,11912,11917,11918,11921],{},[24,11913,11914,11916],{},[73,11915,11561],{}," for preludes",": many crates expose ",[73,11919,11920],{},"pub mod prelude { pub use ...; }"," for one-line imports.",[15,11923,11925],{"id":11924},"best-practices","Best Practices",[33,11927,11928,11931,11934,11939,11945,11951],{},[36,11929,11930],{},"One responsibility per module.",[36,11932,11933],{},"Hide internals; expose minimal API.",[36,11935,1876,11936,11938],{},[73,11937,11561],{}," to flatten the surface.",[36,11940,11941,11942,526],{},"Test files live alongside source (",[73,11943,11944],{},"#[cfg(test)] mod tests",[36,11946,11947,11948,526],{},"Re-export crates you wrap so users don't need direct deps (",[73,11949,11950],{},"pub use serde;",[36,11952,11953],{},"Don't go too deep — 3 levels is usually enough.",[15,11955,349],{"id":348},[20,11957,11958,11959,11961,11962,11964,11965,1212,11967,1212,11969,11971,11972,11974],{},"Modules organize code; ",[73,11960,5468],{}," controls visibility; ",[73,11963,11535],{}," brings items into scope; ",[73,11966,11578],{},[73,11968,11583],{},[73,11970,2245],{}," form absolute\u002Frelative paths; ",[73,11973,11561],{}," re-exports flatten APIs. Files and modules are connected but distinct — the 2018 edition simplified the file\u002Fmodule mapping.",[20,11976,11977],{},"Next: Cargo features, build scripts, and release engineering.",{"title":117,"searchDepth":357,"depth":357,"links":11979},[11980,11981,11982,11983,11987,11989,11991,11992,11993,11994,11995,11997,11998,11999,12000,12001,12006,12007,12008],{"id":11448,"depth":357,"text":11449},{"id":11490,"depth":357,"text":11491},{"id":11513,"depth":357,"text":11514},{"id":11532,"depth":357,"text":11984,"children":11985},"use — Importing",[11986],{"id":11545,"depth":364,"text":11546},{"id":11558,"depth":357,"text":11988},"pub use — Re-exports",{"id":11574,"depth":357,"text":11990},"Paths and crate, self, super",{"id":11612,"depth":357,"text":11613},{"id":11634,"depth":357,"text":11635},{"id":11644,"depth":357,"text":11645},{"id":11675,"depth":357,"text":11676},{"id":11685,"depth":357,"text":11996},"pub Items and #[doc(hidden)]",{"id":11702,"depth":357,"text":11703},{"id":11718,"depth":357,"text":11719},{"id":687,"depth":357,"text":688},{"id":11764,"depth":357,"text":11765},{"id":11790,"depth":357,"text":11791,"children":12002},[12003,12004,12005],{"id":11794,"depth":364,"text":11795},{"id":11804,"depth":364,"text":11805},{"id":11814,"depth":364,"text":11815},{"id":710,"depth":357,"text":711},{"id":11924,"depth":357,"text":11925},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F20-modules-and-crates",{"title":11437,"description":11445},"rust\u002F20-modules-and-crates","3n6C6ryI6hx1vWvPJ6ga0coTN4w7TQq70J9bCSw7dC0",{"id":12015,"title":12016,"body":12017,"description":12586,"extension":373,"meta":12587,"navigation":375,"path":12588,"seo":12589,"stem":12590,"__hash__":12591},"content\u002Frust\u002F21-testing.md","21 — Testing in Rust",{"type":8,"value":12018,"toc":12561},[12019,12022,12028,12032,12062,12066,12072,12105,12109,12115,12119,12125,12131,12145,12149,12155,12162,12166,12172,12189,12195,12200,12204,12210,12228,12232,12238,12255,12261,12267,12272,12278,12285,12289,12295,12304,12308,12314,12320,12324,12332,12338,12342,12347,12353,12357,12377,12379,12472,12476,12482,12489,12493,12499,12505,12515,12523,12525,12558],[11,12020,12016],{"id":12021},"_21-testing-in-rust",[20,12023,12024,12025,12027],{},"Rust's testing is built into the language and ",[73,12026,75],{},". There are three layers: unit tests, integration tests, and documentation tests.",[15,12029,12031],{"id":12030},"test-categories","Test Categories",[3037,12033,12034,12043,12052],{},[36,12035,12036,12039,12040,12042],{},[24,12037,12038],{},"Unit tests",": live inside the source, in ",[73,12041,11944],{},". Test private items.",[36,12044,12045,12048,12049,12051],{},[24,12046,12047],{},"Integration tests",": live in ",[73,12050,11827],{}," as separate crates. Test public API.",[36,12053,12054,12057,12058,12061],{},[24,12055,12056],{},"Doc tests",": code in ",[73,12059,12060],{},"\u002F\u002F\u002F"," doc comments, run as examples.",[15,12063,12065],{"id":12064},"writing-a-unit-test","Writing a Unit Test",[111,12067,12070],{"className":12068,"code":12069,"language":397,"meta":117},[395],"pub fn add(a: i32, b: i32) -> i32 { a + b }\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn adds_two() {\n        assert_eq!(add(2, 2), 4);\n    }\n\n    #[test]\n    #[should_panic(expected = \"negative\")]\n    fn rejects_negative() {\n        panic!(\"negative not allowed\");\n    }\n\n    #[test]\n    fn returns_result() -> Result\u003C(), String> {\n        if add(2, 2) == 4 { Ok(()) } else { Err(String::from(\"bad\")) }\n    }\n}\n",[73,12071,12069],{"__ignoreMap":117},[33,12073,12074,12080,12090,12096],{},[36,12075,12076,12079],{},[73,12077,12078],{},"#[test]"," marks a test function.",[36,12081,12082,480,12084,480,12087,259],{},[73,12083,10361],{},[73,12085,12086],{},"assert_eq!",[73,12088,12089],{},"assert_ne!",[36,12091,12092,12095],{},[73,12093,12094],{},"#[should_panic(expected = \"...\")]"," for panic tests.",[36,12097,10022,12098,12101,12102,12104],{},[73,12099,12100],{},"Result\u003C(), E: Debug>"," is allowed — ",[73,12103,2778],{}," fails the test.",[15,12106,12108],{"id":12107},"running-tests","Running Tests",[111,12110,12113],{"className":12111,"code":12112,"language":116,"meta":117},[114],"cargo test                 # all tests\ncargo test add             # filter by name substring\ncargo test --lib           # only lib unit tests\ncargo test --test integration   # only specific integration test\ncargo test -- --nocapture  # show println! output\ncargo test -- --test-threads=4\ncargo test --release       # tests in release mode\ncargo test -- --ignored    # run #[ignore] tests\n",[73,12114,12112],{"__ignoreMap":117},[15,12116,12118],{"id":12117},"integration-tests","Integration Tests",[111,12120,12123],{"className":12121,"code":12122,"language":212},[210],"tests\u002F\n└── integration_test.rs\n",[73,12124,12122],{"__ignoreMap":117},[111,12126,12129],{"className":12127,"code":12128,"language":397,"meta":117},[395],"use my_crate::add;\n\n#[test]\nfn integration_add() {\n    assert_eq!(add(2, 3), 5);\n}\n",[73,12130,12128],{"__ignoreMap":117},[33,12132,12133,12139],{},[36,12134,12135,12136,12138],{},"Separate crate; can only use ",[73,12137,5468],{}," API.",[36,12140,12141,12142,12144],{},"Multiple files in ",[73,12143,11827],{}," become separate test binaries.",[130,12146,12148],{"id":12147},"common-setup-module","Common Setup Module",[111,12150,12153],{"className":12151,"code":12152,"language":212},[210],"tests\u002F\n├── common\u002F\n│   └── mod.rs          # NOT a test file\n└── integration_test.rs   # use mod common;\n",[73,12154,12152],{"__ignoreMap":117},[20,12156,12157,12158,12161],{},"Files in ",[73,12159,12160],{},"tests\u002Fcommon\u002F"," (without a top-level test fn) are helpers, not test binaries.",[15,12163,12165],{"id":12164},"doc-tests","Doc Tests",[111,12167,12170],{"className":12168,"code":12169,"language":397,"meta":117},[395],"\u002F\u002F\u002F Adds two numbers.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Examples\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F use my_crate::add;\n\u002F\u002F\u002F assert_eq!(add(2, 2), 4);\n\u002F\u002F\u002F ```\npub fn add(a: i32, b: i32) -> i32 { a + b }\n",[73,12171,12169],{"__ignoreMap":117},[33,12173,12174,12180,12183],{},[36,12175,12176,12177,259],{},"Compiled and run by ",[73,12178,12179],{},"cargo test",[36,12181,12182],{},"Must compile as a separate binary.",[36,12184,12185,12186,170],{},"Hidden imports via ",[73,12187,12188],{},"#",[111,12190,12193],{"className":12191,"code":12192,"language":397,"meta":117},[395],"\u002F\u002F\u002F ```\n\u002F\u002F\u002F # use my_crate::add;\n\u002F\u002F\u002F assert_eq!(add(2, 2), 4);\n\u002F\u002F\u002F ```\n",[73,12194,12192],{"__ignoreMap":117},[20,12196,3106,12197,12199],{},[73,12198,12188],{}," line is hidden from rendered docs but included when testing.",[130,12201,12203],{"id":12202},"skipping-doc-tests","Skipping Doc Tests",[111,12205,12208],{"className":12206,"code":12207,"language":212,"meta":117},[210],"\u002F\u002F\u002F ```no_run\n\u002F\u002F\u002F loop { \u002F* don't actually run *\u002F }\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```ignore\n\u002F\u002F\u002F let x = todo!();\n\u002F\u002F\u002F ```\n",[73,12209,12207],{"__ignoreMap":117},[20,12211,12212,12215,12216,12219,12220,12223,12224,12227],{},[73,12213,12214],{},"no_run"," compiles but doesn't execute. ",[73,12217,12218],{},"ignore"," skips compilation. ",[73,12221,12222],{},"compile_fail"," asserts the snippet fails to compile (negative tests). ",[73,12225,12226],{},"rust,no_run"," etc. customize.",[15,12229,12231],{"id":12230},"assertions-cheat-sheet","Assertions Cheat Sheet",[111,12233,12236],{"className":12234,"code":12235,"language":397,"meta":117},[395],"assert!(cond);\nassert!(cond, \"custom message {x}\");\nassert_eq!(a, b);\nassert_eq!(a, b, \"msg\");\nassert_ne!(a, b);\ndebug_assert!(cond);          \u002F\u002F only in debug builds\ndebug_assert_eq!(a, b);\n",[73,12237,12235],{"__ignoreMap":117},[20,12239,5146,12240,12242,12243,12246,12247,1212,12250,2022,12252,12254],{},[73,12241,2792],{},"-returning assertions, use the ",[73,12244,12245],{},"matches"," style or ",[73,12248,12249],{},".unwrap()",[73,12251,2404],{},[73,12253,2792],{},"-returning tests.",[15,12256,12258],{"id":12257},"should_panic",[73,12259,12260],{},"#[should_panic]",[111,12262,12265],{"className":12263,"code":12264,"language":397,"meta":117},[395],"#[test]\n#[should_panic]\nfn panics() { panic!(); }\n\n#[test]\n#[should_panic(expected = \"exact substring\")]\nfn panics_specifically() { panic!(\"exact substring here\"); }\n",[73,12266,12264],{"__ignoreMap":117},[15,12268,12269],{"id":12218},[73,12270,12271],{},"#[ignore]",[111,12273,12276],{"className":12274,"code":12275,"language":397,"meta":117},[395],"#[test]\n#[ignore = \"slow, run manually\"]\nfn slow_test() { \u002F* ... *\u002F }\n",[73,12277,12275],{"__ignoreMap":117},[20,12279,12280,12281,12284],{},"Skipped unless ",[73,12282,12283],{},"--ignored"," is passed.",[15,12286,12288],{"id":12287},"asynchronous-tests","Asynchronous Tests",[111,12290,12293],{"className":12291,"code":12292,"language":397,"meta":117},[395],"#[tokio::test]\nasync fn async_test() {\n    let v = async_fn().await;\n    assert_eq!(v, 5);\n}\n",[73,12294,12292],{"__ignoreMap":117},[20,12296,12297,12298,480,12301,526],{},"Use the runtime's test attribute (",[73,12299,12300],{},"tokio::test",[73,12302,12303],{},"async_std::test",[15,12305,12307],{"id":12306},"benchmark-tests-unstable","Benchmark Tests (unstable)",[111,12309,12312],{"className":12310,"code":12311,"language":397,"meta":117},[395],"\u002F\u002F requires nightly or use the `criterion` crate\n#![feature(test)]\nextern crate test;\nuse test::Bencher;\n\n#[bench]\nfn bench_add(b: &mut Bencher) {\n    b.iter(|| add(test::black_box(2), test::black_box(2)));\n}\n",[73,12313,12311],{"__ignoreMap":117},[20,12315,12316,12319],{},[73,12317,12318],{},"criterion"," is the de facto stable benchmarking tool.",[15,12321,12323],{"id":12322},"property-based-testing","Property-Based Testing",[20,12325,1876,12326,1546,12329,170],{},[73,12327,12328],{},"proptest",[73,12330,12331],{},"quickcheck",[111,12333,12336],{"className":12334,"code":12335,"language":397,"meta":117},[395],"proptest! {\n    #[test]\n    fn add_commutative(a in -1000i32..1000, b in -1000i32..1000) {\n        proptest::prop_assert_eq!(add(a, b), add(b, a));\n    }\n}\n",[73,12337,12335],{"__ignoreMap":117},[15,12339,12341],{"id":12340},"snapshot-testing","Snapshot Testing",[20,12343,1876,12344,170],{},[73,12345,12346],{},"insta",[111,12348,12351],{"className":12349,"code":12350,"language":397,"meta":117},[395],"#[test]\nfn snapshot() {\n    let v = render();\n    insta::assert_snapshot!(v);\n}\n",[73,12352,12350],{"__ignoreMap":117},[15,12354,12356],{"id":12355},"test-organization-tips","Test Organization Tips",[33,12358,12359,12365,12368,12371],{},[36,12360,12361,12362,12364],{},"Unit tests inside ",[73,12363,11944],{}," so they don't bloat the production binary.",[36,12366,12367],{},"Don't test private functions if you can test them through the public API.",[36,12369,12370],{},"Test edge cases: empty, boundary, max\u002Fmin, overflow, unicode, concurrency.",[36,12372,1876,12373,12376],{},[73,12374,12375],{},"mockall"," or hand-rolled traits for dependency injection.",[15,12378,6469],{"id":6468},[33,12380,12381,12389,12401,12419,12428,12436,12448,12462],{},[36,12382,12383,12388],{},[24,12384,12385],{},[73,12386,12387],{},"#[should_panic(expected = ...)]"," is a substring match, not regex\u002Fexact.",[36,12390,12391,12396,12397,12400],{},[24,12392,12393,12395],{},[73,12394,12179],{}," runs in parallel by default",": shared files \u002F ports can race. Use ",[73,12398,12399],{},"--test-threads=1"," or unique tempdirs.",[36,12402,12403,12410,12411,292,12413,1212,12415,12418],{},[24,12404,12405,12406,12409],{},"Tests in ",[73,12407,12408],{},"bin\u002F"," files",": put ",[73,12412,11944],{},[73,12414,11469],{},[73,12416,12417],{},"bin\u002Fx.rs"," too.",[36,12420,12421,12424,12425,526],{},[24,12422,12423],{},"Doc tests slow",": many crates skip them in CI for speed (",[73,12426,12427],{},"cargo test --lib --bins --tests",[36,12429,12430,12435],{},[24,12431,12432,12434],{},[73,12433,10151],{}," in tests is fine",": tests aren't production code; panicking is OK.",[36,12437,12438,2927,12441,12444,12445,259],{},[24,12439,12440],{},"Floating point equality",[73,12442,12443],{},"approx"," crate or ",[73,12446,12447],{},"assert!((a - b).abs() \u003C EPS)",[36,12449,12450,2927,12455,12458,12459,12461],{},[24,12451,12452,12454],{},[73,12453,12179],{}," shows only failures by default",[73,12456,12457],{},"--nocapture"," to see ",[73,12460,428],{}," output even on success.",[36,12463,12464,12467,12468,12471],{},[24,12465,12466],{},"Time-sensitive tests",": inject a ",[73,12469,12470],{},"Clock"," trait for deterministic tests.",[15,12473,12475],{"id":12474},"coverage","Coverage",[111,12477,12480],{"className":12478,"code":12479,"language":116,"meta":117},[114],"cargo install cargo-tarpaulin\ncargo tarpaulin\n",[73,12481,12479],{"__ignoreMap":117},[20,12483,12484,12485,12488],{},"Or ",[73,12486,12487],{},"cargo-llvm-cov"," for source-based coverage.",[15,12490,12492],{"id":12491},"fuzzing","Fuzzing",[20,12494,1876,12495,12498],{},[73,12496,12497],{},"cargo-fuzz"," (libFuzzer-based) for finding panics\u002FUB:",[111,12500,12503],{"className":12501,"code":12502,"language":116,"meta":117},[114],"cargo install cargo-fuzz\ncargo fuzz add parse_target\n# edit fuzz\u002Ffuzz_targets\u002Fparse_target.rs\ncargo fuzz run parse_target\n",[73,12504,12502],{"__ignoreMap":117},[15,12506,12508,71,12511,6859,12513],{"id":12507},"test-traits-debug-for-assert_eq",[73,12509,12510],{},"Test Traits",[73,12512,469],{},[73,12514,12086],{},[20,12516,12517,1052,12519,12522],{},[73,12518,12086],{},[73,12520,12521],{},"T: PartialEq + Debug",". If you see \"the trait Debug is not implemented,\" derive it.",[15,12524,349],{"id":348},[20,12526,12527,12528,12531,12532,12534,12535,1212,12537,1212,12539,1212,12541,12543,12544,7020,12546,12548,12549,1212,12551,1212,12553,1212,12555,12557],{},"Tests live alongside code (",[73,12529,12530],{},"#[cfg(test)]","), in ",[73,12533,11827],{}," for integration, and in doc comments for doc tests. Use ",[73,12536,10361],{},[73,12538,12086],{},[73,12540,12257],{},[73,12542,12271],{},". Run with ",[73,12545,12179],{},[73,12547,12300],{}," for async. Use ",[73,12550,12328],{},[73,12552,12346],{},[73,12554,12318],{},[73,12556,12497],{}," for advanced testing.",[20,12559,12560],{},"Next: Concurrency, threads, and the message-passing vs shared-state story.",{"title":117,"searchDepth":357,"depth":357,"links":12562},[12563,12564,12565,12566,12569,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12585],{"id":12030,"depth":357,"text":12031},{"id":12064,"depth":357,"text":12065},{"id":12107,"depth":357,"text":12108},{"id":12117,"depth":357,"text":12118,"children":12567},[12568],{"id":12147,"depth":364,"text":12148},{"id":12164,"depth":357,"text":12165,"children":12570},[12571],{"id":12202,"depth":364,"text":12203},{"id":12230,"depth":357,"text":12231},{"id":12257,"depth":357,"text":12260},{"id":12218,"depth":357,"text":12271},{"id":12287,"depth":357,"text":12288},{"id":12306,"depth":357,"text":12307},{"id":12322,"depth":357,"text":12323},{"id":12340,"depth":357,"text":12341},{"id":12355,"depth":357,"text":12356},{"id":6468,"depth":357,"text":6469},{"id":12474,"depth":357,"text":12475},{"id":12491,"depth":357,"text":12492},{"id":12507,"depth":357,"text":12584},"Test Traits: Debug for assert_eq!",{"id":348,"depth":357,"text":349},"Rust's testing is built into the language and cargo. There are three layers: unit tests, integration tests, and documentation tests.",{},"\u002Frust\u002F21-testing",{"title":12016,"description":12586},"rust\u002F21-testing","xj_YPMZ6GkmmN5jpGKJOjQ2BCVfBAu81yb0iX7fpqac",{"id":12593,"title":12594,"body":12595,"description":13325,"extension":373,"meta":13326,"navigation":375,"path":13327,"seo":13328,"stem":13329,"__hash__":13330},"content\u002Frust\u002F22-concurrency.md","22 — Concurrency & Multithreading",{"type":8,"value":12596,"toc":13293},[12597,12600,12611,12618,12635,12646,12649,12719,12723,12729,12756,12760,12766,12773,12781,12787,12795,12801,12807,12811,12817,12823,12827,12833,12837,12858,12862,12871,12877,12886,12889,12895,12904,12909,12915,12922,12927,12933,12940,12946,12950,12966,12972,12976,13013,13025,13029,13035,13038,13042,13056,13070,13076,13082,13088,13090,13185,13189,13240,13244,13263,13265,13290],[11,12598,12594],{"id":12599},"_22-concurrency-multithreading",[20,12601,12602,12603,12606,12607,27,12609,259],{},"Rust's promise: ",[24,12604,12605],{},"fearless concurrency",". The type system prevents data races at compile time via ",[73,12608,8563],{},[73,12610,8566],{},[15,12612,12614,27,12616],{"id":12613},"send-and-sync",[73,12615,8563],{},[73,12617,8566],{},[33,12619,12620,12625],{},[36,12621,12622,12624],{},[73,12623,8563],{},": a type can be transferred across threads (ownership moves safely).",[36,12626,12627,71,12629,12631,12632,12634],{},[73,12628,8566],{},[73,12630,3130],{}," can be shared across threads (multiple threads can hold ",[73,12633,3130],{}," simultaneously).",[20,12636,12637,12638,12641,12642,1212,12644,259],{},"They're ",[24,12639,12640],{},"auto-traits",": the compiler implements them automatically when all fields are ",[73,12643,8563],{},[73,12645,8566],{},[20,12647,12648],{},"Examples:",[33,12650,12651,12661,12671,12679,12694,12704],{},[36,12652,12653,480,12655,480,12657,71,12659,259],{},[73,12654,1387],{},[73,12656,1197],{},[73,12658,4930],{},[73,12660,10230],{},[36,12662,12663,7163,12665,12667,12668,12670],{},[73,12664,10537],{},[73,12666,8566],{}," (shared non-atomic refcount), not ",[73,12669,8563],{}," (cheap counter).",[36,12672,12673,71,12675,10642,12677,259],{},[73,12674,10620],{},[73,12676,10230],{},[73,12678,10645],{},[36,12680,12681,1212,12683,71,12685,12687,12688,12690,12691,12693],{},[73,12682,4712],{},[73,12684,4715],{},[73,12686,8563],{}," (if ",[73,12689,11028],{},") but not ",[73,12692,8566],{}," (no synchronization).",[36,12695,12696,1212,12698,71,12700,10642,12702,259],{},[73,12697,10820],{},[73,12699,10823],{},[73,12701,10230],{},[73,12703,11028],{},[36,12705,12706,12707,1212,12709,7163,12711,1212,12713,12715,12716,526],{},"Raw pointers ",[73,12708,10971],{},[73,12710,10968],{},[73,12712,8563],{},[73,12714,8566],{}," (the compiler is conservative; opt in with ",[73,12717,12718],{},"unsafe impl",[15,12720,12722],{"id":12721},"spawning-threads","Spawning Threads",[111,12724,12727],{"className":12725,"code":12726,"language":397,"meta":117},[395],"use std::thread;\nuse std::time::Duration;\n\nlet handle = thread::spawn(|| {\n    for i in 0..5 {\n        println!(\"thread: {i}\");\n        thread::sleep(Duration::from_millis(10));\n    }\n});\n\nfor i in 0..5 {\n    println!(\"main: {i}\");\n    thread::sleep(Duration::from_millis(10));\n}\n\nhandle.join().unwrap();\n",[73,12728,12726],{"__ignoreMap":117},[33,12730,12731,12739,12751],{},[36,12732,12733,12735,12736,259],{},[73,12734,9571],{}," returns a ",[73,12737,12738],{},"JoinHandle\u003CT>",[36,12740,12741,12744,12745,12748,12749,526],{},[73,12742,12743],{},".join()"," blocks until the thread exits, returning ",[73,12746,12747],{},"Result\u003CT, Box\u003Cdyn Any + Send>>"," (panic propagates as ",[73,12750,2778],{},[36,12752,12753,12754,259],{},"Closures must be ",[73,12755,9565],{},[15,12757,12759],{"id":12758},"moving-data-into-threads","Moving Data into Threads",[111,12761,12764],{"className":12762,"code":12763,"language":397,"meta":117},[395],"let data = vec![1, 2, 3];\nlet handle = thread::spawn(move || {\n    println!(\"{:?}\", data);   \u002F\u002F data moved in\n});\n\u002F\u002F data not accessible here\nhandle.join().unwrap();\n",[73,12765,12763],{"__ignoreMap":117},[20,12767,12768,12770,12771,526],{},[73,12769,9142],{}," is almost always required — captures must outlive the thread (",[73,12772,4560],{},[15,12774,12776,12777,9593,12779],{"id":12775},"shared-state-with-arc-mutex","Shared State with ",[73,12778,10566],{},[73,12780,10713],{},[111,12782,12785],{"className":12783,"code":12784,"language":397,"meta":117},[395],"use std::sync::{Arc, Mutex};\nuse std::thread;\n\nlet counter = Arc::new(Mutex::new(0));\nlet mut handles = vec![];\n\nfor _ in 0..10 {\n    let counter = Arc::clone(&counter);\n    handles.push(thread::spawn(move || {\n        let mut n = counter.lock().unwrap();\n        *n += 1;\n    }));\n}\n\nfor h in handles { h.join().unwrap(); }\nprintln!(\"{:?}\", counter);   \u002F\u002F 10\n",[73,12786,12784],{"__ignoreMap":117},[20,12788,12789,12791,12792,12794],{},[73,12790,10566],{}," for shared ownership; ",[73,12793,10713],{}," for synchronized mutation. Lock guards auto-unlock on drop (RAII).",[15,12796,12798,12800],{"id":12797},"rwlock-for-read-heavy-workloads",[73,12799,10841],{}," for Read-Heavy Workloads",[111,12802,12805],{"className":12803,"code":12804,"language":397,"meta":117},[395],"use std::sync::RwLock;\nlet lock = RwLock::new(0);\n\nlet r1 = lock.read().unwrap();\nlet r2 = lock.read().unwrap();   \u002F\u002F multiple readers OK\n\u002F\u002F let w = lock.write().unwrap();   \u002F\u002F would block above\ndrop(r1); drop(r2);\nlet mut w = lock.write().unwrap();\n*w += 1;\n",[73,12806,12804],{"__ignoreMap":117},[15,12808,12810],{"id":12809},"channel-message-passing","Channel — Message Passing",[20,12812,12813,12816],{},[73,12814,12815],{},"std::sync::mpsc"," (multi-producer, single-consumer):",[111,12818,12821],{"className":12819,"code":12820,"language":397,"meta":117},[395],"use std::sync::mpsc;\nuse std::thread;\n\nlet (tx, rx) = mpsc::channel();\n\nlet h = thread::spawn(move || {\n    let v = rx.recv().unwrap();\n    println!(\"got {v}\");\n});\n\ntx.send(42).unwrap();\nh.join().unwrap();\n",[73,12822,12820],{"__ignoreMap":117},[130,12824,12826],{"id":12825},"multi-producer","Multi-Producer",[111,12828,12831],{"className":12829,"code":12830,"language":397,"meta":117},[395],"let (tx, rx) = mpsc::channel();\nlet tx2 = tx.clone();   \u002F\u002F multiple senders\nthread::spawn(move || tx.send(1).unwrap());\nthread::spawn(move || tx2.send(2).unwrap());\n",[73,12832,12830],{"__ignoreMap":117},[130,12834,12836],{"id":12835},"sync-vs-async-channels","Sync vs Async Channels",[33,12838,12839,12849],{},[36,12840,12841,12844,12845,12848],{},[73,12842,12843],{},"channel()",": unbounded, ",[73,12846,12847],{},"send"," never blocks.",[36,12850,12851,12854,12855,12857],{},[73,12852,12853],{},"sync_channel(n)",": bounded; ",[73,12856,12847],{}," blocks when buffer full (backpressure).",[130,12859,12861],{"id":12860},"crossbeam-channels","Crossbeam Channels",[20,12863,12864,12867,12868,259],{},[73,12865,12866],{},"crossbeam-channel"," is more featureful: bounded\u002Funbounded, select, after\u002Ftimeout, easy multi-consumer. Often preferred over ",[73,12869,12870],{},"std::mpsc",[111,12872,12875],{"className":12873,"code":12874,"language":397,"meta":117},[395],"let (s, r) = crossbeam_channel::unbounded();\ns.send(5).unwrap();\n",[73,12876,12874],{"__ignoreMap":117},[15,12878,12880,27,12883],{"id":12879},"park-and-unpark",[73,12881,12882],{},"park",[73,12884,12885],{},"unpark",[20,12887,12888],{},"Threads can be paused and woken:",[111,12890,12893],{"className":12891,"code":12892,"language":397,"meta":117},[395],"let h = thread::spawn(|| {\n    thread::park();\n    println!(\"unparked\");\n});\nh.thread().unpark();\nh.join().unwrap();\n",[73,12894,12892],{"__ignoreMap":117},[20,12896,12897,12898,2755,12901,259],{},"Low-level synchronization — usually use channels, ",[73,12899,12900],{},"Condvar",[73,12902,12903],{},"Barrier",[15,12905,12907],{"id":12906},"condvar",[73,12908,12900],{},[111,12910,12913],{"className":12911,"code":12912,"language":397,"meta":117},[395],"use std::sync::{Arc, Mutex, Condvar};\n\nlet pair = Arc::new((Mutex::new(false), Condvar::new()));\nlet (lock, cvar) = Arc::clone(&pair);\n\nlet h = thread::spawn(move || {\n    let (mut started, cvar) = (&lock.0.lock().unwrap(), &lock.1);\n    while !*started {\n        started = cvar.wait(started).unwrap();\n    }\n});\n\n{\n    let (mut started, cvar) = (&lock.0.lock().unwrap(), &lock.1);\n    *started = true;\n    cvar.notify_one();\n}\n\nh.join().unwrap();\n",[73,12914,12912],{"__ignoreMap":117},[20,12916,12917,12918,12921],{},"The classic pattern: wait inside the lock; ",[73,12919,12920],{},"wait"," atomically releases + sleeps + reacquires.",[15,12923,12925],{"id":12924},"barrier",[73,12926,12903],{},[111,12928,12931],{"className":12929,"code":12930,"language":397,"meta":117},[395],"use std::sync::Barrier;\nlet barrier = Arc::new(Barrier::new(3));\n\u002F\u002F each thread calls barrier.wait(); all unblock once 3 reach it\n",[73,12932,12930],{"__ignoreMap":117},[15,12934,12936,27,12938],{"id":12935},"once-and-oncelock",[73,12937,10883],{},[73,12939,10886],{},[111,12941,12944],{"className":12942,"code":12943,"language":397,"meta":117},[395],"use std::sync::OnceLock;\nstatic INIT: OnceLock\u003CVec\u003Cu8>> = OnceLock::new();\nlet data = INIT.get_or_init(|| load_config());\n",[73,12945,12943],{"__ignoreMap":117},[15,12947,12949],{"id":12948},"atomic-types","Atomic Types",[20,12951,12952,71,12954,480,12957,480,12960,480,12963,8663],{},[73,12953,1061],{},[73,12955,12956],{},"AtomicBool",[73,12958,12959],{},"AtomicI32",[73,12961,12962],{},"AtomicUsize",[73,12964,12965],{},"AtomicPtr\u003CT>",[111,12967,12970],{"className":12968,"code":12969,"language":397,"meta":117},[395],"use std::sync::atomic::{AtomicUsize, Ordering};\nlet n = AtomicUsize::new(0);\nn.fetch_add(1, Ordering::SeqCst);\nn.compare_exchange(0, 1, Ordering::SeqCst, Ordering::Relaxed);\n",[73,12971,12969],{"__ignoreMap":117},[130,12973,12975],{"id":12974},"orderings","Orderings",[33,12977,12978,12984,12993,13001,13007],{},[36,12979,12980,12983],{},[73,12981,12982],{},"Relaxed",": no ordering constraints, just atomicity.",[36,12985,12986,12989,12990,526],{},[73,12987,12988],{},"Acquire",": later reads see the latest writes (pair with ",[73,12991,12992],{},"Release",[36,12994,12995,12997,12998,13000],{},[73,12996,12992],{},": prior writes are visible to ",[73,12999,12988],{}," readers.",[36,13002,13003,13006],{},[73,13004,13005],{},"AcqRel",": both.",[36,13008,13009,13012],{},[73,13010,13011],{},"SeqCst",": total order across threads (most expensive).",[20,13014,1876,13015,13017,13018,1212,13020,1212,13022,13024],{},[73,13016,13011],{}," if unsure; switch to ",[73,13019,12982],{},[73,13021,12988],{},[73,13023,12992],{}," once you understand the memory model.",[15,13026,13028],{"id":13027},"thread-local-storage","Thread-Local Storage",[111,13030,13033],{"className":13031,"code":13032,"language":397,"meta":117},[395],"use std::cell::RefCell;\nthread_local! {\n    static COUNTER: RefCell\u003Cu32> = RefCell::new(0);\n}\n\nCOUNTER.with(|c| { *c.borrow_mut() += 1; });\n",[73,13034,13032],{"__ignoreMap":117},[20,13036,13037],{},"Per-thread state, no synchronization needed.",[15,13039,13041],{"id":13040},"async-vs-threads","Async vs Threads",[33,13043,13044,13050],{},[36,13045,13046,13049],{},[24,13047,13048],{},"Threads",": OS-level, ~1 MB stack, ~few µs context switch. Good for blocking I\u002FO.",[36,13051,13052,13055],{},[24,13053,13054],{},"Async",": lightweight tasks, ~few KB stack, runtime-driven. Good for many concurrent I\u002FO-bound tasks.",[20,13057,13058,13059,13062,13063,1212,13066,13069],{},"For CPU-bound work, threads or ",[73,13060,13061],{},"rayon"," (data parallelism) are appropriate. For many concurrent I\u002FO operations, async (",[73,13064,13065],{},"tokio",[73,13067,13068],{},"async-std",") scales better.",[15,13071,13073,13075],{"id":13072},"rayon-for-data-parallelism",[73,13074,13061],{}," for Data Parallelism",[111,13077,13080],{"className":13078,"code":13079,"language":397,"meta":117},[395],"use rayon::prelude::*;\nlet v: Vec\u003Ci32> = (1..=100).collect();\nlet sum: i32 = v.par_iter().map(|x| x * 2).sum();\n",[73,13081,13079],{"__ignoreMap":117},[20,13083,13084,13087],{},[73,13085,13086],{},"par_iter()"," runs the iteration across a thread pool. Drop-in replacement for sequential iterators in many cases.",[15,13089,6469],{"id":6468},[33,13091,13092,13097,13106,13126,13132,13140,13152,13158,13166,13172],{},[36,13093,13094,13096],{},[24,13095,11265],{},": inconsistent lock ordering. Acquire locks in a fixed global order, or use a single lock.",[36,13098,13099,13103,13104,259],{},[24,13100,13101,11239],{},[73,13102,3803],{},": compile error. Use ",[73,13105,10566],{},[36,13107,13108,13114,13115,13118,13119,13122,13123,259],{},[24,13109,13110,13111],{},"Holding a lock across ",[73,13112,13113],{},"await",": in async code, this can deadlock; use ",[73,13116,13117],{},"tokio::sync::Mutex"," instead of ",[73,13120,13121],{},"std::sync::Mutex"," for async contexts, or ",[73,13124,13125],{},"spawn_blocking",[36,13127,13128,13131],{},[24,13129,13130],{},"Lock poisoning",": if a thread panics while holding a lock, the lock becomes poisoned. Decide on a recovery policy.",[36,13133,13134,13139],{},[24,13135,13136,13137],{},"Spawning without ",[73,13138,10295],{},": detached threads can outlive main, dropping work mid-flight. Detach deliberately, not by accident.",[36,13141,13142,13148,13149,13151],{},[24,13143,13144,1052,13146],{},[73,13145,9571],{},[73,13147,4560],{},": closures can't borrow stack data unless ",[73,13150,9142],{},"d.",[36,13153,13154,13157],{},[24,13155,13156],{},"Shared mutable state",": prefer message passing (channels) when possible — it isolates state and avoids locking.",[36,13159,13160,13165],{},[24,13161,13162,13164],{},[73,13163,10230],{}," are not enough for correctness",": they prevent data races, not logical races or deadlocks.",[36,13167,13168,13171],{},[24,13169,13170],{},"Atomic orderings are subtle",": wrong ordering causes bugs that don't show on x86 (which is strongly ordered). Test on weak architectures (ARM).",[36,13173,13174,13181,13182,13184],{},[24,13175,13176,1538,13179],{},[73,13177,13178],{},"Mutex::lock()",[73,13180,2792],{},": poison is the failure mode. Don't ",[73,13183,10168],{}," blindly in production code paths.",[15,13186,13188],{"id":13187},"patterns","Patterns",[33,13190,13191,13200,13212,13222,13234],{},[36,13192,13193,71,13196,13199],{},[24,13194,13195],{},"Work queue",[73,13197,13198],{},"mpsc"," channels + worker pool.",[36,13201,13202,71,13205,1212,13208,13211],{},[24,13203,13204],{},"Pub-Sub",[73,13206,13207],{},"async-channel",[73,13209,13210],{},"tokio::sync::broadcast"," for multiple receivers.",[36,13213,13214,13217,13218,13221],{},[24,13215,13216],{},"Producer-consumer",": bounded ",[73,13219,13220],{},"sync_channel"," for backpressure.",[36,13223,13224,71,13227,1546,13230,13233],{},[24,13225,13226],{},"Read-mostly cache",[73,13228,13229],{},"RwLock\u003CHashMap\u003C...>>",[73,13231,13232],{},"arc-swap"," for atomic replacement.",[36,13235,13236,13239],{},[24,13237,13238],{},"Sharded locks",": split data into N shards each with its own lock (reduces contention).",[15,13241,13243],{"id":13242},"thread-pool","Thread Pool",[20,13245,13246,13249,13250,13252,13253,13255,13256,1212,13259,13262],{},[73,13247,13248],{},"std::thread"," doesn't have a built-in pool. Use ",[73,13251,13061],{}," (data parallel), ",[73,13254,13065],{}," (async), or ",[73,13257,13258],{},"threadpool",[73,13260,13261],{},"crossbeam_pool"," (custom).",[15,13264,349],{"id":348},[20,13266,13267,1212,13269,13271,13272,13274,13275,1212,13277,13279,13280,1212,13282,1212,13284,13286,13287,13289],{},[73,13268,8563],{},[73,13270,8566],{}," are the foundation. Use ",[73,13273,10566],{}," for shared ownership, ",[73,13276,10713],{},[73,13278,10841],{}," for synchronization, channels for message passing. Atomics for low-level coordination. ",[73,13281,12900],{},[73,13283,12903],{},[73,13285,10886],{}," for common patterns. ",[73,13288,13061],{}," for data parallelism. Prefer async for I\u002FO-bound concurrency.",[20,13291,13292],{},"Next: Async\u002Fawait — the modern Rust concurrency story.",{"title":117,"searchDepth":357,"depth":357,"links":13294},[13295,13297,13298,13299,13301,13303,13308,13310,13311,13312,13314,13317,13318,13319,13321,13322,13323,13324],{"id":12613,"depth":357,"text":13296},"Send and Sync",{"id":12721,"depth":357,"text":12722},{"id":12758,"depth":357,"text":12759},{"id":12775,"depth":357,"text":13300},"Shared State with Arc + Mutex",{"id":12797,"depth":357,"text":13302},"RwLock for Read-Heavy Workloads",{"id":12809,"depth":357,"text":12810,"children":13304},[13305,13306,13307],{"id":12825,"depth":364,"text":12826},{"id":12835,"depth":364,"text":12836},{"id":12860,"depth":364,"text":12861},{"id":12879,"depth":357,"text":13309},"park and unpark",{"id":12906,"depth":357,"text":12900},{"id":12924,"depth":357,"text":12903},{"id":12935,"depth":357,"text":13313},"Once and OnceLock",{"id":12948,"depth":357,"text":12949,"children":13315},[13316],{"id":12974,"depth":364,"text":12975},{"id":13027,"depth":357,"text":13028},{"id":13040,"depth":357,"text":13041},{"id":13072,"depth":357,"text":13320},"rayon for Data Parallelism",{"id":6468,"depth":357,"text":6469},{"id":13187,"depth":357,"text":13188},{"id":13242,"depth":357,"text":13243},{"id":348,"depth":357,"text":349},"Rust's promise: fearless concurrency. The type system prevents data races at compile time via Send and Sync.",{},"\u002Frust\u002F22-concurrency",{"title":12594,"description":13325},"rust\u002F22-concurrency","w8eg4tGR5WT_DyRw_YcxbZFdwdPWnOHcsBSv8R3DWGw",{"id":13332,"title":13333,"body":13334,"description":14086,"extension":373,"meta":14087,"navigation":375,"path":14088,"seo":14089,"stem":14090,"__hash__":14091},"content\u002Frust\u002F23-async-await.md","23 — Async \u002F Await",{"type":8,"value":13335,"toc":14053},[13336,13339,13345,13349,13355,13364,13368,13374,13379,13383,13389,13398,13402,13409,13436,13442,13450,13454,13460,13484,13488,13494,13504,13509,13517,13523,13540,13549,13552,13558,13563,13569,13575,13578,13582,13624,13628,13634,13644,13650,13653,13659,13669,13673,13679,13695,13699,13712,13718,13724,13728,13731,13737,13740,13747,13753,13770,13774,13785,13789,13799,13803,13809,13826,13828,13835,13841,13844,13850,13856,13864,13870,13876,13879,13881,13992,13996,14020,14022,14050],[11,13337,13333],{"id":13338},"_23-async-await",[20,13340,13341,13342,13344],{},"Async lets you write concurrent code that looks sequential. Rust's async is ",[24,13343,7533],{}," — futures are state machines compiled by the compiler.",[15,13346,13348],{"id":13347},"async-functions","Async Functions",[111,13350,13353],{"className":13351,"code":13352,"language":397,"meta":117},[395],"async fn fetch(url: &str) -> String {\n    \u002F\u002F ... await something ...\n    String::from(\"data\")\n}\n",[73,13354,13352],{"__ignoreMap":117},[20,13356,6069,13357,12735,13360,13363],{},[73,13358,13359],{},"fetch(...)",[24,13361,13362],{},"future",", not a value. The body doesn't run until the future is polled.",[15,13365,13366],{"id":13113},[73,13367,13113],{},[111,13369,13372],{"className":13370,"code":13371,"language":397,"meta":117},[395],"let s = fetch(\"https:\u002F\u002Fx\").await;\n",[73,13373,13371],{"__ignoreMap":117},[20,13375,13376,13378],{},[73,13377,5022],{}," yields control to the executor if the future is pending. The current task is suspended and later resumed.",[15,13380,13382],{"id":13381},"async-is-lazy","Async Is Lazy",[111,13384,13387],{"className":13385,"code":13386,"language":397,"meta":117},[395],"let f = async { println!(\"hi\"); };\n\u002F\u002F nothing happens yet\nf.await;   \u002F\u002F body runs now\n",[73,13388,13386],{"__ignoreMap":117},[20,13390,13391,13392,2519,13394,13397],{},"You must ",[73,13393,5022],{},[73,13395,13396],{},"spawn",") a future for it to make progress.",[15,13399,13401],{"id":13400},"runtimes","Runtimes",[20,13403,13404,13405,13408],{},"Rust ships ",[24,13406,13407],{},"no built-in async runtime"," — you choose one:",[33,13410,13411,13416,13421,13427],{},[36,13412,13413,13415],{},[73,13414,13065],{},": most popular, multi-threaded scheduler, mature ecosystem.",[36,13417,13418,13420],{},[73,13419,13068],{},": mirrors std API, single-threaded by default.",[36,13422,13423,13426],{},[73,13424,13425],{},"smol",": small, simple.",[36,13428,13429,13432,13433,526],{},[73,13430,13431],{},"embassy",": embedded (",[73,13434,13435],{},"no_std",[111,13437,13440],{"className":13438,"code":13439,"language":397,"meta":117},[395],"#[tokio::main]\nasync fn main() {\n    println!(\"hello from tokio\");\n}\n",[73,13441,13439],{"__ignoreMap":117},[20,13443,13444,13447,13448,259],{},[73,13445,13446],{},"tokio::main"," builds a runtime and runs your async ",[73,13449,509],{},[15,13451,13453],{"id":13452},"spawning-tasks","Spawning Tasks",[111,13455,13458],{"className":13456,"code":13457,"language":397,"meta":117},[395],"#[tokio::main]\nasync fn main() {\n    let h = tokio::spawn(async {\n        5\n    });\n    let n: i32 = h.await.unwrap();\n    println!(\"{n}\");\n}\n",[73,13459,13457],{"__ignoreMap":117},[33,13461,13462,13469,13475],{},[36,13463,13464,12735,13467,259],{},[73,13465,13466],{},"tokio::spawn",[73,13468,12738],{},[36,13470,13471,13472,259],{},"Spawned tasks must be ",[73,13473,13474],{},"Send + 'static",[36,13476,13477,13479,13480,13483],{},[73,13478,5022],{}," on the handle gives ",[73,13481,13482],{},"Result\u003CT, JoinError>"," (panic propagates).",[15,13485,13487],{"id":13486},"futures","Futures",[111,13489,13492],{"className":13490,"code":13491,"language":397,"meta":117},[395],"trait Future {\n    type Output;\n    fn poll(self: Pin\u003C&mut Self>, cx: &mut Context) -> Poll\u003CSelf::Output>;\n}\n\nenum Poll\u003CT> { Ready(T), Pending }\n",[73,13493,13491],{"__ignoreMap":117},[20,13495,13496,13497,13500,13501,13503],{},"You rarely implement ",[73,13498,13499],{},"Future"," manually. Async functions desugar to anonymous ",[73,13502,13499],{},"-implementing state machines.",[15,13505,13507],{"id":13506},"pin",[73,13508,8579],{},[20,13510,13511,13513,13514,13516],{},[73,13512,8579],{}," guarantees a value won't be moved in memory. Required because self-referential futures (which reference their own stack across ",[73,13515,5022],{},") would break if moved.",[111,13518,13521],{"className":13519,"code":13520,"language":397,"meta":117},[395],"let mut fut = async { 5 };\nlet pinned: Pin\u003C&mut _> = Pin::new(&mut fut);\n",[73,13522,13520],{"__ignoreMap":117},[20,13524,13525,13526,13528,13529,13532,13533,1546,13536,13539],{},"You mostly encounter ",[73,13527,8579],{}," in trait signatures and APIs (e.g., ",[73,13530,13531],{},"Future::poll","). The ",[73,13534,13535],{},"pin-utils",[73,13537,13538],{},"Box::pin"," handle the common cases.",[15,13541,13543,27,13546],{"id":13542},"boxdyn-future-and-pinboxdyn-future",[73,13544,13545],{},"Box\u003Cdyn Future>",[73,13547,13548],{},"Pin\u003CBox\u003Cdyn Future>>",[20,13550,13551],{},"Because futures have unique unnameable types, storing them in collections or returning them generically requires boxing:",[111,13553,13556],{"className":13554,"code":13555,"language":397,"meta":117},[395],"fn make_fut() -> Pin\u003CBox\u003Cdyn Future\u003COutput = i32> + Send>> {\n    Box::pin(async { 5 })\n}\n",[73,13557,13555],{"__ignoreMap":117},[20,13559,13560,13562],{},[73,13561,13548],{}," is the trait-object form of a future.",[15,13564,13566],{"id":13565},"impl-future",[73,13567,13568],{},"impl Future",[111,13570,13573],{"className":13571,"code":13572,"language":397,"meta":117},[395],"fn make_fut() -> impl Future\u003COutput = i32> {\n    async { 5 }\n}\n",[73,13574,13572],{"__ignoreMap":117},[20,13576,13577],{},"Returns a concrete future type, hidden. Single type per return site.",[15,13579,13581],{"id":13580},"common-async-crates","Common Async Crates",[33,13583,13584,13589,13594,13600,13606,13612,13618],{},[36,13585,13586,13588],{},[73,13587,13065],{}," — runtime, I\u002FO, networking, synchronization.",[36,13590,13591,13593],{},[73,13592,13486],{}," — combinators, streams, sinks.",[36,13595,13596,13599],{},[73,13597,13598],{},"async-trait"," — async functions in traits (until native support stabilizes; partial in 1.75+).",[36,13601,13602,13605],{},[73,13603,13604],{},"reqwest"," — HTTP client.",[36,13607,13608,13611],{},[73,13609,13610],{},"hyper"," — HTTP server\u002Fclient.",[36,13613,13614,13617],{},[73,13615,13616],{},"sqlx"," — async DB.",[36,13619,13620,13623],{},[73,13621,13622],{},"axum"," — web framework (tokio-based).",[15,13625,13627],{"id":13626},"async-io","Async IO",[111,13629,13632],{"className":13630,"code":13631,"language":397,"meta":117},[395],"use tokio::fs;\n#[tokio::main]\nasync fn main() -> std::io::Result\u003C()> {\n    let s = fs::read_to_string(\"file.txt\").await?;\n    println!(\"{s}\");\n    Ok(())\n}\n",[73,13633,13631],{"__ignoreMap":117},[20,13635,13636,13637,1212,13640,13643],{},"Async ",[73,13638,13639],{},"read",[73,13641,13642],{},"write"," yield when the syscall would block. The runtime parks the task and wakes it when the OS signals readiness.",[15,13645,13647],{"id":13646},"tokioselect",[73,13648,13649],{},"tokio::select!",[20,13651,13652],{},"Race multiple futures, take the first to complete:",[111,13654,13657],{"className":13655,"code":13656,"language":397,"meta":117},[395],"tokio::select! {\n    v = first_future() => println!(\"first: {v}\"),\n    _ = tokio::time::sleep(Duration::from_secs(1)) => println!(\"timeout\"),\n}\n",[73,13658,13656],{"__ignoreMap":117},[20,13660,13661,13662,13665,13666,13668],{},"Unselected branches are dropped. Use ",[73,13663,13664],{},"biased"," for ordering, or branch with ",[73,13667,3850],{}," futures to reuse them.",[15,13670,13672],{"id":13671},"streams-async-iterators","Streams (Async Iterators)",[111,13674,13677],{"className":13675,"code":13676,"language":397,"meta":117},[395],"use futures::stream::{self, StreamExt};\n\nlet mut s = stream::iter(vec![1, 2, 3]).map(|x| x * 2);\nwhile let Some(v) = s.next().await {\n    println!(\"{v}\");\n}\n",[73,13678,13676],{"__ignoreMap":117},[20,13680,13681,13684,13685,2559,13688,1212,13691,13694],{},[73,13682,13683],{},"StreamExt::next().await"," is the async equivalent of ",[73,13686,13687],{},"Iterator::next()",[73,13689,13690],{},"try_stream",[73,13692,13693],{},"tokio_stream"," for building streams.",[15,13696,13698],{"id":13697},"channels","Channels",[20,13700,13701,480,13704,480,13706,480,13709,170],{},[73,13702,13703],{},"tokio::sync::mpsc",[73,13705,13210],{},[73,13707,13708],{},"tokio::sync::oneshot",[73,13710,13711],{},"tokio::sync::watch",[111,13713,13716],{"className":13714,"code":13715,"language":397,"meta":117},[395],"let (tx, mut rx) = tokio::sync::mpsc::channel(100);\ntokio::spawn(async move {\n    tx.send(5).await.unwrap();\n});\nlet v = rx.recv().await;\n",[73,13717,13715],{"__ignoreMap":117},[20,13719,13720,13721,13723],{},"Async channels ",[73,13722,5022],{}," on send\u002Frecv instead of blocking.",[15,13725,13726],{"id":13125},[73,13727,13125],{},[20,13729,13730],{},"For CPU-bound work or blocking syscalls inside async code:",[111,13732,13735],{"className":13733,"code":13734,"language":397,"meta":117},[395],"let v = tokio::task::spawn_blocking(|| {\n    cpu_heavy_computation()\n}).await.unwrap();\n",[73,13736,13734],{"__ignoreMap":117},[20,13738,13739],{},"Offloads work to a separate thread pool so the async executor isn't blocked.",[15,13741,13743,13744,13746],{"id":13742},"holding-locks-across-await-pitfall","Holding Locks Across ",[73,13745,5022],{}," — Pitfall",[111,13748,13751],{"className":13749,"code":13750,"language":397,"meta":117},[395],"\u002F\u002F BAD: holding std Mutex across await can deadlock \u002F block executor\nlet guard = std_mutex.lock().unwrap();\nsome_async().await;     \u002F\u002F ⚠️ guard held\n\u002F\u002F GOOD:\nlet val = {\n    let g = std_mutex.lock().unwrap();\n    g.clone()\n};\nsome_async(val).await;\n\n\u002F\u002F OR use tokio's async Mutex:\nlet guard = tokio_mutex.lock().await;\nsome_async().await;\n",[73,13752,13750],{"__ignoreMap":117},[20,13754,13755,13757,13758,13761,13762,13764,13765,13767,13768,259],{},[73,13756,13121],{}," is fine ",[183,13759,13760],{},"within"," an async function if released before ",[73,13763,5022],{},". For locks held across ",[73,13766,5022],{},", use ",[73,13769,13117],{},[15,13771,13773],{"id":13772},"canceling-futures","Canceling Futures",[20,13775,13776,13777,13780,13781,13784],{},"Dropping a future cancels it. The ",[73,13778,13779],{},"select!"," drop semantics mean unselected branches are canceled. Use ",[73,13782,13783],{},"CancellationToken"," for cooperative cancellation.",[15,13786,13788],{"id":13787},"backpressure","Backpressure",[20,13790,13791,13792,9610,13795,13798],{},"Use bounded channels (",[73,13793,13794],{},"mpsc::channel(n)",[73,13796,13797],{},".send().await"," blocks when full, naturally propagating backpressure to producers.",[15,13800,13802],{"id":13801},"async-traits-175","Async Traits (1.75+)",[111,13804,13807],{"className":13805,"code":13806,"language":397,"meta":117},[395],"trait Service {\n    async fn call(&self, req: Request) -> Response;\n}\n",[73,13808,13806],{"__ignoreMap":117},[20,13810,13811,13812,13815,13816,13819,13820,13822,13823,13825],{},"Native async traits stabilized in 1.75 with limitations (no ",[73,13813,13814],{},"dyn"," dispatch without ",[73,13817,13818],{},"#[async_trait]"," crate, no recursion in some cases). For full features including ",[73,13821,13814],{},", use the ",[73,13824,13598],{}," crate.",[15,13827,7895],{"id":7894},[130,13829,13831,13832],{"id":13830},"concurrency-with-join","Concurrency with ",[73,13833,13834],{},"join!",[111,13836,13839],{"className":13837,"code":13838,"language":397,"meta":117},[395],"let (a, b, c) = tokio::join!(fa(), fb(), fc());\n",[73,13840,13838],{"__ignoreMap":117},[20,13842,13843],{},"Runs all three concurrently, waits for all, returns a tuple.",[130,13845,13831,13847],{"id":13846},"concurrency-with-try_join",[73,13848,13849],{},"try_join!",[111,13851,13854],{"className":13852,"code":13853,"language":397,"meta":117},[395],"let (a, b) = tokio::try_join!(fa(), fb())?;\n",[73,13855,13853],{"__ignoreMap":117},[20,13857,13858,13859,13861,13862,259],{},"Like ",[73,13860,13834],{}," but short-circuits on ",[73,13863,2778],{},[130,13865,13867],{"id":13866},"futuresunordered",[73,13868,13869],{},"FuturesUnordered",[111,13871,13874],{"className":13872,"code":13873,"language":397,"meta":117},[395],"use futures::stream::FuturesUnordered;\nlet mut futs = FuturesUnordered::new();\nfuts.push(fa());\nfuts.push(fb());\nwhile let Some(r) = futs.next().await { \u002F* ... *\u002F }\n",[73,13875,13873],{"__ignoreMap":117},[20,13877,13878],{},"Spawn N futures, await results as they complete (unordered).",[15,13880,6469],{"id":6468},[33,13882,13883,13897,13905,13914,13927,13947,13955,13968,13979],{},[36,13884,13885,13892,13893,1546,13895,259],{},[24,13886,13887,2281,13889,13891],{},[73,13888,5022],{},[73,13890,2614],{}," loop over a sync iterator",": fine; just don't accidentally serialize tasks you wanted to run concurrently — use ",[73,13894,13834],{},[73,13896,13396],{},[36,13898,13899,13904],{},[24,13900,13901,13902],{},"Forgetting to ",[73,13903,13113],{},": the future is created but never runs — silent bug.",[36,13906,13907,13913],{},[24,13908,13909,13912],{},[73,13910,13911],{},"async fn"," in a trait"," still has rough edges; check current support.",[36,13915,13916,13919,13920,27,13923,13926],{},[24,13917,13918],{},"Runtime-locked I\u002FO",": mixing ",[73,13921,13922],{},"tokio::fs",[73,13924,13925],{},"async-std::fs"," is fine functionally but wasteful; pick one runtime's I\u002FO.",[36,13928,13929,13934,13935,13937,13938,1958,13940,13943,13944,13946],{},[24,13930,13931,13933],{},[73,13932,8563],{}," futures",": futures that hold non-",[73,13936,8563],{}," types across ",[73,13939,5022],{},[73,13941,13942],{},"!Send"," and can't be ",[73,13945,13466],{},"ed.",[36,13948,13949,13952,13953,259],{},[24,13950,13951],{},"Long-running blocking code in async",": blocks the executor. Use ",[73,13954,13125],{},[36,13956,13957,13963,13964,13967],{},[24,13958,13959,13960,13962],{},"Memory leaks with ",[73,13961,13779],{}," loops",": each iteration may allocate. Use ",[73,13965,13966],{},"pin_mut!"," or pinned variables.",[36,13969,13970,71,13975,13978],{},[24,13971,13972,13974],{},[73,13973,13446],{}," flavor",[73,13976,13977],{},"#[tokio::main(flavor = \"current_thread\")]"," is single-threaded (less overhead). Default is multi-threaded.",[36,13980,13981,13986,13987,13989,13990,259],{},[24,13982,13983,13985],{},[73,13984,3217],{}," cancels futures",": a future dropped mid-",[73,13988,13113],{}," is silently canceled; resources are cleaned up via ",[73,13991,3217],{},[15,13993,13995],{"id":13994},"when-to-use-async","When to Use Async",[33,13997,13998,14001,14004,14009],{},[36,13999,14000],{},"Many concurrent I\u002FO-bound tasks (HTTP servers, proxies, scrapers).",[36,14002,14003],{},"Latency-sensitive workloads with lots of waiting.",[36,14005,14006,14007,259],{},"Avoid for CPU-bound work — use threads or ",[73,14008,13061],{},[36,14010,14011,14012,14014,14015,14017,14018,526],{},"Avoid in ",[73,14013,13435],{},"\u002Fembedded unless using a ",[73,14016,13435],{},"-friendly runtime (",[73,14019,13431],{},[15,14021,349],{"id":348},[20,14023,14024,14025,14027,14028,14030,14031,14033,14034,14036,14037,1212,14039,14041,14042,14044,14045,7020,14047,14049],{},"Async is lazy (futures are polled); runtimes drive them. ",[73,14026,13065],{}," is the dominant runtime. ",[73,14029,13113],{}," yields control; ",[73,14032,13396],{}," schedules tasks. ",[73,14035,13779],{}," races; ",[73,14038,13834],{},[73,14040,13849],{}," runs concurrently. Use async-aware channels and locks. Beware holding ",[73,14043,13121],{}," across ",[73,14046,5022],{},[73,14048,13125],{}," for CPU work or blocking calls.",[20,14051,14052],{},"Next: Macros — code that writes code.",{"title":117,"searchDepth":357,"depth":357,"links":14054},[14055,14056,14057,14058,14059,14060,14061,14062,14064,14065,14066,14067,14068,14069,14070,14071,14073,14074,14075,14076,14083,14084,14085],{"id":13347,"depth":357,"text":13348},{"id":13113,"depth":357,"text":13113},{"id":13381,"depth":357,"text":13382},{"id":13400,"depth":357,"text":13401},{"id":13452,"depth":357,"text":13453},{"id":13486,"depth":357,"text":13487},{"id":13506,"depth":357,"text":8579},{"id":13542,"depth":357,"text":14063},"Box\u003Cdyn Future> and Pin\u003CBox\u003Cdyn Future>>",{"id":13565,"depth":357,"text":13568},{"id":13580,"depth":357,"text":13581},{"id":13626,"depth":357,"text":13627},{"id":13646,"depth":357,"text":13649},{"id":13671,"depth":357,"text":13672},{"id":13697,"depth":357,"text":13698},{"id":13125,"depth":357,"text":13125},{"id":13742,"depth":357,"text":14072},"Holding Locks Across .await — Pitfall",{"id":13772,"depth":357,"text":13773},{"id":13787,"depth":357,"text":13788},{"id":13801,"depth":357,"text":13802},{"id":7894,"depth":357,"text":7895,"children":14077},[14078,14080,14082],{"id":13830,"depth":364,"text":14079},"Concurrency with join!",{"id":13846,"depth":364,"text":14081},"Concurrency with try_join!",{"id":13866,"depth":364,"text":13869},{"id":6468,"depth":357,"text":6469},{"id":13994,"depth":357,"text":13995},{"id":348,"depth":357,"text":349},"Async lets you write concurrent code that looks sequential. Rust's async is zero-cost — futures are state machines compiled by the compiler.",{},"\u002Frust\u002F23-async-await",{"title":13333,"description":14086},"rust\u002F23-async-await","IiBldr5YM-nmjZESo61RiAuSxeeLDh_acJt7BUTff5w",{"id":14093,"title":14094,"body":14095,"description":14102,"extension":373,"meta":14870,"navigation":375,"path":14871,"seo":14872,"stem":14873,"__hash__":14874},"content\u002Frust\u002F24-macros.md","24 — Macros",{"type":8,"value":14096,"toc":14838},[14097,14100,14103,14119,14123,14147,14153,14159,14165,14169,14242,14249,14255,14259,14265,14276,14280,14302,14306,14309,14315,14320,14326,14331,14335,14341,14345,14351,14357,14365,14370,14376,14385,14389,14498,14502,14509,14540,14544,14550,14558,14568,14574,14577,14583,14587,14593,14595,14601,14605,14611,14615,14621,14624,14628,14662,14666,14672,14675,14679,14773,14778,14784,14787,14791,14794,14808,14811,14813,14835],[11,14098,14094],{"id":14099},"_24-macros",[20,14101,14102],{},"Macros generate code at compile time. Rust has two kinds:",[3037,14104,14105,14113],{},[36,14106,14107,2681,14110,14112],{},[24,14108,14109],{},"Declarative macros",[73,14111,11670],{},"): pattern-matching code generators.",[36,14114,14115,14118],{},[24,14116,14117],{},"Procedural macros",": real Rust functions that consume\u002Fproduce token streams (custom derive, attribute, function-like).",[15,14120,14122],{"id":14121},"why-macros","Why Macros?",[33,14124,14125,14132,14135,14138],{},[36,14126,14127,14128,480,14130,526],{},"Variadic arguments (",[73,14129,428],{},[73,14131,2310],{},[36,14133,14134],{},"Compile-time string interpolation (format strings are checked).",[36,14136,14137],{},"Reducing boilerplate (derive macros).",[36,14139,14140,14141,292,14144,526],{},"DSLs (",[73,14142,14143],{},"html!",[73,14145,14146],{},"yew",[20,14148,14149,14150,14152],{},"Macros run ",[24,14151,3673],{}," the type checker — they expand into AST, which is then type-checked.",[15,14154,14156,14157],{"id":14155},"declarative-macros-macro_rules","Declarative Macros: ",[73,14158,11670],{},[111,14160,14163],{"className":14161,"code":14162,"language":397,"meta":117},[395],"macro_rules! vec_of {\n    ($($x:expr),*) => {{\n        let mut v = Vec::new();\n        $( v.push($x); )*\n        v\n    }};\n}\n\nlet v = vec_of!(1, 2, 3);\n",[73,14164,14162],{"__ignoreMap":117},[130,14166,14168],{"id":14167},"macro-syntax","Macro Syntax",[33,14170,14171,14177,14222,14236],{},[36,14172,14173,14176],{},[73,14174,14175],{},"$name",": a \"metavariable\".",[36,14178,14179,480,14182,480,14185,480,14188,480,14191,480,14194,480,14197,480,14200,480,14203,480,14206,480,14209,480,14212,480,14215,480,14218,14221],{},[73,14180,14181],{},":expr",[73,14183,14184],{},":ident",[73,14186,14187],{},":ty",[73,14189,14190],{},":tt",[73,14192,14193],{},":item",[73,14195,14196],{},":pat",[73,14198,14199],{},":stmt",[73,14201,14202],{},":literal",[73,14204,14205],{},":vis",[73,14207,14208],{},":lifetime",[73,14210,14211],{},":block",[73,14213,14214],{},":path",[73,14216,14217],{},":meta",[73,14219,14220],{},":expr_2021"," — fragment types.",[36,14223,14224,14227,14228,14231,14232,14235],{},[73,14225,14226],{},"$(...)*"," repeats zero or more; ",[73,14229,14230],{},"$(...)+"," repeats one or more; ",[73,14233,14234],{},"$(...)?"," optional.",[36,14237,14238,14239,14241],{},"Multiple arms separated by ",[73,14240,2090],{},", first match wins (top to bottom).",[130,14243,14245,14246,6030],{"id":14244},"example-a-hashmap-macro","Example: a ",[73,14247,14248],{},"hashmap!",[111,14250,14253],{"className":14251,"code":14252,"language":397,"meta":117},[395],"macro_rules! hashmap {\n    ($( $key:expr => $val:expr ),* $(,)?) => {{\n        let mut m = std::collections::HashMap::new();\n        $(\n            m.insert($key, $val);\n        )*\n        m\n    }};\n}\nlet m = hashmap!(\"a\" => 1, \"b\" => 2);\n",[73,14254,14252],{"__ignoreMap":117},[130,14256,14258],{"id":14257},"repetition-specifiers","Repetition Specifiers",[111,14260,14263],{"className":14261,"code":14262,"language":397,"meta":117},[395],"macro_rules! sum {\n    ($($x:expr),*) => { 0 $(+ $x)* };\n    ($first:expr $(, $rest:expr)*) => { $first $(+ $rest)* };\n}\n",[73,14264,14262],{"__ignoreMap":117},[20,14266,14267,14268,14271,14272,14275],{},"Two arms handle empty\u002Fone\u002Fmany. The first arm matches the empty case (sum of nothing = 0). The ",[73,14269,14270],{},"$(+ $x)*"," expands to ",[73,14273,14274],{},"+ $x"," repeated.",[130,14277,14279],{"id":14278},"fragment-capturing-and-follow-rules","Fragment Capturing and Follow Rules",[20,14281,14282,14283,14285,14286,14289,14290,14285,14292,14294,14295,14297,14298,14301],{},"Each fragment type has rules about what can follow it (because the parser is ambiguous otherwise). E.g., ",[73,14284,14181],{}," followed by ",[73,14287,14288],{},"=>"," is OK, but ",[73,14291,14181],{},[73,14293,2090],{}," is not (because ",[73,14296,2090],{}," could be part of the expression). Common workaround: use ",[73,14299,14300],{},"$(,)?"," for trailing commas.",[130,14303,14305],{"id":14304},"hygiene","Hygiene",[20,14307,14308],{},"Macro-introduced identifiers don't collide with caller identifiers:",[111,14310,14313],{"className":14311,"code":14312,"language":397,"meta":117},[395],"macro_rules! swap {\n    ($a:expr, $b:expr) => {\n        let tmp = $a;     \u002F\u002F 'tmp' is hygienic — won't clash with caller's tmp\n        $a = $b;\n        $b = tmp;\n    };\n}\nlet mut a = 1; let mut b = 2;\nswap!(a, b);\n",[73,14314,14312],{"__ignoreMap":117},[130,14316,14318],{"id":14317},"macro_export",[73,14319,14317],{},[111,14321,14324],{"className":14322,"code":14323,"language":397,"meta":117},[395],"#[macro_export]\nmacro_rules! my_macro {\n    ($x:expr) => { \u002F* ... *\u002F };\n}\n",[73,14325,14323],{"__ignoreMap":117},[20,14327,14328,14330],{},[73,14329,11773],{}," makes the macro available crate-wide and externally (placed at crate root, regardless of where it's defined).",[130,14332,14334],{"id":14333},"re-exporting","Re-Exporting",[111,14336,14339],{"className":14337,"code":14338,"language":397,"meta":117},[395],"pub use crate::my_macro;\n",[73,14340,14338],{"__ignoreMap":117},[130,14342,14344],{"id":14343},"importing","Importing",[111,14346,14349],{"className":14347,"code":14348,"language":397,"meta":117},[395],"use my_crate::my_macro;\n",[73,14350,14348],{"__ignoreMap":117},[20,14352,14353,14354,14356],{},"In edition 2018+, macros are imported via ",[73,14355,11535],{}," like any item.",[15,14358,14360,27,14362,14364],{"id":14359},"vec-and-println-internals",[73,14361,2310],{},[73,14363,428],{}," Internals",[20,14366,14367,14369],{},[73,14368,2310],{}," matches several patterns:",[111,14371,14374],{"className":14372,"code":14373,"language":397,"meta":117},[395],"macro_rules! vec {\n    () => ($crate::Vec::new());\n    ($elem:expr; $n:expr) => ($crate::vec::from_elem($elem, $n));\n    ($($x:expr),+ $(,)?) => ([$($x),+].into_iter().collect());\n}\n",[73,14375,14373],{"__ignoreMap":117},[20,14377,14378,14380,14381,14384],{},[73,14379,428],{}," parses the format string and expands into ",[73,14382,14383],{},"std::io::_print(format_args!(...))",". Format args are validated at compile time.",[15,14386,14388],{"id":14387},"common-built-in-macros","Common Built-in Macros",[33,14390,14391,14413,14420,14433,14443,14464,14484,14492],{},[36,14392,14393,480,14395,480,14398,480,14401,480,14404,480,14407,480,14410],{},[73,14394,428],{},[73,14396,14397],{},"eprintln!",[73,14399,14400],{},"print!",[73,14402,14403],{},"eprint!",[73,14405,14406],{},"format!",[73,14408,14409],{},"write!",[73,14411,14412],{},"writeln!",[36,14414,14415,480,14417],{},[73,14416,2310],{},[73,14418,14419],{},"format_args!",[36,14421,14422,480,14424,480,14427,480,14430],{},[73,14423,1814],{},[73,14425,14426],{},"unreachable!",[73,14428,14429],{},"todo!",[73,14431,14432],{},"unimplemented!",[36,14434,14435,480,14437,480,14439,480,14441],{},[73,14436,10361],{},[73,14438,12086],{},[73,14440,12089],{},[73,14442,10365],{},[36,14444,14445,480,14447,480,14450,480,14452,480,14455,480,14458,480,14461],{},[73,14446,6029],{},[73,14448,14449],{},"cfg!",[73,14451,811],{},[73,14453,14454],{},"option_env!",[73,14456,14457],{},"include!",[73,14459,14460],{},"include_str!",[73,14462,14463],{},"include_bytes!",[36,14465,14466,480,14469,480,14472,480,14475,480,14478,480,14481],{},[73,14467,14468],{},"concat!",[73,14470,14471],{},"stringify!",[73,14473,14474],{},"file!",[73,14476,14477],{},"line!",[73,14479,14480],{},"column!",[73,14482,14483],{},"module_path!",[36,14485,14486,480,14489],{},[73,14487,14488],{},"cfg",[73,14490,14491],{},"cfg_attr",[36,14493,14494,480,14496],{},[73,14495,14409],{},[73,14497,14412],{},[15,14499,14501],{"id":14500},"procedural-macros","Procedural Macros",[20,14503,14504,14505,14508],{},"Procedural macros run real Rust code (a separate crate of type ",[73,14506,14507],{},"proc-macro = true","). Three flavors:",[3037,14510,14511,14524,14532],{},[36,14512,14513,14516,14517,14520,14521,259],{},[24,14514,14515],{},"Function-like"," (custom ",[73,14518,14519],{},"macro!"," syntax): ",[73,14522,14523],{},"custom_macro!(...)",[36,14525,14526,71,14529,259],{},[24,14527,14528],{},"Derive",[73,14530,14531],{},"#[derive(MyTrait)]",[36,14533,14534,71,14537,259],{},[24,14535,14536],{},"Attribute",[73,14538,14539],{},"#[my_attr]",[130,14541,14543],{"id":14542},"setup","Setup",[111,14545,14548],{"className":14546,"code":14547,"language":212},[210],"my_crate\u002F\n├── Cargo.toml           # the user-facing crate\n└── my_crate_derive\u002F     # the proc-macro crate\n    └── Cargo.toml       # [lib] proc-macro = true\n",[73,14549,14547],{"__ignoreMap":117},[20,14551,14552,14553,292,14555,259],{},"Proc-macro crates must be separate and have ",[73,14554,14507],{},[73,14556,14557],{},"[lib]",[130,14559,14561,14562,1212,14565,1587],{"id":14560},"function-like-example-using-synquote","Function-Like Example (using ",[73,14563,14564],{},"syn",[73,14566,14567],{},"quote",[111,14569,14572],{"className":14570,"code":14571,"language":397,"meta":117},[395],"\u002F\u002F my_crate_derive\u002Fsrc\u002Flib.rs\nuse proc_macro::TokenStream;\nuse quote::quote;\nuse syn::{parse_macro_input, ItemStruct};\n\n#[proc_macro]\npub fn make_hello(_item: TokenStream) -> TokenStream {\n    \"fn hello() { println!(\\\"hi\\\"); }\".parse().unwrap()\n}\n",[73,14573,14571],{"__ignoreMap":117},[20,14575,14576],{},"Usage:",[111,14578,14581],{"className":14579,"code":14580,"language":397,"meta":117},[395],"use my_crate_derive::make_hello;\nmake_hello!();\nhello();\n",[73,14582,14580],{"__ignoreMap":117},[130,14584,14586],{"id":14585},"derive-example","Derive Example",[111,14588,14591],{"className":14589,"code":14590,"language":397,"meta":117},[395],"#[proc_macro_derive(Hello)]\npub fn derive_hello(input: TokenStream) -> TokenStream {\n    let input = parse_macro_input!(input as ItemStruct);\n    let name = &input.ident;\n    let expanded = quote! {\n        impl #name {\n            fn hello() { println!(\"Hello from {}\", stringify!(#name)); }\n        }\n    };\n    expanded.into()\n}\n",[73,14592,14590],{"__ignoreMap":117},[20,14594,14576],{},[111,14596,14599],{"className":14597,"code":14598,"language":397,"meta":117},[395],"#[derive(Hello)]\nstruct Foo;\nFoo::hello();\n",[73,14600,14598],{"__ignoreMap":117},[130,14602,14604],{"id":14603},"derive-with-helper-attributes","Derive with Helper Attributes",[111,14606,14609],{"className":14607,"code":14608,"language":397,"meta":117},[395],"#[proc_macro_derive(Hello, attributes(hello_name))]\npub fn derive_hello(input: TokenStream) -> TokenStream { \u002F* ... *\u002F }\n\n\u002F\u002F user:\n#[derive(Hello)]\n#[hello_name = \"Bar\"]\nstruct Foo;\n",[73,14610,14608],{"__ignoreMap":117},[130,14612,14614],{"id":14613},"attribute-macros","Attribute Macros",[111,14616,14619],{"className":14617,"code":14618,"language":397,"meta":117},[395],"#[proc_macro_attribute]\npub fn log_calls(attr: TokenStream, item: TokenStream) -> TokenStream { \u002F* ... *\u002F }\n",[73,14620,14618],{"__ignoreMap":117},[20,14622,14623],{},"Receives both the attribute arguments and the item being annotated.",[130,14625,14627],{"id":14626},"helper-crates","Helper Crates",[33,14629,14630,14635,14644,14650,14656],{},[36,14631,14632,14634],{},[73,14633,14564],{},": parse Rust syntax.",[36,14636,14637,14639,14640,14643],{},[73,14638,14567],{},": build TokenStreams with ",[73,14641,14642],{},"quote!"," macro.",[36,14645,14646,14649],{},[73,14647,14648],{},"proc-macro2",": works with stable Rust (proc_macro types are unstable-only).",[36,14651,14652,14655],{},[73,14653,14654],{},"darling",": ergonomic derive attribute parsing.",[36,14657,14658,14661],{},[73,14659,14660],{},"proc-macro-error",": better error reporting.",[15,14663,14665],{"id":14664},"built-in-derives","Built-in Derives",[111,14667,14670],{"className":14668,"code":14669,"language":397,"meta":117},[395],"#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]\nstruct Foo { \u002F* ... *\u002F }\n",[73,14671,14669],{"__ignoreMap":117},[20,14673,14674],{},"These are built into the compiler.",[15,14676,14678],{"id":14677},"macro-pitfalls","Macro Pitfalls",[33,14680,14681,14687,14693,14704,14714,14727,14737,14746,14757,14765],{},[36,14682,14683,14686],{},[24,14684,14685],{},"Order of arms",": declarative macros match top-to-bottom. Specific patterns must come before general ones.",[36,14688,14689,14692],{},[24,14690,14691],{},"Hygiene surprises",": macro-introduced variables are isolated; sometimes you want unhygienic behavior (rare).",[36,14694,14695,71,14698,14700,14701,14703],{},[24,14696,14697],{},"Expression vs statement fragments",[73,14699,14181],{}," captures the whole expression and double-evaluates if used multiple times. Use a ",[73,14702,868],{}," binding inside the macro to evaluate once.",[36,14705,14706,71,14708,14710,14711,526],{},[24,14707,2340],{},[73,14709,11670],{}," recursion is limited (default 64 deep; can be raised via ",[73,14712,14713],{},"#![recursion_limit = \"256\"]",[36,14715,14716,71,14719,14722,14723,14726],{},[24,14717,14718],{},"Debugging macros",[73,14720,14721],{},"cargo expand"," (install with ",[73,14724,14725],{},"cargo install cargo-expand",") shows the expanded code.",[36,14728,14729,14732,14733,14736],{},[24,14730,14731],{},"Compile time",": heavy macros (especially proc-macros like ",[73,14734,14735],{},"serde",") slow compilation.",[36,14738,14739,14745],{},[24,14740,14741,14744],{},[73,14742,14743],{},"proc-macro"," crate isolation",": a proc-macro crate can't export anything else; it's a separate compilation unit.",[36,14747,14748,71,14751,14753,14754,14756],{},[24,14749,14750],{},"Span info",[73,14752,14642],{},"'s default spans can produce confusing errors. Use ",[73,14755,14564],{},"'s spans carefully.",[36,14758,14759,14764],{},[24,14760,14761,14762],{},"Macro in ",[73,14763,11561],{},": ensure you re-export macros from a top-level module.",[36,14766,14767,14772],{},[24,14768,14769,14771],{},[73,14770,11773],{}," placement",": places the macro at the crate root regardless of where it's defined.",[15,14774,14776],{"id":14775},"cargo-expand",[73,14777,14721],{},[111,14779,14782],{"className":14780,"code":14781,"language":116,"meta":117},[114],"cargo install cargo-expand\ncargo expand\n",[73,14783,14781],{"__ignoreMap":117},[20,14785,14786],{},"Prints the post-macro-expansion source. Invaluable for debugging declarative and proc-macros.",[15,14788,14790],{"id":14789},"when-to-use-a-macro-vs-a-function","When to Use a Macro vs a Function",[20,14792,14793],{},"Use a macro when:",[33,14795,14796,14799,14802,14805],{},[36,14797,14798],{},"You need variadic arguments.",[36,14800,14801],{},"You need to take types as arguments.",[36,14803,14804],{},"You need to generate code based on structure (e.g., derive).",[36,14806,14807],{},"You need compile-time string parsing (format strings).",[20,14809,14810],{},"Otherwise, use a function (simpler, easier to debug, type-checks better).",[15,14812,349],{"id":348},[33,14814,14815,14821,14827,14832],{},[36,14816,14817,14818,14820],{},"Declarative macros (",[73,14819,11670],{},") pattern-match and emit code; hygiene prevents name collisions.",[36,14822,14823,14824,14826],{},"Proc-macros (separate ",[73,14825,14507],{}," crate) write real code: derive, attribute, function-like.",[36,14828,14829,14831],{},[73,14830,14721],{}," is essential for debugging.",[36,14833,14834],{},"Use macros sparingly — they're powerful but add compile-time cost and complexity.",[20,14836,14837],{},"Next: Unsafe Rust.",{"title":117,"searchDepth":357,"depth":357,"links":14839},[14840,14841,14853,14855,14856,14865,14866,14867,14868,14869],{"id":14121,"depth":357,"text":14122},{"id":14155,"depth":357,"text":14842,"children":14843},"Declarative Macros: macro_rules!",[14844,14845,14847,14848,14849,14850,14851,14852],{"id":14167,"depth":364,"text":14168},{"id":14244,"depth":364,"text":14846},"Example: a hashmap! macro",{"id":14257,"depth":364,"text":14258},{"id":14278,"depth":364,"text":14279},{"id":14304,"depth":364,"text":14305},{"id":14317,"depth":364,"text":14317},{"id":14333,"depth":364,"text":14334},{"id":14343,"depth":364,"text":14344},{"id":14359,"depth":357,"text":14854},"vec! and println! Internals",{"id":14387,"depth":357,"text":14388},{"id":14500,"depth":357,"text":14501,"children":14857},[14858,14859,14861,14862,14863,14864],{"id":14542,"depth":364,"text":14543},{"id":14560,"depth":364,"text":14860},"Function-Like Example (using syn\u002Fquote)",{"id":14585,"depth":364,"text":14586},{"id":14603,"depth":364,"text":14604},{"id":14613,"depth":364,"text":14614},{"id":14626,"depth":364,"text":14627},{"id":14664,"depth":357,"text":14665},{"id":14677,"depth":357,"text":14678},{"id":14775,"depth":357,"text":14721},{"id":14789,"depth":357,"text":14790},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F24-macros",{"title":14094,"description":14102},"rust\u002F24-macros","5Mmmkm6YbZqh5shpiC6X2zTfZuP7QUOcDL6Sqeou_gg",{"id":14876,"title":14877,"body":14878,"description":15803,"extension":373,"meta":15804,"navigation":375,"path":15805,"seo":15806,"stem":15807,"__hash__":15808},"content\u002Frust\u002F25-unsafe-rust.md","25 — Unsafe Rust",{"type":8,"value":14879,"toc":15759},[14880,14883,14891,14924,14931,14936,14942,14953,14959,14971,14975,14981,15008,15012,15024,15028,15034,15042,15048,15057,15063,15069,15079,15085,15090,15096,15106,15110,15116,15122,15129,15135,15151,15158,15164,15182,15186,15192,15200,15204,15217,15237,15244,15249,15255,15258,15262,15313,15319,15323,15326,15332,15339,15347,15355,15359,15365,15384,15388,15392,15398,15404,15410,15415,15422,15428,15439,15458,15462,15467,15473,15479,15487,15522,15524,15622,15628,15689,15693,15721,15730,15732,15756],[11,14881,14877],{"id":14882},"_25-unsafe-rust",[20,14884,14885,14887,14888,170],{},[73,14886,197],{}," lets you do things the compiler can't verify. It doesn't turn off the borrow checker — it adds five ",[24,14889,14890],{},"superpowers",[3037,14892,14893,14900,14906,14915,14921],{},[36,14894,14895,14896,480,14898,526],{},"Dereference raw pointers (",[73,14897,10971],{},[73,14899,10968],{},[36,14901,14902,14903,14905],{},"Call ",[73,14904,197],{}," functions (including FFI).",[36,14907,7549,14908,14910,14911,1212,14913,526],{},[73,14909,197],{}," traits (e.g., ",[73,14912,8563],{},[73,14914,8566],{},[36,14916,14917,14918,14920],{},"Access\u002Fmutate ",[73,14919,1051],{}," globals.",[36,14922,14923],{},"Access union fields.",[20,14925,14926,14927,14930],{},"The rest of Rust still applies (borrowing, types, lifetimes). Unsafe is a contract: ",[24,14928,14929],{},"you"," prove soundness; the compiler trusts you.",[15,14932,14934,5203],{"id":14933},"unsafe-blocks",[73,14935,197],{},[111,14937,14940],{"className":14938,"code":14939,"language":397,"meta":117},[395],"let p: *const i32 = &5;\nunsafe {\n    println!(\"{}\", *p);\n}\n",[73,14941,14939],{"__ignoreMap":117},[20,14943,14944,14947,14948,14950,14951,526],{},[73,14945,14946],{},"unsafe fn"," is a function whose body requires unsafe — calling it from safe code is allowed only in an ",[73,14949,197],{}," block (or ",[73,14952,14946],{},[111,14954,14957],{"className":14955,"code":14956,"language":397,"meta":117},[395],"unsafe fn dangerous() {}\nunsafe { dangerous(); }   \u002F\u002F OK\n",[73,14958,14956],{"__ignoreMap":117},[20,14960,14961,14962,14964,14965,14967,14968,14970],{},"In edition 2024+, calling ",[73,14963,14946],{}," inside ",[73,14966,14946],{}," also requires an explicit ",[73,14969,197],{}," block (no longer implicit).",[15,14972,14974],{"id":14973},"raw-pointers","Raw Pointers",[111,14976,14979],{"className":14977,"code":14978,"language":397,"meta":117},[395],"let x = 5;\nlet p1: *const i32 = &x;        \u002F\u002F implicit coercion\nlet p2: *mut i32 = &mut x as *mut i32;\n\nunsafe { println!(\"{} {}\", *p1, *p2); }\n",[73,14980,14978],{"__ignoreMap":117},[33,14982,14983,14991,14996,15001],{},[36,14984,14985,14987,14988,14990],{},[73,14986,10971],{}," (read-only) and ",[73,14989,10968],{}," (writable).",[36,14992,14993,14994,259],{},"Can be created from any reference, even outside ",[73,14995,197],{},[36,14997,14998,14999,259],{},"Dereferencing requires ",[73,15000,197],{},[36,15002,10558,15003,1212,15005,15007],{},[73,15004,8563],{},[73,15006,8566],{}," by default.",[130,15009,15011],{"id":15010},"validity-invariants","Validity Invariants",[20,15013,15014,15015,15017,15018,15020,15021,259],{},"Reading a ",[73,15016,10971],{}," requires the pointer to point to a valid ",[73,15019,4705],{}," (properly initialized, properly aligned). Dereferencing an uninitialized or misaligned pointer is ",[24,15022,15023],{},"undefined behavior (UB)",[15,15025,15027],{"id":15026},"ffi","FFI",[111,15029,15032],{"className":15030,"code":15031,"language":397,"meta":117},[395],"extern \"C\" {\n    fn abs(x: i32) -> i32;\n}\n\nfn main() {\n    unsafe { println!(\"{}\", abs(-5)); }\n}\n",[73,15033,15031],{"__ignoreMap":117},[20,15035,15036,15037,15039,15040,170],{},"Calling C functions requires ",[73,15038,197],{},". Functions can be marked ",[73,15041,2295],{},[111,15043,15046],{"className":15044,"code":15045,"language":397,"meta":117},[395],"#[no_mangle]\npub extern \"C\" fn add(a: i32, b: i32) -> i32 { a + b }\n",[73,15047,15045],{"__ignoreMap":117},[20,15049,15050,15053,15054,15056],{},[73,15051,15052],{},"#[no_mangle]"," preserves the symbol name for C to call. ",[73,15055,2295],{}," sets the ABI.",[15,15058,15060,15062],{"id":15059},"unsafe-traits",[73,15061,197],{}," Traits",[111,15064,15067],{"className":15065,"code":15066,"language":397,"meta":117},[395],"unsafe trait TrustedIter {}\nunsafe impl TrustedIter for std::slice::Iter\u003C'static, u8> {}\n",[73,15068,15066],{"__ignoreMap":117},[20,15070,15071,27,15073,15075,15076,15078],{},[73,15072,8563],{},[73,15074,8566],{}," are unsafe traits; the compiler auto-derives them, but you can opt in via ",[73,15077,12718],{}," if you've verified thread-safety.",[111,15080,15083],{"className":15081,"code":15082,"language":397,"meta":117},[395],"struct MyType(*mut u8);\nunsafe impl Send for MyType {}     \u002F\u002F we promise the pointer is safe to move to another thread\n",[73,15084,15082],{"__ignoreMap":117},[15,15086,15088],{"id":15087},"static-mut",[73,15089,1051],{},[111,15091,15094],{"className":15092,"code":15093,"language":397,"meta":117},[395],"static mut COUNTER: u32 = 0;\n\nfn incr() {\n    unsafe { COUNTER += 1; }\n}\n",[73,15095,15093],{"__ignoreMap":117},[33,15097,15098,15103],{},[36,15099,15100,15101,259],{},"Reading\u002Fwriting requires ",[73,15102,197],{},[36,15104,15105],{},"No synchronization — use atomics instead.",[15,15107,15109],{"id":15108},"unions","Unions",[111,15111,15114],{"className":15112,"code":15113,"language":397,"meta":117},[395],"union Value {\n    int_val: i32,\n    float_val: f32,\n}\n\nlet v = Value { int_val: 5 };\nunsafe { println!(\"{}\", v.float_val); }   \u002F\u002F ⚠️ UB if int_val was the active field\n",[73,15115,15113],{"__ignoreMap":117},[20,15117,15118,15119,15121],{},"Unions overlap memory; reading the inactive field is UB. Reading requires ",[73,15120,197],{},". Useful for FFI\u002FC interop; otherwise use enums.",[15,15123,15125,15128],{"id":15124},"maybeuninitt-uninitialized-memory",[73,15126,15127],{},"MaybeUninit\u003CT>"," — Uninitialized Memory",[111,15130,15133],{"className":15131,"code":15132,"language":397,"meta":117},[395],"use std::mem::MaybeUninit;\n\nlet mut mu: MaybeUninit\u003CVec\u003Cu8>> = MaybeUninit::uninit();\nunsafe { mu.write(Vec::new()); }\nlet v: Vec\u003Cu8> = unsafe { mu.assume_init() };\n",[73,15134,15132],{"__ignoreMap":117},[20,15136,15137,15139,15140,15143,15144,15146,15147,15150],{},[73,15138,15127],{}," is the safe way to ",[183,15141,15142],{},"hold"," uninitialized memory; reading it requires ",[73,15145,197],{},". The standard alternative to uninitialized ",[73,15148,15149],{},"mem::uninitialized"," (deprecated).",[15,15152,15154,15157],{"id":15153},"manuallydropt-suppress-drop",[73,15155,15156],{},"ManuallyDrop\u003CT>"," — Suppress Drop",[111,15159,15162],{"className":15160,"code":15161,"language":397,"meta":117},[395],"use std::mem::ManuallyDrop;\nlet s = ManuallyDrop::new(String::from(\"hi\"));\n\u002F\u002F s's destructor won't run; manual cleanup needed\nunsafe { drop(ManuallyDrop::into_inner(s)) };   \u002F\u002F no, into_inner extracts\n",[73,15163,15161],{"__ignoreMap":117},[20,15165,15166,15168,15169,15171,15172,7020,15174,15177,15178,15181],{},[73,15167,15156],{}," wraps a ",[73,15170,4705],{}," and disables its ",[73,15173,3217],{},[73,15175,15176],{},"ManuallyDrop::into_inner"," to recover the value, or ",[73,15179,15180],{},"unsafe { ManuallyDrop::take(&mut md) }"," to extract without dropping.",[15,15183,15185],{"id":15184},"splitting-borrows-safely","Splitting Borrows Safely",[111,15187,15190],{"className":15188,"code":15189,"language":397,"meta":117},[395],"let mut v = vec![1, 2, 3, 4];\nlet slice: &mut [i32] = &mut v[..];\nlet (left, right) = slice.split_at_mut(2);\n\u002F\u002F left = &mut [1, 2], right = &mut [3, 4]\n",[73,15191,15189],{"__ignoreMap":117},[20,15193,15194,15196,15197,15199],{},[73,15195,4207],{}," is internally ",[73,15198,197],{}," because the compiler can't prove disjointness, but it's a safe API.",[15,15201,15203],{"id":15202},"unsafe-code-soundness","Unsafe Code Soundness",[20,15205,15206,15207,15209,15210,15213,15214,15216],{},"A piece of ",[73,15208,197],{}," code is ",[24,15211,15212],{},"sound"," if safe code can't trigger UB through its public API. Writing sound ",[73,15215,197],{}," requires:",[33,15218,15219,15222,15228,15231],{},[36,15220,15221],{},"Reasoning about aliasing, alignment, lifetimes, initialization, thread-safety.",[36,15223,15224,15225,526],{},"Documenting invariants (",[73,15226,15227],{},"\u002F\u002F SAFETY: ...",[36,15229,15230],{},"Considering all possible inputs.",[36,15232,15233,15234,15236],{},"Not leaking ",[73,15235,197],{}," to safe callers.",[20,15238,15239,15240,15243],{},"Miri (",[73,15241,15242],{},"cargo +nightly miri test",") is a tool that detects UB in unsafe code at runtime — use it.",[15,15245,15247],{"id":15246},"miri",[73,15248,15246],{},[111,15250,15253],{"className":15251,"code":15252,"language":116,"meta":117},[114],"rustup +nightly component add miri\ncargo +nightly miri test\n",[73,15254,15252],{"__ignoreMap":117},[20,15256,15257],{},"Miri interprets your code and catches many UB forms (invalid pointer arithmetic, unaligned access, data races in some cases, use of uninitialized memory). Doesn't catch all bugs but catches many.",[15,15259,15261],{"id":15260},"common-sources-of-ub","Common Sources of UB",[33,15263,15264,15267,15270,15273,15276,15281,15284,15290,15296,15304],{},[36,15265,15266],{},"Dereferencing a NULL, dangling, or misaligned pointer.",[36,15268,15269],{},"Reading uninitialized memory as a typed value.",[36,15271,15272],{},"Reading a union's inactive field.",[36,15274,15275],{},"Data races (concurrent reads + writes to the same memory without synchronization).",[36,15277,15278,15279,526],{},"Mutating immutable data (via ",[73,15280,197],{},[36,15282,15283],{},"Calling a function with the wrong ABI.",[36,15285,15286,15287,15289],{},"Violating the ",[73,15288,3217],{}," ordering or skipping destructors of owned data.",[36,15291,15292,15293,15295],{},"Integer overflow in ",[73,15294,197],{}," (e.g., pointer arithmetic that wraps).",[36,15297,15298,15299,15301,15302,526],{},"Unwinding across FFI boundaries (set ",[73,15300,9964],{}," or use ",[73,15303,10178],{},[36,15305,15306,15307,15309,15310,526],{},"Constructing invalid enum values (e.g., transmuting a number to ",[73,15308,5892],{}," that creates ",[73,15311,15312],{},"Some(null)",[15,15314,15316,15318],{"id":15315},"unsafe-patterns",[73,15317,197],{}," Patterns",[130,15320,15322],{"id":15321},"safe-abstractions-over-unsafe","Safe Abstractions over Unsafe",[20,15324,15325],{},"The idiomatic way: expose a safe API, do the unsafe internally:",[111,15327,15330],{"className":15328,"code":15329,"language":397,"meta":117},[395],"pub fn first_byte(s: &str) -> u8 {\n    let ptr = s.as_ptr();\n    unsafe { *ptr }   \u002F\u002F SAFETY: ptr is valid (s is a valid &str)\n}\n",[73,15331,15329],{"__ignoreMap":117},[20,15333,15334,15335,15338],{},"Comment with ",[73,15336,15337],{},"\u002F\u002F SAFETY:"," explaining why each unsafe operation is sound.",[130,15340,15342,1212,15345],{"id":15341},"unsafe-impl-sendsync",[73,15343,15344],{},"unsafe impl Send",[73,15346,8566],{},[20,15348,15349,15350,1212,15352,15354],{},"Only when you've verified the type can be safely transferred\u002Fshared across threads. Usually because the inner is ",[73,15351,8563],{},[73,15353,8566],{}," via raw pointer you control.",[130,15356,15358],{"id":15357},"reusing-raw-memory","Reusing Raw Memory",[111,15360,15363],{"className":15361,"code":15362,"language":397,"meta":117},[395],"let mut buf: Vec\u003Cu8> = Vec::with_capacity(100);\nlet ptr = buf.as_mut_ptr() as *mut u64;\nunsafe { *ptr = 5; }   \u002F\u002F ⚠️ requires valid alignment and within capacity\n",[73,15364,15362],{"__ignoreMap":117},[20,15366,15367,15369,15370,480,15372,1356,15374,15376,15377,15380,15381,259],{},[73,15368,6904],{}," allocations are aligned to ",[73,15371,1359],{},[24,15373,1578],{},[73,15375,1406],{},". To get a properly-aligned buffer, use ",[73,15378,15379],{},"Vec\u003Cu64>"," directly or ",[73,15382,15383],{},"alloc::alloc_aligned",[15,15385,15387],{"id":15386},"ffi-patterns","FFI Patterns",[130,15389,15391],{"id":15390},"owning-foreign-memory","Owning Foreign Memory",[111,15393,15396],{"className":15394,"code":15395,"language":397,"meta":117},[395],"struct Buffer(*mut u8, usize);\nimpl Drop for Buffer {\n    fn drop(&mut self) {\n        unsafe { free(self.0) }\n    }\n}\nunsafe impl Send for Buffer {}\n",[73,15397,15395],{"__ignoreMap":117},[20,15399,15400,15401,15403],{},"RAII: the constructor allocates, ",[73,15402,3217],{}," deallocates.",[130,15405,15407,15409],{"id":15406},"extern-c-block-with-variadics",[73,15408,2295],{}," Block with Variadics",[111,15411,15413],{"className":15412,"code":2300,"language":397,"meta":117},[395],[73,15414,2300],{"__ignoreMap":117},[130,15416,15418,15421],{"id":15417},"link-attributes",[73,15419,15420],{},"link"," Attributes",[111,15423,15426],{"className":15424,"code":15425,"language":397,"meta":117},[395],"#[link(name = \"crypto\")]\nextern \"C\" {\n    fn sha256(input: *const u8, len: usize) -> *mut u8;\n}\n",[73,15427,15425],{"__ignoreMap":117},[15,15429,15431,480,15433,480,15436],{"id":15430},"no_mangle-export_name-link_name",[73,15432,15052],{},[73,15434,15435],{},"#[export_name]",[73,15437,15438],{},"#[link_name]",[33,15440,15441,15446,15452],{},[36,15442,15443,15445],{},[73,15444,15052],{},": keep the function's name as-is in the symbol table.",[36,15447,15448,15451],{},[73,15449,15450],{},"#[export_name = \"foo\"]",": rename the exported symbol.",[36,15453,15454,15457],{},[73,15455,15456],{},"#[link_name = \"...\"]",": rename the symbol you're linking against.",[15,15459,15461],{"id":15460},"bindgen","Bindgen",[20,15463,1876,15464,15466],{},[73,15465,15460],{}," to auto-generate Rust FFI bindings from C headers:",[111,15468,15471],{"className":15469,"code":15470,"language":176,"meta":117},[174],"[build-dependencies]\nbindgen = \"0.69\"\n",[73,15472,15470],{"__ignoreMap":117},[111,15474,15477],{"className":15475,"code":15476,"language":397,"meta":117},[395],"\u002F\u002F build.rs\nfn main() {\n    let bindings = bindgen::Builder::default()\n        .header(\"wrapper.h\")\n        .generate().unwrap();\n    bindings.write_to_file(\"src\u002Fbindings.rs\").unwrap();\n}\n",[73,15478,15476],{"__ignoreMap":117},[15,15480,15482,559,15484],{"id":15481},"extern-c-vs-extern-rust",[73,15483,2295],{},[73,15485,15486],{},"extern \"Rust\"",[20,15488,15489,15490,15492,15493,15495,15496,480,15499,15502,15503,15505,15506,15509,15510,480,15513,480,15516,480,15519,259],{},"The default ABI is ",[73,15491,15486],{}," (not stable to name explicitly until 1.86+). C ABI is ",[73,15494,2295],{},". Other ABIs: ",[73,15497,15498],{},"stdcall",[73,15500,15501],{},"system"," (Windows: ",[73,15504,15498],{}," on x86, ",[73,15507,15508],{},"C"," on x64), ",[73,15511,15512],{},"aapcs",[73,15514,15515],{},"fastcall",[73,15517,15518],{},"win64",[73,15520,15521],{},"sysv64",[15,15523,711],{"id":710},[33,15525,15526,15539,15550,15558,15566,15578,15589,15606],{},[36,15527,15528,15533,15534,15536,15537,259],{},[24,15529,15530,15532],{},[73,15531,197],{}," doesn't disable the borrow checker",": you still can't have aliasing ",[73,15535,1117],{}," even with ",[73,15538,197],{},[36,15540,15541,15546,15547,15549],{},[24,15542,15543,15545],{},[73,15544,14946],{}," body has implicit unsafe in pre-2024 editions",": 2024 changes this — explicit ",[73,15548,197],{}," blocks required inside.",[36,15551,15552,15557],{},[24,15553,15554],{},[73,15555,15556],{},"std::mem::transmute",": reinterprets bytes as another type. Extremely dangerous (size\u002Fvalidity\u002Falignment). Avoid; use specific methods.",[36,15559,15560,15565],{},[24,15561,15562],{},[73,15563,15564],{},"std::mem::transmute_copy",": reads bytes from one place as another type — also very dangerous.",[36,15567,15568,15575,15576,259],{},[24,15569,15570,27,15572],{},[73,15571,197],{},[73,15573,15574],{},"async",": async unsafe functions are unstable; you can wrap blocking unsafe calls in ",[73,15577,13125],{},[36,15579,15580,15585,15586,15588],{},[24,15581,15582,15584],{},[73,15583,1051],{}," races",": undetectable by ",[73,15587,12179],{}," in many cases; use atomics.",[36,15590,15591,7163,15599,15601,15602,10642,15604,259],{},[24,15592,15593,1212,15595,27,15597],{},[73,15594,5449],{},[73,15596,5452],{},[73,15598,8563],{},[73,15600,8566],{},", but they are ",[73,15603,8563],{},[73,15605,11028],{},[36,15607,15608,15613,15614,1052,15616,15618,15619,15621],{},[24,15609,15610,15611],{},"Pin and ",[73,15612,197],{},": implementing your own ",[73,15615,13499],{},[73,15617,197],{}," because of ",[73,15620,8579],{}," invariants.",[15,15623,15625,15627],{"id":15624},"unsafe-anti-patterns",[73,15626,197],{}," Anti-Patterns",[33,15629,15630,15643,15652,15664,15677],{},[36,15631,15632,71,15637,15639,15640,15642],{},[24,15633,15634],{},[73,15635,15636],{},"unsafe impl Send for Rc\u003CT>",[73,15638,3803],{}," has a non-atomic refcount; making it ",[73,15641,8563],{}," causes data races.",[36,15644,15645,15651],{},[24,15646,15647,15648,15650],{},"Bare ",[73,15649,10968],{}," in public API",": exposes raw pointer semantics; wrap in a safe abstraction.",[36,15653,15654,2927,15660,1212,15662,259],{},[24,15655,15656,15659],{},[73,15657,15658],{},"transmute"," for type conversions",[73,15661,1879],{},[73,15663,1885],{},[36,15665,15666,15673,15674,15676],{},[24,15667,15668,2806,15670],{},[73,15669,14946],{},[73,15671,15672],{},"&'static T"," without ",[73,15675,4560],{}," input: usually lies.",[36,15678,15679,71,15682,15684,15685,15688],{},[24,15680,15681],{},"Assuming pointer alignment",[73,15683,1766],{}," is aligned to 1; casting to ",[73,15686,15687],{},"&u64"," is UB unless you check.",[15,15690,15692],{"id":15691},"when-to-use-unsafe","When to Use Unsafe",[33,15694,15695,15698,15708,15711,15714],{},[36,15696,15697],{},"FFI to C.",[36,15699,15700,15701,480,15703,480,15705,15707],{},"Implementing low-level collections (",[73,15702,1194],{},[73,15704,1687],{},[73,15706,7254],{}," internals).",[36,15709,15710],{},"Performance-critical code that can't be expressed safely (rare; usually the compiler is fine).",[36,15712,15713],{},"Interfacing with the OS (syscalls).",[36,15715,8611,15716,1212,15718,15720],{},[73,15717,8563],{},[73,15719,8566],{}," for a wrapper you control.",[20,15722,15723,15724,7020,15727,15729],{},"Most Rust code is ",[24,15725,15726],{},"fully safe",[73,15728,197],{}," sparingly; confine it to small, well-reviewed modules.",[15,15731,349],{"id":348},[20,15733,15734,15736,15737,15740,15741,15743,15744,15747,15748,1212,15750,15752,15753,15755],{},[73,15735,197],{}," gives you five superpowers; the rest of Rust still applies. Aim for ",[24,15738,15739],{},"safe abstractions",": do the unsafe internally, expose a safe API, document ",[73,15742,15337],{}," invariants. Use ",[73,15745,15746],{},"Miri"," to catch UB. ",[73,15749,3354],{},[73,15751,3351],{}," are safer than the legacy uninitialized-memory APIs. Confine ",[73,15754,197],{}," to small audited surfaces.",[20,15757,15758],{},"Next: FFI deep dive.",{"title":117,"searchDepth":357,"depth":357,"links":15760},[15761,15763,15766,15767,15769,15770,15771,15773,15775,15776,15777,15778,15779,15786,15793,15795,15796,15798,15799,15801,15802],{"id":14933,"depth":357,"text":15762},"unsafe Blocks",{"id":14973,"depth":357,"text":14974,"children":15764},[15765],{"id":15010,"depth":364,"text":15011},{"id":15026,"depth":357,"text":15027},{"id":15059,"depth":357,"text":15768},"unsafe Traits",{"id":15087,"depth":357,"text":1051},{"id":15108,"depth":357,"text":15109},{"id":15124,"depth":357,"text":15772},"MaybeUninit\u003CT> — Uninitialized Memory",{"id":15153,"depth":357,"text":15774},"ManuallyDrop\u003CT> — Suppress Drop",{"id":15184,"depth":357,"text":15185},{"id":15202,"depth":357,"text":15203},{"id":15246,"depth":357,"text":15246},{"id":15260,"depth":357,"text":15261},{"id":15315,"depth":357,"text":15780,"children":15781},"unsafe Patterns",[15782,15783,15785],{"id":15321,"depth":364,"text":15322},{"id":15341,"depth":364,"text":15784},"unsafe impl Send\u002FSync",{"id":15357,"depth":364,"text":15358},{"id":15386,"depth":357,"text":15387,"children":15787},[15788,15789,15791],{"id":15390,"depth":364,"text":15391},{"id":15406,"depth":364,"text":15790},"extern \"C\" Block with Variadics",{"id":15417,"depth":364,"text":15792},"link Attributes",{"id":15430,"depth":357,"text":15794},"#[no_mangle], #[export_name], #[link_name]",{"id":15460,"depth":357,"text":15461},{"id":15481,"depth":357,"text":15797},"extern \"C\" vs extern \"Rust\"",{"id":710,"depth":357,"text":711},{"id":15624,"depth":357,"text":15800},"unsafe Anti-Patterns",{"id":15691,"depth":357,"text":15692},{"id":348,"depth":357,"text":349},"unsafe lets you do things the compiler can't verify. It doesn't turn off the borrow checker — it adds five superpowers:",{},"\u002Frust\u002F25-unsafe-rust",{"title":14877,"description":15803},"rust\u002F25-unsafe-rust","e8HANNgyX2nv9TPO-GlrnH-cJ3LWZq2ALqIV2tHsAcc",{"id":15810,"title":15811,"body":15812,"description":15819,"extension":373,"meta":16641,"navigation":375,"path":16642,"seo":16643,"stem":16644,"__hash__":16645},"content\u002Frust\u002F26-ffi.md","26 — FFI (Foreign Function Interface)",{"type":8,"value":15813,"toc":16603},[15814,15817,15820,15824,15830,15846,15850,15858,15864,15870,15876,15882,15888,15894,15900,15906,15910,15913,15919,15928,15932,15938,15955,15958,15964,15999,16003,16009,16015,16023,16029,16049,16059,16062,16068,16085,16089,16095,16104,16108,16119,16123,16138,16142,16148,16169,16173,16180,16186,16192,16196,16202,16213,16217,16220,16235,16241,16245,16249,16255,16262,16269,16275,16280,16284,16290,16296,16302,16309,16314,16320,16325,16331,16334,16368,16374,16378,16384,16394,16400,16402,16490,16494,16570,16572,16600],[11,15815,15811],{"id":15816},"_26-ffi-foreign-function-interface",[20,15818,15819],{},"Rust talks to C — and through C, to almost every other language. This chapter covers calling C from Rust, Rust from C, and the supporting ecosystem.",[15,15821,15823],{"id":15822},"calling-c-from-rust","Calling C from Rust",[111,15825,15828],{"className":15826,"code":15827,"language":397,"meta":117},[395],"extern \"C\" {\n    fn abs(x: i32) -> i32;\n}\n\nfn main() {\n    let x = unsafe { abs(-5) };\n    println!(\"{x}\");\n}\n",[73,15829,15827],{"__ignoreMap":117},[33,15831,15832,15837,15843],{},[36,15833,15834,15836],{},[73,15835,2295],{}," declares a foreign function with the C ABI.",[36,15838,15839,15840,15842],{},"Calling requires ",[73,15841,197],{}," (the compiler can't verify the signature or memory safety).",[36,15844,15845],{},"The linker resolves the symbol at link time.",[130,15847,15849],{"id":15848},"linking","Linking",[20,15851,15852,15853,15855,15856,170],{},"Add the C library to ",[73,15854,169],{}," via ",[73,15857,792],{},[111,15859,15862],{"className":15860,"code":15861,"language":397,"meta":117},[395],"\u002F\u002F build.rs\nfn main() {\n    println!(\"cargo:rustc-link-lib=c\");\n}\n",[73,15863,15861],{"__ignoreMap":117},[20,15865,15866,15867,170],{},"Or use ",[73,15868,15869],{},"#[link(name = \"mylib\")]",[111,15871,15874],{"className":15872,"code":15873,"language":397,"meta":117},[395],"#[link(name = \"mylib\")]\nextern \"C\" {\n    fn my_func(x: i32) -> i32;\n}\n",[73,15875,15873],{"__ignoreMap":117},[130,15877,15879,15881],{"id":15878},"bindgen-for-auto-binding",[73,15880,15460],{}," for Auto-Binding",[20,15883,15884,15885,15887],{},"Hand-writing extern blocks is error-prone. ",[73,15886,15460],{}," generates Rust bindings from C headers:",[111,15889,15892],{"className":15890,"code":15891,"language":176,"meta":117},[174],"# Cargo.toml\n[build-dependencies]\nbindgen = \"0.69\"\n",[73,15893,15891],{"__ignoreMap":117},[111,15895,15898],{"className":15896,"code":15897,"language":397,"meta":117},[395],"\u002F\u002F build.rs\nuse std::env;\nuse std::path::PathBuf;\n\nfn main() {\n    let bindings = bindgen::Builder::default()\n        .header(\"wrapper.h\")\n        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))\n        .generate()\n        .expect(\"Unable to generate bindings\");\n\n    let out_path = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n    bindings.write_to_file(out_path.join(\"bindings.rs\")).unwrap();\n}\n",[73,15899,15897],{"__ignoreMap":117},[111,15901,15904],{"className":15902,"code":15903,"language":397,"meta":117},[395],"\u002F\u002F src\u002Flib.rs\ninclude!(concat!(env!(\"OUT_DIR\"), \"\u002Fbindings.rs\"));\n",[73,15905,15903],{"__ignoreMap":117},[130,15907,15909],{"id":15908},"wrapping-in-safe-apis","Wrapping in Safe APIs",[20,15911,15912],{},"Raw FFI bindings are unsafe. Wrap them:",[111,15914,15917],{"className":15915,"code":15916,"language":397,"meta":117},[395],"mod sys {\n    extern \"C\" {\n        pub fn strlen(s: *const u8) -> usize;\n    }\n}\n\npub fn strlen(s: &CStr) -> usize {\n    unsafe { sys::strlen(s.as_ptr()) }\n}\n",[73,15918,15916],{"__ignoreMap":117},[20,15920,15921,1212,15924,15927],{},[73,15922,15923],{},"CStr",[73,15925,15926],{},"CString"," are the safe wrappers around C's null-terminated strings.",[15,15929,15931],{"id":15930},"calling-rust-from-c","Calling Rust from C",[111,15933,15936],{"className":15934,"code":15935,"language":397,"meta":117},[395],"#[no_mangle]\npub extern \"C\" fn add(a: i32, b: i32) -> i32 {\n    a + b\n}\n",[73,15937,15935],{"__ignoreMap":117},[33,15939,15940,15949],{},[36,15941,15942,15944,15945,15948],{},[73,15943,15052],{},": keep the symbol name exactly ",[73,15946,15947],{},"add"," (don't mangle).",[36,15950,15951,15954],{},[73,15952,15953],{},"pub extern \"C\"",": export with C ABI.",[20,15956,15957],{},"Build as a static or dynamic library:",[111,15959,15962],{"className":15960,"code":15961,"language":176,"meta":117},[174],"[lib]\ncrate-type = [\"staticlib\", \"cdylib\", \"rlib\"]\n",[73,15963,15961],{"__ignoreMap":117},[33,15965,15966,15978,15993],{},[36,15967,15968,71,15971,1212,15974,15977],{},[73,15969,15970],{},"staticlib",[73,15972,15973],{},".a",[73,15975,15976],{},".lib"," static archive.",[36,15979,15980,71,15983,1212,15986,1212,15989,15992],{},[73,15981,15982],{},"cdylib",[73,15984,15985],{},".so",[73,15987,15988],{},".dylib",[73,15990,15991],{},".dll"," dynamic library.",[36,15994,15995,15998],{},[73,15996,15997],{},"rlib",": Rust-specific (for other Rust crates).",[130,16000,16002],{"id":16001},"c-header","C Header",[20,16004,16005,16006,170],{},"Generate a header for C consumers with ",[73,16007,16008],{},"cbindgen",[111,16010,16013],{"className":16011,"code":16012,"language":116,"meta":117},[114],"cargo install cbindgen\ncbindgen --crate my_lib --output my_lib.h\n",[73,16014,16012],{"__ignoreMap":117},[15,16016,16018,16019,27,16021],{"id":16017},"c-strings-cstring-and-cstr","C Strings: ",[73,16020,15926],{},[73,16022,15923],{},[111,16024,16027],{"className":16025,"code":16026,"language":397,"meta":117},[395],"use std::ffi::{CString, CStr};\n\nlet c_string = CString::new(\"hello\").unwrap();\nlet ptr: *const u8 = c_string.as_ptr();    \u002F\u002F null-terminated\nlet cstr = unsafe { CStr::from_ptr(ptr) };\nlet rust_str = cstr.to_str().unwrap();\n",[73,16028,16026],{"__ignoreMap":117},[33,16030,16031,16038],{},[36,16032,16033,16035,16036,526],{},[73,16034,15926],{},": owned, null-terminated; can't contain interior NUL bytes (constructor returns ",[73,16037,2792],{},[36,16039,16040,16042,16043,16046,16047,259],{},[73,16041,15923],{},": borrowed, null-terminated; from ",[73,16044,16045],{},"from_ptr"," (unsafe) or by deref of ",[73,16048,15926],{},[15,16050,16052,16053,27,16056],{"id":16051},"os-strings-osstring-and-osstr","OS Strings: ",[73,16054,16055],{},"OsString",[73,16057,16058],{},"OsStr",[20,16060,16061],{},"For platform-native strings (file paths, env):",[111,16063,16066],{"className":16064,"code":16065,"language":397,"meta":117},[395],"use std::ffi::OsString;\nlet s: OsString = std::env::args_os().next().unwrap();\n",[73,16067,16065],{"__ignoreMap":117},[33,16069,16070,16077],{},[36,16071,16072,1212,16074,16076],{},[73,16073,16055],{},[73,16075,16058],{}," are the OS-native string equivalents.",[36,16078,16079,1212,16082,16084],{},[73,16080,16081],{},"PathBuf",[73,16083,11483],{}," are wrappers for path semantics (cross-platform).",[15,16086,16088],{"id":16087},"memory-ownership-across-ffi","Memory Ownership Across FFI",[111,16090,16093],{"className":16091,"code":16092,"language":397,"meta":117},[395],"\u002F\u002F Rust allocates, C frees\n#[no_mangle]\npub extern \"C\" fn make_string() -> *mut u8 {\n    let s = CString::new(\"hello\").unwrap();\n    s.into_raw()      \u002F\u002F leaks ownership to C\n}\n\n\u002F\u002F C frees via this\n#[no_mangle]\npub extern \"C\" fn free_string(ptr: *mut u8) {\n    unsafe { let _ = CString::from_raw(ptr); }\n}\n",[73,16094,16092],{"__ignoreMap":117},[20,16096,16097,1212,16100,16103],{},[73,16098,16099],{},"CString::into_raw",[73,16101,16102],{},"from_raw"," are the standard pattern for handing Rust strings to C and getting them back.",[130,16105,16107],{"id":16106},"c-allocates-rust-frees","C allocates, Rust frees",[20,16109,16110,16111,16114,16115,16118],{},"If C allocates with ",[73,16112,16113],{},"malloc",", Rust must call ",[73,16116,16117],{},"free"," (or the equivalent), not Rust's allocator. Provide a destructor function on the C side.",[130,16120,16122],{"id":16121},"common-pitfall-mismatched-allocators","Common Pitfall: Mismatched Allocators",[20,16124,16125,16126,1212,16129,16132,16133,1212,16135,16137],{},"Rust's ",[73,16127,16128],{},"Vec::push",[73,16130,16131],{},"String::push"," use Rust's allocator. C's ",[73,16134,16113],{},[73,16136,16117],{}," use the C library. Mixing them is UB. Always free with the allocator that allocated.",[15,16139,16141],{"id":16140},"structs-across-ffi","Structs Across FFI",[111,16143,16146],{"className":16144,"code":16145,"language":397,"meta":117},[395],"#[repr(C)]\nstruct Point {\n    x: f64,\n    y: f64,\n}\n\n#[no_mangle]\npub extern \"C\" fn translate(p: Point, dx: f64, dy: f64) -> Point {\n    Point { x: p.x + dx, y: p.y + dy }\n}\n",[73,16147,16145],{"__ignoreMap":117},[33,16149,16150,16155,16158],{},[36,16151,16152,16154],{},[73,16153,5557],{}," forces C-compatible layout (no Rust-specific reordering).",[36,16156,16157],{},"Field order matters and matches C's.",[36,16159,16160,16161,1212,16163,292,16165,16168],{},"Avoid ",[73,16162,10461],{},[73,16164,4930],{},[73,16166,16167],{},"repr(C)"," structs (Rust-specific layout).",[130,16170,16172],{"id":16171},"opaque-types","Opaque Types",[20,16174,16175,16176,16179],{},"When C uses an opaque pointer (",[73,16177,16178],{},"typedef struct Foo Foo;","), use a zero-sized ZST:",[111,16181,16184],{"className":16182,"code":16183,"language":397,"meta":117},[395],"#[repr(C)]\npub struct Foo { _private: [u8; 0] }\n\nextern \"C\" {\n    pub fn foo_new() -> *mut Foo;\n    pub fn foo_free(f: *mut Foo);\n}\n",[73,16185,16183],{"__ignoreMap":117},[20,16187,16188,16191],{},[73,16189,16190],{},"[u8; 0]"," is the convention for opaque types.",[15,16193,16195],{"id":16194},"function-pointers","Function Pointers",[111,16197,16200],{"className":16198,"code":16199,"language":397,"meta":117},[395],"#[repr(C)]\nstruct Callbacks {\n    on_event: Option\u003Cextern \"C\" fn(data: *mut u8)>,\n}\n\nextern \"C\" fn my_callback(data: *mut u8) {\n    let s = unsafe { CStr::from_ptr(data as *const i8) };\n    println!(\"event: {:?}\", s);\n}\n",[73,16201,16199],{"__ignoreMap":117},[20,16203,16204,16205,16208,16209,16212],{},"C callbacks into Rust: store as ",[73,16206,16207],{},"Option\u003Cextern \"C\" fn(...)>",", pass ",[73,16210,16211],{},"my_callback as extern \"C\" fn(...)",", handle the user-data void pointer.",[15,16214,16216],{"id":16215},"panic-across-ffi-ub","Panic Across FFI — UB",[20,16218,16219],{},"Unwinding across an FFI boundary is UB. Solutions:",[33,16221,16222,16230],{},[36,16223,16224,16225,292,16227,16229],{},"Set ",[73,16226,9964],{},[73,16228,169],{}," (kills the process on panic).",[36,16231,1876,16232,16234],{},[73,16233,9996],{}," at the boundary and convert to a C error code.",[111,16236,16239],{"className":16237,"code":16238,"language":397,"meta":117},[395],"#[no_mangle]\npub extern \"C\" fn safe_call() -> i32 {\n    match std::panic::catch_unwind(|| risky_fn()) {\n        Ok(_) => 0,\n        Err(_) => -1,\n    }\n}\n",[73,16240,16238],{"__ignoreMap":117},[15,16242,16244],{"id":16243},"calling-other-languages","Calling Other Languages",[130,16246,16248],{"id":16247},"python-pyo3","Python (PyO3)",[111,16250,16253],{"className":16251,"code":16252,"language":397,"meta":117},[395],"use pyo3::prelude::*;\n\n#[pyfunction]\nfn add(a: i64, b: i64) -> i64 { a + b }\n\n#[pymodule]\nfn my_module(_py: Python, m: &PyModule) -> PyResult\u003C()> {\n    m.add_function(wrap_pyfunction!(add, m)?)?;\n    Ok(())\n}\n",[73,16254,16252],{"__ignoreMap":117},[20,16256,16257,16258,16261],{},"Build with ",[73,16259,16260],{},"maturin develop",". PyO3 handles Python ABI.",[130,16263,16265,16266,1587],{"id":16264},"nodejs-napi-rs","Node.js (",[73,16267,16268],{},"napi-rs",[111,16270,16273],{"className":16271,"code":16272,"language":397,"meta":117},[395],"#[napi]\npub fn add(a: i32, b: i32) -> i32 { a + b }\n",[73,16274,16272],{"__ignoreMap":117},[20,16276,16257,16277,259],{},[73,16278,16279],{},"napi build",[130,16281,16283],{"id":16282},"webassembly","WebAssembly",[111,16285,16288],{"className":16286,"code":16287,"language":116,"meta":117},[114],"rustup target add wasm32-unknown-unknown\ncargo build --target wasm32-unknown-unknown --release\n",[73,16289,16287],{"__ignoreMap":117},[20,16291,16292,16293,170],{},"For JS interop, use ",[73,16294,16295],{},"wasm-bindgen",[111,16297,16300],{"className":16298,"code":16299,"language":397,"meta":117},[395],"use wasm_bindgen::prelude::*;\n\n#[wasm_bindgen]\npub fn add(a: i32, b: i32) -> i32 { a + b }\n",[73,16301,16299],{"__ignoreMap":117},[130,16303,16305,16306,1587],{"id":16304},"c-cxx","C++ (",[73,16307,16308],{},"cxx",[20,16310,3106,16311,16313],{},[73,16312,16308],{}," crate provides safe bidirectional FFI:",[111,16315,16318],{"className":16316,"code":16317,"language":397,"meta":117},[395],"#[cxx::bridge]\nmod ffi {\n    extern \"C++\" {\n        include!(\"mylib.h\");\n        fn cpp_func(x: i32) -> i32;\n    }\n}\n",[73,16319,16317],{"__ignoreMap":117},[20,16321,16322,16324],{},[73,16323,16308],{}," generates both sides; types are restricted to a safe subset.",[15,16326,16328,16330],{"id":16327},"extern-c-and-abis",[73,16329,2295],{}," and ABIs",[20,16332,16333],{},"Common ABIs:",[33,16335,16336,16342,16348,16359],{},[36,16337,16338,16341],{},[73,16339,16340],{},"\"C\""," — System V \u002F cdecl depending on platform.",[36,16343,16344,16347],{},[73,16345,16346],{},"\"stdcall\""," — Windows x32.",[36,16349,16350,11738,16353,16355,16356,16358],{},[73,16351,16352],{},"\"system\"",[73,16354,15498],{}," on Win32, ",[73,16357,16340],{}," on Win64.",[36,16360,16361,480,16364,16367],{},[73,16362,16363],{},"\"win64\"",[73,16365,16366],{},"\"sysv64\""," — explicit x64\u002FSysV.",[20,16369,16370,16371,16373],{},"Mismatched ABIs cause subtle corruption. Use ",[73,16372,15460],{}," to get them right.",[15,16375,16377],{"id":16376},"build-scripts-for-ffi","Build Scripts for FFI",[111,16379,16382],{"className":16380,"code":16381,"language":397,"meta":117},[395],"\u002F\u002F build.rs\nfn main() {\n    cc::Build::new()\n        .file(\"src\u002Fc_code.c\")\n        .compile(\"my_c_code\");\n    println!(\"cargo:rerun-if-changed=src\u002Fc_code.c\");\n}\n",[73,16383,16381],{"__ignoreMap":117},[20,16385,16386,16389,16390,16393],{},[73,16387,16388],{},"cc"," crate compiles C\u002FC++ as part of ",[73,16391,16392],{},"cargo build",". Add it as a build dependency:",[111,16395,16398],{"className":16396,"code":16397,"language":176,"meta":117},[174],"[build-dependencies]\ncc = \"1.0\"\n",[73,16399,16397],{"__ignoreMap":117},[15,16401,6469],{"id":6468},[33,16403,16404,16410,16418,16428,16434,16442,16453,16462,16468,16477],{},[36,16405,16406,16409],{},[24,16407,16408],{},"Mismatched allocators",": UB; always free with the originating allocator.",[36,16411,16412,16415,16416,259],{},[24,16413,16414],{},"Wrong ABI",": silent corruption; use ",[73,16417,15460],{},[36,16419,16420,16423,16424,1546,16426,259],{},[24,16421,16422],{},"Unwinding across FFI",": UB; use ",[73,16425,10178],{},[73,16427,9964],{},[36,16429,16430,16433],{},[24,16431,16432],{},"Returning references to stack data",": classic UB; return owned or pass buffers in.",[36,16435,16436,16441],{},[24,16437,16438,16440],{},[73,16439,5557],{}," missing",": Rust may reorder fields; mismatch with C struct.",[36,16443,16444,2927,16447,16449,16450,16452],{},[24,16445,16446],{},"Nullable function pointers",[73,16448,16207],{}," so the ",[73,16451,1541],{}," variant is a null pointer.",[36,16454,16455,16458,16459,16461],{},[24,16456,16457],{},"Variadic FFI",": only ",[73,16460,2295],{}," functions can be variadic.",[36,16463,16464,16467],{},[24,16465,16466],{},"Thread-local state",": FFI calls into Rust from C threads don't have Rust's thread-local set up.",[36,16469,16470,16473,16474,16476],{},[24,16471,16472],{},"String encoding",": C strings are NUL-terminated byte arrays; Rust strings are UTF-8. ",[73,16475,16058],{}," for paths.",[36,16478,16479,16484,16485,1212,16488,259],{},[24,16480,16481,16483],{},[73,16482,10461],{}," across FFI",": not stable layout; use raw pointers explicitly with ",[73,16486,16487],{},"Box::into_raw",[73,16489,16102],{},[15,16491,16493],{"id":16492},"useful-crates","Useful Crates",[33,16495,16496,16501,16506,16513,16518,16524,16529,16534,16540,16555],{},[36,16497,16498,16500],{},[73,16499,15460],{},": auto-generate Rust bindings from C.",[36,16502,16503,16505],{},[73,16504,16008],{},": generate C headers from Rust.",[36,16507,16508,16510,16511,259],{},[73,16509,16388],{},": compile C\u002FC++ in ",[73,16512,792],{},[36,16514,16515,16517],{},[73,16516,16308],{},": safe C++ interop.",[36,16519,16520,16523],{},[73,16521,16522],{},"pyo3",": Python bindings.",[36,16525,16526,16528],{},[73,16527,16268],{},": Node.js bindings.",[36,16530,16531,16533],{},[73,16532,16295],{},": JS\u002FWebAssembly bindings.",[36,16535,16536,16539],{},[73,16537,16538],{},"jni",": Java\u002FJVM bindings.",[36,16541,16542,16545,16546,480,16549,480,16552,6643],{},[73,16543,16544],{},"libc",": raw C types and constants (",[73,16547,16548],{},"c_int",[73,16550,16551],{},"c_char",[73,16553,16554],{},"size_t",[36,16556,16557,480,16560,480,16563,1212,16566,16569],{},[73,16558,16559],{},"raw-cpuid",[73,16561,16562],{},"nix",[73,16564,16565],{},"winapi",[73,16567,16568],{},"windows-sys",": OS bindings.",[15,16571,349],{"id":348},[20,16573,16574,16576,16577,16580,16581,16583,16584,1212,16586,1212,16588,16590,16591,1212,16593,1212,16595,1212,16597,16599],{},[73,16575,2295],{}," declares FFI. ",[73,16578,16579],{},"#[no_mangle] pub extern \"C\" fn"," exports Rust to C. ",[73,16582,5557],{}," controls struct layout. Use ",[73,16585,15460],{},[73,16587,16008],{},[73,16589,16308],{}," for safe interop. Memory ownership must match allocators. Panics must not cross FFI. ",[73,16592,15926],{},[73,16594,15923],{},[73,16596,16055],{},[73,16598,16058],{}," for string interop. Wrap unsafe bindings in safe abstractions.",[20,16601,16602],{},"Next: Attributes and conditional compilation.",{"title":117,"searchDepth":357,"depth":357,"links":16604},[16605,16611,16614,16616,16618,16622,16625,16626,16627,16635,16637,16638,16639,16640],{"id":15822,"depth":357,"text":15823,"children":16606},[16607,16608,16610],{"id":15848,"depth":364,"text":15849},{"id":15878,"depth":364,"text":16609},"bindgen for Auto-Binding",{"id":15908,"depth":364,"text":15909},{"id":15930,"depth":357,"text":15931,"children":16612},[16613],{"id":16001,"depth":364,"text":16002},{"id":16017,"depth":357,"text":16615},"C Strings: CString and CStr",{"id":16051,"depth":357,"text":16617},"OS Strings: OsString and OsStr",{"id":16087,"depth":357,"text":16088,"children":16619},[16620,16621],{"id":16106,"depth":364,"text":16107},{"id":16121,"depth":364,"text":16122},{"id":16140,"depth":357,"text":16141,"children":16623},[16624],{"id":16171,"depth":364,"text":16172},{"id":16194,"depth":357,"text":16195},{"id":16215,"depth":357,"text":16216},{"id":16243,"depth":357,"text":16244,"children":16628},[16629,16630,16632,16633],{"id":16247,"depth":364,"text":16248},{"id":16264,"depth":364,"text":16631},"Node.js (napi-rs)",{"id":16282,"depth":364,"text":16283},{"id":16304,"depth":364,"text":16634},"C++ (cxx)",{"id":16327,"depth":357,"text":16636},"extern \"C\" and ABIs",{"id":16376,"depth":357,"text":16377},{"id":6468,"depth":357,"text":6469},{"id":16492,"depth":357,"text":16493},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F26-ffi",{"title":15811,"description":15819},"rust\u002F26-ffi","VxJv4AO9q9ydPLDC1sELBVpznBuyD6nIfTPbsqqjcDY",{"id":16647,"title":16648,"body":16649,"description":17539,"extension":373,"meta":17540,"navigation":375,"path":17541,"seo":17542,"stem":17543,"__hash__":17544},"content\u002Frust\u002F27-attributes-and-cfg.md","27 — Attributes & Conditional Compilation",{"type":8,"value":16650,"toc":17492},[16651,16654,16665,16669,16673,16697,16701,16724,16728,16734,16740,16803,16807,16810,16816,16823,16830,16835,16838,16844,16850,16860,16866,16872,16900,16904,16910,16936,16940,17001,17004,17010,17016,17021,17027,17043,17049,17055,17058,17064,17070,17073,17079,17085,17089,17095,17102,17109,17115,17118,17123,17129,17145,17154,17160,17168,17174,17180,17186,17192,17199,17205,17210,17216,17219,17225,17231,17238,17244,17250,17255,17261,17264,17270,17276,17282,17291,17297,17312,17316,17330,17334,17340,17346,17348,17440,17445,17451,17456,17458,17489],[11,16652,16648],{"id":16653},"_27-attributes-conditional-compilation",[20,16655,16656,16657,16660,16661,16664],{},"Attributes are metadata annotations that influence compilation, linting, codegen, and tooling. They appear as ",[73,16658,16659],{},"#[...]"," (outer) or ",[73,16662,16663],{},"#![...]"," (inner, applies to the enclosing item\u002Fwhole crate).",[15,16666,16668],{"id":16667},"common-attributes","Common Attributes",[130,16670,16672],{"id":16671},"visibility-abi","Visibility & ABI",[33,16674,16675,16687],{},[36,16676,16677,480,16679,480,16681,480,16684],{},[73,16678,5468],{},[73,16680,5476],{},[73,16682,16683],{},"pub(super)",[73,16685,16686],{},"pub(in path)",[36,16688,16689,480,16691,480,16693,480,16695],{},[73,16690,2295],{},[73,16692,15052],{},[73,16694,15435],{},[73,16696,15438],{},[130,16698,16700],{"id":16699},"code-generation","Code Generation",[33,16702,16703,16712,16718],{},[36,16704,16705,3818,16707,3818,16709],{},[73,16706,2452],{},[73,16708,2456],{},[73,16710,16711],{},"#[inline(never)]",[36,16713,16714,16717],{},[73,16715,16716],{},"#[cold]"," (cold path — hint to optimizer)",[36,16719,16720,16723],{},[73,16721,16722],{},"#[track_caller]"," (captures caller location for panic messages)",[130,16725,16727],{"id":16726},"conditional-compilation","Conditional Compilation",[111,16729,16732],{"className":16730,"code":16731,"language":397,"meta":117},[395],"#[cfg(target_os = \"linux\")]\nfn linux_only() {}\n\n#[cfg(not(target_os = \"linux\"))]\nfn non_linux() {}\n\n#[cfg(all(unix, target_pointer_width = \"64\"))]\nfn unix_64() {}\n\n#[cfg(any(feature = \"json\", feature = \"yaml\"))]\nfn with_format() {}\n\n#[cfg(feature = \"serde\")]\n#[derive(serde::Serialize)]\nstruct S;\n",[73,16733,16731],{"__ignoreMap":117},[130,16735,16737,16739],{"id":16736},"cfg-predicates",[73,16738,14488],{}," Predicates",[33,16741,16742,16759,16764,16770,16776,16785,16796],{},[36,16743,16744,480,16747,480,16750,480,16753,480,16756,259],{},[73,16745,16746],{},"target_os = \"linux\"",[73,16748,16749],{},"target_arch = \"x86_64\"",[73,16751,16752],{},"target_family = \"unix\"",[73,16754,16755],{},"target_pointer_width = \"32\"",[73,16757,16758],{},"target_endian = \"little\"",[36,16760,16761,259],{},[73,16762,16763],{},"feature = \"name\"",[36,16765,16766,16769],{},[73,16767,16768],{},"debug_assertions"," (true in debug builds).",[36,16771,16772,16775],{},[73,16773,16774],{},"test"," (true when compiled as a test).",[36,16777,16778,480,16781,16784],{},[73,16779,16780],{},"unix",[73,16782,16783],{},"windows"," (family shortcuts).",[36,16786,16787,480,16790,480,16793,259],{},[73,16788,16789],{},"any(...)",[73,16791,16792],{},"all(...)",[73,16794,16795],{},"not(...)",[36,16797,16798,16799,16802],{},"Custom: ",[73,16800,16801],{},"#[cfg(accessible(std::sync::OnceLock))]"," (nightly).",[130,16804,16805],{"id":14491},[73,16806,14491],{},[20,16808,16809],{},"Apply an attribute conditionally:",[111,16811,16814],{"className":16812,"code":16813,"language":397,"meta":117},[395],"#[cfg_attr(feature = \"serde\", derive(serde::Serialize))]\nstruct S;\n",[73,16815,16813],{"__ignoreMap":117},[20,16817,16818,16819,16822],{},"Equivalent to ",[73,16820,16821],{},"#[cfg(feature = \"serde\")] #[derive(...)]"," but cleaner.",[130,16824,16826,16829],{"id":16825},"cfg-on-modules",[73,16827,16828],{},"#[cfg]"," on Modules",[111,16831,16833],{"className":16832,"code":11809,"language":397,"meta":117},[395],[73,16834,11809],{"__ignoreMap":117},[20,16836,16837],{},"The module is only compiled when the feature is on.",[15,16839,16841,16842,6040],{"id":16840},"compile-time-cfg-macro","Compile-Time ",[73,16843,14449],{},[111,16845,16848],{"className":16846,"code":16847,"language":397,"meta":117},[395],"if cfg!(target_os = \"linux\") {\n    println!(\"linux\");\n}\n",[73,16849,16847],{"__ignoreMap":117},[20,16851,8355,16852,1212,16854,16856,16857,16859],{},[73,16853,1563],{},[73,16855,1566],{}," at compile time — the dead branch is still type-checked but eliminated at codegen. Use ",[73,16858,16828],{}," for actual code removal.",[15,16861,16863],{"id":16862},"derive",[73,16864,16865],{},"#[derive(...)]",[111,16867,16870],{"className":16868,"code":16869,"language":397,"meta":117},[395],"#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]\nstruct Foo;\n",[73,16871,16869],{"__ignoreMap":117},[20,16873,16874,16875,480,16877,480,16879,480,16881,480,16883,480,16885,480,16887,480,16889,480,16891,16893,16894,480,16897,526],{},"Standard derives: ",[73,16876,469],{},[73,16878,8252],{},[73,16880,1795],{},[73,16882,5365],{},[73,16884,5355],{},[73,16886,1529],{},[73,16888,1533],{},[73,16890,5336],{},[73,16892,5347],{},". External crates add more (",[73,16895,16896],{},"serde::Serialize",[73,16898,16899],{},"thiserror::Error",[15,16901,16903],{"id":16902},"lint-attributes","Lint Attributes",[111,16905,16908],{"className":16906,"code":16907,"language":397,"meta":117},[395],"#![allow(dead_code)]              \u002F\u002F crate-wide\n#[allow(unused)]                  \u002F\u002F item-level\n#[warn(unused)]\n#[deny(unused)]\n#[forbid(unused)]                 \u002F\u002F can't be overridden downstream\n",[73,16909,16907],{"__ignoreMap":117},[33,16911,16912,16918,16924,16930],{},[36,16913,16914,16917],{},[73,16915,16916],{},"allow",": silence.",[36,16919,16920,16923],{},[73,16921,16922],{},"warn",": warn (default for many lints).",[36,16925,16926,16929],{},[73,16927,16928],{},"deny",": error.",[36,16931,16932,16935],{},[73,16933,16934],{},"forbid",": deny that can't be undone in inner scopes.",[130,16937,16939],{"id":16938},"common-lints","Common Lints",[33,16941,16942,16962,16973,16981,16989],{},[36,16943,16944,480,16947,480,16950,480,16953,480,16956,480,16959],{},[73,16945,16946],{},"unused",[73,16948,16949],{},"dead_code",[73,16951,16952],{},"unused_variables",[73,16954,16955],{},"unused_imports",[73,16957,16958],{},"unused_mut",[73,16960,16961],{},"unused_assignments",[36,16963,16964,480,16967,480,16970],{},[73,16965,16966],{},"non_snake_case",[73,16968,16969],{},"non_camel_case_types",[73,16971,16972],{},"non_upper_case_globals",[36,16974,16975,480,16978],{},[73,16976,16977],{},"missing_docs",[73,16979,16980],{},"missing_debug_implementations",[36,16982,16983,480,16986],{},[73,16984,16985],{},"unsafe_code",[73,16987,16988],{},"unused_unsafe",[36,16990,16991,480,16994,480,16997,17000],{},[73,16992,16993],{},"clippy::all",[73,16995,16996],{},"clippy::pedantic",[73,16998,16999],{},"clippy::nursery"," (Clippy lint groups)",[130,17002,17003],{"id":83},"Clippy",[111,17005,17008],{"className":17006,"code":17007,"language":397,"meta":117},[395],"#![warn(clippy::all, clippy::pedantic)]\n#![allow(clippy::module_inception)]\n",[73,17009,17007],{"__ignoreMap":117},[20,17011,17012,17013,17015],{},"Configure in ",[73,17014,169],{}," or source.",[15,17017,17019],{"id":17018},"non_exhaustive",[73,17020,2890],{},[111,17022,17025],{"className":17023,"code":17024,"language":397,"meta":117},[395],"#[non_exhaustive]\npub enum Event { Login, Logout }\n\n#[non_exhaustive]\npub struct Config { pub host: String }\n",[73,17026,17024],{"__ignoreMap":117},[33,17028,17029,17040],{},[36,17030,17031,17032,17035,17036,17039],{},"External crates must include a ",[73,17033,17034],{},"_ => ..."," arm (enum) or use ",[73,17037,17038],{},"..Default::default()","\u002Fconstructor (struct).",[36,17041,17042],{},"Allows adding variants\u002Ffields in non-breaking minor releases.",[15,17044,17046],{"id":17045},"must_use",[73,17047,17048],{},"#[must_use]",[111,17050,17053],{"className":17051,"code":17052,"language":397,"meta":117},[395],"#[must_use = \"the result indicates success\"]\npub fn try_connect() -> bool { \u002F* ... *\u002F }\n",[73,17054,17052],{"__ignoreMap":117},[20,17056,17057],{},"Warns if the return value is ignored. Applied to Result, Option by default.",[15,17059,17061],{"id":17060},"deprecated",[73,17062,17063],{},"#[deprecated]",[111,17065,17068],{"className":17066,"code":17067,"language":397,"meta":117},[395],"#[deprecated(since = \"1.2\", note = \"use new_fn instead\")]\npub fn old_fn() {}\n\n#[deprecated(since = \"1.2\", replacement = \"new_fn\")]\npub fn old_fn2() {}\n",[73,17069,17067],{"__ignoreMap":117},[20,17071,17072],{},"Emits a warning when used.",[15,17074,17076,15421],{"id":17075},"doc-attributes",[73,17077,17078],{},"#[doc]",[111,17080,17083],{"className":17081,"code":17082,"language":397,"meta":117},[395],"\u002F\u002F\u002F Docs.\n#[doc = \"Inline docs string\"]\nfn f() {}\n\n#[doc(hidden)]        \u002F\u002F hide from docs\npub mod internal;\n\n#[doc(alias = \"other_name\")]   \u002F\u002F search alias\npub fn f2() {}\n\n#![doc(html_root_url = \"https:\u002F\u002Fdocs.rs\u002Fmy_crate\u002F1.0\")]\n",[73,17084,17082],{"__ignoreMap":117},[15,17086,17088],{"id":17087},"inner-vs-outer-attributes","Inner vs Outer Attributes",[111,17090,17093],{"className":17091,"code":17092,"language":397,"meta":117},[395],"#![allow(dead_code)]   \u002F\u002F inner — applies to crate\u002Fmodule\n\n#[allow(dead_code)]    \u002F\u002F outer — applies to following item\nfn foo() {}\n\nmod m {\n    #![allow(dead_code)]   \u002F\u002F inner — applies to module m\n    fn bar() {}\n}\n",[73,17094,17092],{"__ignoreMap":117},[20,17096,17097,17098,17101],{},"Inner attributes go ",[183,17099,17100],{},"inside"," the item's braces; outer attributes go before it.",[15,17103,17105,17108],{"id":17104},"path-for-module-files",[73,17106,17107],{},"#[path]"," for Module Files",[111,17110,17113],{"className":17111,"code":17112,"language":397,"meta":117},[395],"#[path = \"other\u002Fpath.rs\"]\nmod my_mod;\n",[73,17114,17112],{"__ignoreMap":117},[20,17116,17117],{},"Overrides the default file lookup.",[15,17119,17120],{"id":15420},[73,17121,17122],{},"#[link]",[111,17124,17127],{"className":17125,"code":17126,"language":397,"meta":117},[395],"#[link(name = \"crypto\", kind = \"static\")]\nextern \"C\" { \u002F* ... *\u002F }\n",[73,17128,17126],{"__ignoreMap":117},[20,17130,17131,71,17134,480,17136,480,17139,17142,17143,259],{},[73,17132,17133],{},"kind",[73,17135,1207],{},[73,17137,17138],{},"dylib",[73,17140,17141],{},"framework"," (macOS). Default is ",[73,17144,17138],{},[15,17146,17148,480,17151],{"id":17147},"link_section-used",[73,17149,17150],{},"#[link_section]",[73,17152,17153],{},"#[used]",[111,17155,17158],{"className":17156,"code":17157,"language":397,"meta":117},[395],"#[link_section = \".custom\"]\n#[used]\nstatic DATA: [u8; 4] = [0, 1, 2, 3];\n",[73,17159,17157],{"__ignoreMap":117},[20,17161,17162,17164,17165,17167],{},[73,17163,17153],{}," prevents the compiler from optimizing away the symbol. ",[73,17166,17150],{}," places it in a custom section (advanced\u002Fembedded).",[15,17169,17171],{"id":17170},"target_feature",[73,17172,17173],{},"#[target_feature]",[111,17175,17178],{"className":17176,"code":17177,"language":397,"meta":117},[395],"#[target_feature(enable = \"avx2\")]\nunsafe fn avx2_func() {}\n",[73,17179,17177],{"__ignoreMap":117},[20,17181,17182,17183,17185],{},"Enables CPU features for a specific function. Requires ",[73,17184,197],{}," (calling on a CPU without the feature is UB).",[111,17187,17190],{"className":17188,"code":17189,"language":397,"meta":117},[395],"#[target_feature(enable = \"avx2\")]\n#[cfg(target_arch = \"x86_64\")]\nunsafe fn fast() {}\n\nif is_x86_feature_detected!(\"avx2\") {\n    unsafe { fast(); }\n}\n",[73,17191,17189],{"__ignoreMap":117},[15,17193,17195,480,17197],{"id":17194},"cold-inline",[73,17196,16716],{},[73,17198,2452],{},[111,17200,17203],{"className":17201,"code":17202,"language":397,"meta":117},[395],"#[cold]\nfn error_path() {}     \u002F\u002F hint: rare path\n",[73,17204,17202],{"__ignoreMap":117},[15,17206,17208],{"id":17207},"track_caller",[73,17209,16722],{},[111,17211,17214],{"className":17212,"code":17213,"language":397,"meta":117},[395],"#[track_caller]\nfn caller() -> &'static Location {\n    Location::caller()\n}\n",[73,17215,17213],{"__ignoreMap":117},[20,17217,17218],{},"Captures the source location of the call site; useful for panic messages and assertion helpers.",[15,17220,17222],{"id":17221},"automatically_derived",[73,17223,17224],{},"#[automatically_derived]",[20,17226,17227,17228,17230],{},"Applied by ",[73,17229,16865],{}," to prevent lints from firing on generated code.",[15,17232,17234,17237],{"id":17233},"repr-layout",[73,17235,17236],{},"#[repr(...)]"," (Layout)",[111,17239,17242],{"className":17240,"code":17241,"language":397,"meta":117},[395],"#[repr(C)]              \u002F\u002F C-compatible layout\n#[repr(transparent)]    \u002F\u002F same layout as a single field\n#[repr(packed)]         \u002F\u002F no padding\n#[repr(packed(1))]      \u002F\u002F explicit alignment\n#[repr(align(16))]      \u002F\u002F force alignment\n#[repr(C, u8)]           \u002F\u002F C layout + explicit enum discriminant width\n",[73,17243,17241],{"__ignoreMap":117},[15,17245,17247],{"id":17246},"panic_handler",[73,17248,17249],{},"#[panic_handler]",[20,17251,1458,17252,17254],{},[73,17253,13435],{}," environments:",[111,17256,17259],{"className":17257,"code":17258,"language":397,"meta":117},[395],"#[panic_handler]\nfn panic(_: &PanicInfo) -> ! { loop {} }\n",[73,17260,17258],{"__ignoreMap":117},[20,17262,17263],{},"Defines the panic behavior for a custom target.",[15,17265,17267],{"id":17266},"global_allocator",[73,17268,17269],{},"#[global_allocator]",[111,17271,17274],{"className":17272,"code":17273,"language":397,"meta":117},[395],"use std::alloc::{GlobalAlloc, Layout};\n\nstruct MyAlloc;\nunsafe impl GlobalAlloc for MyAlloc {\n    unsafe fn alloc(&self, layout: Layout) -> *mut u8 { \u002F* ... *\u002F }\n    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { \u002F* ... *\u002F }\n}\n\n#[global_allocator]\nstatic A: MyAlloc = MyAlloc;\n",[73,17275,17273],{"__ignoreMap":117},[20,17277,17278,17279,526],{},"Replace Rust's default allocator (e.g., for ",[73,17280,17281],{},"jemalloc",[15,17283,17285,27,17288],{"id":17284},"no_std-and-no_std",[73,17286,17287],{},"#[no_std]",[73,17289,17290],{},"#![no_std]",[111,17292,17295],{"className":17293,"code":17294,"language":397,"meta":117},[395],"#![no_std]\n",[73,17296,17294],{"__ignoreMap":117},[20,17298,17299,17300,17303,17304,17307,17308,17311],{},"Disables ",[73,17301,17302],{},"std",", only ",[73,17305,17306],{},"core"," (+ optional ",[73,17309,17310],{},"alloc",") available. For embedded\u002Fwasm\u002Fkernels.",[15,17313,17315],{"id":17314},"edition-related-attributes","Edition-Related Attributes",[33,17317,17318,17324],{},[36,17319,17320,17323],{},[73,17321,17322],{},"#![feature(...)]"," (nightly only)",[36,17325,17326,17329],{},[73,17327,17328],{},"#![allow(...)]"," for transition warnings between editions.",[15,17331,17333],{"id":17332},"feature-attributes","Feature Attributes",[111,17335,17338],{"className":17336,"code":17337,"language":397,"meta":117},[395],"#![feature(async_fn_traits)]   \u002F\u002F nightly\n",[73,17339,17337],{"__ignoreMap":117},[20,17341,17342,17343,259],{},"Unstable features require nightly + explicit ",[73,17344,17345],{},"#![feature]",[15,17347,6469],{"id":6468},[33,17349,17350,17364,17376,17389,17396,17404,17419,17430],{},[36,17351,17352,71,17355,17357,17358,480,17361,17363],{},[24,17353,17354],{},"Inner vs outer",[73,17356,16663],{}," (inner) inside braces\u002F",[73,17359,17360],{},"mod",[73,17362,16659],{}," (outer) before items.",[36,17365,17366,17371,17372,17375],{},[24,17367,17368,17370],{},[73,17369,16828],{}," doesn't type-check dead branches",": actually it does — both branches are type-checked. Use ",[73,17373,17374],{},"cfg_if!"," macro or modular code to fully isolate.",[36,17377,17378,17383,17384,1212,17386,17388],{},[24,17379,17380,14771],{},[73,17381,17382],{},"#[cfg_attr]",": same as ",[73,17385,16828],{},[73,17387,5764],{}," placement.",[36,17390,17391,17395],{},[24,17392,17393],{},[73,17394,2890],{}," can't be applied to local types (only exported across crates).",[36,17397,17398,17403],{},[24,17399,17400,17402],{},[73,17401,17048],{}," on a type"," warns when the value is dropped unused.",[36,17405,17406,71,17412,17415,17416,17418],{},[24,17407,17408,17411],{},[73,17409,17410],{},"#[allow]"," ignores lints in nested scopes",[73,17413,17414],{},"#[deny]"," in an inner scope overrides ",[73,17417,17410],{}," from the outer.",[36,17420,17421,17426,17427,17429],{},[24,17422,17423],{},[73,17424,17425],{},"#[forbid]"," is sticky: an inner ",[73,17428,17410],{}," triggers a \"forbid overridden by allow\" error.",[36,17431,17432,17435,17436,17439],{},[24,17433,17434],{},"Attribute parsing",": some attributes take ",[73,17437,17438],{},"key = \"value\"",", others take bare tokens; check the docs.",[15,17441,17443,6040],{"id":17442},"cfg_if-macro",[73,17444,17374],{},[111,17446,17449],{"className":17447,"code":17448,"language":397,"meta":117},[395],"cfg_if::cfg_if! {\n    if #[cfg(unix)] {\n        fn posix_api() {}\n    } else if #[cfg(windows)] {\n        fn win_api() {}\n    } else {\n        fn other_api() {}\n    }\n}\n",[73,17450,17448],{"__ignoreMap":117},[20,17452,17453,17454,5578],{},"Cleaner than stacked ",[73,17455,16828],{},[15,17457,349],{"id":348},[20,17459,17460,17462,17463,17465,17466,1212,17468,1212,17470,17472,17473,17475,17476,1212,17478,17480,17481,17483,17484,1212,17486,17488],{},[73,17461,16828],{}," controls what compiles; ",[73,17464,5764],{}," auto-implements traits; ",[73,17467,17410],{},[73,17469,17414],{},[73,17471,17425],{}," tune lints; ",[73,17474,2890],{}," future-proofs APIs; ",[73,17477,17048],{},[73,17479,17063],{}," drive correctness; ",[73,17482,5557],{}," controls layout; ",[73,17485,17269],{},[73,17487,17249],{}," customize the runtime.",[20,17490,17491],{},"Next: Cargo features and release engineering.",{"title":117,"searchDepth":357,"depth":357,"links":17493},[17494,17504,17506,17507,17511,17512,17513,17514,17516,17517,17519,17520,17522,17523,17525,17526,17527,17529,17530,17531,17533,17534,17535,17536,17538],{"id":16667,"depth":357,"text":16668,"children":17495},[17496,17497,17498,17499,17501,17502],{"id":16671,"depth":364,"text":16672},{"id":16699,"depth":364,"text":16700},{"id":16726,"depth":364,"text":16727},{"id":16736,"depth":364,"text":17500},"cfg Predicates",{"id":14491,"depth":364,"text":14491},{"id":16825,"depth":364,"text":17503},"#[cfg] on Modules",{"id":16840,"depth":357,"text":17505},"Compile-Time cfg! Macro",{"id":16862,"depth":357,"text":16865},{"id":16902,"depth":357,"text":16903,"children":17508},[17509,17510],{"id":16938,"depth":364,"text":16939},{"id":83,"depth":364,"text":17003},{"id":17018,"depth":357,"text":2890},{"id":17045,"depth":357,"text":17048},{"id":17060,"depth":357,"text":17063},{"id":17075,"depth":357,"text":17515},"#[doc] Attributes",{"id":17087,"depth":357,"text":17088},{"id":17104,"depth":357,"text":17518},"#[path] for Module Files",{"id":15420,"depth":357,"text":17122},{"id":17147,"depth":357,"text":17521},"#[link_section], #[used]",{"id":17170,"depth":357,"text":17173},{"id":17194,"depth":357,"text":17524},"#[cold], #[inline]",{"id":17207,"depth":357,"text":16722},{"id":17221,"depth":357,"text":17224},{"id":17233,"depth":357,"text":17528},"#[repr(...)] (Layout)",{"id":17246,"depth":357,"text":17249},{"id":17266,"depth":357,"text":17269},{"id":17284,"depth":357,"text":17532},"#[no_std] and #![no_std]",{"id":17314,"depth":357,"text":17315},{"id":17332,"depth":357,"text":17333},{"id":6468,"depth":357,"text":6469},{"id":17442,"depth":357,"text":17537},"cfg_if! Macro",{"id":348,"depth":357,"text":349},"Attributes are metadata annotations that influence compilation, linting, codegen, and tooling. They appear as #[...] (outer) or #![...] (inner, applies to the enclosing item\u002Fwhole crate).",{},"\u002Frust\u002F27-attributes-and-cfg",{"title":16648,"description":17539},"rust\u002F27-attributes-and-cfg","aWd4RLnDIf8RNy0H8Z3nRMeuS1BrhEgwF0olijtsi-M",{"id":17546,"title":17547,"body":17548,"description":17555,"extension":373,"meta":18351,"navigation":375,"path":18352,"seo":18353,"stem":18354,"__hash__":18355},"content\u002Frust\u002F28-cargo-features.md","28 — Cargo Features & Release Engineering",{"type":8,"value":17549,"toc":18316},[17550,17553,17556,17560,17566,17570,17590,17594,17600,17607,17611,17617,17623,17627,17642,17658,17662,17665,17669,17705,17709,17715,17720,17723,17729,17738,17742,17786,17792,17798,17801,17821,17849,17858,17865,17871,17883,17887,17891,17925,17929,17959,17962,17998,18002,18032,18036,18060,18064,18070,18084,18088,18101,18105,18113,18127,18131,18137,18144,18150,18156,18160,18193,18197,18203,18206,18212,18215,18221,18223,18297,18299,18313],[11,17551,17547],{"id":17552},"_28-cargo-features-release-engineering",[20,17554,17555],{},"Cargo features are the standard mechanism for conditional compilation. Combined with profiles and CI, they form the release engineering story.",[15,17557,17559],{"id":17558},"defining-features","Defining Features",[111,17561,17564],{"className":17562,"code":17563,"language":176,"meta":117},[174],"# Cargo.toml\n[features]\ndefault = [\"json\", \"csv\"]\njson = [\"dep:serde_json\"]          # optional dependency 'serde_json'\ncsv = [\"dep:csv\"]\nfull = [\"json\", \"csv\", \"yaml\"]\nyaml = []\n",[73,17565,17563],{"__ignoreMap":117},[130,17567,17569],{"id":17568},"feature-syntax-160","Feature Syntax (1.60+)",[33,17571,17572,17578,17584],{},[36,17573,17574,17577],{},[73,17575,17576],{},"dep:crate_name"," — enables an optional dependency without exposing a feature of the same name.",[36,17579,17580,17583],{},[73,17581,17582],{},"dep_crate\u002Ffeature"," — enables a specific feature of a dependency.",[36,17585,17586,17589],{},[73,17587,17588],{},"?dep_crate\u002Ffeature"," — only enables the dep's feature if it's already enabled by someone else.",[130,17591,17593],{"id":17592},"optional-dependencies","Optional Dependencies",[111,17595,17598],{"className":17596,"code":17597,"language":176,"meta":117},[174],"[dependencies]\nserde_json = { version = \"1.0\", optional = true }\n",[73,17599,17597],{"__ignoreMap":117},[20,17601,17602,17603,17606],{},"Optional deps implicitly create a feature of the same name (unless ",[73,17604,17605],{},"dep:"," is used).",[15,17608,17610],{"id":17609},"using-features","Using Features",[111,17612,17615],{"className":17613,"code":17614,"language":397,"meta":117},[395],"#[cfg(feature = \"json\")]\nmod json;\n\n#[cfg(feature = \"json\")]\npub use json::parse_json;\n",[73,17616,17614],{"__ignoreMap":117},[111,17618,17621],{"className":17619,"code":17620,"language":116,"meta":117},[114],"cargo build --no-default-features\ncargo build --features json,yaml\ncargo build --all-features\n",[73,17622,17620],{"__ignoreMap":117},[15,17624,17626],{"id":17625},"feature-unification","Feature Unification",[20,17628,17629,17630,480,17633,17635,17636,17638,17639,17641],{},"Cargo unifies features across the dependency graph: if any crate enables ",[73,17631,17632],{},"serde\u002Fderive",[183,17634,5779],{}," user of ",[73,17637,14735],{}," gets ",[73,17640,16862],{}," on. Design features accordingly:",[33,17643,17644,17649,17652],{},[36,17645,17646,17648],{},[24,17647,8744],{}," expose \"private\" features that change behavior of your crate depending on who else in the graph enabled them.",[36,17650,17651],{},"Use additive features (more code enabled), not subtractive.",[36,17653,16160,17654,17657],{},[73,17655,17656],{},"default-features = false"," on transitive deps unless you understand the consequences.",[130,17659,17661],{"id":17660},"additive-only-rule","Additive-Only Rule",[20,17663,17664],{},"Features should be strictly additive: enabling a feature adds capabilities, never removes them. If you need mutually-exclusive features, consider splitting crates.",[15,17666,17668],{"id":17667},"common-feature-pitfalls","Common Feature Pitfalls",[33,17670,17671,17682,17688,17696],{},[36,17672,17673,71,17676,17679,17680,259],{},[24,17674,17675],{},"Exposing transitive features",[73,17677,17678],{},"[\"serde\u002Fderive\"]"," from your crate forces all downstream users to also enable ",[73,17681,17632],{},[36,17683,17684,17687],{},[24,17685,17686],{},"Cargo feature unification surprise",": if a dep is also enabled by another crate with extra features, you get them all.",[36,17689,17690,17695],{},[24,17691,17692,17694],{},[73,17693,17656],{}," on transitive deps",": hard to reason about; usually wrong.",[36,17697,17698,17701,17702,17704],{},[24,17699,17700],{},"Negation",": features can't disable features. The only \"negation\" is ",[73,17703,17656],{}," when depending on a crate.",[15,17706,17708],{"id":17707},"build-profiles","Build Profiles",[111,17710,17713],{"className":17711,"code":17712,"language":176,"meta":117},[174],"[profile.dev]\nopt-level = 0\ndebug = true\nincremental = true\noverflow-checks = true\n\n[profile.release]\nopt-level = 3\ndebug = false\nlto = \"fat\"               # or \"thin\", or true\u002Ffalse\ncodegen-units = 1          # best optimization, slower compile\npanic = \"unwind\"           # or \"abort\"\nstrip = \"symbols\"\nopt-level = \"z\"            # optimize for size (vs \"s\" or numeric 0-3)\n\n[profile.release.package.\"*\"]\nopt-level = 2              # dependencies at lower opt level for faster compile\n\n[profile.bench]\ninherits = \"release\"\ndebug = true\n\n[profile.dist]\ninherits = \"release\"\nlto = \"thin\"\n",[73,17714,17712],{"__ignoreMap":117},[130,17716,17718],{"id":17717},"inherits",[73,17719,17717],{},[20,17721,17722],{},"Custom profiles can inherit from existing ones:",[111,17724,17727],{"className":17725,"code":17726,"language":176,"meta":117},[174],"[profile.profiling]\ninherits = \"release\"\ndebug = true\n",[73,17728,17726],{"__ignoreMap":117},[20,17730,16257,17731,17734,17735,259],{},[73,17732,17733],{},"cargo build --profile profiling",". Output goes to ",[73,17736,17737],{},"target\u002Fprofiling",[130,17739,17741],{"id":17740},"profile-pitfalls","Profile Pitfalls",[33,17743,17744,17753,17761,17770],{},[36,17745,17746,17752],{},[24,17747,17748,17751],{},[73,17749,17750],{},"lto = \"fat\""," dramatically slows compile"," but produces smaller\u002Ffaster binaries. Use only in release.",[36,17754,17755,17760],{},[24,17756,17757],{},[73,17758,17759],{},"codegen-units = 1"," is best for performance, slowest to compile.",[36,17762,17763,17767,17768,526],{},[24,17764,17765],{},[73,17766,9964],{}," breaks some code that relies on unwinding (and on catching panics via ",[73,17769,10178],{},[36,17771,17772,17777,17778,17781,17782,17785],{},[24,17773,17774],{},[73,17775,17776],{},"opt-level = \"z\""," optimizes for binary size; ",[73,17779,17780],{},"\"s\""," for size + some speed; ",[73,17783,17784],{},"3"," for max speed.",[15,17787,17789,17790,1587],{"id":17788},"build-scripts-buildrs","Build Scripts (",[73,17791,792],{},[111,17793,17796],{"className":17794,"code":17795,"language":397,"meta":117},[395],"\u002F\u002F build.rs\nfn main() {\n    println!(\"cargo:rustc-env=MY_VAR=value\");\n    println!(\"cargo:rerun-if-changed=some_file.txt\");\n    println!(\"cargo:rustc-link-lib=mylib\");\n    println!(\"cargo:rustc-link-search=vendor\u002Flib\");\n}\n",[73,17797,17795],{"__ignoreMap":117},[20,17799,17800],{},"Use for:",[33,17802,17803,17809,17812,17818],{},[36,17804,17805,17806,17808],{},"Compiling C code (",[73,17807,16388],{}," crate).",[36,17810,17811],{},"Generating code (e.g., protobuf, SQL).",[36,17813,17814,17815,259],{},"Setting env vars for ",[73,17816,17817],{},"env!()",[36,17819,17820],{},"Link configuration.",[20,17822,17823,17824,480,17826,480,17828,480,17831,480,17834,480,17837,480,17840,480,17843,480,17846,259],{},"Read env vars set by Cargo: ",[73,17825,804],{},[73,17827,807],{},[73,17829,17830],{},"OUT_DIR",[73,17832,17833],{},"TARGET",[73,17835,17836],{},"HOST",[73,17838,17839],{},"OPT_LEVEL",[73,17841,17842],{},"PROFILE",[73,17844,17845],{},"DEBUG",[73,17847,17848],{},"NUM_JOBS",[20,17850,1876,17851,1546,17854,17857],{},[73,17852,17853],{},"env!(\"VAR\")",[73,17855,17856],{},"option_env!(\"VAR\")"," in code to read build-time env vars.",[15,17859,17861,17864],{"id":17860},"links-key",[73,17862,17863],{},"links"," Key",[111,17866,17869],{"className":17867,"code":17868,"language":176,"meta":117},[174],"[links]\nfoo = \"1.0\"\n",[73,17870,17868],{"__ignoreMap":117},[20,17872,17873,17875,17876,17879,17880,17882],{},[73,17874,17863],{}," declares that the crate links to a native library named ",[73,17877,17878],{},"foo",". Prevents two crates from both linking to ",[73,17881,17878],{}," with conflicting build scripts.",[15,17884,17886],{"id":17885},"release-checklist","Release Checklist",[130,17888,17890],{"id":17889},"code-quality","Code Quality",[33,17892,17893,17898,17907,17913,17919],{},[36,17894,17895],{},[73,17896,17897],{},"cargo fmt -- --check",[36,17899,17900,17903,17904,1587],{},[73,17901,17902],{},"cargo clippy -- -D warnings"," (and ",[73,17905,17906],{},"--all-targets",[36,17908,17909,17912],{},[73,17910,17911],{},"cargo deny check"," (licenses, advisories, bans)",[36,17914,17915,17918],{},[73,17916,17917],{},"cargo audit"," (RustSec advisories)",[36,17920,17921,17924],{},[73,17922,17923],{},"cargo machete"," (unused deps)",[130,17926,17928],{"id":17927},"testing","Testing",[33,17930,17931,17936,17942,17947,17953],{},[36,17932,17933],{},[73,17934,17935],{},"cargo test --all-features",[36,17937,17938,17941],{},[73,17939,17940],{},"cargo test --no-default-features"," (smoke)",[36,17943,17944],{},[73,17945,17946],{},"cargo test --workspace",[36,17948,17949,17950],{},"Doc tests: ",[73,17951,17952],{},"cargo test --doc",[36,17954,17955,17956],{},"Cross-compile: ",[73,17957,17958],{},"cargo build --target x86_64-unknown-linux-musl",[130,17960,40],{"id":17961},"performance",[33,17963,17964,17973,17985],{},[36,17965,17966,17967,17970,17971],{},"Benchmarks: ",[73,17968,17969],{},"cargo bench"," (nightly) or ",[73,17972,12318],{},[36,17974,17975,17976,480,17979,480,17982],{},"Profile with ",[73,17977,17978],{},"cargo flamegraph",[73,17980,17981],{},"perf",[73,17983,17984],{},"samply",[36,17986,17987,17988,480,17991,17994,17995],{},"Check binary size: ",[73,17989,17990],{},"cargo bloat",[73,17992,17993],{},"cargo build --release"," then ",[73,17996,17997],{},"ls -lh",[130,17999,18001],{"id":18000},"binary","Binary",[33,18003,18004,18010,18016,18022],{},[36,18005,18006,18007],{},"Strip symbols: ",[73,18008,18009],{},"strip = \"symbols\"",[36,18011,18012,18013,18015],{},"LTO: ",[73,18014,17750],{}," for final",[36,18017,18018,18019,18021],{},"Consider ",[73,18020,9964],{}," if you don't need unwinding",[36,18023,18024,18025,480,18027,480,18029],{},"For size-critical: ",[73,18026,17776],{},[73,18028,17759],{},[73,18030,18031],{},"lto = true",[130,18033,18035],{"id":18034},"versioning","Versioning",[33,18037,18038,18044,18053],{},[36,18039,18040,18041],{},"Semver: ",[73,18042,18043],{},"MAJOR.MINOR.PATCH",[36,18045,1876,18046,2681,18049,18052],{},[73,18047,18048],{},"cargo release",[73,18050,18051],{},"cargo install cargo-release",") to bump, tag, publish.",[36,18054,16224,18055,18057,18058,259],{},[73,18056,291],{}," (MSRV) in ",[73,18059,169],{},[130,18061,18063],{"id":18062},"publishing","Publishing",[111,18065,18068],{"className":18066,"code":18067,"language":116,"meta":117},[114],"cargo login \u003Ctoken>\ncargo publish --dry-run\ncargo publish\n",[73,18069,18067],{"__ignoreMap":117},[33,18071,18072,18075,18081],{},[36,18073,18074],{},"Crates.io is the public registry.",[36,18076,18077,18080],{},[73,18078,18079],{},"publish = false"," to prevent accidental publication.",[36,18082,18083],{},"Documentation is auto-built on docs.rs.",[130,18085,18087],{"id":18086},"changelog","Changelog",[20,18089,1876,18090,480,18092,2755,18095,18098,18099,259],{},[73,18091,18048],{},[73,18093,18094],{},"git-cliff",[73,18096,18097],{},"changesets"," to generate from commits\u002FPRs. Conventional Commits format works well with ",[73,18100,18094],{},[15,18102,18104],{"id":18103},"ci-github-actions","CI (GitHub Actions)",[111,18106,18111],{"className":18107,"code":18109,"language":18110,"meta":117},[18108],"language-yaml","name: CI\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        rust: [stable, beta, nightly]\n        os: [ubuntu-latest, windows-latest, macos-latest]\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: dtolnay\u002Frust-toolchain@master\n        with: { toolchain: ${{ matrix.rust }}, components: clippy, rustfmt }\n      - run: cargo fmt -- --check\n      - run: cargo clippy --all-targets -- -D warnings\n      - run: cargo test --all-features\n      - run: cargo doc --no-deps\n","yaml",[73,18112,18109],{"__ignoreMap":117},[20,18114,18115,18116,480,18119,18122,18123,18126],{},"Add ",[73,18117,18118],{},"cargo-deny",[73,18120,18121],{},"cargo-audit"," for security. Use ",[73,18124,18125],{},"cargo nextest run"," for faster test execution.",[15,18128,18130],{"id":18129},"cross-compilation","Cross-Compilation",[111,18132,18135],{"className":18133,"code":18134,"language":116,"meta":117},[114],"rustup target add x86_64-unknown-linux-musl\ncargo build --target x86_64-unknown-linux-musl\n",[73,18136,18134],{"__ignoreMap":117},[20,18138,18139,18140,18143],{},"For cross-platform, ",[73,18141,18142],{},"cross"," (Docker-based) is the easiest:",[111,18145,18148],{"className":18146,"code":18147,"language":116,"meta":117},[114],"cargo install cross\ncross build --target aarch64-unknown-linux-gnu\n",[73,18149,18147],{"__ignoreMap":117},[20,18151,18152,18155],{},[73,18153,18154],{},"cargo-zigbuild"," uses Zig as a cross-linker (good for musl and Windows targets from Linux).",[15,18157,18159],{"id":18158},"binary-distribution","Binary Distribution",[33,18161,18162,18169,18182],{},[36,18163,18164,18165,18168],{},"Static linking with ",[73,18166,18167],{},"musl"," for portable Linux binaries.",[36,18170,18171,18172,27,18175,18178,18179,259],{},"Universal binaries on macOS: build for both ",[73,18173,18174],{},"x86_64-apple-darwin",[73,18176,18177],{},"aarch64-apple-darwin",", combine with ",[73,18180,18181],{},"lipo",[36,18183,18184,18185,18188,18189,18192],{},"Windows: ",[73,18186,18187],{},"cargo build --target x86_64-pc-windows-gnu"," for portable static binaries (or use ",[73,18190,18191],{},"cargo-wix"," for MSI installers).",[15,18194,18196],{"id":18195},"workspace-releases","Workspace Releases",[20,18198,18199,18200,18202],{},"For multi-crate workspaces, ",[73,18201,18048],{}," handles inter-crate version bumps and dependency updates.",[15,18204,287],{"id":18205},"msrv",[111,18207,18210],{"className":18208,"code":18209,"language":176,"meta":117},[174],"[package]\nrust-version = \"1.75\"\n",[73,18211,18209],{"__ignoreMap":117},[20,18213,18214],{},"CI must test with that version:",[111,18216,18219],{"className":18217,"code":18218,"language":18110,"meta":117},[18108],"- run: rustup install 1.75\n- run: rustup override set 1.75\n- run: cargo build\n",[73,18220,18218],{"__ignoreMap":117},[15,18222,711],{"id":710},[33,18224,18225,18241,18253,18264,18276,18285],{},[36,18226,18227,18230,18231,18234,18235,1212,18237,18240],{},[24,18228,18229],{},"Feature unification breaking builds",": if your crate's ",[73,18232,18233],{},"cfg(feature = \"x\")"," only makes sense with another crate's feature, you can't express that without ",[73,18236,17605],{},[73,18238,18239],{},"?dep\u002Ffeat"," syntax.",[36,18242,18243,18248,18249,18252],{},[24,18244,18245,18246],{},"Optional dep without ",[73,18247,17605],{}," creates an implicit feature of the same name; sometimes you want this (so users can ",[73,18250,18251],{},"features = [\"serde_json\"]","), sometimes you don't.",[36,18254,18255,18260,18261,259],{},[24,18256,18257],{},[73,18258,18259],{},"cargo build --features \"\""," is sometimes needed to override ",[73,18262,18263],{},"default-features",[36,18265,18266,18271,18272,18275],{},[24,18267,18268,18270],{},[73,18269,792],{}," and feature interaction",": read ",[73,18273,18274],{},"CARGO_FEATURE_*"," env vars in build scripts.",[36,18277,18278,18281,18282,18284],{},[24,18279,18280],{},"Profile inheritance",": a custom profile that doesn't ",[73,18283,17717],{}," from another starts empty (potentially wrong optimization).",[36,18286,18287,18291,18292,1546,18294,18296],{},[24,18288,18289],{},[73,18290,17776],{}," can be slower at runtime than ",[73,18293,17780],{},[73,18295,17784],{}," despite smaller binaries.",[15,18298,349],{"id":348},[20,18300,18301,18302,27,18304,18306,18307,18309,18310,18312],{},"Features are additive conditional-compilation flags. Design them additive-only. Use ",[73,18303,17605],{},[73,18305,18239],{}," for clean dep\u002Ffeature separation. Profiles control optimization and binary properties. ",[73,18308,792],{}," enables codegen and linking. CI should run fmt\u002Fclippy\u002Ftest\u002Fdoc and cross-compile. Use ",[73,18311,18048],{}," for versioning and publishing. Set and test the MSRV.",[20,18314,18315],{},"Next: The deeper type system — variance, HRTBs, and tricky generics.",{"title":117,"searchDepth":357,"depth":357,"links":18317},[18318,18322,18323,18326,18327,18331,18333,18335,18344,18345,18346,18347,18348,18349,18350],{"id":17558,"depth":357,"text":17559,"children":18319},[18320,18321],{"id":17568,"depth":364,"text":17569},{"id":17592,"depth":364,"text":17593},{"id":17609,"depth":357,"text":17610},{"id":17625,"depth":357,"text":17626,"children":18324},[18325],{"id":17660,"depth":364,"text":17661},{"id":17667,"depth":357,"text":17668},{"id":17707,"depth":357,"text":17708,"children":18328},[18329,18330],{"id":17717,"depth":364,"text":17717},{"id":17740,"depth":364,"text":17741},{"id":17788,"depth":357,"text":18332},"Build Scripts (build.rs)",{"id":17860,"depth":357,"text":18334},"links Key",{"id":17885,"depth":357,"text":17886,"children":18336},[18337,18338,18339,18340,18341,18342,18343],{"id":17889,"depth":364,"text":17890},{"id":17927,"depth":364,"text":17928},{"id":17961,"depth":364,"text":40},{"id":18000,"depth":364,"text":18001},{"id":18034,"depth":364,"text":18035},{"id":18062,"depth":364,"text":18063},{"id":18086,"depth":364,"text":18087},{"id":18103,"depth":357,"text":18104},{"id":18129,"depth":357,"text":18130},{"id":18158,"depth":357,"text":18159},{"id":18195,"depth":357,"text":18196},{"id":18205,"depth":357,"text":287},{"id":710,"depth":357,"text":711},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F28-cargo-features",{"title":17547,"description":17555},"rust\u002F28-cargo-features","tfhUA4l_UirIeSuZ-SZcD0UcwP7Ifg-UpqxJFL0s_58",{"id":18357,"title":18358,"body":18359,"description":18366,"extension":373,"meta":19313,"navigation":375,"path":19314,"seo":19315,"stem":19316,"__hash__":19317},"content\u002Frust\u002F29-advanced-type-system.md","29 — Advanced Type System (Variance, HRTBs, Subtyping)",{"type":8,"value":18360,"toc":19276},[18361,18364,18367,18371,18386,18401,18407,18411,18414,18449,18453,18578,18582,18589,18595,18603,18607,18613,18617,18623,18636,18640,18660,18664,18670,18674,18680,18692,18697,18708,18718,18724,18746,18756,18760,18762,18796,18799,18814,18817,18828,18834,18843,18848,18858,18873,18882,18888,18895,18901,18907,18948,18954,18958,18964,18989,18993,18996,19002,19017,19021,19027,19031,19048,19055,19058,19064,19071,19075,19078,19095,19105,19109,19115,19118,19124,19130,19144,19148,19154,19163,19165,19262,19264,19273],[11,18362,18358],{"id":18363},"_29-advanced-type-system-variance-hrtbs-subtyping",[20,18365,18366],{},"This chapter covers the parts of the type system that most Rust developers never need to write, but should understand to read errors and design libraries.",[15,18368,18370],{"id":18369},"subtyping-in-rust","Subtyping in Rust",[20,18372,18373,18374,18377,18378,18381,18382,18385],{},"Most languages have subtyping via inheritance (Cat : Animal). Rust's subtyping is ",[24,18375,18376],{},"only"," through ",[24,18379,18380],{},"lifetimes",": a longer lifetime is a ",[183,18383,18384],{},"subtype"," of a shorter one.",[20,18387,18388,18390,18391,18393,18394,6840,18396,18398,18399,9258],{},[73,18389,4560],{}," is a subtype of ",[73,18392,4499],{}," for any ",[73,18395,4499],{},[73,18397,4577],{}," can be used where ",[73,18400,4491],{},[111,18402,18405],{"className":18403,"code":18404,"language":397,"meta":117},[395],"fn takes_str\u003C'a>(s: &'a str) { \u002F* ... *\u002F }\nlet s: &'static str = \"hi\";\ntakes_str(s);    \u002F\u002F OK: 'static \u003C: 'a\n",[73,18406,18404],{"__ignoreMap":117},[15,18408,18410],{"id":18409},"variance","Variance",[20,18412,18413],{},"Variance describes how subtyping of parameters affects subtyping of the constructed type:",[33,18415,18416,18427,18437,18443],{},[36,18417,18418,1356,18421,592,18424],{},[24,18419,18420],{},"Covariant",[73,18422,18423],{},"T \u003C: U",[73,18425,18426],{},"F\u003CT> \u003C: F\u003CU>",[36,18428,18429,1356,18432,592,18434],{},[24,18430,18431],{},"Contravariant",[73,18433,18423],{},[73,18435,18436],{},"F\u003CU> \u003C: F\u003CT>",[36,18438,18439,18442],{},[24,18440,18441],{},"Invariant"," no subtyping relationship",[36,18444,18445,18448],{},[24,18446,18447],{},"Bivariant"," both directions (rare; only happens with unused params)",[130,18450,18452],{"id":18451},"examples","Examples",[917,18454,18455,18463],{},[920,18456,18457],{},[923,18458,18459,18461],{},[926,18460,1340],{},[926,18462,18410],{},[936,18464,18465,18478,18492,18502,18513,18529,18543,18557],{},[923,18466,18467,18471],{},[941,18468,18469],{},[73,18470,4678],{},[941,18472,18473,18474,27,18476],{},"covariant in ",[73,18475,4499],{},[73,18477,4705],{},[923,18479,18480,18484],{},[941,18481,18482],{},[73,18483,4695],{},[941,18485,18473,18486,480,18488,292,18490],{},[73,18487,4499],{},[24,18489,4702],{},[73,18491,4705],{},[923,18493,18494,18498],{},[941,18495,18496],{},[73,18497,10971],{},[941,18499,18473,18500],{},[73,18501,4705],{},[923,18503,18504,18508],{},[941,18505,18506],{},[73,18507,10968],{},[941,18509,18510,18511],{},"invariant in ",[73,18512,4705],{},[923,18514,18515,18520],{},[941,18516,18517],{},[73,18518,18519],{},"fn(T) -> U",[941,18521,18522,18523,18525,18526],{},"contravariant in ",[73,18524,4705],{},", covariant in ",[73,18527,18528],{},"U",[923,18530,18531,18539],{},[941,18532,18533,480,18535,480,18537],{},[73,18534,10461],{},[73,18536,10620],{},[73,18538,4930],{},[941,18540,18473,18541],{},[73,18542,4705],{},[923,18544,18545,18553],{},[941,18546,18547,480,18549,480,18551],{},[73,18548,4712],{},[73,18550,4715],{},[73,18552,4718],{},[941,18554,18510,18555],{},[73,18556,4705],{},[923,18558,18559,18564],{},[941,18560,18561],{},[73,18562,18563],{},"&'a mut &'b T",[941,18565,18473,18566,18568,18569,18572,18573,27,18576,1587],{},[73,18567,4499],{},", invariant in ",[73,18570,18571],{},"&'b T"," (which is covariant in ",[73,18574,18575],{},"'b",[73,18577,4705],{},[130,18579,18581],{"id":18580},"why-does-variance-matter","Why does variance matter?",[20,18583,9814,18584,18586,18587,170],{},[73,18585,4695],{}," were covariant in ",[73,18588,4705],{},[111,18590,18593],{"className":18591,"code":18592,"language":397,"meta":117},[395],"let mut s = String::from(\"hi\");\nlet r: &mut &'static str = &mut s;    \u002F\u002F would-be covariance\nlet short = String::from(\"bye\");\n*r = &short;                          \u002F\u002F writes &'short str into a &'static slot\nprintln!(\"{}\", s);                    \u002F\u002F s dangling!\n",[73,18594,18592],{"__ignoreMap":117},[20,18596,18597,18598,6859,18600,18602],{},"Invariance in ",[73,18599,4705],{},[73,18601,1117],{}," is what prevents this. The compiler rejects the first assignment.",[130,18604,18606],{"id":18605},"practical-implication","Practical Implication",[20,18608,18609,18610,18612],{},"When you get a weird lifetime error, invariance is often the cause. The fix is usually to add an explicit lifetime tie or to introduce indirection (",[73,18611,10461],{}," makes some invariance problems tractable).",[15,18614,18616],{"id":18615},"higher-rank-trait-bounds-hrtbs","Higher-Rank Trait Bounds (HRTBs)",[111,18618,18621],{"className":18619,"code":18620,"language":397,"meta":117},[395],"fn foo\u003CF>(f: F) where F: for\u003C'a> Fn(&'a str) { \u002F* ... *\u002F }\n",[73,18622,18620],{"__ignoreMap":117},[20,18624,18625,4744,18627,18629,18630,18632,18633,18635],{},[73,18626,4743],{},[73,18628,4499],{},"\". The function ",[73,18631,9362],{}," must accept any borrowed ",[73,18634,1630],{},", not just one with a specific lifetime.",[130,18637,18639],{"id":18638},"where-hrtbs-appear","Where HRTBs Appear",[33,18641,18642,18651],{},[36,18643,18644,18645],{},"Closures that take references without explicit lifetimes:\n",[111,18646,18649],{"className":18647,"code":18648,"language":397,"meta":117},[395],"let f: impl for\u003C'a> Fn(&'a str) = |s| println!(\"{s}\");\n",[73,18650,18648],{"__ignoreMap":117},[36,18652,18653,1212,18655,1212,18657,18659],{},[73,18654,1799],{},[73,18656,2332],{},[73,18658,2335],{}," implicitly use HRTB on their arguments.",[130,18661,18663],{"id":18662},"common-pattern","Common Pattern",[111,18665,18668],{"className":18666,"code":18667,"language":397,"meta":117},[395],"fn apply_any(f: impl for\u003C'a> Fn(&'a [u8])) {\n    let buf = [0u8; 16];\n    f(&buf);\n}\n",[73,18669,18667],{"__ignoreMap":117},[15,18671,18673],{"id":18672},"associated-types-vs-generics","Associated Types vs Generics",[111,18675,18678],{"className":18676,"code":18677,"language":397,"meta":117},[395],"\u002F\u002F Associated type — impl picks:\ntrait Iterator { type Item; fn next(&mut self) -> Option\u003CSelf::Item>; }\n\n\u002F\u002F Generic — caller picks:\ntrait From\u003CT> { fn from(value: T) -> Self; }\n",[73,18679,18677],{"__ignoreMap":117},[20,18681,18682,18683,18685,18686,480,18689,526],{},"Use associated types when each impl has ",[183,18684,8291],{}," natural type. Use generics when multiple impls can coexist (",[73,18687,18688],{},"From\u003C&str>",[73,18690,18691],{},"From\u003CString>",[15,18693,18695,14364],{"id":18694},"impl-trait-internals",[73,18696,4771],{},[20,18698,18699,1538,18702,18704,18705,18707],{},[73,18700,18701],{},"fn f() -> impl Trait",[183,18703,4476],{}," concrete type that implements ",[73,18706,8387],{},". The type is inferred per return path; if branches return different concrete types, you must box.",[20,18709,18710,18713,18714,18717],{},[73,18711,18712],{},"fn f(x: impl Trait)"," is sugar for ",[73,18715,18716],{},"fn f\u003CT: Trait>(x: T)",". The caller picks the type.",[15,18719,18721,18723],{"id":18720},"dyn-trait-type-erasure",[73,18722,8210],{}," Type Erasure",[20,18725,18726,1592,18728,18731,18732,480,18734,480,18737,480,18740,480,18743,526],{},[73,18727,8210],{},[24,18729,18730],{},"dynamic"," type — values are behind a pointer (",[73,18733,8373],{},[73,18735,18736],{},"&dyn Trait",[73,18738,18739],{},"Arc\u003Cdyn Trait>",[73,18741,18742],{},"Rc\u003Cdyn Trait>",[73,18744,18745],{},"Pin\u003CBox\u003Cdyn Trait>>",[20,18747,18748,18749,18752,18753,259],{},"The pointer is ",[24,18750,18751],{},"wide"," (fat): ",[73,18754,18755],{},"(data_ptr, vtable_ptr)",[15,18757,18759],{"id":18758},"object-safety-recap","Object Safety (Recap)",[20,18761,8221],{},[33,18763,18764,18770,18772,18781,18786],{},[36,18765,18766,18767,18769],{},"No ",[73,18768,2264],{}," in argument positions or return by value.",[36,18771,8232],{},[36,18773,18774,18775,18777,18778,18780],{},"All methods have ",[73,18776,8241],{}," or take ",[73,18779,2245],{}," by reference.",[36,18782,18783,18784,259],{},"No associated constants without a default that depend on ",[73,18785,2264],{},[36,18787,18788,1212,18790,18792,18793,18795],{},[73,18789,8563],{},[73,18791,8566],{}," as supertraits are OK; ",[73,18794,8553],{}," as a supertrait disqualifies.",[20,18797,18798],{},"Workarounds for non-object-safe traits:",[33,18800,18801,18806,18809],{},[36,18802,18803,18804,259],{},"Use a wrapper trait that doesn't return ",[73,18805,2264],{},[36,18807,18808],{},"Use generic dispatch instead of trait objects.",[36,18810,18115,18811,18813],{},[73,18812,8241],{}," to static methods.",[15,18815,18816],{"id":12640},"Auto Traits",[20,18818,18819,480,18821,480,18823,480,18825,18827],{},[73,18820,8563],{},[73,18822,8566],{},[73,18824,8576],{},[73,18826,8553],{}," are auto traits — the compiler auto-implements them based on constituent types.",[111,18829,18832],{"className":18830,"code":18831,"language":397,"meta":117},[395],"struct MyType(Rc\u003Cu8>);   \u002F\u002F not Send, not Sync because Rc isn't\nstruct MyType2(Arc\u003Cu8>); \u002F\u002F Send + Sync\n",[73,18833,18831],{"__ignoreMap":117},[20,18835,18836,18837,1212,18839,18842],{},"You can opt out or opt in via ",[73,18838,12718],{},[73,18840,18841],{},"impl !Send"," (negative impls are unstable).",[15,18844,18846,3109],{"id":18845},"sized-trait",[73,18847,8553],{},[20,18849,18850,18851,18853,18854,18857],{},"Most types are ",[73,18852,8553],{}," (known size at compile time). Exceptions are ",[73,18855,18856],{},"?Sized"," types:",[33,18859,18860],{},[36,18861,18862,480,18864,480,18867,480,18869,18872],{},[73,18863,4495],{},[73,18865,18866],{},"[T]",[73,18868,8210],{},[73,18870,18871],{},"*const ()"," (in some contexts)",[20,18874,18875,18876,18878,18879,170],{},"Generic parameters default to ",[73,18877,8553],{},"; relax with ",[73,18880,18881],{},"T: ?Sized",[111,18883,18886],{"className":18884,"code":18885,"language":397,"meta":117},[395],"fn first_byte(s: &str) -> u8 { \u002F* str is !Sized but you can take &str *\u002F }\nfn foo\u003CT: ?Sized>(x: &T) { \u002F* works for unsized T *\u002F }\n",[73,18887,18885],{"__ignoreMap":117},[15,18889,18891,18894],{"id":18890},"phantomdatat-marker-for-unused-type-params",[73,18892,18893],{},"PhantomData\u003CT>"," — Marker for Unused Type Params",[111,18896,18899],{"className":18897,"code":18898,"language":397,"meta":117},[395],"use std::marker::PhantomData;\n\nstruct Tagged\u003CTag, T> {\n    data: T,\n    _tag: PhantomData\u003CTag>,\n}\n",[73,18900,18898],{"__ignoreMap":117},[20,18902,18903,18906],{},[73,18904,18905],{},"PhantomData"," is zero-sized but tells the compiler about ownership\u002Fvariance:",[33,18908,18909,18917,18925,18936],{},[36,18910,18911,18913,18914,18916],{},[73,18912,18893],{}," makes your type behave like it owns a ",[73,18915,4705],{}," for drop-checking and variance.",[36,18918,18919,18922,18923,259],{},[73,18920,18921],{},"PhantomData\u003C&'a T>"," makes it covariant in ",[73,18924,4499],{},[36,18926,18927,18930,18931,1212,18933,259],{},[73,18928,18929],{},"PhantomData\u003C*mut T>"," makes it invariant and ",[73,18932,13942],{},[73,18934,18935],{},"!Sync",[36,18937,18938,18941,18942,27,18944,1212,18946,259],{},[73,18939,18940],{},"PhantomData\u003Cfn(T) -> ()>"," makes it contravariant in ",[73,18943,4705],{},[73,18945,13942],{},[73,18947,18935],{},[20,18949,18950,18951,18953],{},"Picking the right ",[73,18952,18905],{}," variant is critical for unsafe collections.",[15,18955,18957],{"id":18956},"newtype-pattern","Newtype Pattern",[111,18959,18962],{"className":18960,"code":18961,"language":397,"meta":117},[395],"struct Meters(f64);\nstruct Miles(f64);\n\nimpl Meters { fn to_miles(self) -> Miles { Miles(self.0 \u002F 1609.344) } }\n",[73,18963,18961],{"__ignoreMap":117},[33,18965,18966,18969,18976],{},[36,18967,18968],{},"Zero-cost wrapper for type safety.",[36,18970,18971,18972,18975],{},"No accidental mixing: ",[73,18973,18974],{},"Meters(5.0) + Miles(1.0)"," is a type error.",[36,18977,7549,18978,1212,18980,1212,18982,1212,18984,1212,18986,18988],{},[73,18979,1879],{},[73,18981,1882],{},[73,18983,461],{},[73,18985,3782],{},[73,18987,8510],{}," as needed.",[15,18990,18992],{"id":18991},"type-level-programming","Type-Level Programming",[20,18994,18995],{},"With traits and associated types:",[111,18997,19000],{"className":18998,"code":18999,"language":397,"meta":117},[395],"trait Peano { type Next; }\nstruct Zero;\nstruct Succ\u003CT>(T);\n\nimpl Peano for Zero { type Next = Succ\u003CZero>; }\nimpl\u003CT: Peano> Peano for Succ\u003CT> { type Next = Succ\u003CSucc\u003CT>>; }\n\ntype One = \u003CZero as Peano>::Next;\ntype Two = \u003COne as Peano>::Next;\n",[73,19001,18999],{"__ignoreMap":117},[20,19003,19004,19005,19008,19009,19012,19013,19016],{},"Practical for ",[73,19006,19007],{},"typenum"," (compile-time integers), dimension tracking (",[73,19010,19011],{},"uom","), and ",[73,19014,19015],{},"frunk","'s HList.",[15,19018,19020],{"id":19019},"const-generics-deep","Const Generics (Deep)",[111,19022,19025],{"className":19023,"code":19024,"language":397,"meta":117},[395],"struct Arr\u003Cconst N: usize> { data: [u8; N] }\n\nimpl\u003Cconst N: usize> Arr\u003CN> {\n    fn len(&self) -> usize { N }\n}\n\nfn sum\u003Cconst N: usize>(arr: &[i32; N]) -> i32 { arr.iter().sum() }\n",[73,19026,19024],{"__ignoreMap":117},[130,19028,19030],{"id":19029},"limits","Limits",[33,19032,19033,19036,19042],{},[36,19034,19035],{},"Only integer\u002Fbool\u002Fchar const params on stable.",[36,19037,19038,19039,526],{},"Const expressions as params are unstable (",[73,19040,19041],{},"[T; N + 1]",[36,19043,19044,19045,19047],{},"Min const generics only — full generics (e.g., ",[73,19046,4491],{}," const param) is unstable.",[15,19049,19051,19054],{"id":19050},"min_specialization-and-full-specialization",[73,19052,19053],{},"min_specialization"," and Full Specialization",[20,19056,19057],{},"Specialization lets you provide a more specific impl overriding a general one:",[111,19059,19062],{"className":19060,"code":19061,"language":397,"meta":117},[395],"#![feature(min_specialization)]\ntrait Pick { fn pick(&self); }\nimpl\u003CT> Pick for T { default fn pick(&self) { println!(\"default\"); } }\nimpl Pick for String { fn pick(&self) { println!(\"string\"); } }  \u002F\u002F specialized\n",[73,19063,19061],{"__ignoreMap":117},[20,19065,19066,19067,19070],{},"Unstable. Avoid in production. Workarounds: macros, separate traits, or ",[73,19068,19069],{},"auto impl","-style delegation.",[15,19072,19074],{"id":19073},"higher-kinded-types-hkt","Higher-Kinded Types (HKT)",[20,19076,19077],{},"Rust doesn't have HKTs (types parameterized over type constructors). Workarounds:",[33,19079,19080,19086,19089],{},[36,19081,19082,19085],{},[73,19083,19084],{},"higher"," crate",[36,19087,19088],{},"Associated type families (unstable)",[36,19090,19091,19092,19094],{},"Manual \"Functor\" traits via ",[73,19093,18905],{}," (clunky)",[20,19096,19097,19098,480,19100,480,19102,19104],{},"The lack of HKTs limits abstracting over ",[73,19099,1481],{},[73,19101,1194],{},[73,19103,2792],{}," uniformly. Most code doesn't need it.",[15,19106,19108],{"id":19107},"gats-generic-associated-types","GATs (Generic Associated Types)",[111,19110,19113],{"className":19111,"code":19112,"language":397,"meta":117},[395],"trait LendingIterator {\n    type Item\u003C'a> where Self: 'a;\n    fn next(&mut self) -> Option\u003CSelf::Item\u003C'_>>;\n}\n",[73,19114,19112],{"__ignoreMap":117},[20,19116,19117],{},"Associated types that themselves have generic params (lifetimes\u002Ftypes). Stable since 1.65. Lets you express borrowing iterators, async traits, etc.",[15,19119,19121,19122],{"id":19120},"subtyping-and-cow","Subtyping and ",[73,19123,11369],{},[111,19125,19128],{"className":19126,"code":19127,"language":397,"meta":117},[395],"fn process\u003C'a>(s: Cow\u003C'a, str>) { \u002F* ... *\u002F }\nprocess(\"static\".into());      \u002F\u002F Cow::Borrowed(&'static str)\nprocess(String::from(\"x\").into());   \u002F\u002F Cow::Owned\n",[73,19129,19127],{"__ignoreMap":117},[20,19131,19132,19134,19135,19137,19138,18390,19141,259],{},[73,19133,10925],{}," is variant in ",[73,19136,4499],{}," (covariant), so ",[73,19139,19140],{},"Cow\u003C'static, str>",[73,19142,19143],{},"Cow\u003C'a, str>",[15,19145,19147],{"id":19146},"negative-trait-impls","Negative Trait Impls",[111,19149,19152],{"className":19150,"code":19151,"language":397,"meta":117},[395],"impl !Send for MyType {}\n",[73,19153,19151],{"__ignoreMap":117},[20,19155,19156,19157,1546,19160,259],{},"Unstable; you can opt out of auto traits today via ",[73,19158,19159],{},"PhantomData\u003C*const ()>",[73,19161,19162],{},"Rc\u003C()>",[15,19164,6469],{"id":6468},[33,19166,19167,19180,19198,19212,19221,19233,19247],{},[36,19168,19169,19172,19173,19175,19176,19179],{},[24,19170,19171],{},"Forgetting variance",": writing ",[73,19174,18893],{}," when you needed ",[73,19177,19178],{},"PhantomData\u003Cfn() -> T>"," (covariant vs invariant).",[36,19181,19182,71,19185,2534,19188,470,19191,1592,19194,19197],{},[24,19183,19184],{},"HRTB vs named lifetime",[73,19186,19187],{},"fn(&str)",[73,19189,19190],{},"for\u003C'a> fn(&'a str)",[73,19192,19193],{},"fn\u003C'a>(&'a str)",[183,19195,19196],{},"specific"," lifetime.",[36,19199,19200,19205,19206,19208,19209,259],{},[24,19201,19202],{},[73,19203,19204],{},"dyn Trait + 'static",": by default ",[73,19207,8210],{}," borrows for some lifetime; you usually want ",[73,19210,19211],{},"Box\u003Cdyn Trait + 'static>",[36,19213,19214,19217,19218,19220],{},[24,19215,19216],{},"Object safety regression",": adding a generic method to a trait breaks all ",[73,19219,8210],{}," users.",[36,19222,19223,19226,19227,19229,19230,259],{},[24,19224,19225],{},"Auto-trait inference",": a struct containing a ",[73,19228,3803],{}," makes the whole struct ",[73,19231,19232],{},"!Send + !Sync",[36,19234,19235,71,19240,1052,19243,19246],{},[24,19236,19237,19239],{},[73,19238,8553],{}," default",[73,19241,19242],{},"fn foo\u003CT>()",[73,19244,19245],{},"T: Sized","; unsized locals and parameters are unstable.",[36,19248,19249,71,19254,8025,19256,19258,19259,259],{},[24,19250,19251,19252],{},"Trait objects and ",[73,19253,8563],{},[73,19255,8373],{},[73,19257,8563],{}," unless you write ",[73,19260,19261],{},"Box\u003Cdyn Trait + Send>",[15,19263,349],{"id":348},[20,19265,19266,19267,19269,19270,19272],{},"Variance governs subtype relationships and is mostly about lifetimes (and ",[73,19268,3850],{},"'s invariance in ",[73,19271,4705],{},"). HRTBs express \"for all lifetimes.\" Associated types vs generics: one natural type vs caller-supplied. Object safety limits trait objects. GATs (1.65+) enable borrowing in associated types. Const generics (1.51+) parameterize by integers\u002Fbools. PhantomData tunes variance and drop behavior. Newtype pattern is the idiomatic type-distinctness tool.",[20,19274,19275],{},"Next: Common design patterns and idiomatic Rust.",{"title":117,"searchDepth":357,"depth":357,"links":19277},[19278,19279,19284,19288,19289,19291,19293,19294,19295,19297,19299,19300,19301,19304,19306,19307,19308,19310,19311,19312],{"id":18369,"depth":357,"text":18370},{"id":18409,"depth":357,"text":18410,"children":19280},[19281,19282,19283],{"id":18451,"depth":364,"text":18452},{"id":18580,"depth":364,"text":18581},{"id":18605,"depth":364,"text":18606},{"id":18615,"depth":357,"text":18616,"children":19285},[19286,19287],{"id":18638,"depth":364,"text":18639},{"id":18662,"depth":364,"text":18663},{"id":18672,"depth":357,"text":18673},{"id":18694,"depth":357,"text":19290},"impl Trait Internals",{"id":18720,"depth":357,"text":19292},"dyn Trait Type Erasure",{"id":18758,"depth":357,"text":18759},{"id":12640,"depth":357,"text":18816},{"id":18845,"depth":357,"text":19296},"Sized Trait",{"id":18890,"depth":357,"text":19298},"PhantomData\u003CT> — Marker for Unused Type Params",{"id":18956,"depth":357,"text":18957},{"id":18991,"depth":357,"text":18992},{"id":19019,"depth":357,"text":19020,"children":19302},[19303],{"id":19029,"depth":364,"text":19030},{"id":19050,"depth":357,"text":19305},"min_specialization and Full Specialization",{"id":19073,"depth":357,"text":19074},{"id":19107,"depth":357,"text":19108},{"id":19120,"depth":357,"text":19309},"Subtyping and Cow",{"id":19146,"depth":357,"text":19147},{"id":6468,"depth":357,"text":6469},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F29-advanced-type-system",{"title":18358,"description":18366},"rust\u002F29-advanced-type-system","Y--nO5U9dBnj3bPBhZO-2muR9Y0TY7Q9yEqh7W7ZlhI",{"id":19319,"title":19320,"body":19321,"description":19328,"extension":373,"meta":20104,"navigation":375,"path":20105,"seo":20106,"stem":20107,"__hash__":20108},"content\u002Frust\u002F30-design-patterns.md","30 — Design Patterns & Idiomatic Rust",{"type":8,"value":19322,"toc":20061},[19323,19326,19329,19333,19336,19342,19352,19356,19362,19376,19380,19386,19405,19409,19412,19418,19427,19431,19437,19443,19447,19450,19456,19462,19466,19472,19476,19482,19488,19491,19495,19498,19504,19515,19519,19525,19528,19532,19535,19541,19547,19551,19557,19566,19572,19579,19588,19594,19600,19606,19615,19619,19625,19628,19632,19638,19642,19648,19651,19655,19664,19668,19671,19675,19681,19692,19696,19704,19711,19720,19724,19730,19733,19740,19755,19759,19765,19794,19798,19828,19832,19844,19848,19859,19866,19872,19875,19879,19885,19889,19975,19977,20037,20039,20058],[11,19324,19320],{"id":19325},"_30-design-patterns-idiomatic-rust",[20,19327,19328],{},"Rust isn't OOP, but it has idioms for abstraction, polymorphism, and reuse. Here are the patterns every pro Rust developer should know.",[15,19330,19332],{"id":19331},"_1-builder-pattern","1. Builder Pattern",[20,19334,19335],{},"For complex construction with many optional parameters:",[111,19337,19340],{"className":19338,"code":19339,"language":397,"meta":117},[395],"pub struct Server {\n    host: String,\n    port: u16,\n    tls: bool,\n    max_conn: usize,\n}\n\npub struct ServerBuilder {\n    host: Option\u003CString>,\n    port: Option\u003Cu16>,\n    tls: Option\u003Cbool>,\n    max_conn: Option\u003Cusize>,\n}\n\nimpl ServerBuilder {\n    pub fn new() -> Self {\n        ServerBuilder { host: None, port: None, tls: None, max_conn: None }\n    }\n    pub fn host(mut self, host: impl Into\u003CString>) -> Self { self.host = Some(host.into()); self }\n    pub fn port(mut self, port: u16) -> Self { self.port = Some(port); self }\n    pub fn tls(mut self, tls: bool) -> Self { self.tls = Some(tls); self }\n    pub fn max_conn(mut self, m: usize) -> Self { self.max_conn = Some(m); self }\n    pub fn build(self) -> Result\u003CServer, String> {\n        Ok(Server {\n            host: self.host.ok_or(\"host required\")?,\n            port: self.port.unwrap_or(80),\n            tls: self.tls.unwrap_or(false),\n            max_conn: self.max_conn.unwrap_or(100),\n        })\n    }\n}\n\nlet s = ServerBuilder::new().host(\"localhost\").tls(true).build()?;\n",[73,19341,19339],{"__ignoreMap":117},[20,19343,19344,19345,1546,19348,19351],{},"For less boilerplate, use the ",[73,19346,19347],{},"derive_builder",[73,19349,19350],{},"typed_builder"," crates.",[130,19353,19355],{"id":19354},"typestate-builder","Typestate Builder",[111,19357,19360],{"className":19358,"code":19359,"language":397,"meta":117},[395],"pub struct MissingHost;\npub struct WithHost(String);\n\npub struct ServerBuilder\u003CH> { host: H, \u002F* ... *\u002F }\n\nimpl ServerBuilder\u003CMissingHost> {\n    pub fn new() -> Self { ServerBuilder { host: MissingHost } }\n    pub fn host(self, h: String) -> ServerBuilder\u003CWithHost> { ServerBuilder { host: WithHost(h) } }\n}\nimpl ServerBuilder\u003CWithHost> {\n    pub fn build(self) -> Server { \u002F* ... *\u002F }\n}\n",[73,19361,19359],{"__ignoreMap":117},[20,19363,19364,19365,19368,19369,9877,19372,19375],{},"Compile-time enforcement: you can't ",[73,19366,19367],{},"build()"," without setting ",[73,19370,19371],{},"host",[73,19373,19374],{},"bon"," crate provides this ergonomically.",[15,19377,19379],{"id":19378},"_2-newtype-pattern","2. Newtype Pattern",[111,19381,19384],{"className":19382,"code":19383,"language":397,"meta":117},[395],"pub struct UserId(pub u64);\npub struct Email(pub String);\n\nimpl Email {\n    pub fn new(s: String) -> Result\u003CSelf, &'static str> {\n        if s.contains('@') { Ok(Email(s)) } else { Err(\"invalid\") }\n    }\n}\n",[73,19385,19383],{"__ignoreMap":117},[33,19387,19388,19391,19394],{},[36,19389,19390],{},"Zero-cost type distinction.",[36,19392,19393],{},"Constructor can validate invariants.",[36,19395,7549,19396,1212,19398,1212,19400,1212,19402,19404],{},[73,19397,1879],{},[73,19399,461],{},[73,19401,469],{},[73,19403,3782],{}," as appropriate (don't over-implement).",[15,19406,19408],{"id":19407},"_3-typestate-pattern","3. Typestate Pattern",[20,19410,19411],{},"Encode state machines in types:",[111,19413,19416],{"className":19414,"code":19415,"language":397,"meta":117},[395],"pub struct Draft; pub struct Reviewed; pub struct Published;\n\npub struct Article\u003CS> { content: String, _state: PhantomData\u003CS> }\n\nimpl Article\u003CDraft> {\n    pub fn new(content: String) -> Self { Article { content, _state: PhantomData } }\n    pub fn review(self) -> Article\u003CReviewed> { Article { content: self.content, _state: PhantomData } }\n}\nimpl Article\u003CReviewed> {\n    pub fn publish(self) -> Article\u003CPublished> { Article { content: self.content, _state: PhantomData } }\n}\nimpl\u003CS> Article\u003CS> {\n    pub fn content(&self) -> &str { &self.content }\n}\n",[73,19417,19415],{"__ignoreMap":117},[20,19419,6069,19420,8014,19423,19426],{},[73,19421,19422],{},"publish",[73,19424,19425],{},"Draft"," is a compile-time error. Methods only exist in valid states.",[15,19428,19430],{"id":19429},"_4-strategy-via-traits","4. Strategy via Traits",[111,19432,19435],{"className":19433,"code":19434,"language":397,"meta":117},[395],"pub trait Compressor { fn compress(&self, data: &[u8]) -> Vec\u003Cu8>; }\n\npub struct Gzip; pub struct Lz4;\n\nimpl Compressor for Gzip { fn compress(&self, data: &[u8]) -> Vec\u003Cu8> { \u002F* ... *\u002F Vec::new() } }\nimpl Compressor for Lz4  { fn compress(&self, data: &[u8]) -> Vec\u003Cu8> { \u002F* ... *\u002F Vec::new() } }\n\npub fn archive\u003CC: Compressor>(compressor: &C, files: &[File]) -> Vec\u003Cu8> {\n    let mut out = Vec::new();\n    for f in files { out.extend(compressor.compress(&f.data)); }\n    out\n}\n",[73,19436,19434],{"__ignoreMap":117},[20,19438,19439,19440,259],{},"Static dispatch via generics, or dynamic via ",[73,19441,19442],{},"Box\u003Cdyn Compressor>",[15,19444,19446],{"id":19445},"_5-visitor-pattern","5. Visitor Pattern",[20,19448,19449],{},"For traversing heterogeneous structures:",[111,19451,19454],{"className":19452,"code":19453,"language":397,"meta":117},[395],"pub trait Visitor {\n    fn visit_string(&mut self, s: &str);\n    fn visit_number(&mut self, n: f64);\n    fn visit_array(&mut self, elems: &[Value]);\n}\n\npub enum Value { Str(String), Num(f64), Arr(Vec\u003CValue>) }\n\nimpl Value {\n    pub fn accept(&self, v: &mut impl Visitor) {\n        match self {\n            Value::Str(s) => v.visit_string(s),\n            Value::Num(n) => v.visit_number(*n),\n            Value::Arr(a) => v.visit_array(a),\n        }\n    }\n}\n",[73,19455,19453],{"__ignoreMap":117},[20,19457,19458,19459,19461],{},"Common in ",[73,19460,14735],{}," deserializers and AST traversal.",[15,19463,19465],{"id":19464},"_6-command-pattern","6. Command Pattern",[111,19467,19470],{"className":19468,"code":19469,"language":397,"meta":117},[395],"pub trait Command { fn execute(&self, ctx: &mut Context); }\n\npub struct Save { pub path: String }\nimpl Command for Save { fn execute(&self, ctx: &mut Context) { \u002F* ... *\u002F } }\n\npub struct Print { pub text: String }\nimpl Command for Print { fn execute(&self, ctx: &mut Context) { \u002F* ... *\u002F } }\n\nlet cmds: Vec\u003CBox\u003Cdyn Command>> = vec![\n    Box::new(Save { path: \"x\".into() }),\n    Box::new(Print { text: \"hi\".into() }),\n];\nfor c in cmds { c.execute(&mut ctx); }\n",[73,19471,19469],{"__ignoreMap":117},[15,19473,19475],{"id":19474},"_7-raii-resource-acquisition-is-initialization","7. RAII — Resource Acquisition Is Initialization",[20,19477,19478,19479,19481],{},"The most Rust-idiomatic pattern. Resources are tied to types; ",[73,19480,3217],{}," cleans up:",[111,19483,19486],{"className":19484,"code":19485,"language":397,"meta":117},[395],"pub struct File { handle: RawFd }\nimpl File {\n    pub fn open(path: &str) -> std::io::Result\u003CSelf> {\n        let fd = unsafe { libc::open(...) };\n        Ok(File { handle: fd })\n    }\n}\nimpl Drop for File {\n    fn drop(&mut self) { unsafe { libc::close(self.handle); } }\n}\n",[73,19487,19485],{"__ignoreMap":117},[20,19489,19490],{},"No leak, no double-close, no use-after-close — all enforced by the compiler.",[15,19492,19494],{"id":19493},"_8-iterator-pattern","8. Iterator Pattern",[20,19496,19497],{},"Lazy, composable:",[111,19499,19502],{"className":19500,"code":19501,"language":397,"meta":117},[395],"let v: Vec\u003Ci32> = (1..100)\n    .filter(|x| x % 2 == 0)\n    .map(|x| x * x)\n    .take(10)\n    .collect();\n",[73,19503,19501],{"__ignoreMap":117},[20,19505,19506,19507,2559,19510,19512,19513,259],{},"Custom iterators implement ",[73,19508,19509],{},"Iterator::next",[73,19511,7565],{}," builds any ",[73,19514,7713],{},[15,19516,19518],{"id":19517},"_9-smart-constructor-pattern","9. Smart-Constructor Pattern",[111,19520,19523],{"className":19521,"code":19522,"language":397,"meta":117},[395],"pub struct Percent(u8);\nimpl Percent {\n    pub fn new(p: u8) -> Option\u003CSelf> {\n        if p \u003C= 100 { Some(Percent(p)) } else { None }\n    }\n}\n",[73,19524,19522],{"__ignoreMap":117},[20,19526,19527],{},"Never expose the inner; force construction through validation.",[15,19529,19531],{"id":19530},"_10-extension-trait","10. Extension Trait",[20,19533,19534],{},"Add methods to external types (with a wrapper):",[111,19536,19539],{"className":19537,"code":19538,"language":397,"meta":117},[395],"pub trait StrExt { fn shout(&self) -> String; }\nimpl StrExt for str { fn shout(&self) -> String { format!(\"{}!!!\", self.to_uppercase()) } }\nuse crate::StrExt;\n\"hi\".shout();\n",[73,19540,19538],{"__ignoreMap":117},[20,19542,19543,19544,19546],{},"You can't implement an external trait for an external type (orphan rule), but you ",[183,19545,6372],{}," implement your own trait for any type.",[15,19548,19550],{"id":19549},"_11-handle-raii-wrapper-around-foreign-types","11. Handle \u002F RAII Wrapper around Foreign Types",[111,19552,19555],{"className":19553,"code":19554,"language":397,"meta":117},[395],"pub struct Database(*mut bindings::sqlite3);\nimpl Drop for Database { fn drop(&mut self) { unsafe { bindings::close(self.0) } } }\n",[73,19556,19554],{"__ignoreMap":117},[15,19558,19560,19561,1212,19563,19565],{"id":19559},"_12-frominto-for-conversions","12. ",[73,19562,1879],{},[73,19564,1882],{}," for Conversions",[111,19567,19570],{"className":19568,"code":19569,"language":397,"meta":117},[395],"impl From\u003CRawData> for Processed { fn from(r: RawData) -> Self { \u002F* ... *\u002F } }\nlet p: Processed = raw.into();\n",[73,19571,19569],{"__ignoreMap":117},[20,19573,19574,19575,8620,19577,8623],{},"Idiomatic conversion path. Implement ",[73,19576,1879],{},[73,19578,1882],{},[15,19580,19582,19583,1212,19585,19587],{"id":19581},"_13-asrefasmut-for-flexible-borrowing","13. ",[73,19584,8489],{},[73,19586,8492],{}," for Flexible Borrowing",[111,19589,19592],{"className":19590,"code":19591,"language":397,"meta":117},[395],"pub fn open\u003CP: AsRef\u003CPath>>(path: P) { let p = path.as_ref(); \u002F* ... *\u002F }\nopen(\"file.txt\"); open(Path::new(\"f\")); open(String::from(\"f\"));\n",[73,19593,19591],{"__ignoreMap":117},[15,19595,19597,19598],{"id":19596},"_14-error-conversion-via","14. Error-Conversion via ",[73,19599,2404],{},[111,19601,19604],{"className":19602,"code":19603,"language":397,"meta":117},[395],"pub fn run() -> Result\u003C(), AppError> {\n    let n: i32 = \"x\".parse()?;     \u002F\u002F uses From\u003CParseIntError> for AppError\n    Ok(())\n}\n",[73,19605,19603],{"__ignoreMap":117},[20,19607,19608,10124,19610,9913,19612,19614],{},[73,19609,9900],{},[73,19611,9912],{},[73,19613,1879],{}," impl automatically.",[15,19616,19618],{"id":19617},"_15-trait-composition-via-supertraits","15. Trait Composition via Supertraits",[111,19620,19623],{"className":19621,"code":19622,"language":397,"meta":117},[395],"pub trait Service: Send + Sync + Debug {\n    fn call(&self, req: Request) -> Response;\n}\n",[73,19624,19622],{"__ignoreMap":117},[20,19626,19627],{},"A supertrait bound requires all subtraits. Implementations must provide all.",[15,19629,19631],{"id":19630},"_16-default-trait-for-defaults","16. Default Trait for Defaults",[111,19633,19636],{"className":19634,"code":19635,"language":397,"meta":117},[395],"#[derive(Default)]\npub struct Config { pub host: String, pub port: u16 }\nlet c = Config { port: 8080, ..Default::default() };\n",[73,19637,19635],{"__ignoreMap":117},[15,19639,19641],{"id":19640},"_17-phantom-type-parameters","17. Phantom Type Parameters",[111,19643,19646],{"className":19644,"code":19645,"language":397,"meta":117},[395],"pub struct Id\u003CT>(u64, PhantomData\u003CT>);\npub struct User; pub struct Post;\ntype UserId = Id\u003CUser>; type PostId = Id\u003CPost>;\n",[73,19647,19645],{"__ignoreMap":117},[20,19649,19650],{},"Same numeric value, distinct types — prevents mixing IDs.",[15,19652,19654],{"id":19653},"_18-crtp-disabled-in-safe-rust","18. CRTP (Disabled in safe Rust)",[20,19656,19657,19658,19660,19661,19663],{},"You can't easily do \"compile-time virtual\" the way C++ does. The closest is a trait with an associated type for ",[73,19659,2264],{},"-like dispatch, or ",[73,19662,13814],{}," for runtime. The typestate pattern covers many use cases.",[15,19665,19667],{"id":19666},"_19-tagged-unions-via-enums","19. Tagged Unions via Enums",[20,19669,19670],{},"The Rust-native \"tagged union\" — see the Enums chapter.",[15,19672,19674],{"id":19673},"_20-dependency-injection-via-traits","20. Dependency Injection via Traits",[111,19676,19679],{"className":19677,"code":19678,"language":397,"meta":117},[395],"pub trait Clock { fn now(&self) -> Instant; }\npub struct RealClock; impl Clock for RealClock { fn now(&self) -> Instant { Instant::now() } }\npub struct MockClock(Instant); impl Clock for MockClock { fn now(&self) -> Instant { self.0 } }\n\npub struct Service\u003CC: Clock> { clock: C }\n",[73,19680,19678],{"__ignoreMap":117},[20,19682,19683,19684,19687,19688,19691],{},"Tests inject ",[73,19685,19686],{},"MockClock","; production uses ",[73,19689,19690],{},"RealClock",". No global mutable state needed.",[15,19693,19695],{"id":19694},"_21-avoid-global-mutable-state","21. Avoid Global Mutable State",[20,19697,19698,19699,19701,19702,5427],{},"Use dependency injection, or ",[73,19700,10886],{}," for genuinely global immutable data. Mutable globals are a smell — wrap in ",[73,19703,11250],{},[15,19705,19707,19708,19710],{"id":19706},"_22-use-liberally","22. Use ",[73,19709,2404],{}," Liberally",[20,19712,19713,19714,19716,19717,19719],{},"Idiomatic error propagation. Avoid deeply nested ",[73,19715,1907],{}," when ",[73,19718,2404],{}," works.",[15,19721,19723],{"id":19722},"_23-use-iterators-over-loops","23. Use Iterators Over Loops",[111,19725,19728],{"className":19726,"code":19727,"language":397,"meta":117},[395],"\u002F\u002F Idiomatic\nlet sum: i32 = v.iter().map(|x| x * 2).sum();\n\n\u002F\u002F Less idiomatic\nlet mut sum = 0;\nfor x in &v { sum += x * 2; }\n",[73,19729,19727],{"__ignoreMap":117},[20,19731,19732],{},"The iterator form is equally fast (zero-cost) and more declarative.",[15,19734,19736,19737,19739],{"id":19735},"_24-avoid-unwrap-in-public-code","24. Avoid ",[73,19738,10168],{}," in Public Code",[20,19741,19742,19743,19745,19746,19748,19749,2755,19751,19754],{},"In tests, ",[73,19744,10168],{}," is fine. In production APIs, use ",[73,19747,2404],{},", return ",[73,19750,2792],{},[73,19752,19753],{},"expect(\"invariant message\")"," if you really can't fail.",[15,19756,19758],{"id":19757},"_25-documentation-comments","25. Documentation Comments",[111,19760,19763],{"className":19761,"code":19762,"language":397,"meta":117},[395],"\u002F\u002F\u002F Adds two numbers.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Panics\n\u002F\u002F\u002F Panics if overflow occurs (debug builds).\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Examples\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F use my::add;\n\u002F\u002F\u002F assert_eq!(add(2, 2), 4);\n\u002F\u002F\u002F ```\npub fn add(a: i32, b: i32) -> i32 { a + b }\n",[73,19764,19762],{"__ignoreMap":117},[33,19766,19767,19776],{},[36,19768,19769,19771,19772,19775],{},[73,19770,12060],{}," for items, ",[73,19773,19774],{},"\u002F\u002F!"," for crates\u002Fmodules.",[36,19777,19778,19779,480,19782,480,19785,480,19788,480,19791,259],{},"Sections: ",[73,19780,19781],{},"# Panics",[73,19783,19784],{},"# Errors",[73,19786,19787],{},"# Examples",[73,19789,19790],{},"# Safety",[73,19792,19793],{},"# Arguments",[15,19795,19797],{"id":19796},"_26-naming-conventions","26. Naming Conventions",[33,19799,19800,19806,19812,19817],{},[36,19801,19802,19805],{},[73,19803,19804],{},"snake_case"," for functions, variables, modules.",[36,19807,19808,19811],{},[73,19809,19810],{},"CamelCase"," for types\u002Ftraits\u002Fenum variants.",[36,19813,19814,19816],{},[73,19815,1002],{}," for constants and statics.",[36,19818,19819,19820,480,19822,480,19824,480,19826,259],{},"Lifetime params: ",[73,19821,4499],{},[73,19823,18575],{},[73,19825,4656],{},[73,19827,4660],{},[15,19829,19831],{"id":19830},"_27-error-vs-option-heuristic","27. Error vs Option Heuristic",[33,19833,19834,19839],{},[36,19835,19836,19838],{},[73,19837,1481],{}," for \"absent\" (looking up a key, optional config).",[36,19840,19841,19843],{},[73,19842,2792],{}," for \"operation failed\" (parse, IO, network).",[15,19845,19847],{"id":19846},"_28-when-to-box-vs-generic","28. When to Box vs Generic",[33,19849,19850,19853],{},[36,19851,19852],{},"Generic: monomorphization is acceptable (caller can static-dispatch).",[36,19854,19855,19858],{},[73,19856,19857],{},"Box\u003Cdyn>",": heterogeneous collections, runtime polymorphism, smaller binary.",[15,19860,19862,19863,19865],{"id":19861},"_29-cow-for-borrowed-or-owned","29. ",[73,19864,11369],{}," for Borrowed-or-Owned",[111,19867,19870],{"className":19868,"code":19869,"language":397,"meta":117},[395],"pub fn normalize(s: &str) -> Cow\u003Cstr> {\n    if s.chars().any(|c| c.is_uppercase()) {\n        Cow::Owned(s.to_lowercase())\n    } else {\n        Cow::Borrowed(s)\n    }\n}\n",[73,19871,19869],{"__ignoreMap":117},[20,19873,19874],{},"Avoids cloning when no transformation is needed.",[15,19876,19878],{"id":19877},"_30-avoid-premature-abstraction","30. Avoid Premature Abstraction",[20,19880,19881,19882,19884],{},"Don't define traits until you have a second implementation. Don't reach for ",[73,19883,13814],{}," until you need runtime polymorphism. Don't introduce generics until you have a second type. \"Rule of three\" — abstract when you see repetition.",[15,19886,19888],{"id":19887},"common-anti-patterns","Common Anti-Patterns",[33,19890,19891,19899,19907,19917,19928,19934,19943,19951,19957,19969],{},[36,19892,19893,19898],{},[24,19894,8611,19895,19897],{},[73,19896,3782],{}," for non-smart-pointer types",": misleading. Use explicit methods.",[36,19900,19901,19906],{},[24,19902,19903,19904],{},"Overusing ",[73,19905,8373],{},": kills performance and inlining; prefer generics.",[36,19908,19909,19914,19915,259],{},[24,19910,19911,19913],{},[73,19912,10168],{}," everywhere",": panics on edge cases. Use ",[73,19916,2404],{},[36,19918,19919,19923,19924,1546,19926,259],{},[24,19920,10022,19921,19913],{},[73,19922,1197],{},": returns ownership unnecessarily; consider ",[73,19925,11369],{},[73,19927,4577],{},[36,19929,19930,19933],{},[24,19931,19932],{},"God objects",": huge structs with all state. Split by responsibility.",[36,19935,19936,19942],{},[24,19937,19938,19939,19941],{},"Inheriting via ",[73,19940,3782],{}," chains",": doesn't work like OOP inheritance; produces confusing method resolution.",[36,19944,19945,19950],{},[24,19946,19947,19949],{},[73,19948,197],{}," to silence borrow errors",": the borrow checker is right; restructure.",[36,19952,19953,19956],{},[24,19954,19955],{},"Trait objects for performance-critical code",": vtable dispatch is slow; genericize.",[36,19958,19959,19965,19966,19968],{},[24,19960,19961,19964],{},[73,19962,19963],{},"Vec\u003CVec\u003CT>>"," for matrices",": cache-unfriendly; use a flat ",[73,19967,4930],{}," with row-major indexing.",[36,19970,19971,19974],{},[24,19972,19973],{},"Mutable globals",": makes testing and reasoning hard. Inject dependencies.",[15,19976,10323],{"id":10322},[33,19978,19979,19986,19993,20000,20008,20013,20022,20027,20032],{},[36,19980,19981,11526,19983,19985],{},[73,19982,2541],{},[73,19984,1907],{}," for single-arm.",[36,19987,19988,19990,19991,259],{},[73,19989,2404],{}," over nested ",[73,19992,1907],{},[36,19994,19995,11526,19997,19999],{},[73,19996,10574],{},[73,19998,10578],{}," (clarity).",[36,20001,20002,11526,20005,259],{},[73,20003,20004],{},".iter()",[73,20006,20007],{},"for i in 0..v.len()",[36,20009,20010,20012],{},[73,20011,14406],{}," over manual string concatenation.",[36,20014,20015,1212,20017,11526,20019,20021],{},[73,20016,14409],{},[73,20018,14412],{},[73,20020,14406],{}," when writing to a buffer.",[36,20023,20024,20026],{},[73,20025,6029],{}," for one-arm boolean checks.",[36,20028,20029,20031],{},[73,20030,2949],{}," for early-return validation.",[36,20033,20034,20036],{},[73,20035,11369],{}," for borrowed-or-owned APIs.",[15,20038,349],{"id":348},[20,20040,20041,20042,20044,20045,1212,20047,1212,20049,20051,20052,20054,20055,20057],{},"Builder for complex construction. Newtype for type safety. Typestate for compile-time state machines. Traits for polymorphism (static via generics, dynamic via ",[73,20043,13814],{},"). RAII for resources. ",[73,20046,1879],{},[73,20048,8489],{},[73,20050,2404],{}," for conversions. Iterators over loops. Avoid ",[73,20053,10168],{},", globals, and over-abstraction. Document with ",[73,20056,12060],{},". Use idiomatic naming and patterns.",[20,20059,20060],{},"Next: Performance, profiling, and optimization.",{"title":117,"searchDepth":357,"depth":357,"links":20062},[20063,20066,20067,20068,20069,20070,20071,20072,20073,20074,20075,20076,20078,20080,20082,20083,20084,20085,20086,20087,20088,20089,20091,20092,20094,20095,20096,20097,20098,20100,20101,20102,20103],{"id":19331,"depth":357,"text":19332,"children":20064},[20065],{"id":19354,"depth":364,"text":19355},{"id":19378,"depth":357,"text":19379},{"id":19407,"depth":357,"text":19408},{"id":19429,"depth":357,"text":19430},{"id":19445,"depth":357,"text":19446},{"id":19464,"depth":357,"text":19465},{"id":19474,"depth":357,"text":19475},{"id":19493,"depth":357,"text":19494},{"id":19517,"depth":357,"text":19518},{"id":19530,"depth":357,"text":19531},{"id":19549,"depth":357,"text":19550},{"id":19559,"depth":357,"text":20077},"12. From\u002FInto for Conversions",{"id":19581,"depth":357,"text":20079},"13. AsRef\u002FAsMut for Flexible Borrowing",{"id":19596,"depth":357,"text":20081},"14. Error-Conversion via ?",{"id":19617,"depth":357,"text":19618},{"id":19630,"depth":357,"text":19631},{"id":19640,"depth":357,"text":19641},{"id":19653,"depth":357,"text":19654},{"id":19666,"depth":357,"text":19667},{"id":19673,"depth":357,"text":19674},{"id":19694,"depth":357,"text":19695},{"id":19706,"depth":357,"text":20090},"22. Use ? Liberally",{"id":19722,"depth":357,"text":19723},{"id":19735,"depth":357,"text":20093},"24. Avoid unwrap in Public Code",{"id":19757,"depth":357,"text":19758},{"id":19796,"depth":357,"text":19797},{"id":19830,"depth":357,"text":19831},{"id":19846,"depth":357,"text":19847},{"id":19861,"depth":357,"text":20099},"29. Cow for Borrowed-or-Owned",{"id":19877,"depth":357,"text":19878},{"id":19887,"depth":357,"text":19888},{"id":10322,"depth":357,"text":10323},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F30-design-patterns",{"title":19320,"description":19328},"rust\u002F30-design-patterns","Tl3a0INdneShIloKSPzqmgM_Vjo0zzdTvAvGH3BJrTw",{"id":20110,"title":20111,"body":20112,"description":20119,"extension":373,"meta":20998,"navigation":375,"path":20999,"seo":21000,"stem":21001,"__hash__":21002},"content\u002Frust\u002F31-performance.md","31 — Performance, Profiling & Optimization",{"type":8,"value":20113,"toc":20937},[20114,20117,20120,20124,20144,20148,20154,20160,20166,20172,20178,20186,20190,20193,20199,20203,20209,20215,20218,20224,20228,20234,20237,20244,20250,20260,20263,20267,20271,20277,20286,20295,20303,20307,20310,20314,20332,20339,20342,20346,20365,20378,20384,20391,20398,20404,20410,20414,20432,20439,20445,20448,20452,20465,20469,20475,20483,20489,20497,20503,20506,20510,20516,20523,20529,20533,20539,20542,20546,20556,20563,20566,20570,20576,20583,20589,20629,20633,20636,20642,20648,20653,20659,20662,20666,20672,20675,20679,20836,20838,20858,20865,20878,20882,20907,20909,20934],[11,20115,20111],{"id":20116},"_31-performance-profiling-optimization",[20,20118,20119],{},"Rust gives you C-level performance by default, but you can still write slow Rust. This chapter covers how to find and fix bottlenecks.",[15,20121,20123],{"id":20122},"mindset","Mindset",[3037,20125,20126,20132,20138],{},[36,20127,20128,20131],{},[24,20129,20130],{},"Don't optimize prematurely."," Write clear code; profile; optimize hot spots.",[36,20133,20134,20137],{},[24,20135,20136],{},"Measure, measure, measure."," Intuition is often wrong.",[36,20139,20140,20143],{},[24,20141,20142],{},"Iterate."," One change at a time; re-measure each time.",[15,20145,20147],{"id":20146},"benchmarking","Benchmarking",[130,20149,20151,20153],{"id":20150},"cargo-bench-nightly",[73,20152,17969],{}," (nightly)",[111,20155,20158],{"className":20156,"code":20157,"language":397,"meta":117},[395],"#![feature(test)]\nextern crate test;\nuse test::Bencher;\n\n#[bench]\nfn bench_add(b: &mut Bencher) {\n    b.iter(|| test::black_box(2) + test::black_box(2));\n}\n",[73,20159,20157],{"__ignoreMap":117},[20,20161,20162,20165],{},[73,20163,20164],{},"black_box"," prevents the optimizer from constant-folding.",[130,20167,20169,20171],{"id":20168},"criterion-stable-recommended",[73,20170,12318],{}," (stable, recommended)",[111,20173,20176],{"className":20174,"code":20175,"language":397,"meta":117},[395],"use criterion::{criterion_group, criterion_main, Criterion};\n\nfn bench_fib(c: &mut Criterion) {\n    c.bench_function(\"fib 20\", |b| b.iter(|| fib(black_box(20))));\n}\n\ncriterion_group!(benches, bench_fib);\ncriterion_main!(benches);\n",[73,20177,20175],{"__ignoreMap":117},[20,20179,1458,20180,12543,20183,20185],{},[73,20181,20182],{},"benches\u002Fmy_bench.rs",[73,20184,17969],{},". Criterion provides statistics, regressions, and HTML reports.",[130,20187,20189],{"id":20188},"custom-benchmarks","Custom Benchmarks",[20,20191,20192],{},"For ad-hoc timing:",[111,20194,20197],{"className":20195,"code":20196,"language":397,"meta":117},[395],"let start = std::time::Instant::now();\nwork();\nlet elapsed = start.elapsed();\n",[73,20198,20196],{"__ignoreMap":117},[15,20200,20202],{"id":20201},"profiling","Profiling",[130,20204,20206,20208],{"id":20205},"perf-linux",[73,20207,17981],{}," (Linux)",[111,20210,20213],{"className":20211,"code":20212,"language":116,"meta":117},[114],"cargo build --release\nperf record -g .\u002Ftarget\u002Frelease\u002Fmy_app\nperf report\n",[73,20214,20212],{"__ignoreMap":117},[20,20216,20217],{},"For flamegraphs:",[111,20219,20222],{"className":20220,"code":20221,"language":116,"meta":117},[114],"cargo install flamegraph\ncargo flamegraph\n",[73,20223,20221],{"__ignoreMap":117},[130,20225,20226],{"id":17984},[73,20227,17984],{},[111,20229,20232],{"className":20230,"code":20231,"language":116,"meta":117},[114],"cargo install samply\nsamply record .\u002Ftarget\u002Frelease\u002Fmy_app\n",[73,20233,20231],{"__ignoreMap":117},[20,20235,20236],{},"Samply gives a web UI with call trees and source-level annotations.",[130,20238,20240,20243],{"id":20239},"instruments-macos",[73,20241,20242],{},"Instruments"," (macOS)",[111,20245,20248],{"className":20246,"code":20247,"language":116,"meta":117},[114],"xcrun xctrace record --template \"Time Profiler\" --launch .\u002Ftarget\u002Frelease\u002Fmy_app\n",[73,20249,20247],{"__ignoreMap":117},[130,20251,20253,480,20256,20259],{"id":20252},"dtrace-vtune-advanced",[73,20254,20255],{},"dtrace",[73,20257,20258],{},"vtune"," (advanced)",[20,20261,20262],{},"For deeper hardware analysis (cache misses, branch mispredicts).",[15,20264,20266],{"id":20265},"optimization-techniques","Optimization Techniques",[130,20268,20270],{"id":20269},"_1-avoid-unnecessary-allocation","1. Avoid Unnecessary Allocation",[111,20272,20275],{"className":20273,"code":20274,"language":397,"meta":117},[395],"\u002F\u002F Bad: allocates per call\nfn process(items: &[u8]) -> Vec\u003Cu8> { items.iter().map(|x| x + 1).collect() }\n\n\u002F\u002F Good: caller provides buffer\nfn process_into(items: &[u8], out: &mut [u8]) {\n    for (i, x) in items.iter().enumerate() { out[i] = x + 1; }\n}\n",[73,20276,20274],{"__ignoreMap":117},[20,20278,20279,480,20282,20285],{},[73,20280,20281],{},"String::with_capacity",[73,20283,20284],{},"Vec::with_capacity"," to avoid regrowth.",[130,20287,20289,20290,3818,20292,20294],{"id":20288},"_2-use-t-str-in-apis","2. Use ",[73,20291,1739],{},[73,20293,1630],{}," in APIs",[20,20296,20297,20298,1546,20300,20302],{},"Don't take ",[73,20299,4245],{},[73,20301,3411],{}," — they impose ownership and lose the more general slice form. Slices are flexible and equally fast.",[130,20304,20306],{"id":20305},"_3-choose-iterators-over-explicit-loops-sometimes","3. Choose Iterators Over Explicit Loops (Sometimes)",[20,20308,20309],{},"Iterators often compile to tighter loops because the compiler can reason about them. But for very tight inner loops, the explicit form sometimes wins (or with manual SIMD). Profile both.",[130,20311,20313],{"id":20312},"_4-box-large-struct-fields","4. Box Large Struct Fields",[20,20315,20316,20317,20319,20320,20323,20324,20327,20328,20331],{},"A struct with a large ",[73,20318,1194],{}," field still has its ",[73,20321,20322],{},"(ptr, len, cap)"," header inline (24 bytes), but a large ",[73,20325,20326],{},"[u8; 1024]"," field makes the struct huge. Use ",[73,20329,20330],{},"Box\u003C[u8; 1024]>"," for large fixed-size data to keep the struct small (good for cache and copying).",[130,20333,20335,20336,20338],{"id":20334},"_5-avoid-boxdyn-trait-in-hot-paths","5. Avoid ",[73,20337,8373],{}," in Hot Paths",[20,20340,20341],{},"Vtable indirection is ~few ns but kills inlining. Genericize hot paths.",[130,20343,20345],{"id":20344},"_6-cache-locality","6. Cache Locality",[33,20347,20348,20355,20362],{},[36,20349,20350,20351,11526,20353,259],{},"Flat ",[73,20352,4930],{},[73,20354,19963],{},[36,20356,20357,1212,20359,20361],{},[73,20358,7438],{},[73,20360,7441],{}," for inline storage.",[36,20363,20364],{},"Structure-of-arrays over array-of-structures for SIMD-friendly access.",[130,20366,20368,20369,17970,20372,1212,20374,20377],{"id":20367},"_7-simd-via-stdsimd-nightly-or-widepulp-stable","7. SIMD via ",[73,20370,20371],{},"std::simd",[73,20373,18751],{},[73,20375,20376],{},"pulp"," (stable)",[111,20379,20382],{"className":20380,"code":20381,"language":397,"meta":117},[395],"#![feature(portable_simd)]\nuse std::simd::f32x4;\nlet a = f32x4::from_array([1.0, 2.0, 3.0, 4.0]);\nlet b = f32x4::from_array([5.0, 6.0, 7.0, 8.0]);\nlet c = a + b;\n",[73,20383,20381],{"__ignoreMap":117},[20,20385,20386,20387,20390],{},"For auto-vectorization, write iterator chains and let LLVM do it; check with ",[73,20388,20389],{},"cargo asm"," or Godbolt.",[130,20392,20394,20395,20397],{"id":20393},"_8-inline-selectively","8. ",[73,20396,2452],{}," Selectively",[111,20399,20402],{"className":20400,"code":20401,"language":397,"meta":117},[395],"#[inline]\nfn small() -> u32 { \u002F* ... *\u002F }\n\n#[inline(always)]\nfn tiny() -> u32 { \u002F* ... *\u002F }\n",[73,20403,20401],{"__ignoreMap":117},[20,20405,20406,20407,20409],{},"Don't ",[73,20408,2456],{}," everywhere — it bloats code and hurts i-cache.",[130,20411,20413],{"id":20412},"_9-avoid-heap-allocations-in-hot-loops","9. Avoid Heap Allocations in Hot Loops",[33,20415,20416,20422,20427],{},[36,20417,20418,20419,259],{},"Reuse buffers via ",[73,20420,20421],{},"&mut Vec",[36,20423,1876,20424,20426],{},[73,20425,7342],{}," for fixed-size buffers.",[36,20428,1876,20429,20431],{},[73,20430,7336],{}," for small-but-may-grow.",[130,20433,20435,20436,20438],{"id":20434},"_10-use-mut-t-for-in-place-mutation","10. Use ",[73,20437,1742],{}," for In-Place Mutation",[111,20440,20443],{"className":20441,"code":20442,"language":397,"meta":117},[395],"fn sum_of_squares(v: &mut [i32]) {\n    for x in v.iter_mut() { *x = *x * *x; }\n}\n",[73,20444,20442],{"__ignoreMap":117},[20,20446,20447],{},"Avoids allocation; cache-friendly.",[130,20449,20451],{"id":20450},"_11-lock-granularity","11. Lock Granularity",[33,20453,20454,20459,20462],{},[36,20455,20456,20458],{},[73,20457,10841],{}," for read-heavy.",[36,20460,20461],{},"Shard locks across N buckets for parallel writes.",[36,20463,20464],{},"Lock-free via atomics when possible.",[130,20466,20468],{"id":20467},"_12-avoid-reallocations","12. Avoid Reallocations",[111,20470,20473],{"className":20471,"code":20472,"language":397,"meta":117},[395],"let mut v = Vec::with_capacity(N);\nfor x in iter { v.push(x); }\n",[73,20474,20472],{"__ignoreMap":117},[130,20476,20478,20479,20482],{"id":20477},"_13-use-arcclone-carefully","13. Use ",[73,20480,20481],{},"Arc::clone"," Carefully",[20,20484,20485,20486,20488],{},"Each ",[73,20487,20481],{}," does an atomic increment — much cheaper than a deep clone but not free. Avoid in tightest loops.",[130,20490,20492,20493,27,20495],{"id":20491},"_14-memreplace-and-memtake","14. ",[73,20494,3433],{},[73,20496,3430],{},[111,20498,20501],{"className":20499,"code":20500,"language":397,"meta":117},[395],"let old = mem::take(&mut self.buffer);   \u002F\u002F self.buffer is now empty\nprocess(old);\n",[73,20502,20500],{"__ignoreMap":117},[20,20504,20505],{},"Avoids cloning; useful for swap-and-go state.",[130,20507,20509],{"id":20508},"_15-reduce-trait-object-dispatch","15. Reduce Trait Object Dispatch",[111,20511,20514],{"className":20512,"code":20513,"language":397,"meta":117},[395],"\u002F\u002F Generic\nfn sum\u003CT>(v: &[T]) -> T where T: Sum + Copy { v.iter().copied().sum() }\n\n\u002F\u002F Box\u003Cdyn> — slower\nfn sum_dyn(v: &[Box\u003Cdyn Additive>]) { \u002F* vtable per call *\u002F }\n",[73,20515,20513],{"__ignoreMap":117},[130,20517,20519,20520,20522],{"id":20518},"_16-cow-to-avoid-allocations","16. ",[73,20521,11369],{}," to Avoid Allocations",[111,20524,20527],{"className":20525,"code":20526,"language":397,"meta":117},[395],"fn normalize(s: &str) -> Cow\u003Cstr> {\n    if needs_transform(s) { Cow::Owned(s.to_uppercase()) } else { Cow::Borrowed(s) }\n}\n",[73,20528,20526],{"__ignoreMap":117},[130,20530,20532],{"id":20531},"_17-pre-compute-and-cache","17. Pre-compute and Cache",[111,20534,20537],{"className":20535,"code":20536,"language":397,"meta":117},[395],"struct Cached { data: Vec\u003Cu8> }\nimpl Cached {\n    fn lookup(&self, key: usize) -> u8 { self.data[key] }\n}\n",[73,20538,20536],{"__ignoreMap":117},[20,20540,20541],{},"Avoid recomputing in hot paths.",[130,20543,20545],{"id":20544},"_18-string-interning","18. String Interning",[20,20547,20548,20549,1212,20552,20555],{},"For repeated short strings, use ",[73,20550,20551],{},"string_interner",[73,20553,20554],{},"lasso"," to assign integer IDs.",[130,20557,20559,20560,20562],{"id":20558},"_19-use-static-where-appropriate","19. Use ",[73,20561,4946],{}," Where Appropriate",[20,20564,20565],{},"Avoids lifetime-tracking overhead in some generic contexts. Don't overuse.",[130,20567,20569],{"id":20568},"_20-compile-time-computation","20. Compile-Time Computation",[111,20571,20574],{"className":20572,"code":20573,"language":397,"meta":117},[395],"const N: usize = 1000;\nlet arr = [0; N];\n",[73,20575,20573],{"__ignoreMap":117},[20,20577,20578,20579,1212,20581,9485],{},"Move computation to compile time via ",[73,20580,992],{},[73,20582,1030],{},[15,20584,20586,20588],{"id":20585},"release-profile-pitfalls",[73,20587,1467],{}," Profile Pitfalls",[33,20590,20591,20608,20615,20622],{},[36,20592,20593,71,20598,4699,20601,480,20604,20607],{},[24,20594,20595,20596],{},"Default ",[73,20597,1467],{},[73,20599,20600],{},"opt-level = 3",[73,20602,20603],{},"lto = false",[73,20605,20606],{},"codegen-units = 16"," (parallel compile, less optimization). For final binaries, bump these.",[36,20609,20610,20614],{},[24,20611,20612],{},[73,20613,9964],{}," can unlock more optimizations (no unwinding tables).",[36,20616,20617,20621],{},[24,20618,20619],{},[73,20620,18009],{}," reduces binary size.",[36,20623,20624,20628],{},[24,20625,20626],{},[73,20627,17776],{}," minimizes size, often at a perf cost.",[15,20630,20632],{"id":20631},"measuring-allocations","Measuring Allocations",[20,20634,20635],{},"Use a custom allocator that logs:",[111,20637,20640],{"className":20638,"code":20639,"language":397,"meta":117},[395],"use std::alloc::{GlobalAlloc, Layout, System};\n\nstruct Counting;\nunsafe impl GlobalAlloc for Counting {\n    unsafe fn alloc(&self, l: Layout) -> *mut u8 { eprintln!(\"alloc {:?}\", l); System.alloc(l) }\n    unsafe fn dealloc(&self, p: *mut u8, l: Layout) { System.dealloc(p, l) }\n}\n\n#[global_allocator]\nstatic A: Counting = Counting;\n",[73,20641,20639],{"__ignoreMap":117},[20,20643,15866,20644,20647],{},[73,20645,20646],{},"dhat"," for heap profiling.",[15,20649,20651],{"id":20650},"cargo-bloat",[73,20652,17990],{},[111,20654,20657],{"className":20655,"code":20656,"language":116,"meta":117},[114],"cargo install cargo-bloat\ncargo bloat --release\ncargo bloat --release --crates\n",[73,20658,20656],{"__ignoreMap":117},[20,20660,20661],{},"Shows which functions take the most binary size.",[15,20663,20665],{"id":20664},"inspecting-assembly","Inspecting Assembly",[111,20667,20670],{"className":20668,"code":20669,"language":116,"meta":117},[114],"cargo install cargo-asm\ncargo asm my_crate::function\n",[73,20671,20669],{"__ignoreMap":117},[20,20673,20674],{},"Or use Godbolt (godbolt.org) — paste Rust code, see assembly.",[15,20676,20678],{"id":20677},"common-performance-pitfalls","Common Performance Pitfalls",[33,20680,20681,20694,20705,20718,20730,20740,20753,20761,20771,20779,20787,20795,20811,20823],{},[36,20682,20683,2927,20692,259],{},[24,20684,20685,20688,20689],{},[73,20686,20687],{},"String::new()"," followed by many ",[73,20690,20691],{},"push_str",[73,20693,2192],{},[36,20695,20696,20701,20702,20704],{},[24,20697,20698,20700],{},[73,20699,14406],{}," in hot loops",": pre-format or use ",[73,20703,14409],{}," into a reused buffer.",[36,20706,20707,71,20712,1212,20714,20717],{},[24,20708,20709,20711],{},[73,20710,6904],{}," for byte parsing",[73,20713,7348],{},[73,20715,20716],{},"BytesMut"," are often faster.",[36,20719,20720,2927,20725,1212,20727,20729],{},[24,20721,20722,20724],{},[73,20723,1687],{}," with crypto-strong hash",[73,20726,7105],{},[73,20728,7108],{}," for non-adversarial keys.",[36,20731,20732,71,20737,20739],{},[24,20733,20734,20736],{},[73,20735,16128],{}," in a counted loop",[73,20738,20284],{}," once.",[36,20741,20742,20750,20751,259],{},[24,20743,20744,6530,20747,20749],{},[73,20745,20746],{},"to_string()",[73,20748,1630],{}," you only need to read",": just use the ",[73,20752,1630],{},[36,20754,20755,20760],{},[24,20756,20757,20759],{},[73,20758,20481],{}," in inner loop",": clone once, reuse.",[36,20762,20763,2927,20768,20770],{},[24,20764,20765,20767],{},[73,20766,1687],{}," lookup-then-insert",[73,20769,7121],{}," (one hash).",[36,20772,20773,20778],{},[24,20774,20775,20776],{},"Locks held across ",[73,20777,13113],{},": contention; drop lock first.",[36,20780,20781,20786],{},[24,20782,20783,20785],{},[73,20784,19963],{}," matrices",": flat layout + index math is faster.",[36,20788,20789,20794],{},[24,20790,20791,20793],{},[73,20792,19857],{}," in inner loops",": indirect calls prevent inlining.",[36,20796,20797,71,20805,20808,20809,3125],{},[24,20798,20799,13118,20802],{},[73,20800,20801],{},"cloned()",[73,20803,20804],{},"copied()",[73,20806,20807],{},"copied"," is faster for ",[73,20810,1795],{},[36,20812,20813,2927,20820,20822],{},[24,20814,20815,8018,20817],{},[73,20816,6904],{},[73,20818,20819],{},"read_to_end",[73,20821,20284],{}," if size is known.",[36,20824,20825,71,20832,20835],{},[24,20826,20827,17994,20830],{},[73,20828,20829],{},"String::from_utf8",[73,20831,10168],{},[73,20833,20834],{},"from_utf8_lossy"," avoids the check.",[15,20837,5549],{"id":5548},[33,20839,20840,20845,20850,20855],{},[36,20841,20842,20844],{},[73,20843,5557],{},": fixed, predictable, no padding-optimization.",[36,20846,20847,20849],{},[73,20848,5561],{},": same layout as inner.",[36,20851,20852,20854],{},[73,20853,5565],{},": no padding, alignment 1 — slow on many platforms, UB risk.",[36,20856,20857],{},"Field reordering (default Rust layout) minimizes padding; let the compiler do it.",[15,20859,20861,20864],{"id":20860},"stdalloc-layout",[73,20862,20863],{},"std::alloc"," Layout",[20,20866,20867,20868,20871,20872,1212,20875,20877],{},"Allocations must be aligned to ",[73,20869,20870],{},"Layout::align",". Mismatched alignment is UB. ",[73,20873,20874],{},"Box::new_uninit_slice",[73,20876,20284],{}," handle this for you.",[15,20879,20881],{"id":20880},"async-performance","Async Performance",[33,20883,20884,20889,20898,20901],{},[36,20885,16160,20886,20888],{},[73,20887,13545],{}," in hot paths; use generics.",[36,20890,16160,20891,20893,20894,1212,20896,9494],{},[73,20892,13466],{}," for short-lived work — overhead. Use ",[73,20895,13834],{},[73,20897,13869],{},[36,20899,20900],{},"Bounded channels for backpressure (vs unbounded that grow).",[36,20902,20903,20906],{},[73,20904,20905],{},"current_thread"," runtime for single-threaded apps.",[15,20908,349],{"id":348},[20,20910,17975,20911,480,20913,480,20916,480,20918,20920,20921,480,20923,20925,20926,20928,20929,9593,20931,20933],{},[73,20912,12318],{},[73,20914,20915],{},"flamegraph",[73,20917,17984],{},[73,20919,17990],{},". Optimize hot paths: avoid allocation, use slices, generic over ",[73,20922,13814],{},[73,20924,2192],{},", lock granularity, SIMD. Use ",[73,20927,1467],{}," profile + ",[73,20930,17750],{},[73,20932,17759],{}," for final binaries. Don't trust intuition; measure. Iterate.",[20,20935,20936],{},"Next: Documentation.",{"title":117,"searchDepth":357,"depth":357,"links":20938},[20939,20940,20947,20956,20987,20989,20990,20991,20992,20993,20994,20996,20997],{"id":20122,"depth":357,"text":20123},{"id":20146,"depth":357,"text":20147,"children":20941},[20942,20944,20946],{"id":20150,"depth":364,"text":20943},"cargo bench (nightly)",{"id":20168,"depth":364,"text":20945},"criterion (stable, recommended)",{"id":20188,"depth":364,"text":20189},{"id":20201,"depth":357,"text":20202,"children":20948},[20949,20951,20952,20954],{"id":20205,"depth":364,"text":20950},"perf (Linux)",{"id":17984,"depth":364,"text":17984},{"id":20239,"depth":364,"text":20953},"Instruments (macOS)",{"id":20252,"depth":364,"text":20955},"dtrace, vtune (advanced)",{"id":20265,"depth":357,"text":20266,"children":20957},[20958,20959,20961,20962,20963,20965,20966,20968,20970,20971,20973,20974,20975,20977,20979,20980,20982,20983,20984,20986],{"id":20269,"depth":364,"text":20270},{"id":20288,"depth":364,"text":20960},"2. Use &[T] \u002F &str in APIs",{"id":20305,"depth":364,"text":20306},{"id":20312,"depth":364,"text":20313},{"id":20334,"depth":364,"text":20964},"5. Avoid Box\u003Cdyn Trait> in Hot Paths",{"id":20344,"depth":364,"text":20345},{"id":20367,"depth":364,"text":20967},"7. SIMD via std::simd (nightly) or wide\u002Fpulp (stable)",{"id":20393,"depth":364,"text":20969},"8. #[inline] Selectively",{"id":20412,"depth":364,"text":20413},{"id":20434,"depth":364,"text":20972},"10. Use &mut [T] for In-Place Mutation",{"id":20450,"depth":364,"text":20451},{"id":20467,"depth":364,"text":20468},{"id":20477,"depth":364,"text":20976},"13. Use Arc::clone Carefully",{"id":20491,"depth":364,"text":20978},"14. mem::replace and mem::take",{"id":20508,"depth":364,"text":20509},{"id":20518,"depth":364,"text":20981},"16. Cow to Avoid Allocations",{"id":20531,"depth":364,"text":20532},{"id":20544,"depth":364,"text":20545},{"id":20558,"depth":364,"text":20985},"19. Use &'static Where Appropriate",{"id":20568,"depth":364,"text":20569},{"id":20585,"depth":357,"text":20988},"release Profile Pitfalls",{"id":20631,"depth":357,"text":20632},{"id":20650,"depth":357,"text":17990},{"id":20664,"depth":357,"text":20665},{"id":20677,"depth":357,"text":20678},{"id":5548,"depth":357,"text":5549},{"id":20860,"depth":357,"text":20995},"std::alloc Layout",{"id":20880,"depth":357,"text":20881},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F31-performance",{"title":20111,"description":20119},"rust\u002F31-performance","RJjv0IN7bYgvMC4fFS3VH3nWySWbfdRXGP37jOcVs5E",{"id":21004,"title":21005,"body":21006,"description":21608,"extension":373,"meta":21609,"navigation":375,"path":21610,"seo":21611,"stem":21612,"__hash__":21613},"content\u002Frust\u002F32-documentation.md","32 — Documentation",{"type":8,"value":21007,"toc":21583},[21008,21011,21020,21024,21030,21042,21048,21052,21137,21140,21144,21150,21153,21159,21163,21169,21175,21179,21185,21219,21224,21227,21233,21237,21243,21248,21252,21258,21261,21268,21274,21277,21281,21315,21317,21321,21327,21331,21337,21341,21347,21353,21357,21407,21414,21420,21426,21438,21442,21453,21459,21478,21482,21488,21493,21495,21555,21557,21580],[11,21009,21005],{"id":21010},"_32-documentation",[20,21012,21013,21014,21016,21017,21019],{},"Documentation is part of the Rust culture. ",[73,21015,87],{}," produces HTML docs from ",[73,21018,12060],{}," comments; doc-tests run examples.",[15,21021,21023],{"id":21022},"doc-comments","Doc Comments",[111,21025,21028],{"className":21026,"code":21027,"language":397,"meta":117},[395],"\u002F\u002F\u002F Adds two integers.\n\u002F\u002F\u002F\n\u002F\u002F\u002F Returns `a + b`, panicking on overflow in debug builds.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Examples\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F use my_crate::add;\n\u002F\u002F\u002F assert_eq!(add(2, 2), 4);\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Panics\n\u002F\u002F\u002F\n\u002F\u002F\u002F Panics if `a + b` overflows in debug mode.\npub fn add(a: i32, b: i32) -> i32 { a + b }\n",[73,21029,21027],{"__ignoreMap":117},[33,21031,21032,21037],{},[36,21033,21034,21036],{},[73,21035,12060],{}," for items (functions, types, modules).",[36,21038,21039,21041],{},[73,21040,19774],{}," for the enclosing item (crate root, module — used for top-level docs).",[111,21043,21046],{"className":21044,"code":21045,"language":397,"meta":117},[395],"\u002F\u002F! # My Crate\n\u002F\u002F!\n\u002F\u002F! This crate provides wonderful things.\n",[73,21047,21045],{"__ignoreMap":117},[15,21049,21051],{"id":21050},"standard-sections","Standard Sections",[917,21053,21054,21063],{},[920,21055,21056],{},[923,21057,21058,21061],{},[926,21059,21060],{},"Section",[926,21062,8390],{},[936,21064,21065,21074,21083,21097,21108,21117,21127],{},[923,21066,21067,21071],{},[941,21068,21069],{},[73,21070,19787],{},[941,21072,21073],{},"Usage examples (run as doc tests).",[923,21075,21076,21080],{},[941,21077,21078],{},[73,21079,19781],{},[941,21081,21082],{},"When the function panics.",[923,21084,21085,21089],{},[941,21086,21087],{},[73,21088,19784],{},[941,21090,5146,21091,21093,21094,21096],{},[73,21092,2792],{},"-returning functions: which ",[73,21095,2778],{}," variants.",[923,21098,21099,21103],{},[941,21100,21101],{},[73,21102,19790],{},[941,21104,5146,21105,21107],{},[73,21106,197],{}," functions: required invariants.",[923,21109,21110,21114],{},[941,21111,21112],{},[73,21113,19793],{},[941,21115,21116],{},"Parameter docs (sometimes redundant with prose).",[923,21118,21119,21124],{},[941,21120,21121],{},[73,21122,21123],{},"# Returns",[941,21125,21126],{},"Return value docs.",[923,21128,21129,21134],{},[941,21130,21131],{},[73,21132,21133],{},"# Notes",[941,21135,21136],{},"Extra info.",[20,21138,21139],{},"The order convention: Examples, Panics, Errors, Safety.",[15,21141,21143],{"id":21142},"cross-references","Cross-References",[111,21145,21148],{"className":21146,"code":21147,"language":397,"meta":117},[395],"\u002F\u002F\u002F See [`std::fs::read`] for reading a file.\n\u002F\u002F\u002F Uses [`OtherType::method`] internally.\n\u002F\u002F\u002F Implements [`MyTrait`].\n",[73,21149,21147],{"__ignoreMap":117},[20,21151,21152],{},"Backticks create hyperlinks. rustdoc resolves intra-doc links.",[111,21154,21157],{"className":21155,"code":21156,"language":397,"meta":117},[395],"\u002F\u002F\u002F [`OtherType`] is in scope.\n\u002F\u002F\u002F [`crate::sub::Thing`]\n",[73,21158,21156],{"__ignoreMap":117},[15,21160,21162],{"id":21161},"building-docs","Building Docs",[111,21164,21167],{"className":21165,"code":21166,"language":116,"meta":117},[114],"cargo doc                  # generate for the crate\ncargo doc --open           # generate and open in browser\ncargo doc --no-deps        # skip dependencies\ncargo doc --workspace      # all crates in workspace\ncargo doc --document-private-items  # include private items (rare)\n",[73,21168,21166],{"__ignoreMap":117},[20,21170,21171,21172,259],{},"Output goes to ",[73,21173,21174],{},"target\u002Fdoc\u002F",[15,21176,21178],{"id":21177},"doc-tests-recap","Doc Tests (Recap)",[111,21180,21183],{"className":21181,"code":21182,"language":397,"meta":117},[395],"\u002F\u002F\u002F ```\n\u002F\u002F\u002F use my_crate::add;\n\u002F\u002F\u002F assert_eq!(add(2, 2), 4);\n\u002F\u002F\u002F ```\n",[73,21184,21182],{"__ignoreMap":117},[33,21186,21187,21192,21197,21202,21207,21213],{},[36,21188,21189,21191],{},[73,21190,12179],{}," runs them.",[36,21193,21194,21196],{},[73,21195,12214],{},": compile but don't execute.",[36,21198,21199,21201],{},[73,21200,12218],{},": skip.",[36,21203,21204,21206],{},[73,21205,12222],{},": assert it doesn't compile (negative test).",[36,21208,21209,21212],{},[73,21210,21211],{},"rust,edition2018",": pin edition.",[36,21214,21215,21218],{},[73,21216,21217],{},"# use ..."," lines are hidden in rendered docs but executed in tests.",[111,21220,21222],{"className":21221,"code":12192,"language":397,"meta":117},[395],[73,21223,12192],{"__ignoreMap":117},[15,21225,21226],{"id":17075},"Doc Attributes",[111,21228,21231],{"className":21229,"code":21230,"language":397,"meta":117},[395],"#[doc(hidden)]            \u002F\u002F hide from docs (still public)\n#[doc(alias = \"another\")] \u002F\u002F search alias\n#[doc = \"raw text\"]      \u002F\u002F alternative to \u002F\u002F\u002F for non-string content\n#[doc(inline)]           \u002F\u002F inline re-exports\n#[doc(no_inline)]        \u002F\u002F don't inline\n#[doc(cfg(feature = \"...\"))]  \u002F\u002F show \"Available on feature only\" banner\n",[73,21232,21230],{"__ignoreMap":117},[15,21234,21236],{"id":21235},"lints","Lints",[111,21238,21241],{"className":21239,"code":21240,"language":397,"meta":117},[395],"#![warn(missing_docs)]\n#![warn(missing_debug_implementations)]\n#![warn(rustdoc::broken_intra_doc_links)]\n#![warn(rustdoc::missing_crate_level_docs)]\n",[73,21242,21240],{"__ignoreMap":117},[20,21244,21245,21247],{},[73,21246,16977],{}," forces every public item to have docs — good for libraries.",[15,21249,21251],{"id":21250},"crate-level-docs","Crate-Level Docs",[111,21253,21256],{"className":21254,"code":21255,"language":397,"meta":117},[395],"\u002F\u002F src\u002Flib.rs\n\u002F\u002F! # My Crate\n\u002F\u002F!\n\u002F\u002F! This crate does X for Y.\n\u002F\u002F!\n\u002F\u002F! ## Quick Start\n\u002F\u002F! ```\n\u002F\u002F! use my_crate::*;\n\u002F\u002F! ```\n",[73,21257,21255],{"__ignoreMap":117},[20,21259,21260],{},"Include a quick-start at the crate root.",[15,21262,21264,21267],{"id":21263},"readmemd-inclusion",[73,21265,21266],{},"README.md"," Inclusion",[111,21269,21272],{"className":21270,"code":21271,"language":397,"meta":117},[395],"#![doc = include_str!(\"..\u002FREADME.md\")]\n",[73,21273,21271],{"__ignoreMap":117},[20,21275,21276],{},"Treats the README as crate-level docs. Common for projects that want one source of truth.",[15,21278,21280],{"id":21279},"style-guide","Style Guide",[33,21282,21283,21286,21296,21299,21306,21309,21312],{},[36,21284,21285],{},"Write prose, not telegrams. Sentences with verbs.",[36,21287,21288,21289,21292,21293,259],{},"Document the ",[183,21290,21291],{},"why",", not just the ",[183,21294,21295],{},"what",[36,21297,21298],{},"Examples for every public function that's not obvious.",[36,21300,1876,21301,27,21303,21305],{},[73,21302,19781],{},[73,21304,19784],{}," sections consistently.",[36,21307,21308],{},"Cross-reference related items.",[36,21310,21311],{},"Keep examples small and self-contained.",[36,21313,21314],{},"Avoid docs on trivial getters\u002Fsetters; document the field instead.",[15,21316,18452],{"id":18451},[130,21318,21320],{"id":21319},"good","Good",[111,21322,21325],{"className":21323,"code":21324,"language":397,"meta":117},[395],"\u002F\u002F\u002F Computes the Fibonacci number at position `n`.\n\u002F\u002F\u002F\n\u002F\u002F\u002F Uses an iterative algorithm with O(n) time and O(1) space.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Examples\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F use my_crate::fib;\n\u002F\u002F\u002F assert_eq!(fib(0), 0);\n\u002F\u002F\u002F assert_eq!(fib(10), 55);\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Panics\n\u002F\u002F\u002F\n\u002F\u002F\u002F Panics if `n` is large enough to overflow `u64`.\npub fn fib(n: u64) -> u64 { \u002F* ... *\u002F 0 }\n",[73,21326,21324],{"__ignoreMap":117},[130,21328,21330],{"id":21329},"bad","Bad",[111,21332,21335],{"className":21333,"code":21334,"language":397,"meta":117},[395],"\u002F\u002F\u002F fib function\npub fn fib(n: u64) -> u64 { 0 }\n",[73,21336,21334],{"__ignoreMap":117},[15,21338,21340],{"id":21339},"hidden-examples-for-complex-setup","Hidden Examples for Complex Setup",[111,21342,21345],{"className":21343,"code":21344,"language":397,"meta":117},[395],"\u002F\u002F\u002F ```\n\u002F\u002F\u002F # use std::sync::Arc;\n\u002F\u002F\u002F # use std::sync::Mutex;\n\u002F\u002F\u002F # let state = Arc::new(Mutex::new(0));\n\u002F\u002F\u002F let _v = state.lock().unwrap();\n\u002F\u002F\u002F ```\n",[73,21346,21344],{"__ignoreMap":117},[20,21348,21349,21350,21352],{},"Setup lines prefixed with ",[73,21351,12188],{}," are hidden in the rendered doc but executed.",[15,21354,21356],{"id":21355},"doc-test-pitfalls","Doc Test Pitfalls",[33,21358,21359,21371,21380,21387,21399],{},[36,21360,21361,21364,21365,1546,21368,21370],{},[24,21362,21363],{},"External crate imports",": doc tests need ",[73,21366,21367],{},"extern crate",[73,21369,11535],{}," lines.",[36,21372,21373,21379],{},[24,21374,21375,21376,21378],{},"Top-level ",[73,21377,11535],{}," shadowing",": each doc test is its own crate.",[36,21381,21382,21386],{},[24,21383,21384],{},[73,21385,12222],{}," must be on a fenced block, and the block must actually fail to compile.",[36,21388,21389,21395,21396,21398],{},[24,21390,21391,2022,21393],{},[73,21392,12214],{},[73,21394,509],{},": works; ",[73,21397,12214],{}," compiles but skips execution.",[36,21400,21401,21404,21405,259],{},[24,21402,21403],{},"Doc tests are slow",": skip in CI with ",[73,21406,12427],{},[15,21408,21410,21413],{"id":21409},"mdbook-for-standalone-docs",[73,21411,21412],{},"mdbook"," for Standalone Docs",[20,21415,21416,21417,21419],{},"For guides\u002Fbooks, ",[73,21418,21412],{}," is the standard tool:",[111,21421,21424],{"className":21422,"code":21423,"language":116,"meta":117},[114],"cargo install mdbook\nmdbook init docs\nmdbook serve docs\n",[73,21425,21423],{"__ignoreMap":117},[20,21427,21428,21429,480,21432,480,21434,21437],{},"Many major Rust projects (",[73,21430,21431],{},"rust-lang\u002Frust",[73,21433,13065],{},[73,21435,21436],{},"bevy",") have mdbook guides alongside rustdoc.",[15,21439,21441],{"id":21440},"publishing-docs","Publishing Docs",[33,21443,21444],{},[36,21445,21446,21449,21450,170],{},[24,21447,21448],{},"docs.rs",": auto-builds and hosts docs for crates published to crates.io. Configure with ",[73,21451,21452],{},"[package.metadata.docs.rs]",[111,21454,21457],{"className":21455,"code":21456,"language":176,"meta":117},[174],"[package.metadata.docs.rs]\nfeatures = [\"full\", \"all-feature-flags\"]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n",[73,21458,21456],{"__ignoreMap":117},[33,21460,21461,21470],{},[36,21462,21463,21466,21467,21469],{},[24,21464,21465],{},"GitHub Pages",": deploy ",[73,21468,21174],{}," via Actions.",[36,21471,21472,21477],{},[24,21473,21474],{},[73,21475,21476],{},"cargo-docs-rs",": preview docs.rs rendering locally.",[15,21479,21481],{"id":21480},"cross-crate-doc-links","Cross-Crate Doc Links",[111,21483,21486],{"className":21484,"code":21485,"language":397,"meta":117},[395],"\u002F\u002F\u002F See the [`serde`] crate for serialization.\n\u002F\u002F\u002F See [`tokio::sync::mpsc`] for channels.\n",[73,21487,21485],{"__ignoreMap":117},[20,21489,21490,21491,259],{},"rustdoc can resolve links to external crates if they're in your ",[73,21492,169],{},[15,21494,6469],{"id":6468},[33,21496,21497,21506,21514,21520,21528,21539,21547],{},[36,21498,21499,21502,21503,259],{},[24,21500,21501],{},"Broken intra-doc links",": rustdoc warns; turn into errors with ",[73,21504,21505],{},"#![warn(rustdoc::broken_intra_doc_links)]",[36,21507,21508,71,21511,259],{},[24,21509,21510],{},"Missing crate-level docs",[73,21512,21513],{},"#![warn(rustdoc::missing_crate_level_docs)]",[36,21515,21516,21519],{},[24,21517,21518],{},"Examples don't compile",": CI runs doc tests; broken examples break releases.",[36,21521,21522,21527],{},[24,21523,21524,21526],{},[73,21525,11691],{}," on re-exports",": hides the re-export but not the original.",[36,21529,21530,21535,21536,21538],{},[24,21531,11905,21532,21534],{},[73,21533,12188],{}," lines visible in source",": they're hidden in HTML but visible in ",[73,21537,725],{}," source.",[36,21540,21541,21546],{},[24,21542,21543],{},[73,21544,21545],{},"#![doc(html_logo_url = \"...\")]",": branding on docs.",[36,21548,21549,21554],{},[24,21550,21551],{},[73,21552,21553],{},"#![doc(html_root_url = \"https:\u002F\u002Fdocs.rs\u002Fcrate\u002F1.0\")]",": helps intra-doc link resolution.",[15,21556,349],{"id":348},[20,21558,21559,21560,480,21562,21564,21565,27,21568,21570,21571,21573,21574,21576,21577,21579],{},"Write doc comments (",[73,21561,12060],{},[73,21563,19774],{},") with standard sections (Examples, Panics, Errors, Safety). Run ",[73,21566,21567],{},"cargo doc",[73,21569,12179],{}," (doc tests). Use hidden ",[73,21572,12188],{}," lines for setup. Cross-reference with backticks. Enable ",[73,21575,16977],{}," for libraries. Publish to docs.rs. Use ",[73,21578,21412],{}," for guides.",[20,21581,21582],{},"Next: Rust ecosystem tour.",{"title":117,"searchDepth":357,"depth":357,"links":21584},[21585,21586,21587,21588,21589,21590,21591,21592,21593,21595,21596,21600,21601,21602,21604,21605,21606,21607],{"id":21022,"depth":357,"text":21023},{"id":21050,"depth":357,"text":21051},{"id":21142,"depth":357,"text":21143},{"id":21161,"depth":357,"text":21162},{"id":21177,"depth":357,"text":21178},{"id":17075,"depth":357,"text":21226},{"id":21235,"depth":357,"text":21236},{"id":21250,"depth":357,"text":21251},{"id":21263,"depth":357,"text":21594},"README.md Inclusion",{"id":21279,"depth":357,"text":21280},{"id":18451,"depth":357,"text":18452,"children":21597},[21598,21599],{"id":21319,"depth":364,"text":21320},{"id":21329,"depth":364,"text":21330},{"id":21339,"depth":357,"text":21340},{"id":21355,"depth":357,"text":21356},{"id":21409,"depth":357,"text":21603},"mdbook for Standalone Docs",{"id":21440,"depth":357,"text":21441},{"id":21480,"depth":357,"text":21481},{"id":6468,"depth":357,"text":6469},{"id":348,"depth":357,"text":349},"Documentation is part of the Rust culture. rustdoc produces HTML docs from \u002F\u002F\u002F comments; doc-tests run examples.",{},"\u002Frust\u002F32-documentation",{"title":21005,"description":21608},"rust\u002F32-documentation","TzIJQHsCSnu8p_Z7DgtAJXFwPevQVBvB7nYH9ErT9a4",{"id":21615,"title":21616,"body":21617,"description":21624,"extension":373,"meta":23263,"navigation":375,"path":23264,"seo":23265,"stem":23266,"__hash__":23267},"content\u002Frust\u002F33-ecosystem.md","33 — Ecosystem Tour",{"type":8,"value":21618,"toc":23221},[21619,21622,21625,21629,21734,21742,21754,21758,21784,21788,21845,21849,21894,21898,21922,21926,21951,21955,21987,21991,22038,22040,22108,22110,22132,22136,22205,22209,22260,22264,22304,22308,22340,22344,22375,22379,22435,22439,22472,22476,22517,22521,22541,22545,22577,22581,22609,22613,22643,22647,22693,22697,22747,22751,22771,22775,22804,22808,22825,22827,22872,22876,22910,22914,22946,22950,23029,23033,23050,23054,23077,23081,23112,23116,23119,23169,23171,23218],[11,21620,21616],{"id":21621},"_33-ecosystem-tour",[20,21623,21624],{},"A curated map of the Rust ecosystem. Pick the right tool for the job; don't reinvent.",[15,21626,21628],{"id":21627},"web-frameworks","Web Frameworks",[917,21630,21631,21643],{},[920,21632,21633],{},[923,21634,21635,21637,21640],{},[926,21636,11465],{},[926,21638,21639],{},"Style",[926,21641,21642],{},"Notes",[936,21644,21645,21657,21670,21683,21696,21708,21721],{},[923,21646,21647,21651,21654],{},[941,21648,21649],{},[73,21650,13622],{},[941,21652,21653],{},"Tokio-based, middleware via tower",[941,21655,21656],{},"Most popular, mature, by tokio team",[923,21658,21659,21664,21667],{},[941,21660,21661],{},[73,21662,21663],{},"actix-web",[941,21665,21666],{},"Actor model, fast",[941,21668,21669],{},"Pre-dates axum, still popular",[923,21671,21672,21677,21680],{},[941,21673,21674],{},[73,21675,21676],{},"rocket",[941,21678,21679],{},"Ergonomic, macros",[941,21681,21682],{},"Friendly API, slower release cadence",[923,21684,21685,21690,21693],{},[941,21686,21687],{},[73,21688,21689],{},"poem",[941,21691,21692],{},"Modular",[941,21694,21695],{},"Good OpenAPI integration",[923,21697,21698,21703,21706],{},[941,21699,21700],{},[73,21701,21702],{},"salvo",[941,21704,21705],{},"Middleware-centric",[941,21707],{},[923,21709,21710,21715,21718],{},[941,21711,21712],{},[73,21713,21714],{},"warp",[941,21716,21717],{},"Combinator-based",[941,21719,21720],{},"Filter-based, older style",[923,21722,21723,21728,21731],{},[941,21724,21725],{},[73,21726,21727],{},"tide",[941,21729,21730],{},"async-std-based",[941,21732,21733],{},"Less active",[130,21735,21737,21738,21741],{"id":21736},"use-with-tower-middleware","Use with ",[73,21739,21740],{},"tower"," middleware",[20,21743,21744,21746,21747,21749,21750,21753],{},[73,21745,21740],{}," is the standard middleware\u002Fservice abstraction. ",[73,21748,13622],{}," builds on ",[73,21751,21752],{},"tower-http"," (compression, tracing, auth, CORS).",[15,21755,21757],{"id":21756},"http-client","HTTP Client",[33,21759,21760,21767,21773,21778],{},[36,21761,21762,21764,21765,526],{},[73,21763,13604],{},": dominant async HTTP client (built on ",[73,21766,13610],{},[36,21768,21769,21772],{},[73,21770,21771],{},"ureq",": simple blocking client, minimal deps.",[36,21774,21775,21777],{},[73,21776,13610],{},": low-level HTTP\u002F1\u002F2 client\u002Fserver.",[36,21779,21780,21783],{},[73,21781,21782],{},"attohttpc",": blocking, minimal.",[15,21785,21787],{"id":21786},"serialization","Serialization",[33,21789,21790,21801,21807,21813,21821,21827,21833,21839],{},[36,21791,21792,21794,21795,1212,21798,259],{},[73,21793,14735],{},": the universal serialize\u002Fdeserialize framework. Almost every type derives ",[73,21796,21797],{},"Serialize",[73,21799,21800],{},"Deserialize",[36,21802,21803,21806],{},[73,21804,21805],{},"serde_json",": JSON.",[36,21808,21809,21812],{},[73,21810,21811],{},"serde_yaml",": YAML.",[36,21814,21815,1212,21817,21820],{},[73,21816,176],{},[73,21818,21819],{},"toml_edit",": TOML config.",[36,21822,21823,21826],{},[73,21824,21825],{},"rmp-serde",": MessagePack.",[36,21828,21829,21832],{},[73,21830,21831],{},"bincode",": Rust-native binary (fast, not portable).",[36,21834,21835,21838],{},[73,21836,21837],{},"postcard",": compact embedded-friendly binary.",[36,21840,21841,21844],{},[73,21842,21843],{},"ciborium",": CBOR.",[15,21846,21848],{"id":21847},"database","Database",[33,21850,21851,21856,21862,21870,21876,21882,21888],{},[36,21852,21853,21855],{},[73,21854,13616],{},": async, compile-time checked SQL via macros.",[36,21857,21858,21861],{},[73,21859,21860],{},"diesel",": ORM, sync, mature.",[36,21863,21864,21867,21868,259],{},[73,21865,21866],{},"sea-orm",": async ORM on top of ",[73,21869,13616],{},[36,21871,21872,21875],{},[73,21873,21874],{},"tokio-postgres",": async PostgreSQL client.",[36,21877,21878,21881],{},[73,21879,21880],{},"rusqlite",": sync SQLite.",[36,21883,21884,21887],{},[73,21885,21886],{},"redis",": Redis client.",[36,21889,21890,21893],{},[73,21891,21892],{},"mongodb",": official driver.",[15,21895,21897],{"id":21896},"async-runtime","Async Runtime",[33,21899,21900,21905,21910,21915],{},[36,21901,21902,21904],{},[73,21903,13065],{},": default for most projects.",[36,21906,21907,21909],{},[73,21908,13068],{},": std-mirror API.",[36,21911,21912,21914],{},[73,21913,13425],{},": minimal.",[36,21916,21917,71,21919,21921],{},[73,21918,13431],{},[73,21920,13435],{}," for embedded.",[15,21923,21925],{"id":21924},"cli-parsing","CLI Parsing",[33,21927,21928,21934,21940,21945],{},[36,21929,21930,21933],{},[73,21931,21932],{},"clap",": de facto standard. Derive API is ergonomic.",[36,21935,21936,21939],{},[73,21937,21938],{},"argh",": Google's lightweight derive-based.",[36,21941,21942,21914],{},[73,21943,21944],{},"gumdrop",[36,21946,21947,21950],{},[73,21948,21949],{},"pico-args",": tiny, no derive.",[130,21952,21954],{"id":21953},"plus-cli-helpers","Plus CLI helpers",[33,21956,21957,21963,21969,21978],{},[36,21958,21959,21962],{},[73,21960,21961],{},"indicatif",": progress bars.",[36,21964,21965,21968],{},[73,21966,21967],{},"dialoguer",": interactive prompts.",[36,21970,21971,1212,21974,21977],{},[73,21972,21973],{},"console",[73,21975,21976],{},"owo-colors",": terminal colors.",[36,21979,21980,1212,21983,21986],{},[73,21981,21982],{},"comfy-table",[73,21984,21985],{},"tabled",": tables.",[15,21988,21990],{"id":21989},"logging-tracing","Logging & Tracing",[33,21992,21993,22006,22018,22024,22032],{},[36,21994,21995,21998,21999,1212,22002,22005],{},[73,21996,21997],{},"log",": facade, simple ",[73,22000,22001],{},"info!",[73,22003,22004],{},"warn!"," macros.",[36,22007,22008,22011,22012,22014,22015,259],{},[73,22009,22010],{},"env_logger",": backend for ",[73,22013,21997],{}," controlled by ",[73,22016,22017],{},"RUST_LOG",[36,22019,22020,22023],{},[73,22021,22022],{},"tracing",": structured, async-aware, spans. Modern choice.",[36,22025,22026,22029,22030,259],{},[73,22027,22028],{},"tracing-subscriber",": subscriber setup for ",[73,22031,22022],{},[36,22033,22034,22037],{},[73,22035,22036],{},"slog",": structured logging, less common now.",[15,22039,17928],{"id":17927},[33,22041,22042,22047,22052,22057,22063,22068,22074,22080,22085,22090,22096,22102],{},[36,22043,22044,22045,526],{},"Built-in (",[73,22046,12179],{},[36,22048,22049,22051],{},[73,22050,12328],{},": property-based.",[36,22053,22054,22056],{},[73,22055,12331],{},": property-based (older).",[36,22058,22059,22062],{},[73,22060,22061],{},"rstest",": parametrized tests.",[36,22064,22065,22067],{},[73,22066,12375],{},": mock generation.",[36,22069,22070,22073],{},[73,22071,22072],{},"mockito",": HTTP mock server.",[36,22075,22076,22079],{},[73,22077,22078],{},"wiremock",": HTTP mock (async).",[36,22081,22082,22084],{},[73,22083,12346],{},": snapshot testing.",[36,22086,22087,22089],{},[73,22088,12318],{},": benchmarking.",[36,22091,22092,22095],{},[73,22093,22094],{},"cargo-nextest",": faster test runner.",[36,22097,22098,22101],{},[73,22099,22100],{},"trybuild",": UI tests for compile errors.",[36,22103,22104,22107],{},[73,22105,22106],{},"cargo-mutants",": mutation testing.",[15,22109,12492],{"id":12491},[33,22111,22112,22117,22123],{},[36,22113,22114,22116],{},[73,22115,12497],{},": libFuzzer-based.",[36,22118,22119,22122],{},[73,22120,22121],{},"afl.rs",": AFL-based.",[36,22124,22125,1212,22128,22131],{},[73,22126,22127],{},"rutensprika",[73,22129,22130],{},"bolero",": alternatives.",[15,22133,22135],{"id":22134},"cryptography","Cryptography",[33,22137,22138,22144,22152,22161,22167,22175,22187,22196],{},[36,22139,22140,22143],{},[73,22141,22142],{},"ring",": popular, audited.",[36,22145,22146,22149,22150,526],{},[73,22147,22148],{},"rustls",": TLS in Rust (uses ",[73,22151,22142],{},[36,22153,22154,1212,22157,22160],{},[73,22155,22156],{},"openssl",[73,22158,22159],{},"openssl-sys",": OpenSSL bindings.",[36,22162,22163,22166],{},[73,22164,22165],{},"argon2",": password hashing.",[36,22168,22169,1212,22172,22131],{},[73,22170,22171],{},"bcrypt",[73,22173,22174],{},"scrypt",[36,22176,22177,1212,22180,1212,22183,22186],{},[73,22178,22179],{},"sha2",[73,22181,22182],{},"sha3",[73,22184,22185],{},"blake3",": hash functions.",[36,22188,22189,1212,22192,22195],{},[73,22190,22191],{},"chacha20poly1305",[73,22193,22194],{},"aes-gcm",": AEAD ciphers.",[36,22197,22198,1212,22201,22204],{},[73,22199,22200],{},"ed25519-dalek",[73,22202,22203],{},"x25519-dalek",": elliptic curve crypto.",[15,22206,22208],{"id":22207},"networking","Networking",[33,22210,22211,22216,22221,22227,22233,22242,22248,22254],{},[36,22212,22213,22215],{},[73,22214,13065],{},": async runtime + TCP\u002FUDP\u002FUnix sockets.",[36,22217,22218,22220],{},[73,22219,13610],{},": HTTP\u002F1, HTTP\u002F2.",[36,22222,22223,22226],{},[73,22224,22225],{},"quinn",": QUIC.",[36,22228,22229,22232],{},[73,22230,22231],{},"tonic",": gRPC.",[36,22234,22235,1212,22238,22241],{},[73,22236,22237],{},"tungstenite",[73,22239,22240],{},"tokio-tungstenite",": WebSocket.",[36,22243,22244,22247],{},[73,22245,22246],{},"paho-mqtt",": MQTT.",[36,22249,22250,22253],{},[73,22251,22252],{},"lapin",": AMQP (RabbitMQ).",[36,22255,22256,22259],{},[73,22257,22258],{},"rdkafka",": Kafka.",[15,22261,22263],{"id":22262},"file-formats","File Formats",[33,22265,22266,22274,22279,22290,22295],{},[36,22267,22268,1212,22270,1212,22272,259],{},[73,22269,21805],{},[73,22271,21811],{},[73,22273,176],{},[36,22275,22276,259],{},[73,22277,22278],{},"csv",[36,22280,22281,1212,22284,1212,22287,259],{},[73,22282,22283],{},"quick-xml",[73,22285,22286],{},"roxmltree",[73,22288,22289],{},"serde-xml-rs",[36,22291,22292,259],{},[73,22293,22294],{},"serde_urlencoded",[36,22296,22297,1212,22300,22303],{},[73,22298,22299],{},"bytes",[73,22301,22302],{},"byteserde"," for binary protocols.",[15,22305,22307],{"id":22306},"compression","Compression",[33,22309,22310,22316,22322,22334],{},[36,22311,22312,22315],{},[73,22313,22314],{},"flate2",": gzip\u002Fdeflate.",[36,22317,22318,22321],{},[73,22319,22320],{},"zstd",": Zstandard.",[36,22323,22324,1212,22327,1212,22330,22333],{},[73,22325,22326],{},"bzip2",[73,22328,22329],{},"lz4",[73,22331,22332],{},"xz2",": other algorithms.",[36,22335,22336,22339],{},[73,22337,22338],{},"snap",": Snappy.",[15,22341,22343],{"id":22342},"serialization-for-network-protocols","Serialization for Network Protocols",[33,22345,22346,22351,22360,22369],{},[36,22347,22348,22350],{},[73,22349,22299],{}," (tokio ecosystem): zero-copy byte buffers.",[36,22352,22353,1212,22356,22359],{},[73,22354,22355],{},"prost",[73,22357,22358],{},"protobuf",": Protocol Buffers.",[36,22361,22362,1212,22365,22368],{},[73,22363,22364],{},"capnp",[73,22366,22367],{},"capnpc",": Cap'n Proto.",[36,22370,22371,22374],{},[73,22372,22373],{},"flatbuffers",": FlatBuffers.",[15,22376,22378],{"id":22377},"gui","GUI",[33,22380,22381,22390,22396,22402,22408,22414,22423,22429],{},[36,22382,22383,1212,22386,22389],{},[73,22384,22385],{},"egui",[73,22387,22388],{},"eframe",": immediate-mode, easy, cross-platform.",[36,22391,22392,22395],{},[73,22393,22394],{},"iced",": Elm-inspired, reactive.",[36,22397,22398,22401],{},[73,22399,22400],{},"slint",": declarative UI DSL, commercial-friendly.",[36,22403,22404,22407],{},[73,22405,22406],{},"tauri",": web frontend + Rust backend (Electron alternative).",[36,22409,22410,22413],{},[73,22411,22412],{},"dioxus",": React-like.",[36,22415,22416,1212,22419,22422],{},[73,22417,22418],{},"druid",[73,22420,22421],{},"xilem",": research projects.",[36,22424,22425,22428],{},[73,22426,22427],{},"gtk-rs",": GTK bindings.",[36,22430,22431,22434],{},[73,22432,22433],{},"makepad",": live-coded, GPU-rendered.",[15,22436,22438],{"id":22437},"game-development","Game Development",[33,22440,22441,22446,22452,22458,22466],{},[36,22442,22443,22445],{},[73,22444,21436],{},": ECS game engine, modern, popular.",[36,22447,22448,22451],{},[73,22449,22450],{},"wgpu",": portable graphics API (Vulkan\u002FMetal\u002FDX12\u002FWebGPU).",[36,22453,22454,22457],{},[73,22455,22456],{},"macroquad",": simple 2D.",[36,22459,22460,22463,22464,259],{},[73,22461,22462],{},"amethyst",": discontinued, see ",[73,22465,21436],{},[36,22467,22468,22471],{},[73,22469,22470],{},"ggez",": 2D, LÖVE-like.",[15,22473,22475],{"id":22474},"numerical-data-science","Numerical & Data Science",[33,22477,22478,22484,22490,22496,22502,22511],{},[36,22479,22480,22483],{},[73,22481,22482],{},"ndarray",": N-dimensional arrays.",[36,22485,22486,22489],{},[73,22487,22488],{},"nalgebra",": linear algebra.",[36,22491,22492,22495],{},[73,22493,22494],{},"plotters",": plotting.",[36,22497,22498,22501],{},[73,22499,22500],{},"polars",": DataFrames (Pandas-like, fast).",[36,22503,22504,1212,22507,22510],{},[73,22505,22506],{},"arrow",[73,22508,22509],{},"arrow2",": Apache Arrow.",[36,22512,22513,22516],{},[73,22514,22515],{},"linfa",": ML toolkit.",[15,22518,22520],{"id":22519},"date-time","Date & Time",[33,22522,22523,22529,22535],{},[36,22524,22525,22528],{},[73,22526,22527],{},"chrono",": full-featured, popular.",[36,22530,22531,22534],{},[73,22532,22533],{},"time",": lighter, modern API.",[36,22536,22537,22540],{},[73,22538,22539],{},"jiff",": newer, ergonomic (by BurntSushi).",[15,22542,22544],{"id":22543},"regex-text","Regex & Text",[33,22546,22547,22553,22559,22565,22571],{},[36,22548,22549,22552],{},[73,22550,22551],{},"regex",": fast, Unicode-aware.",[36,22554,22555,22558],{},[73,22556,22557],{},"fancy-regex",": backtracking for lookahead\u002Fbackreferences.",[36,22560,22561,22564],{},[73,22562,22563],{},"aho-corasick",": multiple-pattern search.",[36,22566,22567,22570],{},[73,22568,22569],{},"memchr",": byte search primitives.",[36,22572,22573,22576],{},[73,22574,22575],{},"unicode-segmentation",": grapheme\u002Fword splitting.",[15,22578,22580],{"id":22579},"error-handling","Error Handling",[33,22582,22583,22590,22595,22603],{},[36,22584,22585,22587,22588,9925],{},[73,22586,9900],{},": derive ",[73,22589,9861],{},[36,22591,22592,22594],{},[73,22593,9931],{},": ergonomic error type for apps.",[36,22596,22597,71,22600,22602],{},[73,22598,22599],{},"eyre",[73,22601,9931],{}," fork with reports.",[36,22604,22605,22608],{},[73,22606,22607],{},"color-eyre",": prettier error reports.",[15,22610,22612],{"id":22611},"async-utilities","Async Utilities",[33,22614,22615,22620,22628,22634],{},[36,22616,22617,22619],{},[73,22618,13598],{},": async in traits (still useful pre-1.75).",[36,22621,22622,1212,22624,22627],{},[73,22623,13486],{},[73,22625,22626],{},"futures-util",": combinators.",[36,22629,22630,22633],{},[73,22631,22632],{},"tokio-util",": codecs, tasks.",[36,22635,22636,71,22639,22642],{},[73,22637,22638],{},"async-stream",[73,22640,22641],{},"yield","-like streams.",[15,22644,22646],{"id":22645},"concurrency","Concurrency",[33,22648,22649,22655,22666,22671,22679,22687],{},[36,22650,22651,22654],{},[73,22652,22653],{},"crossbeam",": channels, epoch-based GC, scoped threads.",[36,22656,22657,22660,22661,1212,22663,22665],{},[73,22658,22659],{},"parking_lot",": faster ",[73,22662,10713],{},[73,22664,10841],{}," than std.",[36,22667,22668,22670],{},[73,22669,13061],{},": data parallelism.",[36,22672,22673,22676,22677,259],{},[73,22674,22675],{},"dashmap",": concurrent ",[73,22678,1687],{},[36,22680,22681,22683,22684,22686],{},[73,22682,13232],{},": atomic ",[73,22685,10566],{}," swap.",[36,22688,22689,22692],{},[73,22690,22691],{},"loom",": concurrency model checker.",[15,22694,22696],{"id":22695},"collections","Collections",[33,22698,22699,22708,22714,22723,22729,22735,22741],{},[36,22700,22701,22703,22704,1212,22706,259],{},[73,22702,7313],{},": ordered ",[73,22705,1687],{},[73,22707,5340],{},[36,22709,22710,22713],{},[73,22711,22712],{},"hashbrown",": low-level hash map.",[36,22715,22716,1212,22719,22722],{},[73,22717,22718],{},"smallvec",[73,22720,22721],{},"tinyvec",": inline storage.",[36,22724,22725,22728],{},[73,22726,22727],{},"arrayvec",": stack-only fixed capacity.",[36,22730,22731,22734],{},[73,22732,22733],{},"bumpalo",": arena allocator.",[36,22736,22737,22740],{},[73,22738,22739],{},"typed-arena",": typed arena.",[36,22742,22743,22746],{},[73,22744,22745],{},"im",": persistent\u002Fimmutable collections.",[15,22748,22750],{"id":22749},"serialization-helpers","Serialization Helpers",[33,22752,22753,22759,22765],{},[36,22754,22755,22758],{},[73,22756,22757],{},"serde_with",": custom serde helpers.",[36,22760,22761,22764],{},[73,22762,22763],{},"serde_repr",": serialize enums as integers.",[36,22766,22767,22770],{},[73,22768,22769],{},"serde-aux",": extra helpers.",[15,22772,22774],{"id":22773},"configuration","Configuration",[33,22776,22777,22783,22789,22795],{},[36,22778,22779,22782],{},[73,22780,22781],{},"config",": multi-source config (env, file, CLI).",[36,22784,22785,22788],{},[73,22786,22787],{},"figment",": layered config (used by Rocket).",[36,22790,22791,22794],{},[73,22792,22793],{},"envy",": struct-of-env-vars via serde.",[36,22796,22797,1212,22800,1212,22802,259],{},[73,22798,22799],{}," envy",[73,22801,22799],{},[73,22803,22793],{},[15,22805,22807],{"id":22806},"http-server-middleware","HTTP Server Middleware",[33,22809,22810,22815,22820],{},[36,22811,22812,22814],{},[73,22813,21740],{},": middleware abstraction.",[36,22816,22817,22819],{},[73,22818,21752],{},": tracing, compression, CORS, auth, fs, timeout.",[36,22821,22822,259],{},[73,22823,22824],{},"axum::middleware",[15,22826,16283],{"id":16282},[33,22828,22829,22834,22840,22849,22855,22860,22866],{},[36,22830,22831,22833],{},[73,22832,16295],{},": JS interop.",[36,22835,22836,22839],{},[73,22837,22838],{},"wasm-pack",": build & publish.",[36,22841,22842,1212,22845,22848],{},[73,22843,22844],{},"web-sys",[73,22846,22847],{},"js-sys",": bindings to Web APIs.",[36,22850,22851,22854],{},[73,22852,22853],{},"gloo",": idiomatic wrappers.",[36,22856,22857,22859],{},[73,22858,14146],{},": React-like in WASM.",[36,22861,22862,22865],{},[73,22863,22864],{},"seed",": alternative.",[36,22867,22868,22871],{},[73,22869,22870],{},"leptos",": modern, signal-based.",[15,22873,22875],{"id":22874},"embedded","Embedded",[33,22877,22878,22884,22893,22898,22904],{},[36,22879,22880,22883],{},[73,22881,22882],{},"embedded-hal",": hardware abstraction traits.",[36,22885,22886,1212,22889,22892],{},[73,22887,22888],{},"cortex-m",[73,22890,22891],{},"cortex-m-rt",": ARM Cortex.",[36,22894,22895,22897],{},[73,22896,13431],{},": async embedded.",[36,22899,22900,22903],{},[73,22901,22902],{},"defmt",": efficient logging.",[36,22905,22906,22909],{},[73,22907,22908],{},"probe-rs",": debugging\u002Fprobing.",[15,22911,22913],{"id":22912},"parsing-dsls","Parsing & DSLs",[33,22915,22916,22922,22928,22934,22940],{},[36,22917,22918,22921],{},[73,22919,22920],{},"nom",": parser combinators.",[36,22923,22924,22927],{},[73,22925,22926],{},"pest",": PEG-based, easy.",[36,22929,22930,22933],{},[73,22931,22932],{},"lalrpop",": LR parser generator.",[36,22935,22936,22939],{},[73,22937,22938],{},"chumsky",": zero-copy parser combinators.",[36,22941,22942,22945],{},[73,22943,22944],{},"logos",": fast lexer.",[15,22947,22949],{"id":22948},"build-release","Build & Release",[33,22951,22952,22958,22963,22968,22973,22982,22987,22992,22998,23004,23009,23014,23020,23024],{},[36,22953,22954,22957],{},[73,22955,22956],{},"cargo-release",": versioning\u002Fpublishing.",[36,22959,22960,22962],{},[73,22961,18118],{},": license\u002Fadvisory checks.",[36,22964,22965,22967],{},[73,22966,18121],{},": security advisories.",[36,22969,22970,22972],{},[73,22971,22094],{},": faster tests.",[36,22974,22975,1212,22978,22981],{},[73,22976,22977],{},"cargo-udeps",[73,22979,22980],{},"cargo-machete",": unused dep detection.",[36,22983,22984,22986],{},[73,22985,14775],{},": macro expansion.",[36,22988,22989,22991],{},[73,22990,20650],{},": binary size analysis.",[36,22993,22994,22997],{},[73,22995,22996],{},"cargo-flamegraph",": profiling.",[36,22999,23000,23003],{},[73,23001,23002],{},"cargo-miri",": UB detection (nightly).",[36,23005,23006,23008],{},[73,23007,18142],{},": cross-compilation.",[36,23010,23011,23013],{},[73,23012,18154],{},": Zig-backed cross-linker.",[36,23015,23016,23019],{},[73,23017,23018],{},"maturin",": Python package building.",[36,23021,23022,16528],{},[73,23023,16268],{},[36,23025,23026,23028],{},[73,23027,22838],{},": WASM packaging.",[15,23030,23032],{"id":23031},"editor-support","Editor Support",[33,23034,23035,23040,23045],{},[36,23036,23037,23039],{},[73,23038,91],{},": official IDE server (VS Code, Vim, Emacs, Zed).",[36,23041,23042,23044],{},[73,23043,79],{},": formatter.",[36,23046,23047,23049],{},[73,23048,83],{},": linter.",[15,23051,23053],{"id":23052},"quality-lints","Quality Lints",[33,23055,23056,23061,23066,23071],{},[36,23057,23058,23060],{},[73,23059,16993],{},": standard.",[36,23062,23063,23065],{},[73,23064,16996],{},": stricter.",[36,23067,23068,23070],{},[73,23069,16999],{},": experimental.",[36,23072,23073,23076],{},[73,23074,23075],{},"clippy::cargo",": crate-level checks.",[15,23078,23080],{"id":23079},"ci-tools","CI Tools",[33,23082,23083,23088,23093,23099,23103],{},[36,23084,23085,23087],{},[73,23086,18118],{},": license + advisories + bans.",[36,23089,23090,23092],{},[73,23091,18121],{},": RustSec advisories.",[36,23094,23095,23098],{},[73,23096,23097],{},"cargo-hack",": feature matrix testing.",[36,23100,23101,22107],{},[73,23102,22106],{},[36,23104,23105,1212,23108,23111],{},[73,23106,23107],{},"rust-toolchain",[73,23109,23110],{},"dtolnay\u002Frust-toolchain",": GitHub Actions setup.",[15,23113,23115],{"id":23114},"choosing-crates","Choosing Crates",[20,23117,23118],{},"Heuristics:",[33,23120,23121,23127,23134,23142,23148],{},[36,23122,23123,23124,23126],{},"Prefer std\u002F",[73,23125,13065],{}," ecosystem.",[36,23128,23129,23130,23133],{},"Check ",[73,23131,23132],{},"crates.io"," for maintenance (last publish, downloads, open issues).",[36,23135,23136,23137,11526,23139,23141],{},"Prefer crates with ",[73,23138,22148],{},[73,23140,22156],{}," (no system dep).",[36,23143,23144,23145,23147],{},"Prefer ",[73,23146,14735],{},"-based serialization.",[36,23149,23150,23151,9593,23153,9593,23155,9593,23157,9593,23159,9593,23161,9593,23163,23165,23166,23168],{},"For new projects: ",[73,23152,13622],{},[73,23154,13065],{},[73,23156,14735],{},[73,23158,13616],{},[73,23160,21932],{},[73,23162,22022],{},[73,23164,9931],{}," (app) or ",[73,23167,9900],{}," (lib).",[15,23170,349],{"id":348},[20,23172,23173,23174,23176,23177,23176,23179,23181,23182,23184,23185,23187,23188,23190,23191,23193,23194,1212,23196,23198,23199,23201,23202,480,23204,480,23206,480,23208,480,23210,23212,23213,23215,23216,259],{},"Use the ecosystem; don't reinvent. The ",[73,23175,13065],{},"-",[73,23178,14735],{},[73,23180,21740],{}," stack underlies most server-side Rust. For new projects: pick ",[73,23183,13622],{}," (web), ",[73,23186,13616],{}," (DB), ",[73,23189,21932],{}," (CLI), ",[73,23192,22022],{}," (logging), ",[73,23195,9931],{},[73,23197,9900],{}," (errors), ",[73,23200,14735],{}," (serialization). For tools: ",[73,23203,91],{},[73,23205,83],{},[73,23207,18118],{},[73,23209,22094],{},[73,23211,14775],{},". For fuzzing: ",[73,23214,12497],{},". For benchmarks: ",[73,23217,12318],{},[20,23219,23220],{},"Next: Common pitfalls and idiomatic fixes.",{"title":117,"searchDepth":357,"depth":357,"links":23222},[23223,23227,23228,23229,23230,23231,23234,23235,23236,23237,23238,23239,23240,23241,23242,23243,23244,23245,23246,23247,23248,23249,23250,23251,23252,23253,23254,23255,23256,23257,23258,23259,23260,23261,23262],{"id":21627,"depth":357,"text":21628,"children":23224},[23225],{"id":21736,"depth":364,"text":23226},"Use with tower middleware",{"id":21756,"depth":357,"text":21757},{"id":21786,"depth":357,"text":21787},{"id":21847,"depth":357,"text":21848},{"id":21896,"depth":357,"text":21897},{"id":21924,"depth":357,"text":21925,"children":23232},[23233],{"id":21953,"depth":364,"text":21954},{"id":21989,"depth":357,"text":21990},{"id":17927,"depth":357,"text":17928},{"id":12491,"depth":357,"text":12492},{"id":22134,"depth":357,"text":22135},{"id":22207,"depth":357,"text":22208},{"id":22262,"depth":357,"text":22263},{"id":22306,"depth":357,"text":22307},{"id":22342,"depth":357,"text":22343},{"id":22377,"depth":357,"text":22378},{"id":22437,"depth":357,"text":22438},{"id":22474,"depth":357,"text":22475},{"id":22519,"depth":357,"text":22520},{"id":22543,"depth":357,"text":22544},{"id":22579,"depth":357,"text":22580},{"id":22611,"depth":357,"text":22612},{"id":22645,"depth":357,"text":22646},{"id":22695,"depth":357,"text":22696},{"id":22749,"depth":357,"text":22750},{"id":22773,"depth":357,"text":22774},{"id":22806,"depth":357,"text":22807},{"id":16282,"depth":357,"text":16283},{"id":22874,"depth":357,"text":22875},{"id":22912,"depth":357,"text":22913},{"id":22948,"depth":357,"text":22949},{"id":23031,"depth":357,"text":23032},{"id":23052,"depth":357,"text":23053},{"id":23079,"depth":357,"text":23080},{"id":23114,"depth":357,"text":23115},{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F33-ecosystem",{"title":21616,"description":21624},"rust\u002F33-ecosystem","OQ_hn2B5C_NU7ksVrftHjUqMvqTtBqpmY7gz5MlJTRg",{"id":23269,"title":23270,"body":23271,"description":23278,"extension":373,"meta":24229,"navigation":375,"path":24230,"seo":24231,"stem":24232,"__hash__":24233},"content\u002Frust\u002F34-common-pitfalls.md","34 — Common Pitfalls & Idiomatic Fixes",{"type":8,"value":23272,"toc":24144},[23273,23276,23279,23283,23287,23293,23297,23303,23309,23313,23319,23322,23326,23329,23336,23340,23346,23352,23356,23362,23369,23372,23378,23388,23397,23403,23421,23428,23434,23443,23447,23453,23459,23466,23472,23478,23484,23491,23497,23506,23510,23516,23524,23528,23534,23545,23552,23557,23563,23568,23578,23588,23594,23600,23606,23613,23619,23627,23639,23648,23663,23672,23678,23685,23692,23698,23703,23711,23716,23722,23729,23735,23743,23750,23756,23763,23773,23779,23782,23789,23795,23802,23808,23814,23823,23836,23851,23858,23864,23869,23875,23882,23888,23898,23906,23913,23925,23933,23940,23950,23965,23972,23979,23985,23992,24001,24007,24017,24023,24031,24035,24038,24044,24053,24057,24064,24071,24078,24080,24141],[11,23274,23270],{"id":23275},"_34-common-pitfalls-idiomatic-fixes",[20,23277,23278],{},"A checklist of mistakes every Rust developer makes — and the idiomatic fix for each.",[15,23280,23282],{"id":23281},"_1-fighting-the-borrow-checker","1. Fighting the Borrow Checker",[130,23284,23286],{"id":23285},"symptom-cannot-borrow-as-mutable-because-it-is-also-borrowed-as-immutable","Symptom: \"cannot borrow as mutable because it is also borrowed as immutable\"",[111,23288,23291],{"className":23289,"code":23290,"language":397,"meta":117},[395],"\u002F\u002F Bad\nlet mut v = vec![1, 2, 3];\nlet r = &v[0];\nv.push(4);          \u002F\u002F ERROR\nprintln!(\"{r}\");\n\n\u002F\u002F Fix: end the borrow first\nlet r = &v[0];\nprintln!(\"{r}\");\nv.push(4);\n\n\u002F\u002F Fix: copy out\nlet r = v[0];       \u002F\u002F i32 is Copy\nv.push(4);\n\n\u002F\u002F Fix: clone\nlet r = v[0].clone();\nv.push(4);\n",[73,23292,23290],{"__ignoreMap":117},[130,23294,23296],{"id":23295},"symptom-cannot-borrow-as-mutable-as-it-is-not-declared-as-mut","Symptom: \"cannot borrow as mutable, as it is not declared as mut\"",[111,23298,23301],{"className":23299,"code":23300,"language":397,"meta":117},[395],"let s = String::from(\"hi\");\nlet r = &mut s;     \u002F\u002F ERROR\n",[73,23302,23300],{"__ignoreMap":117},[20,23304,23305,23306,259],{},"Fix: ",[73,23307,23308],{},"let mut s = String::from(\"hi\");",[130,23310,23312],{"id":23311},"symptom-borrowed-value-does-not-live-long-enough","Symptom: \"borrowed value does not live long enough\"",[111,23314,23317],{"className":23315,"code":23316,"language":397,"meta":117},[395],"\u002F\u002F Bad\nfn bad() -> &str { let s = String::from(\"hi\"); &s }\n\n\u002F\u002F Fix: return owned\nfn good() -> String { String::from(\"hi\") }\n",[73,23318,23316],{"__ignoreMap":117},[20,23320,23321],{},"Returning a reference to a local is impossible. Return owned, or accept the data as input.",[130,23323,23325],{"id":23324},"symptom-returns-a-value-referencing-data-owned-by-the-current-function","Symptom: \"returns a value referencing data owned by the current function\"",[20,23327,23328],{},"Same as above. Return owned, or restructure so the data lives outside the function.",[15,23330,23332,23333,23335],{"id":23331},"_2-move-closure-footguns","2. ",[73,23334,9142],{}," Closure Footguns",[130,23337,23339],{"id":23338},"symptom-closure-captures-too-much","Symptom: closure captures too much",[111,23341,23344],{"className":23342,"code":23343,"language":397,"meta":117},[395],"let v = vec![1, 2, 3];\nlet n = 5;\nlet f = move || { println!(\"{n}\"); };   \u002F\u002F moves n, doesn't need v\n\u002F\u002F v still owned — but if closure captured v, v would be gone\n",[73,23345,23343],{"__ignoreMap":117},[20,23347,23348,23349,23351],{},"Edition 2021 captures only used variables, but ",[73,23350,9142],{}," still moves all of them.",[130,23353,23355],{"id":23354},"symptom-lifetime-issues-with-thread-closure","Symptom: lifetime issues with thread closure",[111,23357,23360],{"className":23358,"code":23359,"language":397,"meta":117},[395],"let s = String::from(\"hi\");\nstd::thread::spawn(|| println!(\"{s}\"));    \u002F\u002F ERROR: 'static required\n\u002F\u002F Fix\nstd::thread::spawn(move || println!(\"{s}\"));\n\u002F\u002F or clone\n",[73,23361,23359],{"__ignoreMap":117},[15,23363,23365,23366,23368],{"id":23364},"_3-clone-everywhere","3. ",[73,23367,3176],{}," Everywhere",[20,23370,23371],{},"Cloning is fine when necessary but often signals a design issue:",[111,23373,23376],{"className":23374,"code":23375,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nfn process(s: String) { \u002F* ... *\u002F }\nlet s = String::from(\"hi\");\nprocess(s.clone());\nprocess(s.clone());\n\n\u002F\u002F Better: borrow\nfn process(s: &str) { \u002F* ... *\u002F }\nprocess(&s);\nprocess(&s);\n",[73,23377,23375],{"__ignoreMap":117},[20,23379,23380,23381,1212,23383,1212,23385,23387],{},"Borrow by ",[73,23382,1630],{},[73,23384,1739],{},[73,23386,8659],{}," when you don't need ownership.",[15,23389,23391,23392,1212,23394,23396],{"id":23390},"_4-unwrapexpect-in-production","4. ",[73,23393,10151],{},[73,23395,10164],{}," in Production",[111,23398,23401],{"className":23399,"code":23400,"language":397,"meta":117},[395],"\u002F\u002F Bad\nfn parse(s: &str) -> i32 { s.parse().unwrap() }\n\n\u002F\u002F Good\nfn parse(s: &str) -> Result\u003Ci32, ParseIntError> { s.parse() }\n\u002F\u002F or\nfn parse_or_default(s: &str) -> i32 { s.parse().unwrap_or(0) }\n",[73,23402,23400],{"__ignoreMap":117},[20,23404,23405,23407,23408,1212,23410,7020,23412,480,23414,480,23417,23420],{},[73,23406,10168],{}," panics on ",[73,23409,1541],{},[73,23411,2778],{},[73,23413,2404],{},[73,23415,23416],{},"unwrap_or",[73,23418,23419],{},"unwrap_or_default",", or explicit match.",[15,23422,23424,23425,23427],{"id":23423},"_5-treating-result-like-exceptions","5. Treating ",[73,23426,2792],{}," Like Exceptions",[111,23429,23432],{"className":23430,"code":23431,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nfn process() {\n    let a: i32 = \"x\".parse().unwrap();\n    let b: i32 = \"y\".parse().unwrap();\n    \u002F\u002F ...\n}\n\n\u002F\u002F Idiomatic\nfn process() -> Result\u003C(), AppError> {\n    let a: i32 = \"x\".parse()?;\n    let b: i32 = \"y\".parse()?;\n    Ok(())\n}\n",[73,23433,23431],{"__ignoreMap":117},[20,23435,23436,23437,23439,23440,23442],{},"Propagate with ",[73,23438,2404],{},". Don't ",[73,23441,10168],{}," in non-test paths.",[15,23444,23446],{"id":23445},"_6-mutable-global-state","6. Mutable Global State",[111,23448,23451],{"className":23449,"code":23450,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nstatic mut COUNTER: u32 = 0;\nfn incr() { unsafe { COUNTER += 1; } }\n\n\u002F\u002F Idiomatic\nuse std::sync::atomic::{AtomicUsize, Ordering};\nstatic COUNTER: AtomicUsize = AtomicUsize::new(0);\nfn incr() { COUNTER.fetch_add(1, Ordering::Relaxed); }\n",[73,23452,23450],{"__ignoreMap":117},[20,23454,23455,23456,23458],{},"Atomics or ",[73,23457,11250],{}," are safe and testable.",[15,23460,23462,23463,23465],{"id":23461},"_7-vecvect-for-matrices","7. ",[73,23464,19963],{}," for Matrices",[111,23467,23470],{"className":23468,"code":23469,"language":397,"meta":117},[395],"\u002F\u002F Smelly: cache-unfriendly\nlet m: Vec\u003CVec\u003Cf32>> = vec![vec![0.0; 100]; 100];\n\n\u002F\u002F Better: flat layout\nlet m: Vec\u003Cf32> = vec![0.0; 100 * 100];\nfn at(m: &[f32], x: usize, y: usize, w: usize) -> f32 { m[y * w + x] }\n",[73,23471,23469],{"__ignoreMap":117},[15,23473,20394,23475,23477],{"id":23474},"_8-vecu8-repeated-reallocations",[73,23476,6904],{}," Repeated Reallocations",[111,23479,23482],{"className":23480,"code":23481,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nlet mut v = Vec::new();\nfor _ in 0..1000 { v.push(0u8); }    \u002F\u002F regrows ~10 times\n\n\u002F\u002F Better\nlet mut v = Vec::with_capacity(1000);\nfor _ in 0..1000 { v.push(0u8); }\n\u002F\u002F or\nlet v = vec![0u8; 1000];\n",[73,23483,23481],{"__ignoreMap":117},[15,23485,23487,23488,23490],{"id":23486},"_9-string-for-static-text","9. ",[73,23489,1197],{}," for Static Text",[111,23492,23495],{"className":23493,"code":23494,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nfn label() -> String { String::from(\"OK\") }\n\n\u002F\u002F Better\nfn label() -> &'static str { \"OK\" }\n",[73,23496,23494],{"__ignoreMap":117},[20,23498,23499,23500,23502,23503,23505],{},"Return ",[73,23501,4577],{}," for compile-time constants; ",[73,23504,1197],{}," only when constructed.",[15,23507,23509],{"id":23508},"_10-indexing-out-of-bounds","10. Indexing Out of Bounds",[111,23511,23514],{"className":23512,"code":23513,"language":397,"meta":117},[395],"\u002F\u002F Panics\nlet v = vec![1, 2, 3];\nlet x = v[5];\n\n\u002F\u002F Safe\nlet x = v.get(5).copied().unwrap_or(0);\n",[73,23515,23513],{"__ignoreMap":117},[20,23517,1876,23518,1212,23520,23523],{},[73,23519,10750],{},[73,23521,23522],{},"get_mut"," when bounds are uncertain.",[15,23525,23527],{"id":23526},"_11-string-indexing-confusion","11. String Indexing Confusion",[111,23529,23532],{"className":23530,"code":23531,"language":397,"meta":117},[395],"let s = \"héllo\";\nlet c = s[0];       \u002F\u002F ERROR: String can't be indexed by integer\nlet b = s.as_bytes()[0];    \u002F\u002F u8, byte\nlet c = s.chars().nth(0);   \u002F\u002F Option\u003Cchar>\n",[73,23533,23531],{"__ignoreMap":117},[20,23535,23536,23537,23540,23541,23544],{},"UTF-8 strings don't support byte indexing semantically. Iterate ",[73,23538,23539],{},"chars()"," for code points, ",[73,23542,23543],{},"bytes()"," for bytes.",[15,23546,23548,23549,23551],{"id":23547},"_12-using-deref-for-inheritance","12. Using ",[73,23550,3782],{}," for Inheritance",[20,23553,23554,23556],{},[73,23555,3782],{}," is for smart pointers, not modeling. Misuse leads to confusing method resolution:",[111,23558,23561],{"className":23559,"code":23560,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nstruct A { \u002F* ... *\u002F }\nstruct B(A);   \u002F\u002F hope to \"inherit\" A's methods\nimpl Deref for B { type Target = A; fn deref(&self) -> &A { &self.0 } }\n\u002F\u002F B.method_of_a() works, but it's misleading\n\n\u002F\u002F Better: explicit delegation\nimpl B {\n    fn method_of_a(&self) { self.0.method_of_a(); }\n}\n",[73,23562,23560],{"__ignoreMap":117},[15,23564,19582,23566],{"id":23565},"_13-unsafe-impl-send-for-rct",[73,23567,15636],{},[20,23569,23570,23572,23573,23575,23576,259],{},[73,23571,3803],{},"'s refcount is non-atomic; making it ",[73,23574,8563],{}," causes data races. Use ",[73,23577,10566],{},[15,23579,20492,23581,23584,23585,23587],{"id":23580},"_14-vecclone-where-rcclone-would-do",[73,23582,23583],{},"Vec::clone()"," Where ",[73,23586,11273],{}," Would Do",[111,23589,23592],{"className":23590,"code":23591,"language":397,"meta":117},[395],"\u002F\u002F Smelly: deep clones the whole vec\nlet v = vec![1, 2, 3];\nlet v2 = v.clone();\n\n\u002F\u002F If sharing read-only data\nlet v = Rc::new(vec![1, 2, 3]);\nlet v2 = Rc::clone(&v);    \u002F\u002F just bumps refcount\n",[73,23593,23591],{"__ignoreMap":117},[15,23595,23597,23598],{"id":23596},"_15-locking-across-await","15. Locking Across ",[73,23599,5022],{},[111,23601,23604],{"className":23602,"code":23603,"language":397,"meta":117},[395],"\u002F\u002F Smelly: holding std::sync::Mutex across await\nlet m = std::sync::Mutex::new(0);\nlet g = m.lock().unwrap();\nsome_async().await;     \u002F\u002F ⚠️ held during await\ndrop(g);\n\n\u002F\u002F Better: drop before await\nlet v = { let g = m.lock().unwrap(); *g };\nsome_async(v).await;\n\n\u002F\u002F Or use tokio's async Mutex\nlet m = tokio::sync::Mutex::new(0);\nlet mut g = m.lock().await;\nsome_async(&mut *g).await;\n",[73,23605,23603],{"__ignoreMap":117},[15,23607,23609,23610,23612],{"id":23608},"_16-forgetting-move-in-async-blocks","16. Forgetting ",[73,23611,9142],{}," in Async Blocks",[111,23614,23617],{"className":23615,"code":23616,"language":397,"meta":117},[395],"let v = vec![1, 2, 3];\nlet f = async { println!(\"{:?}\", v); };   \u002F\u002F borrows v\n\u002F\u002F f must outlive v — if returned\u002Fspawned, error\nlet f = async move { println!(\"{:?}\", v); };   \u002F\u002F moves v\n",[73,23618,23616],{"__ignoreMap":117},[15,23620,23622,23623,559,23625],{"id":23621},"_17-arcclone-vs-cloneclone","17. ",[73,23624,20481],{},[73,23626,11276],{},[20,23628,23629,23632,23633,23636,23637,259],{},[73,23630,23631],{},"Arc::clone(&arc)"," is identical to ",[73,23634,23635],{},"arc.clone()"," but signals \"this is cheap, just refcount\". Use ",[73,23638,20481],{},[15,23640,23642,23643,559,23645,23647],{"id":23641},"_18-if-vs-match-for-two-path","18. ",[73,23644,2125],{},[73,23646,1907],{}," for Two-Path",[20,23649,23650,23653,23654,23656,23657,23659,23660,23662],{},[73,23651,23652],{},"if cond { } else { }"," is fine for booleans; ",[73,23655,1907],{}," is better for enum dispatch. Don't ",[73,23658,2541],{}," when a full ",[73,23661,1907],{}," is clearer.",[15,23664,23666,23667,8014,23669],{"id":23665},"_19-unwrap-on-lock","19. ",[73,23668,10151],{},[73,23670,23671],{},"lock()",[111,23673,23676],{"className":23674,"code":23675,"language":397,"meta":117},[395],"let g = m.lock().unwrap();    \u002F\u002F panics on poison\n",[73,23677,23675],{"__ignoreMap":117},[20,23679,23680,23681,23684],{},"In production, decide a poison policy: ",[73,23682,23683],{},".lock().unwrap_or_else(|e| e.into_inner())"," to recover the data despite a panic.",[15,23686,23688,23689,23691],{"id":23687},"_20-boxdyn-trait-where-generic-works","20. ",[73,23690,8373],{}," Where Generic Works",[111,23693,23696],{"className":23694,"code":23695,"language":397,"meta":117},[395],"\u002F\u002F Smelly: dyn for a single type\nfn process(items: Vec\u003CBox\u003Cdyn Process>>) { \u002F* ... *\u002F }\n\n\u002F\u002F Better: generic, monomorphizes\nfn process\u003CT: Process>(items: Vec\u003CT>) { \u002F* ... *\u002F }\n",[73,23697,23695],{"__ignoreMap":117},[20,23699,23700,23702],{},[73,23701,13814],{}," is for heterogeneous collections or when binary size matters.",[15,23704,23706,23707,8018,23709],{"id":23705},"_21-vecu8-from-read_to_end","21. ",[73,23708,6904],{},[73,23710,20819],{},[20,23712,23713,23714,170],{},"If you know the size, ",[73,23715,20284],{},[111,23717,23720],{"className":23718,"code":23719,"language":397,"meta":117},[395],"let mut v = Vec::with_capacity(1024);\nfile.read_to_end(&mut v)?;\n",[73,23721,23719],{"__ignoreMap":117},[15,23723,23725,23726,23728],{"id":23724},"_22-format-in-hot-loops","22. ",[73,23727,14406],{}," in Hot Loops",[111,23730,23733],{"className":23731,"code":23732,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nfor x in items { log::info!(\"{}\", format!(\"{:?}\", x)); }\n\n\u002F\u002F Better\nfor x in items { log::info!(\"{:?}\", x); }\n",[73,23734,23732],{"__ignoreMap":117},[20,23736,23737,23739,23740,23742],{},[73,23738,14406],{}," allocates. Use ",[73,23741,14409],{}," into a reused buffer if you must build a string in a loop.",[15,23744,23746,23747,23749],{"id":23745},"_23-returning-from-blocks-by-accident","23. Returning ",[73,23748,525],{}," From Blocks by Accident",[111,23751,23754],{"className":23752,"code":23753,"language":397,"meta":117},[395],"\u002F\u002F Bad\nfn foo() -> i32 {\n    let x = 5;\n    x + 1;     \u002F\u002F ; — block returns ()!\n}\n\n\u002F\u002F Good\nfn foo() -> i32 {\n    let x = 5;\n    x + 1       \u002F\u002F no semicolon — returns 6\n}\n",[73,23755,23753],{"__ignoreMap":117},[20,23757,2094,23758,23760,23761,259],{},[73,23759,2090],{}," turns an expression into a statement returning ",[73,23762,525],{},[15,23764,23766,23767,23769,23770,23772],{"id":23765},"_24-match-without-_-when-all-cases-matter","24. ",[73,23768,1907],{}," Without ",[73,23771,1157],{}," When All Cases Matter",[111,23774,23777],{"className":23775,"code":23776,"language":397,"meta":117},[395],"\u002F\u002F Bad: silently breaks when a new variant is added\nmatch color {\n    Color::Red => 1,\n    _ => 0,    \u002F\u002F catches future variants\n}\n\n\u002F\u002F Better (until you've thought about it)\nmatch color {\n    Color::Red => 1,\n    Color::Green => 0,\n    Color::Blue => 0,\n}\n",[73,23778,23776],{"__ignoreMap":117},[20,23780,23781],{},"Let exhaustiveness drive you to handle new variants.",[15,23783,23785,23786,23788],{"id":23784},"_25-mut-you-dont-need","25. ",[73,23787,885],{}," You Don't Need",[111,23790,23793],{"className":23791,"code":23792,"language":397,"meta":117},[395],"let mut x = 5;\nlet y = x + 1;     \u002F\u002F x never mutates — warning: unused mut\n",[73,23794,23792],{"__ignoreMap":117},[20,23796,23797,23798,1141,23800,1145],{},"Remove ",[73,23799,885],{},[73,23801,6219],{},[15,23803,23805,23806],{"id":23804},"_26-unreachable-unreachable","26. Unreachable ",[73,23807,14426],{},[111,23809,23812],{"className":23810,"code":23811,"language":397,"meta":117},[395],"match opt {\n    Some(_) => 1,\n    None => unreachable!(),   \u002F\u002F will panic if someone passes None\n}\n",[73,23813,23811],{"__ignoreMap":117},[20,23815,23816,23817,23819,23820,23822],{},"If the API allows ",[73,23818,1541],{},", handle it. Reserve ",[73,23821,14426],{}," for truly impossible states.",[15,23824,23826,23827,559,23830,559,23833],{"id":23825},"_27-stringfrom-vs-to_string-vs-into","27. ",[73,23828,23829],{},"String::from",[73,23831,23832],{},".to_string()",[73,23834,23835],{},".into()",[20,23837,23838,23839,2559,23841,23844,23845,23847,23848,23850],{},"All three work for ",[73,23840,1197],{},[73,23842,23843],{},"into()"," is shortest, ",[73,23846,20746],{}," reads clearly, ",[73,23849,23829],{}," is explicit. Pick one and be consistent.",[15,23852,23854,23855,23857],{"id":23853},"_28-vect-parameters","28. ",[73,23856,4245],{}," Parameters",[111,23859,23862],{"className":23860,"code":23861,"language":397,"meta":117},[395],"\u002F\u002F Smelly: forces caller to have a Vec\nfn sum(v: &Vec\u003Ci32>) -> i32 { v.iter().sum() }\n\n\u002F\u002F Better: accepts slices, arrays, Vec\nfn sum(v: &[i32]) -> i32 { v.iter().sum() }\n",[73,23863,23861],{"__ignoreMap":117},[15,23865,19862,23867,23857],{"id":23866},"_29-string-parameters",[73,23868,3411],{},[111,23870,23873],{"className":23871,"code":23872,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nfn greet(s: &String) { \u002F* ... *\u002F }\n\n\u002F\u002F Better\nfn greet(s: &str) { \u002F* ... *\u002F }\n\u002F\u002F accepts String, &str, literals\n",[73,23874,23872],{"__ignoreMap":117},[15,23876,23878,23879],{"id":23877},"_30-if-xis_ok-xunwrap","30. ",[73,23880,23881],{},"if x.is_ok() { x.unwrap() }",[111,23883,23886],{"className":23884,"code":23885,"language":397,"meta":117},[395],"\u002F\u002F Smelly\nif let Ok(v) = result { \u002F* use v *\u002F }\n\n\u002F\u002F Idiomatic\nlet v = result?;\n\u002F\u002F or\nmatch result { Ok(v) => \u002F* use v *\u002F, Err(e) => \u002F* handle *\u002F }\n",[73,23887,23885],{"__ignoreMap":117},[15,23889,23891,23892,559,23895],{"id":23890},"_31-vecnone-n-vs-vec0-n","31. ",[73,23893,23894],{},"vec![None; n]",[73,23896,23897],{},"vec![0; n]",[20,23899,23900,23902,23903,23905],{},[73,23901,23894],{}," works but takes 8 bytes\u002Felement on 64-bit. If you have a \"default\" sentinel, use ",[73,23904,23897],{}," for cache efficiency.",[15,23907,23909,23910,23912],{"id":23908},"_32-hashmap-iteration-order","32. ",[73,23911,1687],{}," Iteration Order",[20,23914,23915,23916,23918,23919,23921,23922,23924],{},"Don't depend on iteration order of ",[73,23917,1687],{}," — it's randomized per run. Use ",[73,23920,7114],{}," for ordered, or ",[73,23923,7309],{}," for insertion order.",[15,23926,23928,23929,559,23931],{"id":23927},"_33-stringnew-vs-stringwith_capacity","33. ",[73,23930,20687],{},[73,23932,20281],{},[20,23934,23935,23936,23939],{},"If you'll push N chars, ",[73,23937,23938],{},"with_capacity(N)"," avoids regrow.",[15,23941,23943,23944,559,23947],{"id":23942},"_34-stringfrom_utf8_lossy-vs-from_utf8","34. ",[73,23945,23946],{},"String::from_utf8_lossy",[73,23948,23949],{},"from_utf8",[20,23951,23952,1538,23954,470,23956,23958,23959,23961,23962,23964],{},[73,23953,23949],{},[73,23955,2792],{},[73,23957,20834],{}," always returns a ",[73,23960,11369],{}," with replacement chars. Use ",[73,23963,23949],{}," if invalid UTF-8 is an error.",[15,23966,23968,23969,23971],{"id":23967},"_35-forgetting-must_use-types","35. Forgetting ",[73,23970,17048],{}," Types",[20,23973,23974,480,23976,23978],{},[73,23975,2792],{},[73,23977,1481],{}," warn by default. For your types:",[111,23980,23983],{"className":23981,"code":23982,"language":397,"meta":117},[395],"#[must_use]\npub struct Handle { \u002F* ... *\u002F }\n",[73,23984,23982],{"__ignoreMap":117},[15,23986,23988,23989,23991],{"id":23987},"_36-as-casts","36. ",[73,23990,1570],{}," Casts",[20,23993,23994,23996,23997,1212,23999,170],{},[73,23995,1570],{}," is unchecked and may truncate. Use ",[73,23998,1885],{},[73,24000,1888],{},[111,24002,24005],{"className":24003,"code":24004,"language":397,"meta":117},[395],"\u002F\u002F Risky\nlet n: u8 = 1000u32 as u8;     \u002F\u002F 232, silently\n\n\u002F\u002F Safe\nlet n: u8 = 1000u32.try_into().unwrap_or(u8::MAX);\n",[73,24006,24004],{"__ignoreMap":117},[15,24008,24010,24011,24013,24014],{"id":24009},"_37-cargo-build-in-ci-without-locked","37. ",[73,24012,16392],{}," in CI Without ",[73,24015,24016],{},"--locked",[111,24018,24021],{"className":24019,"code":24020,"language":18110,"meta":117},[18108],"- run: cargo build --locked --release\n",[73,24022,24020],{"__ignoreMap":117},[20,24024,24025,24027,24028,24030],{},[73,24026,24016],{}," ensures ",[73,24029,245],{}," is honored (reproducible builds).",[15,24032,24034],{"id":24033},"_38-ignoring-clippy","38. Ignoring Clippy",[20,24036,24037],{},"Treat Clippy warnings as errors in CI:",[111,24039,24042],{"className":24040,"code":24041,"language":18110,"meta":117},[18108],"- run: cargo clippy --all-targets -- -D warnings\n",[73,24043,24041],{"__ignoreMap":117},[20,24045,24046,24047,480,24050,526],{},"Many lints catch real bugs (e.g., ",[73,24048,24049],{},"clippy::needless_collect",[73,24051,24052],{},"clippy::mem_forget",[15,24054,24056],{"id":24055},"_39-doc-tests-breaking-on-rust-version","39. Doc Tests Breaking on Rust Version",[20,24058,24059,24060,24063],{},"Pin a MSRV; CI runs ",[73,24061,24062],{},"cargo +1.75 test"," to catch regressions.",[15,24065,24067,24068,24070],{"id":24066},"_40-arcmutext-for-single-threaded-code","40. ",[73,24069,11250],{}," for Single-Threaded Code",[20,24072,24073,24074,24077],{},"If you're not actually going multi-threaded, plain ",[73,24075,24076],{},"Rc\u003CRefCell\u003CT>>"," is cheaper. Match the abstraction to the actual concurrency.",[15,24079,349],{"id":348},[33,24081,24082,24085,24091,24101,24106,24111,24117,24125,24133],{},[36,24083,24084],{},"Borrow, don't clone, when you don't need ownership.",[36,24086,24087,11526,24089,259],{},[73,24088,2404],{},[73,24090,10168],{},[36,24092,24093,1212,24095,11526,24097,1212,24099,259],{},[73,24094,1630],{},[73,24096,1739],{},[73,24098,3411],{},[73,24100,4245],{},[36,24102,24103,24104,259],{},"Don't lock across ",[73,24105,5022],{},[36,24107,20406,24108,24110],{},[73,24109,3782],{}," for inheritance.",[36,24112,24113,24114,24116],{},"Generic over ",[73,24115,13814],{}," for hot paths.",[36,24118,24119,24121,24122,24124],{},[73,24120,1907],{}," exhaustively; ",[73,24123,1157],{}," only when you've considered every variant.",[36,24126,24127,24129,24130,259],{},[73,24128,24016],{}," in CI; clippy with ",[73,24131,24132],{},"-D warnings",[36,24134,24135,24136,24138,24139,259],{},"Document ",[73,24137,15337],{}," in unsafe code; use ",[73,24140,15746],{},[20,24142,24143],{},"Next: Final exam-style questions and project ideas.",{"title":117,"searchDepth":357,"depth":357,"links":24145},[24146,24152,24157,24159,24161,24163,24164,24166,24168,24170,24171,24172,24174,24176,24178,24180,24182,24184,24186,24188,24190,24192,24194,24196,24198,24200,24202,24204,24206,24208,24210,24212,24214,24216,24218,24220,24222,24224,24225,24226,24228],{"id":23281,"depth":357,"text":23282,"children":24147},[24148,24149,24150,24151],{"id":23285,"depth":364,"text":23286},{"id":23295,"depth":364,"text":23296},{"id":23311,"depth":364,"text":23312},{"id":23324,"depth":364,"text":23325},{"id":23331,"depth":357,"text":24153,"children":24154},"2. move Closure Footguns",[24155,24156],{"id":23338,"depth":364,"text":23339},{"id":23354,"depth":364,"text":23355},{"id":23364,"depth":357,"text":24158},"3. clone() Everywhere",{"id":23390,"depth":357,"text":24160},"4. unwrap()\u002Fexpect() in Production",{"id":23423,"depth":357,"text":24162},"5. Treating Result Like Exceptions",{"id":23445,"depth":357,"text":23446},{"id":23461,"depth":357,"text":24165},"7. Vec\u003CVec\u003CT>> for Matrices",{"id":23474,"depth":357,"text":24167},"8. Vec\u003Cu8> Repeated Reallocations",{"id":23486,"depth":357,"text":24169},"9. String for Static Text",{"id":23508,"depth":357,"text":23509},{"id":23526,"depth":357,"text":23527},{"id":23547,"depth":357,"text":24173},"12. Using Deref for Inheritance",{"id":23565,"depth":357,"text":24175},"13. unsafe impl Send for Rc\u003CT>",{"id":23580,"depth":357,"text":24177},"14. Vec::clone() Where Rc::clone Would Do",{"id":23596,"depth":357,"text":24179},"15. Locking Across .await",{"id":23608,"depth":357,"text":24181},"16. Forgetting move in Async Blocks",{"id":23621,"depth":357,"text":24183},"17. Arc::clone vs Clone::clone",{"id":23641,"depth":357,"text":24185},"18. if vs match for Two-Path",{"id":23665,"depth":357,"text":24187},"19. unwrap() on lock()",{"id":23687,"depth":357,"text":24189},"20. Box\u003Cdyn Trait> Where Generic Works",{"id":23705,"depth":357,"text":24191},"21. Vec\u003Cu8> from read_to_end",{"id":23724,"depth":357,"text":24193},"22. format! in Hot Loops",{"id":23745,"depth":357,"text":24195},"23. Returning () From Blocks by Accident",{"id":23765,"depth":357,"text":24197},"24. match Without _ When All Cases Matter",{"id":23784,"depth":357,"text":24199},"25. mut You Don't Need",{"id":23804,"depth":357,"text":24201},"26. Unreachable unreachable!",{"id":23825,"depth":357,"text":24203},"27. String::from vs .to_string() vs .into()",{"id":23853,"depth":357,"text":24205},"28. &Vec\u003CT> Parameters",{"id":23866,"depth":357,"text":24207},"29. &String Parameters",{"id":23877,"depth":357,"text":24209},"30. if x.is_ok() { x.unwrap() }",{"id":23890,"depth":357,"text":24211},"31. vec![None; n] vs vec![0; n]",{"id":23908,"depth":357,"text":24213},"32. HashMap Iteration Order",{"id":23927,"depth":357,"text":24215},"33. String::new() vs String::with_capacity",{"id":23942,"depth":357,"text":24217},"34. String::from_utf8_lossy vs from_utf8",{"id":23967,"depth":357,"text":24219},"35. Forgetting #[must_use] Types",{"id":23987,"depth":357,"text":24221},"36. as Casts",{"id":24009,"depth":357,"text":24223},"37. cargo build in CI Without --locked",{"id":24033,"depth":357,"text":24034},{"id":24055,"depth":357,"text":24056},{"id":24066,"depth":357,"text":24227},"40. Arc\u003CMutex\u003CT>> for Single-Threaded Code",{"id":348,"depth":357,"text":349},{},"\u002Frust\u002F34-common-pitfalls",{"title":23270,"description":23278},"rust\u002F34-common-pitfalls","TKScxnfP3zVcEaj0yyoqKOMFQ3pMScOWjvhmgIUxc6M",{"id":24235,"title":24236,"body":24237,"description":24244,"extension":373,"meta":25103,"navigation":375,"path":25104,"seo":25105,"stem":25106,"__hash__":25107},"content\u002Frust\u002F35-exercises-and-projects.md","35 — Mastery Exercises & Project Ideas",{"type":8,"value":24238,"toc":25067},[24239,24242,24245,24249,24252,24272,24276,24280,24286,24290,24296,24308,24312,24332,24336,24352,24356,24368,24372,24376,24384,24388,24398,24402,24408,24415,24421,24433,24437,24450,24454,24458,24461,24467,24473,24477,24482,24486,24495,24499,24506,24510,24518,24522,24539,24543,24547,24602,24606,24674,24678,24773,24777,24780,24834,24846,24850,24882,24886,24917,24921,24924,25036,25039,25043,25046,25061,25064],[11,24240,24236],{"id":24241},"_35-mastery-exercises-project-ideas",[20,24243,24244],{},"Reading is the first step; building is where you become a pro. This chapter gives you exercises and projects calibrated to internalize everything.",[15,24246,24248],{"id":24247},"how-to-use-this-chapter","How to Use This Chapter",[20,24250,24251],{},"Each section has:",[33,24253,24254,24260,24266],{},[36,24255,24256,24259],{},[24,24257,24258],{},"Exercises",": small, focused tasks to test specific concepts.",[36,24261,24262,24265],{},[24,24263,24264],{},"Projects",": end-to-end builds.",[36,24267,24268,24271],{},[24,24269,24270],{},"Solutions",": don't look until you've tried for 30 minutes.",[15,24273,24275],{"id":24274},"beginner-exercises","Beginner Exercises",[130,24277,24279],{"id":24278},"_1-fizzbuzz-with-iterators","1. FizzBuzz with Iterators",[20,24281,24282,24283,259],{},"Implement FizzBuzz for 1..=100 using iterators and ",[73,24284,24285],{},"collect::\u003CVec\u003CString>>()",[130,24287,24289],{"id":24288},"_2-stack-with-generics","2. Stack with Generics",[111,24291,24294],{"className":24292,"code":24293,"language":397,"meta":117},[395],"struct Stack\u003CT> { \u002F* ... *\u002F }\nimpl\u003CT> Stack\u003CT> {\n    fn new() -> Self;\n    fn push(&mut self, v: T);\n    fn pop(&mut self) -> Option\u003CT>;\n    fn peek(&self) -> Option\u003C&T>;\n    fn len(&self) -> usize;\n    fn is_empty(&self) -> bool;\n}\n",[73,24295,24293],{"__ignoreMap":117},[20,24297,1876,24298,24300,24301,24303,24304,8971,24306,259],{},[73,24299,4930],{}," internally. Implement ",[73,24302,189],{}," for owned, ",[73,24305,2710],{},[73,24307,3850],{},[130,24309,24311],{"id":24310},"_3-result-combinators","3. Result Combinators",[20,24313,4943,24314,24317,24318,27,24320,1212,24323,24326,24327,2022,24330,259],{},[73,24315,24316],{},"parse_csv(s: &str) -> Result\u003CVec\u003CVec\u003Ci32>>, AppError>"," using ",[73,24319,2404],{},[73,24321,24322],{},"split",[73,24324,24325],{},"parse",". Define ",[73,24328,24329],{},"AppError",[73,24331,9900],{},[130,24333,24335],{"id":24334},"_4-linked-list-the-hard-way","4. Linked List (the Hard Way)",[20,24337,24338,24339,24342,24343,480,24346,480,24349,259],{},"Implement a singly linked list using ",[73,24340,24341],{},"Box\u003CNode\u003CT>>",". Then implement ",[73,24344,24345],{},"Iter",[73,24347,24348],{},"IterMut",[73,24350,24351],{},"IntoIter",[130,24353,24355],{"id":24354},"_5-cli-calculator","5. CLI Calculator",[20,24357,4239,24358,1546,24361,24364,24365,24367],{},[73,24359,24360],{},"+ 2 3",[73,24362,24363],{},"* 4 5"," from argv, print the result. Use ",[73,24366,21932],{}," if you want a challenge.",[15,24369,24371],{"id":24370},"intermediate-exercises","Intermediate Exercises",[130,24373,24375],{"id":24374},"_6-json-parser","6. JSON Parser",[20,24377,24378,24379,1546,24381,24383],{},"Hand-write a JSON parser using ",[73,24380,22920],{},[73,24382,22926],{},". Cover objects, arrays, strings (with escapes), numbers, booleans, null.",[130,24385,24387],{"id":24386},"_7-async-file-watcher","7. Async File Watcher",[20,24389,24390,24391,24394,24395,24397],{},"Watch a directory for changes using ",[73,24392,24393],{},"notify"," (crate), print events. Use ",[73,24396,13649],{}," with a Ctrl-C handler for clean shutdown.",[130,24399,24401],{"id":24400},"_8-concurrent-counter-with-channels","8. Concurrent Counter with Channels",[20,24403,24404,24405,24407],{},"10 threads, each sending numbers to a single aggregator thread via ",[73,24406,13198],{},". Print the final sum.",[130,24409,24411,24412,24414],{"id":24410},"_9-custom-iterator-for-fibonacci","9. Custom ",[73,24413,7540],{}," for Fibonacci",[111,24416,24419],{"className":24417,"code":24418,"language":397,"meta":117},[395],"struct Fib { a: u64, b: u64 }\nimpl Iterator for Fib {\n    type Item = u64;\n    fn next(&mut self) -> Option\u003Cu64> { \u002F* ... *\u002F }\n}\n",[73,24420,24418],{"__ignoreMap":117},[20,24422,18115,24423,480,24426,480,24429,24432],{},[73,24424,24425],{},".map",[73,24427,24428],{},".filter",[73,24430,24431],{},".take"," chains.",[130,24434,24436],{"id":24435},"_10-type-state-builder","10. Type-State Builder",[20,24438,1114,24439,24442,24443,480,24446,24449],{},[73,24440,24441],{},"RequestBuilder\u003CMethod, Path, Body>"," with states ",[73,24444,24445],{},"Unset",[73,24447,24448],{},"Set",". Methods only available in valid states.",[15,24451,24453],{"id":24452},"advanced-exercises","Advanced Exercises",[130,24455,24457],{"id":24456},"_11-custom-trait-object","11. Custom Trait Object",[20,24459,24460],{},"Implement your own dispatch table:",[111,24462,24465],{"className":24463,"code":24464,"language":397,"meta":117},[395],"struct VTable { size: usize, drop: unsafe fn(*mut u8), display: unsafe fn(*const u8, &mut Formatter) -> Result }\n",[73,24466,24464],{"__ignoreMap":117},[20,24468,24469,24470,259],{},"Compare with ",[73,24471,24472],{},"Box\u003Cdyn Display>",[130,24474,24476],{"id":24475},"_12-lock-free-queue","12. Lock-Free Queue",[20,24478,24479,24480,259],{},"Implement a bounded MPSC queue using atomics. Benchmark against ",[73,24481,12866],{},[130,24483,24485],{"id":24484},"_13-memory-pool","13. Memory Pool",[20,24487,24488,24489,24491,24492,24494],{},"Implement an arena allocator (bump allocation). Use ",[73,24490,22733],{}," as a reference. Add ",[73,24493,3217],{}," to free the arena.",[130,24496,24498],{"id":24497},"_14-async-stream","14. Async Stream",[20,24500,24501,24502,24505],{},"Implement a ",[73,24503,24504],{},"Stream"," that yields lines from a file asynchronously, with backpressure.",[130,24507,24509],{"id":24508},"_15-custom-allocator","15. Custom Allocator",[20,24511,24501,24512,24515,24516,259],{},[73,24513,24514],{},"GlobalAlloc"," that tracks allocations and prints them. Use it as ",[73,24517,17269],{},[130,24519,24521],{"id":24520},"_16-ffi-wrapper","16. FFI Wrapper",[20,24523,24524,24525,15855,24528,24530,24531,1212,24533,24536,24537,259],{},"Wrap ",[73,24526,24527],{},"sqlite3",[73,24529,15460],{},". Expose a safe ",[73,24532,21848],{},[73,24534,24535],{},"Statement"," API with ",[73,24538,3217],{},[15,24540,24542],{"id":24541},"project-ideas","Project Ideas",[130,24544,24546],{"id":24545},"beginner","Beginner",[3037,24548,24549,24570,24582,24592],{},[36,24550,24551,71,24554,480,24556,480,24559,480,24562,24565,24566,9593,24568,259],{},[24,24552,24553],{},"CLI todo app",[73,24555,15947],{},[73,24557,24558],{},"list",[73,24560,24561],{},"done",[73,24563,24564],{},"remove",". JSON storage. Use ",[73,24567,21932],{},[73,24569,14735],{},[36,24571,24572,24575,24576,9593,24578,1546,24580,259],{},[24,24573,24574],{},"HTTP file server",": serve a directory; ",[73,24577,13065],{},[73,24579,13610],{},[73,24581,13622],{},[36,24583,24584,24587,24588,1212,24590,259],{},[24,24585,24586],{},"Markdown to HTML",": minimal converter; learn ",[73,24589,22920],{},[73,24591,22926],{},[36,24593,24594,24597,24598,1212,24600,259],{},[24,24595,24596],{},"Word frequency counter",": from a file, print top 10. Use ",[73,24599,1687],{},[73,24601,7114],{},[130,24603,24605],{"id":24604},"intermediate","Intermediate",[3037,24607,24609,24618,24631,24641,24655,24666],{"start":24608},5,[36,24610,24611,24614,24615,24617],{},[24,24612,24613],{},"Chat server",": TCP + ",[73,24616,13649],{},". Broadcast to all connected clients.",[36,24619,24620,71,24623,9593,24625,1212,24627,24630],{},[24,24621,24622],{},"URL shortener",[73,24624,13622],{},[73,24626,21886],{},[73,24628,24629],{},"DashMap",". Persistent storage.",[36,24632,24633,24636,24637,9593,24639,259],{},[24,24634,24635],{},"Mini Redis",": implement a subset of Redis protocol. ",[73,24638,13065],{},[73,24640,22299],{},[36,24642,24643,71,24646,9593,24648,1212,24651,24654],{},[24,24644,24645],{},"Web scraper",[73,24647,13604],{},[73,24649,24650],{},"select",[73,24652,24653],{},"scraper",". Polite rate limiting.",[36,24656,24657,71,24660,24663,24664,259],{},[24,24658,24659],{},"Image processor",[73,24661,24662],{},"image"," crate, batch resize with ",[73,24665,13061],{},[36,24667,24668,24671,24672,259],{},[24,24669,24670],{},"Database migration tool",": schema versioning, SQL execution via ",[73,24673,13616],{},[130,24675,24677],{"id":24676},"advanced","Advanced",[3037,24679,24681,24690,24699,24708,24714,24729,24735,24745,24751,24762],{"start":24680},11,[36,24682,24683,24686,24687,24689],{},[24,24684,24685],{},"Async ORM",": derive macro for table mapping; ",[73,24688,13616],{},"; compile-time queries.",[36,24691,24692,24695,24696,24698],{},[24,24693,24694],{},"Game engine",": ECS with ",[73,24697,21436],{},"; render sprites; basic physics.",[36,24700,24701,71,24704,24707],{},[24,24702,24703],{},"TLS proxy",[73,24705,24706],{},"tokio-rustls","; terminate TLS and forward plain TCP.",[36,24709,24710,24713],{},[24,24711,24712],{},"Bittorrent client",": piece assembly, peer protocol, async I\u002FO.",[36,24715,24716,71,24719,470,24721,24724,24725,24728],{},[24,24717,24718],{},"Operating system kernel",[73,24720,13435],{},[73,24722,24723],{}," bootloader","; serial driver; minimal shell. (",[73,24726,24727],{},"blog_os"," tutorial.)",[36,24730,24731,24734],{},[24,24732,24733],{},"Database engine",": B+tree storage, WAL, MVCC. Hard but illuminating.",[36,24736,24737,24740,24741,24744],{},[24,24738,24739],{},"Compiler",": parse a small language; lower to LLVM IR via ",[73,24742,24743],{},"inkwell"," or to Cranelift.",[36,24746,24747,24750],{},[24,24748,24749],{},"Static site generator",": markdown → HTML, templates, RSS, syntax highlighting.",[36,24752,24753,24756,24757,480,24759,12138],{},[24,24754,24755],{},"WASM image editor",": client-side, ",[73,24758,16295],{},[73,24760,24761],{},"Canvas",[36,24763,24764,24767,24768,24770,24771,259],{},[24,24765,24766],{},"Realtime multiplayer game server",": WebSocket + ",[73,24769,13622],{},"; per-room state with ",[73,24772,24629],{},[15,24774,24776],{"id":24775},"reading-code-to-mastery","Reading Code to Mastery",[20,24778,24779],{},"Read source of:",[33,24781,24782,24787,24792,24800,24807,24814,24819,24824,24829],{},[36,24783,24784,24786],{},[73,24785,17302],{}," (slice\u002Fiter\u002Fvec modules).",[36,24788,24789,24791],{},[73,24790,13065],{}," (scheduler, channels).",[36,24793,24794,24796,24797,526],{},[73,24795,14735],{}," (derive macros — ",[73,24798,24799],{},"serde_derive",[36,24801,24802,24804,24805,526],{},[73,24803,13622],{}," (routing, middleware via ",[73,24806,21740],{},[36,24808,24809,24811,24812,526],{},[73,24810,13604],{}," (HTTP client on ",[73,24813,13610],{},[36,24815,24816,24818],{},[73,24817,13616],{}," (compile-time SQL checking via macros).",[36,24820,24821,24823],{},[73,24822,22653],{}," (epoch-based memory reclamation).",[36,24825,24826,24828],{},[73,24827,21436],{}," (ECS, scheduling, renderer).",[36,24830,24831,24833],{},[73,24832,22551],{}," (DFA construction, Unicode tables).",[20,24835,24836,24837,480,24840,480,24843,259],{},"The standard library is the best Rust code you can read. Start with ",[73,24838,24839],{},"alloc::vec",[73,24841,24842],{},"core::iter",[73,24844,24845],{},"core::slice",[15,24847,24849],{"id":24848},"practice-sites","Practice Sites",[33,24851,24852,24858,24864,24870,24876],{},[36,24853,24854,24857],{},[24,24855,24856],{},"Rustlings",": small exercises for each concept.",[36,24859,24860,24863],{},[24,24861,24862],{},"Exercism Rust track",": mentor-reviewed exercises.",[36,24865,24866,24869],{},[24,24867,24868],{},"Advent of Code",": annual puzzles, perfect for Rust.",[36,24871,24872,24875],{},[24,24873,24874],{},"LeetCode Rust",": algos.",[36,24877,24878,24881],{},[24,24879,24880],{},"Rosetta Code Rust",": idiomatic translations.",[15,24883,24885],{"id":24884},"open-source-contribution","Open Source Contribution",[33,24887,24888,24893,24899,24905,24911],{},[36,24889,24890,24892],{},[73,24891,21431],{},": good-first-issue labels; tough but rewarding.",[36,24894,24895,24898],{},[73,24896,24897],{},"tokio-rs\u002F*",": tokio ecosystem.",[36,24900,24901,24904],{},[73,24902,24903],{},"serde-rs\u002F*",": serde, serde_json.",[36,24906,24907,24910],{},[73,24908,24909],{},"bevyengine\u002Fbevy",": game engine.",[36,24912,24913,24916],{},[73,24914,24915],{},"rust-cli\u002F*",": CLI tool templates.",[15,24918,24920],{"id":24919},"mastery-self-check","Mastery Self-Check",[20,24922,24923],{},"Can you confidently:",[33,24925,24926,24929,24938,24945,24953,24960,24969,24999,25013,25019,25030],{},[36,24927,24928],{},"Explain ownership without using the word \"borrow\"? (Use \"one owner, moved on assignment, dropped on scope-end\".)",[36,24930,24931,24932,24934,24935,24937],{},"Predict whether ",[73,24933,4946],{}," is required for a thread closure? (Yes if it borrows stack data — use ",[73,24936,9142],{},".)",[36,24939,24940,24941,24944],{},"Read ",[73,24942,24943],{},"for\u003C'a> Fn(&'a str) -> &'a str"," and explain it? (HRTB; the closure works for any lifetime.)",[36,24946,24947,24948,27,24950,24952],{},"Implement a trait for both ",[73,24949,3130],{},[73,24951,1117],{},"? (Avoid; usually one suffices.)",[36,24954,7549,24955,24957,24958,24937],{},[73,24956,7540],{}," for your own type? (Just ",[73,24959,7552],{},[36,24961,24962,24963,24965,24966,24968],{},"Explain why ",[73,24964,1117],{}," is invariant in ",[73,24967,4705],{},"? (Otherwise you could swap in shorter-lived data.)",[36,24970,24971,24972,480,24974,480,24976,480,24978,480,24980,24982,24983,24985,24986,470,24988,24990,24991,24993,24994,24996,24997,24937],{},"Choose between ",[73,24973,3803],{},[73,24975,10566],{},[73,24977,5449],{},[73,24979,5452],{},[73,24981,10713],{},"? (Single-thread shared\u002Fclone: ",[73,24984,3803],{},"; multi-thread: ",[73,24987,10566],{},[73,24989,1795],{}," interior: ",[73,24992,5449],{},"; mut interior single-thread: ",[73,24995,5452],{},"; mut interior multi-thread: ",[73,24998,10713],{},[36,25000,25001,25002,25005,25006,25009,25010,25012],{},"Read a ",[73,25003,25004],{},"Pin\u003C&mut T>"," and know what ",[73,25007,25008],{},"!Unpin"," implies? (Can't safely move the ",[73,25011,4705],{}," after pinning.)",[36,25014,25015,25016,25018],{},"Reason about variance of your custom smart pointer? (Pick ",[73,25017,18905],{}," accordingly.)",[36,25020,25021,25022,25025,25026,1212,25028,24937],{},"Write a ",[73,25023,25024],{},"proc_macro_derive","? (Use ",[73,25027,14564],{},[73,25029,14567],{},[36,25031,24940,25032,25035],{},[73,25033,25034],{},"unsafe { *ptr }"," and verify the safety invariants? (Alignment, initialization, aliasing, lifetime.)",[20,25037,25038],{},"If you can do all of the above without consulting docs, you're a pro Rust developer.",[15,25040,25042],{"id":25041},"final-words","Final Words",[20,25044,25045],{},"Rust has a steep learning curve but pays dividends. The compiler is strict but kind: errors are messages, not crashes. Once you internalize ownership, lifetimes, and the trait system, the rest is vocabulary.",[20,25047,25048,25049,25051,25052,25054,25055,25057,25058,25060],{},"Build things. Break things. Read the stdlib. Read ",[73,25050,13065],{},". Run ",[73,25053,14721],{}," on macros. Run ",[73,25056,15746],{}," on unsafe. Profile with ",[73,25059,17984],{},". Contribute to open source.",[20,25062,25063],{},"Welcome to being a Rust developer.",[20,25065,25066],{},"🦀",{"title":117,"searchDepth":357,"depth":357,"links":25068},[25069,25070,25077,25085,25093,25098,25099,25100,25101,25102],{"id":24247,"depth":357,"text":24248},{"id":24274,"depth":357,"text":24275,"children":25071},[25072,25073,25074,25075,25076],{"id":24278,"depth":364,"text":24279},{"id":24288,"depth":364,"text":24289},{"id":24310,"depth":364,"text":24311},{"id":24334,"depth":364,"text":24335},{"id":24354,"depth":364,"text":24355},{"id":24370,"depth":357,"text":24371,"children":25078},[25079,25080,25081,25082,25084],{"id":24374,"depth":364,"text":24375},{"id":24386,"depth":364,"text":24387},{"id":24400,"depth":364,"text":24401},{"id":24410,"depth":364,"text":25083},"9. Custom Iterator for Fibonacci",{"id":24435,"depth":364,"text":24436},{"id":24452,"depth":357,"text":24453,"children":25086},[25087,25088,25089,25090,25091,25092],{"id":24456,"depth":364,"text":24457},{"id":24475,"depth":364,"text":24476},{"id":24484,"depth":364,"text":24485},{"id":24497,"depth":364,"text":24498},{"id":24508,"depth":364,"text":24509},{"id":24520,"depth":364,"text":24521},{"id":24541,"depth":357,"text":24542,"children":25094},[25095,25096,25097],{"id":24545,"depth":364,"text":24546},{"id":24604,"depth":364,"text":24605},{"id":24676,"depth":364,"text":24677},{"id":24775,"depth":357,"text":24776},{"id":24848,"depth":357,"text":24849},{"id":24884,"depth":357,"text":24885},{"id":24919,"depth":357,"text":24920},{"id":25041,"depth":357,"text":25042},{},"\u002Frust\u002F35-exercises-and-projects",{"title":24236,"description":24244},"rust\u002F35-exercises-and-projects","lqb6QDZjuetiSNAMTAgUbwdOthLvyC7qdvu0AOBn13g",{"id":25109,"title":25110,"body":25111,"description":26054,"extension":373,"meta":26055,"navigation":375,"path":26056,"seo":26057,"stem":26058,"__hash__":26059},"content\u002Frust\u002Findex.md","Learn Rust — From Zero to Pro",{"type":8,"value":25112,"toc":26031},[25113,25117,25120,25124,25152,25156,25172,25176,25180,25292,25296,25378,25382,25461,25465,25552,25556,25627,25631,25680,25684,25746,25750,25892,25896,25900,25914,25918,25921,25925,25937,25941,25944,25948,26012,26016,26022,26026,26029],[11,25114,25116],{"id":25115},"learn-rust-from-zero-to-pro","🦀 Learn Rust — From Zero to Pro",[20,25118,25119],{},"A comprehensive, edge-case-covering, idiomatic Rust curriculum. Each document is self-contained and covers its concept deeply enough that a careful reader can go from beginner to pro Rust developer.",[15,25121,25123],{"id":25122},"how-to-use-this-course","How to Use This Course",[3037,25125,25126,25132,25138,25144],{},[36,25127,25128,25131],{},[24,25129,25130],{},"Read sequentially"," for a structured path (01 → 35).",[36,25133,25134,25137],{},[24,25135,25136],{},"Jump to a chapter"," as a reference when you hit a concept in the wild.",[36,25139,25140,25143],{},[24,25141,25142],{},"Run the exercises"," in chapter 35 after every few chapters.",[36,25145,25146,25151],{},[24,25147,25148,25149],{},"Read the source of std and ",[73,25150,13065],{}," alongside.",[15,25153,25155],{"id":25154},"prerequisites","Prerequisites",[33,25157,25158,25163,25169],{},[36,25159,25160,25161,526],{},"A working Rust toolchain (",[73,25162,108],{},[36,25164,25165,25166,25168],{},"A code editor (VS Code + ",[73,25167,91],{}," recommended).",[36,25170,25171],{},"Comfort with at least one other programming language.",[15,25173,25175],{"id":25174},"curriculum","Curriculum",[130,25177,25179],{"id":25178},"part-i-foundations","Part I — Foundations",[917,25181,25182,25194],{},[920,25183,25184],{},[923,25185,25186,25188,25191],{},[926,25187,12188],{},[926,25189,25190],{},"Topic",[926,25192,25193],{},"Why It Matters",[936,25195,25196,25210,25225,25242,25255,25269],{},[923,25197,25198,25201,25207],{},[941,25199,25200],{},"01",[941,25202,25203],{},[25204,25205,25206],"a",{"href":376},"Introduction & Setup",[941,25208,25209],{},"Toolchain, cargo, project layout.",[923,25211,25212,25215,25220],{},[941,25213,25214],{},"02",[941,25216,25217],{},[25204,25218,25219],{"href":851},"Hello World & Cargo Deep Dive",[941,25221,25222,25224],{},[73,25223,169],{},", dependencies, workspaces.",[923,25226,25227,25230,25235],{},[941,25228,25229],{},"03",[941,25231,25232],{},[25204,25233,25234],{"href":1312},"Variables & Mutability",[941,25236,25237,25238,480,25240,259],{},"Immutability, shadowing, ",[73,25239,992],{},[73,25241,1207],{},[923,25243,25244,25247,25252],{},[941,25245,25246],{},"04",[941,25248,25249],{},[25204,25250,25251],{"href":2061},"Data Types",[941,25253,25254],{},"Integers, floats, char, tuples, arrays, casts.",[923,25256,25257,25260,25264],{},[941,25258,25259],{},"05",[941,25261,25262],{},[25204,25263,11653],{"href":2487},[941,25265,25266,25267,259],{},"Statements vs expressions, divergence, ",[73,25268,2215],{},[923,25270,25271,25274,25279],{},[941,25272,25273],{},"06",[941,25275,25276],{},[25204,25277,25278],{"href":3013},"Control Flow",[941,25280,25281,1212,25283,1212,25285,1212,25287,1212,25289,25291],{},[73,25282,2125],{},[73,25284,2594],{},[73,25286,2614],{},[73,25288,2130],{},[73,25290,1907],{}," as expressions.",[130,25293,25295],{"id":25294},"part-ii-ownership-borrowing-the-heart-of-rust","Part II — Ownership & Borrowing (The Heart of Rust)",[917,25297,25298,25308],{},[920,25299,25300],{},[923,25301,25302,25304,25306],{},[926,25303,12188],{},[926,25305,25190],{},[926,25307,25193],{},[936,25309,25310,25326,25343,25360],{},[923,25311,25312,25315,25319],{},[941,25313,25314],{},"07",[941,25316,25317],{},[25204,25318,3391],{"href":3505},[941,25320,25321,25322,480,25324,259],{},"Move semantics, ",[73,25323,3217],{},[73,25325,1795],{},[923,25327,25328,25331,25336],{},[941,25329,25330],{},"08",[941,25332,25333],{},[25204,25334,25335],{"href":3998},"References & Borrowing",[941,25337,25338,1212,25340,25342],{},[73,25339,3130],{},[73,25341,1117],{},", NLL, borrow rules.",[923,25344,25345,25348,25353],{},[941,25346,25347],{},"09",[941,25349,25350],{},[25204,25351,25352],{"href":4431},"Slices",[941,25354,25355,480,25357,25359],{},[73,25356,1739],{},[73,25358,1630],{},", fat pointers.",[923,25361,25362,25365,25370],{},[941,25363,25364],{},"10",[941,25366,25367],{},[25204,25368,25369],{"href":5070},"Lifetimes",[941,25371,25372,25374,25375,25377],{},[73,25373,4499],{},", elision, ",[73,25376,4560],{},", variance.",[130,25379,25381],{"id":25380},"part-iii-modeling-data","Part III — Modeling Data",[917,25383,25384,25394],{},[920,25385,25386],{},[923,25387,25388,25390,25392],{},[926,25389,12188],{},[926,25391,25190],{},[926,25393,25193],{},[936,25395,25396,25412,25430,25443],{},[923,25397,25398,25401,25406],{},[941,25399,25400],{},"11",[941,25402,25403],{},[25204,25404,25405],{"href":5607},"Structs",[941,25407,25408,25409,25411],{},"Named\u002Ftuple\u002Funit, ",[73,25410,2215],{},", derives.",[923,25413,25414,25417,25422],{},[941,25415,25416],{},"12",[941,25418,25419],{},[25204,25420,25421],{"href":6125},"Enums",[941,25423,25424,25425,480,25427,25429],{},"ADTs, ",[73,25426,1481],{},[73,25428,2792],{},", niche optimization.",[923,25431,25432,25435,25440],{},[941,25433,25434],{},"13",[941,25436,25437],{},[25204,25438,25439],{"href":6678},"Pattern Matching",[941,25441,25442],{},"All pattern forms, binding modes, guards.",[923,25444,25445,25448,25452],{},[941,25446,25447],{},"14",[941,25449,25450],{},[25204,25451,22696],{"href":7513},[941,25453,25454,480,25456,480,25458,25460],{},[73,25455,1194],{},[73,25457,1197],{},[73,25459,1687],{},", and friends.",[130,25462,25464],{"id":25463},"part-iv-abstraction-reuse","Part IV — Abstraction & Reuse",[917,25466,25467,25477],{},[920,25468,25469],{},[923,25470,25471,25473,25475],{},[926,25472,12188],{},[926,25474,25190],{},[926,25476,25193],{},[936,25478,25479,25497,25511,25531],{},[923,25480,25481,25484,25489],{},[941,25482,25483],{},"15",[941,25485,25486],{},[25204,25487,25488],{"href":8139},"Iterators & Combinators",[941,25490,25491,25492,25494,25495,259],{},"Zero-cost iteration, ",[73,25493,7565],{},", custom ",[73,25496,7540],{},[923,25498,25499,25501,25506],{},[941,25500,1378],{},[941,25502,25503],{},[25204,25504,25505],{"href":9013},"Traits & Generics",[941,25507,25508,25509,259],{},"Trait bounds, object safety, ",[73,25510,4771],{},[923,25512,25513,25516,25520],{},[941,25514,25515],{},"17",[941,25517,25518],{},[25204,25519,8595],{"href":9667},[941,25521,25522,1212,25524,1212,25526,25528,25529,259],{},[73,25523,1799],{},[73,25525,2332],{},[73,25527,2335],{},", capture, ",[73,25530,9142],{},[923,25532,25533,25536,25540],{},[941,25534,25535],{},"18",[941,25537,25538],{},[25204,25539,22580],{"href":10441},[941,25541,25542,1212,25544,480,25546,480,25548,1212,25550,259],{},[73,25543,2792],{},[73,25545,1481],{},[73,25547,2404],{},[73,25549,9900],{},[73,25551,9931],{},[130,25553,25555],{"id":25554},"part-v-memory-organization","Part V — Memory & Organization",[917,25557,25558,25568],{},[920,25559,25560],{},[923,25561,25562,25564,25566],{},[926,25563,12188],{},[926,25565,25190],{},[926,25567,25193],{},[936,25569,25570,25596,25612],{},[923,25571,25572,25575,25580],{},[941,25573,25574],{},"19",[941,25576,25577],{},[25204,25578,25579],{"href":11431},"Smart Pointers",[941,25581,25582,1212,25584,1212,25586,1212,25588,1212,25590,1212,25592,1212,25594,259],{},[73,25583,1200],{},[73,25585,3803],{},[73,25587,10566],{},[73,25589,5452],{},[73,25591,10713],{},[73,25593,11369],{},[73,25595,8579],{},[923,25597,25598,25601,25606],{},[941,25599,25600],{},"20",[941,25602,25603],{},[25204,25604,25605],{"href":12010},"Modules & Crates",[941,25607,25608,25609,25611],{},"Visibility, paths, ",[73,25610,11535],{},", workspaces.",[923,25613,25614,25617,25621],{},[941,25615,25616],{},"21",[941,25618,25619],{},[25204,25620,17928],{"href":12588},[941,25622,25623,25624,25626],{},"Unit\u002Fintegration\u002Fdoc tests, ",[73,25625,12318],{},", fuzzing.",[130,25628,25630],{"id":25629},"part-vi-concurrency-async","Part VI — Concurrency & Async",[917,25632,25633,25643],{},[920,25634,25635],{},[923,25636,25637,25639,25641],{},[926,25638,12188],{},[926,25640,25190],{},[926,25642,25193],{},[936,25644,25645,25662],{},[923,25646,25647,25650,25655],{},[941,25648,25649],{},"22",[941,25651,25652],{},[25204,25653,25654],{"href":13327},"Concurrency & Multithreading",[941,25656,25657,1212,25659,25661],{},[73,25658,8563],{},[73,25660,8566],{},", threads, channels, atomics.",[923,25663,25664,25667,25672],{},[941,25665,25666],{},"23",[941,25668,25669],{},[25204,25670,25671],{"href":14088},"Async \u002F Await",[941,25673,25674,25675,25677,25678,259],{},"Futures, runtimes, ",[73,25676,13779],{},", streams, ",[73,25679,13125],{},[130,25681,25683],{"id":25682},"part-vii-metaprogramming","Part VII — Metaprogramming",[917,25685,25686,25696],{},[920,25687,25688],{},[923,25689,25690,25692,25694],{},[926,25691,12188],{},[926,25693,25190],{},[926,25695,25193],{},[936,25697,25698,25715,25730],{},[923,25699,25700,25703,25708],{},[941,25701,25702],{},"24",[941,25704,25705],{},[25204,25706,25707],{"href":14871},"Macros",[941,25709,25710,25712,25713,259],{},[73,25711,11670],{},", proc-macros, ",[73,25714,14721],{},[923,25716,25717,25720,25725],{},[941,25718,25719],{},"25",[941,25721,25722],{},[25204,25723,25724],{"href":15805},"Unsafe Rust",[941,25726,25727,25728,259],{},"Raw pointers, FFI, soundness, ",[73,25729,15746],{},[923,25731,25732,25735,25739],{},[941,25733,25734],{},"26",[941,25736,25737],{},[25204,25738,15027],{"href":16642},[941,25740,25741,25742,1212,25744,259],{},"Calling C, calling Rust from C, ",[73,25743,15460],{},[73,25745,16308],{},[130,25747,25749],{"id":25748},"part-viii-production-engineering","Part VIII — Production Engineering",[917,25751,25752,25762],{},[920,25753,25754],{},[923,25755,25756,25758,25760],{},[926,25757,12188],{},[926,25759,25190],{},[926,25761,25193],{},[936,25763,25764,25783,25796,25809,25822,25839,25853,25866,25879],{},[923,25765,25766,25769,25774],{},[941,25767,25768],{},"27",[941,25770,25771],{},[25204,25772,25773],{"href":17541},"Attributes & Conditional Compilation",[941,25775,25776,480,25778,25780,25781,259],{},[73,25777,16828],{},[73,25779,5764],{},", lints, ",[73,25782,2890],{},[923,25784,25785,25788,25793],{},[941,25786,25787],{},"28",[941,25789,25790],{},[25204,25791,25792],{"href":18352},"Cargo Features & Release Engineering",[941,25794,25795],{},"Features, profiles, CI, publishing.",[923,25797,25798,25801,25806],{},[941,25799,25800],{},"29",[941,25802,25803],{},[25204,25804,25805],{"href":19314},"Advanced Type System",[941,25807,25808],{},"Variance, HRTBs, GATs, const generics.",[923,25810,25811,25814,25819],{},[941,25812,25813],{},"30",[941,25815,25816],{},[25204,25817,25818],{"href":20105},"Design Patterns & Idiomatic Rust",[941,25820,25821],{},"Builder, typestate, newtype, RAII.",[923,25823,25824,25827,25832],{},[941,25825,25826],{},"31",[941,25828,25829],{},[25204,25830,25831],{"href":20999},"Performance, Profiling & Optimization",[941,25833,25834,480,25836,25838],{},[73,25835,12318],{},[73,25837,20915],{},", allocation pitfalls.",[923,25840,25841,25843,25848],{},[941,25842,1393],{},[941,25844,25845],{},[25204,25846,25847],{"href":21610},"Documentation",[941,25849,25850,25852],{},[73,25851,87],{},", doc tests, intra-doc links.",[923,25854,25855,25858,25863],{},[941,25856,25857],{},"33",[941,25859,25860],{},[25204,25861,25862],{"href":23264},"Ecosystem Tour",[941,25864,25865],{},"Curated map of crates for every domain.",[923,25867,25868,25871,25876],{},[941,25869,25870],{},"34",[941,25872,25873],{},[25204,25874,25875],{"href":24230},"Common Pitfalls & Idiomatic Fixes",[941,25877,25878],{},"40+ traps and their fixes.",[923,25880,25881,25884,25889],{},[941,25882,25883],{},"35",[941,25885,25886],{},[25204,25887,25888],{"href":25104},"Exercises & Project Ideas",[941,25890,25891],{},"From beginner to pro.",[15,25893,25895],{"id":25894},"learning-path-suggestions","Learning Path Suggestions",[130,25897,25899],{"id":25898},"if-youre-new-to-systems-programming","If you're new to systems programming",[3037,25901,25902,25905,25908,25911],{},[36,25903,25904],{},"Read 01–14 in order.",[36,25906,25907],{},"Skip to 21 (Testing) and write tests for everything you've built.",[36,25909,25910],{},"Skim 15–18 and 22–23, then come back to the harder parts.",[36,25912,25913],{},"Do exercises 1–5 in chapter 35.",[130,25915,25917],{"id":25916},"if-youre-coming-from-cc","If you're coming from C\u002FC++",[20,25919,25920],{},"Read 07–10 carefully (ownership is the new mental model). Skim 04 (data types) — you'll find surprises (char is 4 bytes). Read 25 (Unsafe) to understand what Rust adds over C. Then 22 and 23.",[130,25922,25924],{"id":25923},"if-youre-coming-from-pythonjsruby","If you're coming from Python\u002FJS\u002FRuby",[20,25926,25927,25928,1212,25930,25932,25933,25936],{},"Ownership will be new. Read 03–10 slowly. Don't skip 18 (Error Handling) — ",[73,25929,2792],{},[73,25931,2404],{}," is the culture. Don't reach for ",[73,25934,25935],{},"clone"," reflexively.",[130,25938,25940],{"id":25939},"if-youre-a-senior-engineer-learning-rust-for-production","If you're a senior engineer learning Rust for production",[20,25942,25943],{},"Skim 01–14. Read 16, 18, 22, 23, 27, 28 closely. Use 30, 34 as references. Skim 33 for the crate landscape. Then read 35 and pick a project.",[15,25945,25947],{"id":25946},"companion-resources","Companion Resources",[33,25949,25950,25959,25967,25975,25983,25991,25999],{},[36,25951,25952,25958],{},[25204,25953,25957],{"href":25954,"rel":25955},"https:\u002F\u002Fdoc.rust-lang.org\u002Fbook\u002F",[25956],"nofollow","The Rust Book"," — official, free.",[36,25960,25961,25966],{},[25204,25962,25965],{"href":25963,"rel":25964},"https:\u002F\u002Fdoc.rust-lang.org\u002Frust-by-example\u002F",[25956],"Rust by Example"," — code-first.",[36,25968,25969,25974],{},[25204,25970,25973],{"href":25971,"rel":25972},"https:\u002F\u002Fdoc.rust-lang.org\u002Freference\u002F",[25956],"Rust Reference"," — language spec.",[36,25976,25977,25982],{},[25204,25978,25981],{"href":25979,"rel":25980},"https:\u002F\u002Fdoc.rust-lang.org\u002Fnomicon\u002F",[25956],"Rustonomicon"," — unsafe Rust deep dive.",[36,25984,25985,25990],{},[25204,25986,25989],{"href":25987,"rel":25988},"https:\u002F\u002Frust-lang.github.io\u002Fasync-book\u002F",[25956],"Async Book"," — async internals.",[36,25992,25993,25998],{},[25204,25994,25997],{"href":25995,"rel":25996},"https:\u002F\u002Fwww.youtube.com\u002Fplaylist?list=PLqbS7AVKE3Wy6jX6h_AjMpwL9t1ZckhnH",[25956],"Jon Gjengset's Crust of Rust"," — YouTube deep dives.",[36,26000,26001,27,26006,26011],{},[25204,26002,26005],{"href":26003,"rel":26004},"https:\u002F\u002Freddit.com\u002Fr\u002Frust",[25956],"r\u002Frust",[25204,26007,26010],{"href":26008,"rel":26009},"https:\u002F\u002Fusers.rust-lang.org",[25956],"users.rust-lang.org"," — community.",[15,26013,26015],{"id":26014},"tooling-to-install","Tooling to Install",[111,26017,26020],{"className":26018,"code":26019,"language":116,"meta":117},[114],"rustup component add rustfmt clippy rust-src rust-analyzer\ncargo install cargo-expand cargo-nextest cargo-deny cargo-audit \\\n    cargo-flamegraph cargo-bloat cargo-machete cargo-release \\\n    samply cargo-watch mdbook\n",[73,26021,26019],{"__ignoreMap":117},[15,26023,26025],{"id":26024},"license","License",[20,26027,26028],{},"These notes are yours to use, share, and modify.",[20,26030,25066],{},{"title":117,"searchDepth":357,"depth":357,"links":26032},[26033,26034,26035,26045,26051,26052,26053],{"id":25122,"depth":357,"text":25123},{"id":25154,"depth":357,"text":25155},{"id":25174,"depth":357,"text":25175,"children":26036},[26037,26038,26039,26040,26041,26042,26043,26044],{"id":25178,"depth":364,"text":25179},{"id":25294,"depth":364,"text":25295},{"id":25380,"depth":364,"text":25381},{"id":25463,"depth":364,"text":25464},{"id":25554,"depth":364,"text":25555},{"id":25629,"depth":364,"text":25630},{"id":25682,"depth":364,"text":25683},{"id":25748,"depth":364,"text":25749},{"id":25894,"depth":357,"text":25895,"children":26046},[26047,26048,26049,26050],{"id":25898,"depth":364,"text":25899},{"id":25916,"depth":364,"text":25917},{"id":25923,"depth":364,"text":25924},{"id":25939,"depth":364,"text":25940},{"id":25946,"depth":357,"text":25947},{"id":26014,"depth":357,"text":26015},{"id":26024,"depth":357,"text":26025},"A comprehensive, edge-case-covering, idiomatic Rust curriculum. 35 chapters covering ownership, borrowing, lifetimes, traits, generics, closures, async, macros, FFI, and more. Go from beginner to pro Rust developer.",{},"\u002Frust",{"title":25110,"description":26054},"rust\u002Findex","zWuY4j1jUxw36iawCP21nn_Uo4sPrgXARPE_ygGVSS4",1785935255227]