Rust learning: lessons, notes, and exercise crates
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Rust Book coverage map — what is owned, what is missing</title>
|
||||
<link rel="stylesheet" href="../assets/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Coverage map</h1>
|
||||
<p class="subtitle">Every chapter of The Rust Programming Language against this workspace · updated after lesson 0009</p>
|
||||
|
||||
<div class="callout">
|
||||
The chapter list is taken verbatim from the book's
|
||||
<a href="https://github.com/rust-lang/book/blob/main/src/SUMMARY.md">table of contents</a>, not from memory.
|
||||
<strong>Read</strong> means you covered it in your two months. <strong>Produced</strong> means code you wrote in this
|
||||
workspace uses it correctly — that is the only column that counts for the
|
||||
<a href="../MISSION.md">mission</a>.
|
||||
</div>
|
||||
|
||||
<h2>The map</h2>
|
||||
<table>
|
||||
<tr><th>Ch</th><th>Topic</th><th>State</th><th>Evidence / gap</th></tr>
|
||||
|
||||
<tr><td>1–2</td><td>Getting started, guessing game</td><td>Produced</td>
|
||||
<td><code>hello_cargo/</code>, <code>guessing_game/</code></td></tr>
|
||||
|
||||
<tr><td>3</td><td>Variables, types, functions, control flow</td><td>Produced</td>
|
||||
<td><code>variables/</code>, <code>function/</code>, <code>control_flow/</code>; 0001 diagnostic solid</td></tr>
|
||||
|
||||
<tr><td>4</td><td>Ownership, borrowing, slices</td><td>Produced</td>
|
||||
<td><code>&self</code> methods and <code>&[Task]</code> accessor in <code>tasks/</code>. Watch for: you have never hit
|
||||
a borrow-checker fight in your own multi-owner code, because nothing has needed two owners yet.</td></tr>
|
||||
|
||||
<tr><td>5</td><td>Structs and methods</td><td>Produced</td><td>0003 / 0004, <code>tasks/src/task.rs</code></td></tr>
|
||||
|
||||
<tr><td>6</td><td>Enums and <code>match</code></td><td>Produced</td>
|
||||
<td><code>Status</code>, <code>Priority</code>, <code>Command</code>, and now <code>TaskError</code> (0006)</td></tr>
|
||||
|
||||
<tr><td>7</td><td>Packages, crates, modules</td><td>Produced</td>
|
||||
<td><code>tasks/</code> is a lib + bin package with four modules</td></tr>
|
||||
|
||||
<tr><td>8</td><td>Collections: <code>Vec</code>, <code>String</code>, <code>HashMap</code></td><td>Produced</td>
|
||||
<td>0008 drill, in <code>tasks/</code>: <code>HashMap<Priority, usize></code> built with the <code>entry</code>/<code>or_insert</code>
|
||||
counting idiom, <code>get().copied().unwrap_or(0)</code>, <code>Eq + Hash + Copy</code> derives on a key type, and a
|
||||
<code>String</code> built by <code>collect</code>. Not done: <code>BTreeMap</code>, <code>HashSet</code>, string slicing by byte index.</td></tr>
|
||||
|
||||
<tr><td>9</td><td>Error handling: <code>panic!</code>, <code>Result</code>, <code>?</code></td><td>Produced</td>
|
||||
<td>0005 + 0006: <code>?</code>, <code>ok_or</code>, own error enum, stderr + exit 1</td></tr>
|
||||
|
||||
<tr><td>10.1</td><td>Generic data types</td><td>Produced</td>
|
||||
<td>0008 drill: <code>fn tally<T, K, F>(items: &[T], key: F) -> HashMap<K, usize></code> in <code>tasks/src/stats.rs</code>,
|
||||
with a <code>where</code> clause bounding <code>K: Eq + Hash</code> and <code>F: Fn(&T) -> K</code> — written from the
|
||||
signature up, called at three different <code>T</code>/<code>K</code> pairs, one of them a non-<code>Copy</code> key.
|
||||
Not done: generic <em>structs</em> and generic <code>impl</code> blocks.</td></tr>
|
||||
|
||||
<tr><td>10.2</td><td>Traits</td><td>Produced</td>
|
||||
<td><code>Display for Task</code> (0005), <code>Display</code> + <code>Error</code> + <code>From</code> for <code>TaskError</code> (0006),
|
||||
<code>FromStr for Task</code> with an associated <code>type Err</code> and a hand-written <code>PartialEq</code> (0007)</td></tr>
|
||||
|
||||
<tr><td>10.3</td><td>Lifetimes</td><td class="gap">Gap — untouched</td>
|
||||
<td>Zero exposure in this workspace. You have dodged it by owning everything (<code>String</code>, not <code>&'a str</code>).
|
||||
Needed to read other people's code and to review a PR; not needed to ship your CLI.</td></tr>
|
||||
|
||||
<tr><td>11</td><td>Writing automated tests</td><td class="gap">Taught — drill pending</td>
|
||||
<td>0009: <code>#[test]</code>, <code>#[cfg(test)] mod tests</code> with <code>use super::*</code>, the three assertion macros
|
||||
and their failure output, <code>#[should_panic(expected)]</code> vs a <code>-> Result<(), E></code> test, unit vs
|
||||
integration access (<code>E0603</code>/<code>E0616</code>), <code>tests/common/mod.rs</code>, the runner flags, and why
|
||||
<code>src/main.rs</code> is untestable — <code>run</code> moves to the library and writes to <code>&mut impl Write</code>.
|
||||
Graded by six planted mutants, not by a test count.</td></tr>
|
||||
|
||||
<tr><td>12</td><td>I/O project: args, files, stderr, env</td><td>Produced</td>
|
||||
<td>0007: <code>fs::read_to_string</code>/<code>fs::write</code>, <code>io::Error</code> + <code>ErrorKind::NotFound</code> match guard,
|
||||
<code>env::var</code> with a default, on top of <code>env::args</code> and stderr + exit 1 from 0005. Not done: <code>BufReader</code>,
|
||||
file locking, serde — none needed at this size.</td></tr>
|
||||
|
||||
<tr><td>13</td><td>Closures and iterators</td><td>Produced</td>
|
||||
<td>0008 drill: <code>filter</code>/<code>map</code>/<code>collect</code> chains, <code>find</code> vs <code>position</code>,
|
||||
<code>Vec::retain</code>, <code>lines()</code>, <code>collect::<Result<Vec<_>, E>>()</code> short-circuiting, and a
|
||||
function that <em>takes</em> a closure (<code>F: Fn</code>). Not done: <code>fold</code>, <code>zip</code>,
|
||||
<code>impl Iterator</code> for your own type.</td></tr>
|
||||
|
||||
<tr><td>14</td><td>Cargo, crates.io, workspaces</td><td>Skip for now</td>
|
||||
<td>Learn <code>cargo add</code> and features when a dependency is actually needed (serde, axum).</td></tr>
|
||||
|
||||
<tr><td>15</td><td>Smart pointers: <code>Box</code>, <code>Rc</code>, <code>RefCell</code>, <code>Deref</code>, <code>Drop</code></td><td>Partial</td>
|
||||
<td><code>Box<dyn Error></code> met in 0005/0006. <code>Rc</code>/<code>RefCell</code> untouched — reach for them only when a real
|
||||
shared-ownership problem appears, not before.</td></tr>
|
||||
|
||||
<tr><td>16</td><td>Concurrency: threads, channels, <code>Send</code>/<code>Sync</code></td><td class="gap">Gap</td>
|
||||
<td>Untouched apart from the <code>Send + Sync</code> bound your 0006 test asserts. Prerequisite for understanding
|
||||
why an axum handler must be <code>Send</code>.</td></tr>
|
||||
|
||||
<tr><td>17</td><td>Async: futures, <code>async</code>/<code>await</code>, streams</td><td class="gap">Gap — by design</td>
|
||||
<td>Required for axum/tokio, deliberately last. The unused <code>trpl</code> dependency in <code>get-dependecies/</code> is
|
||||
the abandoned first attempt. Do it after 8, 11, 13.</td></tr>
|
||||
|
||||
<tr><td>18</td><td>Trait objects, OOP patterns</td><td>Partial</td>
|
||||
<td><code>dyn Error</code> is the same mechanism as 18.2. The state-machine pattern (18.3) is optional reading.</td></tr>
|
||||
|
||||
<tr><td>19</td><td>Patterns and matching</td><td>Partial</td>
|
||||
<td><code>match</code> ✓, <code>if let</code> ✓ (0005 drill). Guards (<code>Some(x) if x > 5</code>), <code>@</code> bindings,
|
||||
<code>let ... else</code>, and <code>matches!</code> — one page of reading, high value per minute.</td></tr>
|
||||
|
||||
<tr><td>20</td><td>Unsafe, advanced traits, macros</td><td>Skip</td>
|
||||
<td>Associated types and generic-parameter defaults matter eventually (serde uses them). Not now.</td></tr>
|
||||
|
||||
<tr><td>21</td><td>Final project: multithreaded web server</td><td>Not started</td>
|
||||
<td>The natural capstone before axum — it is a backend service with no framework.</td></tr>
|
||||
</table>
|
||||
|
||||
<h2>The order that follows from this</h2>
|
||||
<p>After the 0009 drill, one gap in the book's core is left, and it is the mildest one:</p>
|
||||
<ol>
|
||||
<li><strong>Lifetimes</strong> (ch 10.3) — as reading practice, not as a build. You have written two without
|
||||
noticing: <code>titles_with</code> returns <code>Vec<&str></code> borrowed from <code>&self</code>, and
|
||||
<code>Status::label</code> returns <code>&str</code> the same way. Elision filled both annotations in. Reading
|
||||
other people's signatures needs the explicit form.</li>
|
||||
<li><strong>Patterns</strong> (ch 19) — one page: match guards, <code>@</code> bindings, <code>matches!</code>,
|
||||
<code>let … else</code>. Your suite already uses <code>matches!</code>; the rest is high value per minute.</li>
|
||||
</ol>
|
||||
<p>Then serde → axum → async, where the traits from 0005–0008 and the testing from 0009 stop being an exercise
|
||||
and start being the whole API surface: an axum handler is a function a test can call. Chapter 21's web server is
|
||||
the natural capstone before a framework.</p>
|
||||
|
||||
<footer>
|
||||
<p><strong>Primary source:</strong> <a href="https://doc.rust-lang.org/stable/book/">The Rust Programming Language</a> ·
|
||||
chapter list from <a href="https://github.com/rust-lang/book/blob/main/src/SUMMARY.md">SUMMARY.md</a>.</p>
|
||||
<p>Reference: <a href="rust-syntax.html">Rust syntax reference</a> ·
|
||||
Lessons: <a href="../lessons/0006-your-own-error-type.html">0006</a> ·
|
||||
<a href="../lessons/0007-files-and-fromstr.html">0007</a> ·
|
||||
<a href="../lessons/0008-iterators-and-hashmap.html">0008</a> ·
|
||||
<a href="../lessons/0009-writing-your-own-tests.html">0009</a></p>
|
||||
<p>Disagree with a "Produced" or a "Gap"? Say so — this table decides what gets taught next, so it is worth arguing about.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user