docs(book): Add capstone chapter — a complete Bookmarks Service#605
Conversation
The book teaches concept-by-concept after Chapter 2; this adds a final integration chapter that assembles the core stack into one working service: derives-Weaver data + an RPC trait, a Design-wired in-memory implementation, an RPC/Netty server, a retry around the one network call, and a UniTest suite that overrides a single Design binding to test the real logic without a network. Lands as Chapter 16 / Part IX (after the platform Parts, before the appendices) to avoid renumbering. Nav re-threaded (15 -> 16 -> appendices), ToC and sidebar updated. pnpm docs:build passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a new capstone chapter, 'Part IX — Capstone: 16. A Bookmarks Service,' to the documentation, along with corresponding updates to the navigation links and index. The review feedback highlights two improvement opportunities in the code examples of the new chapter: first, replacing independent atomic variables with a synchronized block in BookmarkServiceImpl to prevent race conditions during compound state transitions; and second, ensuring html.indexOf("</title>") in extractTitle searches from the index of the opening tag to avoid issues with malformed HTML.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| import java.util.concurrent.atomic.AtomicReference | ||
|
|
||
| class BookmarkServiceImpl(seed: Seq[Bookmark]) extends BookmarkService: | ||
| private val store = AtomicReference(seed.toVector) | ||
| private val nextId = java.util.concurrent.atomic.AtomicLong(seed.size + 1L) | ||
|
|
||
| def add(url: String, title: String): Bookmark = | ||
| val bookmark = Bookmark(nextId.getAndIncrement(), url, title) | ||
| store.updateAndGet(_ :+ bookmark) | ||
| bookmark | ||
|
|
||
| def list(): Seq[Bookmark] = store.get() |
There was a problem hiding this comment.
Using multiple independent atomic variables (AtomicReference and AtomicLong) for compound state transitions (generating a unique ID and appending to the store) can lead to race conditions where the order of elements in the list does not match their IDs. Guarding plain variables with a monitor lock (synchronized) ensures atomicity of the compound transition, guarantees consistent ordering, and reduces memory overhead.
| import java.util.concurrent.atomic.AtomicReference | |
| class BookmarkServiceImpl(seed: Seq[Bookmark]) extends BookmarkService: | |
| private val store = AtomicReference(seed.toVector) | |
| private val nextId = java.util.concurrent.atomic.AtomicLong(seed.size + 1L) | |
| def add(url: String, title: String): Bookmark = | |
| val bookmark = Bookmark(nextId.getAndIncrement(), url, title) | |
| store.updateAndGet(_ :+ bookmark) | |
| bookmark | |
| def list(): Seq[Bookmark] = store.get() | |
| class BookmarkServiceImpl(seed: Seq[Bookmark]) extends BookmarkService: | |
| private var store = seed.toVector | |
| private var nextId = seed.size + 1L | |
| def add(url: String, title: String): Bookmark = synchronized { | |
| val bookmark = Bookmark(nextId, url, title) | |
| nextId += 1 | |
| store = store :+ bookmark | |
| bookmark | |
| } | |
| def list(): Seq[Bookmark] = synchronized { store } |
References
- Prefer using plain variables guarded by a monitor lock ('synchronized') instead of multiple independent atomic instances for compound state transitions to ensure atomicity and reduce memory overhead.
| private def extractTitle(html: String): Option[String] = | ||
| val open = html.indexOf("<title>") | ||
| val close = html.indexOf("</title>") | ||
| if open >= 0 && close > open then Some(html.substring(open + 7, close)) else None |
There was a problem hiding this comment.
In extractTitle, html.indexOf("</title>") searches from the beginning of the HTML string. If there is any occurrence of </title> before the <title> tag (for example, in a comment or malformed markup), close will be less than open, causing the method to return None even if a valid <title>...</title> block exists later. Searching for the closing tag starting from open + 7 resolves this issue.
| private def extractTitle(html: String): Option[String] = | |
| val open = html.indexOf("<title>") | |
| val close = html.indexOf("</title>") | |
| if open >= 0 && close > open then Some(html.substring(open + 7, close)) else None | |
| private def extractTitle(html: String): Option[String] = | |
| val open = html.indexOf("<title>") | |
| val close = html.indexOf("</title>", open + 7) | |
| if open >= 0 && close > open then Some(html.substring(open + 7, close)) else None |
The Book teaches one concept per chapter after Chapter 2. This adds a capstone (Chapter 16, the new Part IX) that threads the core stack into a single working service — the integration view the Rust Book gives with its final project.
What it builds
A small Bookmarks Service, end to end:
Bookmarkand aBookmarkServicetrait, bothderives Weaver(ch6)RPCRouter.of+RPCHandler, with a generated typed client (ch9–10)Each section links back to the chapter that taught the piece, so it reads as assembly, not new material.
Placement
Lands as Ch 16 / Part IX, after the platform Parts and before the appendices — chosen to avoid renumbering existing chapters. Nav re-threaded (15 → 16 → Appendix A), ToC and sidebar updated. APIs verified against source.
pnpm docs:buildpasses.🤖 Generated with Claude Code