Skip to content

Katas

Level: reference · the practice track

One line: The lessons explain; the katas make you type. This page is the only place the katas are ordered — each kata itself lives on the page for the topic it teaches.


Where a kata lives

A kata belongs to its topic, not to a folder of its own. The page that explains Option is the page that asks you to write one, under a ## Practice heading near the end, with the solution folded away in a <details> block.

That is a deliberate choice, and the reason is that folders are URLs. A topic — Option, if let, borrowing — is stable for years; a sequence is not. The moment you write a kata that belongs between K1 and K2, a K01_…/ folder either gets renumbered (breaking every link anyone saved) or starts lying about its own order. So the sequence lives here, in a table that costs nothing to reorder, and the numbers below are labels rather than addresses. It is the same rule the sidebar follows: order is presentation, so it belongs in a page, never in a path.

The happy side effect is that a kata arrives with its explanation already written, and a topic can collect several katas over time without any of them needing to restate the background.

The katas

# Kata Lesson Level
K1 One file, three builds — plain, --test and -O, each predicted before you run it Running a scratch program 101
K2 Three misplaced doc comments — predict warning, E0585 or E0753 for each, then move them Comments that compile 101 → 201
K3 Fibonacci, and the width that runs out — write fib, find the exact n that panics, then explain why the same n prints a wrong number in --release Values 101 → 201
K4 The guard that stops protecting you — swap && for & on a bounds check, then write Python's sum(flags) three ways and defend one of them Meet the bool 101 → 201
K5 A favourite number that may not exist — Some / None, one match, and unwrap_or Some and None 101
K6 The arm you deleted — add a variant, and watch only one of the two forms come and find you if let 101
K7 The reason the caller could have used — the same four bad cells, once as None and once as a named error Option vs Result 101
K8 The default that was built for nothing — watch an eager fallback run on the happy path unwrap_or 201
K9 Fall back, but keep the reason — the one job only the _else closure can do unwrap_or_else 201
K10 The type's zero is not your domain's zero — a blank ballot defaults into a real-looking one unwrap_or_default 201
K11 Transform, or fall back — a default written first and run last map_or 201
K12 Follow the responsibility — a Drop that prints, and the function where the free actually happened Ownership and moves 101
K13 Many readers, or one writer — then move one println! and read E0502 Borrowing 101 → 201
K14 Two places, or one? — compile the four-line reference test both ways, time the drops, read the optimizer's answer, then grade the three-option quiz that rejects the right line for the wrong reason A name is not a place 201
K15 The same program with a type that is not Copy — read E0382, then fix it four ways and pick one Shadowing and unwrap 201
K16 The value you can no longer free — shadow a buffer, watch it outlive the work, then fix it three ways A shadow does not drop 201
K17 Three shadows, one of them earned — predict two wrong numbers, then fix one by deleting a let and the other by renaming When to shadow 201
K18 The tally that never tallied — a shadowed accumulator logs three plausible running totals and names a candidate who scored zero Nothing checks a shadow 201
K19 Time three things you cannot see — a report that closes before it writes, a guard released one identifier early, and a borrow ended three ways Scope is about names, not values 201
K20 Six statements, and only two of them drop anything — a shadow and a reassignment that differ by one keyword and by everything else Assignment drops the old value 201
K21 Four warnings, four different right answers — only one of them is an underscore, and one is still broken if you pick the wrong kind What a warning is asking 101 → 201
K22 What the panic left behind — read the damage an unwrap does mid-job, then make the missing row a return value What a panic costs 201
K23 The wall the backtrace stops at — the same panic on a thread of its own, and the submitter's stack carried across by hand Reading a backtrace 201
K24 Four sentences, one of them a hope — name the guarantor for each expect, then watch the one that has none die on a misspelled key expect: writing down the proof 201
K25 Delete four unwraps — rewrite a README-shaped config parser so no line can abort, using a different technique for each unwrap is a TODO you forgot to remove 201
K26 The average of nothing — make a partial function total, and say what your None means Partial functions 201
K27 Four causes, one None — write the operator's error message from a signature that discarded it Returning None on error 201
K28 Declare it, then prove it — a value decided in three branches, with no Option and no mut Initial values 201
K29 Which fields may legitimately be missing? — and telling no ballot apart from an empty one Option fields 101
K30 No match allowed — count, total and average the ballots that exist with iterator methods only Option is a one-item collection 201
K31 A loop with no counter — the body that peeks where it meant to advance while let 201
K32 A list that ends — Option<Box<Node>>, and the size_of proof that it costs nothing Nullable pointers 201
K33 One optional argument, four ways — and the signature that rejects half your callers Optional function arguments 201
K34 Make the invalid score unbuildable — then find the door your own module still leaves open A score is not a number 101 → 201
K35 The line you forgot — desync two parallel Vecs and get a plausible wrong answer What is a record, in memory? 201
K36 Expand the alias — follow a one-parameter Result back to the list of things that can go wrong The Result you are reading is probably an alias 201
K37 Guard the input that has no answer — find the case the careful-looking guard lets through Zero wins is not zero games 201
K38 The Result the lock hands you — a thread dies mid-update, and you answer it three ways Lock poisoning 301
K39 Credit a fourth knob honestly — the same linker swap, first and last in the ladder Compile times 201
K40 The arm you didn't write — a catch-all quietly refiles two spoiled ballots as blanks Six kinds of zero 201
K41 Eight candidates in one byte — pack an approval ballot into a u8 with bit operations, then let a ninth sign up Meet the byte 101 → 201
K42 Two decisions, one literal — write three fields in the base whose digits line up with them, then find the quantity whose obvious width is too narrow Writing a number down 101 → 201
K43 The fingerprint that collided — one missing 0 turns two different ballot files into the same hex string, across 3,600 of the 65,536 two-byte cases Why hexadecimal 101 → 201
K44 A tic-tac-toe game in 18 bits — two nine-bit fields in one u32, eight win masks, then a fourth field arrives on top Bit flags 201
K45 The results table that would not sort — meet E0277 on purpose, then rank the same table three ways and pick the one you would ship What a float actually stores 201
K46 Spend the entitlement, not just the token — close the sign-in hole, then count what the fix costs The right to post is a value 301
K47 The scale that stopped covering the election — hard-code a denominator, grow the election past it, and find the bug that changes no winner Scale the denominator away 301
K48 The average that came out as a three-way tie — collapse three candidates onto one number, then rank them without ever dividing What i128 is exact about 301
K49 Build the count that always finishes — then find the coarsest rounding that still reproduces the exact winners When the denominators compound 301
K50 The audit that has to know when to stop — five wards, one rounded count each, and the escalation loop that never returns on a tie Did the rounding decide it? 301
K51 The error message nobody saw — count the five ways your error can reach a person, then fix it twice and price both fixes Debug and Display 101 → 201
K52 The semicolon that changed the type — cause E0308 on purpose, then seal a mut builder behind braces and make an if be the value A block is an expression 101 → 201
K53 Three places () shows up — take the receipt instead of the result, use ? on a Result<(), E>, then build a set out of HashMap<T, ()> The unit type () 101 → 201
K54 The f-string that isn't — four braces, four refusals, then fix the line three ways and defend the one you ship The braces take a name 101 → 201
K55 The reformat that changed the program — find the one place a whitespace edit is not cosmetic, and watch the formatter decline to help Formatting 101 → 201
K56 The hour that changed its ad — collapse twenty-six match arms into five, then break it three ways and predict which two the compiler catches One arm, many values 101 → 201
K57 Three ways to make Some(None) compile — read E0308 in full, then fix it by deleting, by supplying, and by widening the field, and defend the one you would ship Some is a constructor, not a flag 101 → 201
K58 Three flavors, and the two things the compiler keeps apart — identical tuple structs that will not substitute, the private field that privatises a constructor, and a unit struct whose only content is behaviour What a struct is 101 → 201
K59 Pick the right receiver four times — then call a &mut self method through a non-mut binding, and use a value after a method took self impl blocks 101 → 201
K60 Predict which half of the base survives a ..base — four fields, two of them still readable, and the trailing comma that is its own error Struct update syntax 101 → 201
K61 One E0382, three fixes, and the String field that removes one of them — then rank what each costs the caller Copy vs Clone 101 → 201
K62 One &str parameter, three callers — then flip it to String and catalogue what every call site now pays String vs &str 101 → 201
K63 Cut a name in half without panicking — len()/2 on four names, and the two ways to find a legal boundary String slices 101 → 201
K64 Predict len and capacity through five pushes — which ones reallocate? — then make one up-front allocation serve all of them The anatomy of a String 101 → 201
K65 Five bindings, two buffers — which of five names own bytes and which point into someone else's, with each handle measured in machine words The anatomy of a String 101 → 201
K66 Implement Display once and collect four abilities — then add impl ToString and read the E0119 Making a String 101 → 201
K67 One greeting three ways — then go back and earn all three refusals on purpose (E0369, E0308, E0368) Concatenating strings 101 → 201
K68 One line built four ways — which inputs survive, how many buffers, and which one is wrong inside a loop Building a String 101 → 201
K69 One name, three lengths — a per-char inventory, and the combining accent that makes two identical-looking strings unequal Meet the char 101 → 201
K70 An empty field is data — parse "5,,0" into abstentions, then watch split_whitespace() shorten the row Walking a String 101 → 201
K71 Return a label three ways — read the E0515, then match, leak, or own it, and say which one leaks in a loop &'static str 201
K72 A builder that moves, not copies — impl Into<String> setters counted against &str ones, and what a build(&self) pays on every call String parameters worth copying 201 → 301
K73 Three arrivals, three types — then break the UTF-8 promise and the NUL promise on purpose, and read both refusals Six kinds of string 201
K74 Measure both halves of a reference — the handle in machine words, the value in bytes, one function for &str, &[i32], &dyn Display and &i32, and the ?Sized that lets it take all four str is unsized 201
K75 Pivot both ways, then earn E0106 and E0515 — a borrowed field, an owned rewrite, and a Cow that allocates only when the text changes String vs &str 101 → 201
K76 Slice up to a boundary, then straight through one — is_char_boundary by hand against floor_char_boundary, the panic message in full, and an Option where a crash was String slices 201
K77 Interleaving, with the slices as the state — two lengths as the memo key, and the original test that called a valid interleaving invalid String slices 301
K78 Four arrivals and one pre-payment — a capacity that does not move, a parse that does not trim, and the bytes that turned out not to be UTF-8 Making a String 101 → 201
K79 Five edits, one buffer — retain, an insert at the middle character rather than the middle byte, drain, the + that eats its left operand, and pop Building a String 101 → 201
K80 Run-length encoding both ways — then the run past nine, the input with a digit in it, and the string that comes out bigger Building a String 201
K81 Every distinct permutation, in one buffer — push down and pop back, and a count to predict for "banana" Building a String 201 → 301
K82 Case, whitespace, and STARVoting — a converter pair that is not a round trip, ASCII-only case swapping, and 'ß' as the reason both families exist Meet the char 201
K83 The third ruler — reverse café and lose the accent, count a family emoji as seven, then write the grouper and name what it still cannot do Meet the char 201 → 301
K84 Two rulers over one string — len() against chars().count(), and the two indices that agree until the crab Walking a String 101 → 201
K85 Six searches, and the two that want a regex — palindromes, fields, offsets, every match, then what your hand-rolled email finder gets wrong Walking a String 101 → 201
K86 Build the same choice twice — a hand-maintained tag beside a union, then an enum, and the desync only one of them can have What a union is 301
K87 Seven errors, five root causes, three edits — group them by cause before changing a line When a struct refuses 101 → 201
K88 Both dbg! traps — the alternate flag a hand-written Debug ignores, and the move that costs you the value What dbg! does 101 → 201
K89 Four spellings, four error codes — and the one that compiles into the wrong thing A type is not a constructor 101 → 201
K90 Seed it, prove it, measure the modulo bias, then remove it — counted over the whole output space, never sampled Randomness 101 → 201
K91 Which defence catches a silent catch-all — two lints denied, a variant added, and only one of the three edits does anything A typo becomes a binding 201
K92 Pay only when you have to — ensure_prefix returning Cow, then prove the untouched rows were never copied Cow: borrow until somebody writes 201
K93 Make the sum land on the number you would have typed — eight tenths, two groupings, and the one assertion that survives either Letting the compiler reorder a float sum 201 → 301
K94 Let the source pick the spelling — six things converted to a String, and which of the five you were entitled to use Making a String 101 → 201
K95 Predict the owned twin before you run it — six receivers, and the two everybody gets wrong ToOwned 201
K96 Predict, then count — one line of output built four ways, and what with_capacity buys when the number is one byte short The global allocator 301
K97 Four loops that all look like reuse — predict the allocation count for each, then count them clone_into 201 → 301
K98 A container with two holes — write Pair<A, B>, and work out why swap cannot return Self What a generic is 101 → 201
K99 Walk a linked list without recursion — and without moving or cloning anything on the way A generic recursive type 201 → 301
K100 Predict the count four times, then find the edge that leaks — one roster shared by three tallies, a deep clone that changes none of the numbers, and a back-reference that stops Drop from running Rc: the clone that copies a pointer 201
K101 Predict the closure-call count, then count it — seven chains over six scores, and the three that stop early for three different reasons Iterators are lazy 201
K102 The Vec that could not outlive its string — read E0515, then fix it four ways and say what each one allocates Collect the iterator into a Vec 101 → 201
K103 Six questions, one Vec — predict which of them needs a collection, then count how many pieces each one actually walked Collect the iterator into a Vec 101 → 201
K104 Five references where you meant five numbers — reverse an array two ways, then read the &&i32 in the sum that will not compile, the three routes back to Vec<i32>, and the two adapters rev() does not commute with DoubleEndedIterator and ExactSizeIterator 201 → 301
K105 Five pairings, and the row that vanishes — five loops over two vectors, two that will not compile as first written, then predict how many pairs an eight-against-six zip actually tests zip and enumerate 101 → 201
K106 Three refusals, three fixes, and the one you can delete — an Rc in a spawn, a push through a shared Arc, a missing per-thread clone, and the total you cannot print Sharing across threads: Arc 201
K107 Predict seven sizes, then predict which of seven lines allocates — the array against the Vec, the three numbers that all miss the heap, and a counting allocator over move, Copy, borrow, clone, Box and Rc Stack and heap 101 → 201
K108 Four fields, and the transposition that compiles — total a ballot two ways, then swap two fields and construct the version rustc cannot catch Tuples 101
K109 One function, four callers — write average twice, find the three calls the fixed-length signature turns away, and the 0 / 0 that neither panics nor stops Arrays and slices 101 → 201
K110 The call that printed its own receipt — reverse an array inside a println!, then find which of rustc, clippy and nobody catches each of the three ways to make the same mistake Arrays and slices 101 → 201
K111 Count the reallocations, then delete them — a hundred pushes three ways, and the elements each one copied Vec 101 → 201
K112 Two lengths in one election — one is a fact about STAR and one is a fact about today; snapshot the array, then try to size the other from len() Array or Vec? 101 → 201
K113 Build the same grid twice and send it the bill — a Flat struct against Vec<Vec<u8>>, the allocation count for a million cells, and the set that writes a real cell it was never asked for Grids and nested Vecs 201
K114 Read the wire, and make the short row impossible — regroup a flat byte buffer into four-byte rows, keep the tail that did not divide, and count the allocations both shapes charge for a thousand of them A Vec of arrays 201
K115 Three ways into a signature you cannot change — the conversion a slice-of-slices parameter forces on a Vec<Vec<i32>>, the two allocation counts that are not the same number, and the bound that would have needed neither Slices of slices 201
K116 The window that wrapped — a rolling mean over a VecDeque, the sum over as_slices().0 that quietly drops a reading, and the median that reorders the queue it just measured VecDeque 201
K117 Four ways to count, and the two that are wrong — one reports 2 for a candidate who scored 11, the other is merely three lookups per ballot HashMap 101 → 201
K118 Who voted twice, who never voted — two answers from one operation with its arguments swapped, and the turnout formula that counts a stranger HashSet 201
K119 A word ladder, and the key a lookup hands back — breadth first over a HashSet<&str> that borrows the list, probed from a scratch buffer HashSet 201 → 301
K120 Two orders from one tally, and the key you cannot use — the container hands you one of the two orders free, and keying on each candidate's share puts five in and gets three out BTreeMap and BTreeSet 101 → 201
K121 Two walks and a drop order — the same boxed list recursively and with a cursor, then the error rustc gives when the Box comes out Box 201
K122 Three conversions, one the compiler forbids — the orphan rule in its own words, and the impl Into<T> argument that also accepts the type it converts to From and Into 201
K123 A ballot line parsed twice — one parser names the bad cell, the other returns a plausible ballot with a 9 on a 0-5 scale TryFrom and TryInto 201
K124 Four silent losses — the turnout that rounds to zero, the guard a negative index walks through, and the three named adds Casting with as 201
K125 Six call sites, three repairs — name the rule that stopped each one: not a reference, inside another type, or a generic parameter Coercion: the conversion you never write 201
K126 The door your own module left open — three versions of one newtype, two of which let a caller build an invalid score Modules and visibility 201
K127 Two traits called Write, and a glob that shadows — predict which line the ambiguity error lands on Bringing names in with use 101 → 201
K128 Five paths to one function — super, crate, self and a sibling, then the fifth file that compiles and never runs One module per file 201
K129 The address you may not rely on — two pointer comparisons that agree, only one of which is a promise const and static 201
K130 The derive that changed behaviour when a field moved — one cosmetic diff, and every sort over the type reversed What an attribute is 201
K131 Five assertions, two of which cannot fail — say what would have to break before each one noticed What a test asserts 201
K132 The test that could not see it — a private helper, and a should_panic that goes green on the wrong panic Where a test goes 201
K133 The example that documents half the sentence — then a third case the doc comment never promised at all The example that is a test 201
K134 Four impls for one *, and an operator that should not exist — then three plausible readings of adding two turnout figures Operators are traits 201
K135 Three drop orders, and the guard released one line early — two of the orders are opposites, and one binding is a bug Drop, and what RAII buys 201
K136 Count the drops on four paths — two of them need a run-time decision at the closing brace, and the fourth makes that decision readable The drop flag 301
K137 The same fan-out three ways — sequential, spawn plus Arc, and scope, then the E0373 that explains why the middle one exists Spawning a thread 201 → 301
K138 A three-stage pipeline, and the drop that ends it — then reproduce the classic mpsc hang without hanging Channels 201 → 301
K139 The safe line the unsafe block depends on — write split_at_mut, delete its assert, then find the ordinary pub fn that makes an unchecked read unsound What unsafe turns off 301
K140 Three calls, three causes — one E0599 each, and only one of them is fixed by implementing the trait the method came from "No method named …" 201
K141 Three predictions about one call — which of the two addresses moved, where the free happened, and which of your three claims was about Rust rather than about one build The call stack 101 → 201
K142 Prove the reuse, then get the compiler to stop you exploiting it — the same address twice, E0106 before E0515, and the two fixes that are not annotations A stack slot is reused 201
K143 Freeze a candidate column three ways — handles and buffers for String, Box<str> and an interned Rc<str>, then the .to_owned() that clones a pointer instead of the text The third owned form 201 → 301
K144 An interner that hands out numbers — the &str handle that earns E0499 on the second call, and a Symbol that borrows nothing The third owned form 201 → 301
K145 Rebuild char::from_u32, then price the fixed width — the 2,048 refusals, the ratio for four alphabets, and the one Option that doubles Why a char is 32 bits wide 201 → 301
K146 Measure a column four ways, then truncate it without breaking a letter — the rows where three limit rules disagree, the value that splits all three, and the get(..n) that returns None where a slice would panic Four lengths 201
K147 Edit distance, in the unit you choose — one generic table over char and u8, and one word spelt two ways that is not distance zero Four lengths 301
K148 The invariant nobody wrote down — one ordinary pub fn desyncs a cached total, and the wrong answer arrives with no unsafe, no panic and no error What an invariant is 301
K149 Give every slice a .middle(), then find out why your .first() never runs — one trait, two methods, and only one of them reachable through a dot Extension traits 201
K150 Two numbers, one prefix, and the trait you cannot name — a character index beside find's byte offset, the starts_with constant that goes stale, and the three stable helpers that stand in for P: Pattern Searching without splitting 101 → 201
K151 The longest common prefix, by letters — five tests, a borrowed version that needs one lifetime, and the shared byte that is not a shared letter Searching without splitting 201 → 301
K152 A regex engine with two operators — slice patterns on &[char], and the . that means half an é when it runs on bytes Searching without splitting 201 → 301
K153 replace, without replace — the overlapping pair that yields one match, and the empty pattern that never lets the loop move Replacing part of a string 101 → 201
K154 Escape it twice, then escape it once — the swap no ordering can perform, the last-n replacement replacen counts from the wrong end, and what a stale offset does to a string that has already changed length Replacing part of a string 101 → 201
K155 The conversion you did not need, and the method that changed under you — five owners of one string and how few conversions they need, then an inherent method that silently steals a trait's call site str::as_str 201 → 301
K156 Three questions about nothing — the empty haystack that splits in two, the byte offsets a Polish word refuses to report, and every character as a borrowed &str Splitting on nothing 201
K157 One question, three answers, and the row where none of them is right — then a two-level key that files Ł between L and M, the three match arms that have to change for Swedish, and the Czech letter no per-character key can express Comparing and sorting text 201
K158 One bad config value handled four ways, the space after = that breaks three of them, and a FromStr whose error the caller can match on instead of read Parsing out of a string 101 → 201
K159 A tokenizer with quotes — three rules the tests leave open, and the unterminated quote only a Result can report Parsing out of a string 301
K160 A CSV reader, and the newline inside the quotes — doubled quotes, empty fields kept, and the line-first split that cuts a record in two Parsing out of a string 201 → 301
K161 A table sized from its own data, the {:?} column that refuses to pad, precision that counts chars rather than bytes, and the last alignment fmt cannot fix The format mini-language 201
K162 Full justification, and the width that is not bytes — spare spaces shared leftmost first, the one line {:<16} can do alone, and two Polish words that stop fitting The format mini-language 301
K163 Write the safe version of three CVEs — join, retain and repeat with no unsafe — drive all three with the hostile input that broke std, then name what each fast path has to prove in order to skip the work you just did When the UTF-8 invariant broke 301
K164 The test that would have caught it — sweep every substring of a periodic word, sweep the ASCII run before a final sigma, break a third fast path the same way, then measure the ASCII prefix in std's own sigma tests Wrong, but not unsafe 201 → 301
K165 A closed set of three, and the fourth variant that breaks every match that forgot it What an enum is 101 → 201
K166 The swapped call nothing can catch, and the same call as a build error An enum instead of a bool 201
K167 Four shapes one way and four fields the other -- which one makes a circle with a width impossible Variants that carry data 201
K168 Sixteen cells the compiler counted for you, and the wildcard that would throw them away An enum as a state machine 201
K169 Four closures, four sizes -- and two with identical source text that are still two types What a closure is 101 → 201
K170 Put three closures on the ladder, then find what does NOT decide the rung The three closure traits 201
K171 Two places move is not optional, and the Copy type that makes it look like it did nothing The move keyword 201
K172 Which of the three can become an fn -- and the closure that is smaller than a pointer Function pointers 201
K173 Two programs one character apart, and the u32 binding that is not a u32 Match ergonomics 201
K174 let _ = guard against let _guard = guard, and the one sentence both follow from The wildcard _ 201
K175 Declare without assigning, delete one branch, and read E0381 Uninitialized reads 201
K176 Option<&T> is the same eight bytes as &T -- and Option is not Null dereference 201
K177 Four ways past the end, and the one rejected without running Buffer overruns 201
K178 The comparison -O2 deletes, and four named behaviours that replace it Signed overflow 201
K179 Drop it and use it -- then make a reference outlive its owner Use-after-free 201
K180 Pass it by value twice, and watch responsibility become a compile error Double-free 201
K181 Erase while iterating: the bug that returns a plausible wrong answer Iterator invalidation 201
K182 Eight threads, one counter, and the two traits that reject the bad program Data races 201
K183 The early return that cannot hold the lock, and the one-character way to drop it too soon Forgotten unlock 201
K184 Spanify Clang's own example — a raw pointer and a length, then one slice Safe Buffers 201
K185 One contract, two spellings — a lifetime in Rust, an attribute in C++ Lifetime safety in Clang 301
K186 Price an order before you run it What a clone costs 201
K187 Return a palindrome you did not copy — one reference in so elision works, a tie the test decides, then two references and E0106 How to learn lifetimes 301
K188 A log that keeps views, not copies — a String per line against views into one buffer, the struct that earns E0515 and E0505, and the ranges that fix it How to learn lifetimes 301
K189 Undo escape_ascii — read a printed byte string back, and prove it exact over all 65,536 two-byte strings Printing bytes 201
K190 Predict, then ask the compiler — six values through let b = a;, one of them a Range that holds two i32s and still moves Copy or move? 101
K191 Four impls that disagree with their trait — one compile, four error codes, and a help: line that fixes the arrow but not the body When the impl does not match the trait 201
K192 Half up, by hand — the one-liner everyone writes first, the two inputs that break it, and the version that reads x instead of adding to it Making a float whole 201
K193 A log that is never truncated, and a lock that is claimed once — append needs create, and create_new matched on AlreadyExists rather than on is_err() Opening a file 201
K194 Read a Latin-2 file properly — InvalidData as the signal, an eighteen-letter table as the decoder, and the two bytes that pass as UTF-8 by accident A file is bytes; a String is a promise 201
The numbers are labels, and they live only in this table — a kata's own page does not print its number, so moving one costs a single line here and nothing else. Reorder freely; the order is the order to attempt them in, not the order they were written.

Both columns are links, and they go to different places: the kata title opens the exercise itself, the lesson name opens the page it lives on. Every lesson in 01_Foundations/, 05_Tooling/ and 09_Advanced/ now has one.

By subject

The table above is the order to attempt them in. This is the same katas grouped by the section they live in, for when the question is what practice is there for Option? rather than what do I do next?

Generated from the table above by tools/check_katas.py --fix. Sections in the order their first kata is reached.

First programs — 11 katas

Option and Result — 24 katas

Ownership — 19 katas

Errors — 1 kata

Structs — 8 katas

Advanced — 12 katas

Tooling — 2 katas

Numbers and bytes — 8 katas

Strings — 45 katas

Enums — 5 katas

Traits — 7 katas

Generics — 2 katas

Iterators — 5 katas

Collections — 14 katas

Conversion — 4 katas

Modules — 5 katas

Testing — 3 katas

Closures — 4 katas

Pattern Matching — 2 katas

C and Cpp — 11 katas

Files — 2 katas

Adding one

Three things, in this order:

  1. Write the prompt on the topic page, under ## Practice. State the task, not the method — and say which mistake is worth making on purpose first, because on most of these pages the compiler error is the lesson.
  2. Make the solution a real example. Put it in that lesson's examples/ folder as <topic>_kata.rs, record its key, and pull both the code and its output into the <details> block with generated markers:
<!-- source:some_and_none_kata -->
<!-- /source -->

<!-- output:some_and_none_kata -->
<!-- /output -->

Neither is hand-typed, so a solution cannot quietly stop compiling — which is the one failure a practice page must never ship. 3. Add a row above, at the position in the sequence where it should be attempted — inside the table, not merely above ## Adding one. Two paragraphs of prose sit between the last row and that heading, and a row dropped after them renders as a second one-row table with no header. check_katas.py does not catch it: the row parses, its links resolve, and the numbering still reads in order, so the gate passes on a page that looks broken.

The mechanics of the markers, and the rest of the house conventions, are in CONTRIBUTING.md.