From fc6e5c75261a739afab006b81ee5eeff3239d6dc Mon Sep 17 00:00:00 2001
From: Andrew Walbran <qwandor@google.com>
Date: Fri, 23 May 2025 19:03:03 +0100
Subject: [PATCH] Update to Rust 2024 edition. (#2658)

---
 .github/workflows/build.sh                    |  2 +-
 .github/workflows/build.yml                   |  9 +++++++--
 .github/workflows/install-mdbook/action.yml   |  4 ++--
 .github/workflows/publish.yml                 |  3 +++
 README.md                                     |  2 +-
 book.toml                                     | 11 +++++++++-
 dprint.json                                   |  2 +-
 mdbook-course/Cargo.toml                      |  2 +-
 mdbook-course/src/course.rs                   | 14 ++++++-------
 mdbook-exerciser/Cargo.toml                   |  2 +-
 mdbook-exerciser/src/lib.rs                   |  2 +-
 mdbook-exerciser/src/main.rs                  |  2 +-
 src/android/interoperability/java/src/lib.rs  |  2 +-
 src/android/testing/Cargo.toml                |  2 +-
 src/bare-metal/alloc-example/Cargo.toml       |  2 +-
 src/bare-metal/alloc-example/src/main.rs      |  5 +++--
 src/bare-metal/aps/examples/Cargo.toml        |  2 +-
 src/bare-metal/aps/examples/src/exceptions.rs |  2 +-
 .../aps/examples/src/main_improved.rs         |  2 +-
 .../aps/examples/src/main_logger.rs           |  4 ++--
 .../aps/examples/src/main_minimal.rs          |  2 +-
 src/bare-metal/aps/examples/src/main_rt.rs    |  4 ++--
 .../microcontrollers/examples/Cargo.toml      |  2 +-
 .../microcontrollers/examples/src/bin/hal.rs  |  2 +-
 .../allocator-example/Cargo.toml              |  2 +-
 src/bare-metal/useful-crates/tinyvec.md       |  2 +-
 .../useful-crates/zerocopy-example/Cargo.toml |  2 +-
 src/borrowing/Cargo.toml                      |  2 +-
 src/borrowing/borrowck.md                     |  4 ++--
 src/borrowing/shared.md                       |  2 +-
 src/cargo/rust-ecosystem.md                   |  4 ++--
 src/closures/capturing.md                     |  6 +-----
 src/concurrency/async-control-flow/select.md  |  2 +-
 .../async-exercises/chat-async/Cargo.toml     |  2 +-
 .../chat-async/src/bin/client.rs              |  2 +-
 .../chat-async/src/bin/server.rs              |  2 +-
 .../async-exercises/dining-philosophers.md    |  2 +-
 .../async-exercises/dining-philosophers.rs    |  2 +-
 .../async-pitfalls/async-traits.md            |  2 +-
 src/concurrency/async-pitfalls/pin.md         |  2 +-
 src/concurrency/sync-exercises/Cargo.toml     |  2 +-
 .../sync-exercises/dining-philosophers.md     |  2 +-
 .../sync-exercises/dining-philosophers.rs     |  2 +-
 .../sync-exercises/link-checker.md            |  2 +-
 .../sync-exercises/link-checker.rs            |  4 ++--
 src/control-flow-basics/Cargo.toml            |  2 +-
 src/control-flow-basics/functions.md          |  6 +-----
 src/error-handling/Cargo.toml                 |  2 +-
 src/error-handling/anyhow.md                  |  2 +-
 src/exercises/bare-metal/compass/Cargo.toml   |  2 +-
 src/exercises/bare-metal/compass/src/main.rs  |  2 +-
 src/exercises/bare-metal/rtc/Cargo.toml       |  2 +-
 .../bare-metal/rtc/src/exceptions.rs          |  2 +-
 src/exercises/bare-metal/rtc/src/main.rs      | 10 +++++-----
 src/generics/Cargo.toml                       |  2 +-
 src/generics/generic-functions.md             | 18 +++--------------
 src/iterators/Cargo.toml                      |  2 +-
 src/iterators/intoiterator.md                 |  2 +-
 src/lifetimes/Cargo.toml                      |  4 ++--
 src/lifetimes/lifetime-annotations.md         |  6 +-----
 src/memory-management/Cargo.toml              |  2 +-
 src/methods-and-traits/Cargo.toml             |  2 +-
 src/modules/Cargo.toml                        |  2 +-
 src/modules/encapsulation.md                  |  2 +-
 src/pattern-matching/Cargo.toml               |  2 +-
 src/references/Cargo.toml                     |  2 +-
 src/smart-pointers/Cargo.toml                 |  2 +-
 src/std-traits/Cargo.toml                     |  2 +-
 src/std-types/Cargo.toml                      |  2 +-
 src/testing/Cargo.toml                        |  2 +-
 src/testing/other.md                          |  2 +-
 src/tuples-and-arrays/Cargo.toml              |  2 +-
 src/tuples-and-arrays/exercise.rs             |  4 ++--
 src/types-and-values/Cargo.toml               |  2 +-
 src/unsafe-rust/Cargo.toml                    |  4 ++--
 src/unsafe-rust/mutable-static.md             | 20 ++++++++++++-------
 src/unsafe-rust/unsafe-functions/rust.md      | 18 ++++++++++-------
 src/user-defined-types/Cargo.toml             |  2 +-
 src/user-defined-types/const.md               |  6 +-----
 theme/book.js                                 |  2 ++
 third_party/cxx/blobstore/build.rs            |  3 ++-
 xtask/Cargo.toml                              |  2 +-
 xtask/src/main.rs                             | 10 +++++-----
 83 files changed, 150 insertions(+), 149 deletions(-)

diff --git a/.github/workflows/build.sh b/.github/workflows/build.sh
index f9fecb77..5569b290 100755
--- a/.github/workflows/build.sh
+++ b/.github/workflows/build.sh
@@ -25,7 +25,7 @@ else
     # Back-date the sources to POT-Creation-Date. The content lives in two
     # directories:
     rm -r src/ third_party/
-    git restore --source "$(git rev-list -n 1 --before "$pot_creation_date" @)" src/ third_party/
+    git restore --source "$(git rev-list -n 1 --before "$pot_creation_date" @)" src/ third_party/ book.toml
     # Set language and adjust site URL. Clear the redirects since they are
     # in sync with the source files, not the translation.
     export MDBOOK_BOOK__LANGUAGE=$book_lang
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 648463b0..eb47ce4b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -161,9 +161,11 @@ jobs:
           MDBOOK_OUTPUT='{"xgettext": {"pot-file": "messages.pot"}}' mdbook build -d po
           msgfmt -o /dev/null --statistics po/messages.pot
 
-      - name: Install mdbook-linkcheck
+      - name: Install mdbook-linkcheck2
         if: contains(fromJSON(env.LINK_CHECKED_LANGUAGES), matrix.language)
-        run: cargo install mdbook-linkcheck --locked --version 0.7.7
+        run: |
+          cargo uninstall mdbook-linkcheck || true
+          cargo install mdbook-linkcheck2 --locked --version 0.9.1
 
       - name: Build ${{ matrix.language }} translation
         run: |
@@ -210,6 +212,9 @@ jobs:
         with:
           fetch-depth: 0
 
+      - name: Update Rust
+        run: rustup update
+
       - name: Setup Rust cache
         uses: ./.github/workflows/setup-rust-cache
 
diff --git a/.github/workflows/install-mdbook/action.yml b/.github/workflows/install-mdbook/action.yml
index a0869d00..7c70a10f 100644
--- a/.github/workflows/install-mdbook/action.yml
+++ b/.github/workflows/install-mdbook/action.yml
@@ -13,6 +13,6 @@ runs:
       run: |
         sudo apt-get update
         sudo apt-get install -y texlive texlive-luatex texlive-lang-cjk texlive-lang-arabic librsvg2-bin fonts-noto
-        curl -LsSf https://github.com/jgm/pandoc/releases/download/3.6.2/pandoc-3.6.2-linux-amd64.tar.gz | tar zxf -
-        echo "$PWD/pandoc-3.6.2/bin" >> $GITHUB_PATH
+        curl -LsSf https://github.com/jgm/pandoc/releases/download/3.7.0.1/pandoc-3.7.0.1-linux-amd64.tar.gz | tar zxf -
+        echo "$PWD/pandoc-3.7.0.1/bin" >> $GITHUB_PATH
       shell: bash
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 26df445a..fe0ac230 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -35,6 +35,9 @@ jobs:
         with:
           fetch-depth: 0 # We need the full history for build.sh below.
 
+      - name: Update Rust
+        run: rustup update
+
       - name: Setup Rust cache
         uses: ./.github/workflows/setup-rust-cache
 
diff --git a/README.md b/README.md
index a091b24e..c5357aac 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ The course is built using a few tools:
 - [mdbook-i18n-helpers and i18n-report](https://github.com/google/mdbook-i18n-helpers)
 - [mdbook-exerciser](mdbook-exerciser/)
 - [mdbook-course](mdbook-course/)
-- [mdbook-linkcheck](https://github.com/Michael-F-Bryan/mdbook-linkcheck)
+- [mdbook-linkcheck2](https://github.com/marxin/mdbook-linkcheck2)
 
 First install Rust by following the instructions on https://rustup.rs/. Then
 clone this repository:
diff --git a/book.toml b/book.toml
index ebdf6d96..aa678207 100644
--- a/book.toml
+++ b/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "Comprehensive Rust 🦀"
 
 [rust]
-edition = "2021"
+edition = "2024"
 
 [build]
 extra-watch-dirs = ["po", "third_party"]
@@ -299,3 +299,12 @@ exclude = [
   "comprehensive-rust-exercises.zip",
   # "crates.io", # uncomment when follow-web-links is true
 ]
+
+[output.linkcheck2]
+optional = true
+follow-web-links = false # change to true to check web links
+exclude = [
+  "comprehensive-rust.pdf",
+  "comprehensive-rust-exercises.zip",
+  # "crates.io", # uncomment when follow-web-links is true
+]
diff --git a/dprint.json b/dprint.json
index 244ce753..d47cd41c 100644
--- a/dprint.json
+++ b/dprint.json
@@ -9,7 +9,7 @@
       "command": "yapf3",
       "exts": ["py"]
     }, {
-      "command": "rustup run stable rustfmt --edition 2021",
+      "command": "rustup run stable rustfmt --edition 2024",
       "exts": ["rs"]
     }]
   },
diff --git a/mdbook-course/Cargo.toml b/mdbook-course/Cargo.toml
index 98e57f0e..0b5bb532 100644
--- a/mdbook-course/Cargo.toml
+++ b/mdbook-course/Cargo.toml
@@ -2,7 +2,7 @@
 name = "mdbook-course"
 version = "0.1.0"
 authors = ["Dustin Mitchell <djmitche@google.com>"]
-edition = "2021"
+edition = "2024"
 license = "Apache-2.0"
 publish = false
 repository = "https://github.com/google/comprehensive-rust"
diff --git a/mdbook-course/src/course.rs b/mdbook-course/src/course.rs
index 354b7e24..ec9fa3e7 100644
--- a/mdbook-course/src/course.rs
+++ b/mdbook-course/src/course.rs
@@ -35,8 +35,8 @@
 //! item becomes the first slide in that segment. Any other sub-items of the
 //! top-level item are treated as further slides in the same segment.
 
-use crate::frontmatter::{split_frontmatter, Frontmatter};
-use crate::markdown::{duration, Table};
+use crate::frontmatter::{Frontmatter, split_frontmatter};
+use crate::markdown::{Table, duration};
 use mdbook::book::{Book, BookItem, Chapter};
 use std::fmt::Write;
 use std::path::PathBuf;
@@ -318,7 +318,9 @@ impl Session {
         }
         format!(
             "Including {BREAK_DURATION} minute breaks, this session should take about {}. It contains:\n\n{}",
-            duration(self.minutes()), segments)
+            duration(self.minutes()),
+            segments
+        )
     }
 
     /// Return the total duration of this session.
@@ -337,11 +339,7 @@ impl Session {
     ///
     /// This includes breaks between segments.
     pub fn target_minutes(&self) -> u64 {
-        if self.target_minutes > 0 {
-            self.target_minutes
-        } else {
-            self.minutes()
-        }
+        if self.target_minutes > 0 { self.target_minutes } else { self.minutes() }
     }
 }
 
diff --git a/mdbook-exerciser/Cargo.toml b/mdbook-exerciser/Cargo.toml
index 6479de83..35c4a79f 100644
--- a/mdbook-exerciser/Cargo.toml
+++ b/mdbook-exerciser/Cargo.toml
@@ -2,7 +2,7 @@
 name = "mdbook-exerciser"
 version = "0.1.0"
 authors = ["Andrew Walbran <qwandor@google.com>"]
-edition = "2021"
+edition = "2024"
 license = "Apache-2.0"
 repository = "https://github.com/google/comprehensive-rust"
 description = "A tool for extracting starter code for exercises from Markdown files."
diff --git a/mdbook-exerciser/src/lib.rs b/mdbook-exerciser/src/lib.rs
index 8a13abef..6b62f7ec 100644
--- a/mdbook-exerciser/src/lib.rs
+++ b/mdbook-exerciser/src/lib.rs
@@ -14,7 +14,7 @@
 
 use log::{info, trace};
 use pulldown_cmark::{Event, Parser, Tag, TagEnd};
-use std::fs::{create_dir_all, File};
+use std::fs::{File, create_dir_all};
 use std::io::Write;
 use std::path::Path;
 
diff --git a/mdbook-exerciser/src/main.rs b/mdbook-exerciser/src/main.rs
index 57332ca2..9c2e14e4 100644
--- a/mdbook-exerciser/src/main.rs
+++ b/mdbook-exerciser/src/main.rs
@@ -14,9 +14,9 @@
 
 use anyhow::Context;
 use log::trace;
+use mdbook::BookItem;
 use mdbook::book::Book;
 use mdbook::renderer::RenderContext;
-use mdbook::BookItem;
 use mdbook_exerciser::process;
 use std::fs::{create_dir, remove_dir_all};
 use std::io::stdin;
diff --git a/src/android/interoperability/java/src/lib.rs b/src/android/interoperability/java/src/lib.rs
index 5bf2377c..fa951253 100644
--- a/src/android/interoperability/java/src/lib.rs
+++ b/src/android/interoperability/java/src/lib.rs
@@ -15,9 +15,9 @@
 // ANCHOR: hello
 //! Rust <-> Java FFI demo.
 
+use jni::JNIEnv;
 use jni::objects::{JClass, JString};
 use jni::sys::jstring;
-use jni::JNIEnv;
 
 /// HelloWorld::hello method implementation.
 // SAFETY: There is no other global function of this name.
diff --git a/src/android/testing/Cargo.toml b/src/android/testing/Cargo.toml
index 8b540e81..cb2309c9 100644
--- a/src/android/testing/Cargo.toml
+++ b/src/android/testing/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "android-testing"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[example]]
diff --git a/src/bare-metal/alloc-example/Cargo.toml b/src/bare-metal/alloc-example/Cargo.toml
index 5b83e2c9..a8dc51aa 100644
--- a/src/bare-metal/alloc-example/Cargo.toml
+++ b/src/bare-metal/alloc-example/Cargo.toml
@@ -3,7 +3,7 @@
 [package]
 name = "alloc-example"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/bare-metal/alloc-example/src/main.rs b/src/bare-metal/alloc-example/src/main.rs
index 3e1fd664..cc3e0c84 100644
--- a/src/bare-metal/alloc-example/src/main.rs
+++ b/src/bare-metal/alloc-example/src/main.rs
@@ -26,13 +26,14 @@ use buddy_system_allocator::LockedHeap;
 #[global_allocator]
 static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
 
-static mut HEAP: [u8; 65536] = [0; 65536];
+const HEAP_SIZE: usize = 65536;
+static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
 
 pub fn entry() {
     // SAFETY: `HEAP` is only used here and `entry` is only called once.
     unsafe {
         // Give the allocator some memory to allocate.
-        HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len());
+        HEAP_ALLOCATOR.lock().init(&raw mut HEAP as usize, HEAP_SIZE);
     }
 
     // Now we can do things that require heap allocation.
diff --git a/src/bare-metal/aps/examples/Cargo.toml b/src/bare-metal/aps/examples/Cargo.toml
index d7cd5045..ea84564a 100644
--- a/src/bare-metal/aps/examples/Cargo.toml
+++ b/src/bare-metal/aps/examples/Cargo.toml
@@ -3,7 +3,7 @@
 [package]
 name = "ap-examples"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/bare-metal/aps/examples/src/exceptions.rs b/src/bare-metal/aps/examples/src/exceptions.rs
index 2d5aa0cb..cc52a1fe 100644
--- a/src/bare-metal/aps/examples/src/exceptions.rs
+++ b/src/bare-metal/aps/examples/src/exceptions.rs
@@ -14,8 +14,8 @@
 
 // ANCHOR: exceptions
 use log::error;
-use smccc::psci::system_off;
 use smccc::Hvc;
+use smccc::psci::system_off;
 
 // SAFETY: There is no other global function of this name.
 #[unsafe(no_mangle)]
diff --git a/src/bare-metal/aps/examples/src/main_improved.rs b/src/bare-metal/aps/examples/src/main_improved.rs
index 90996e3a..0e262da0 100644
--- a/src/bare-metal/aps/examples/src/main_improved.rs
+++ b/src/bare-metal/aps/examples/src/main_improved.rs
@@ -23,8 +23,8 @@ use crate::pl011::Uart;
 use core::fmt::Write;
 use core::panic::PanicInfo;
 use log::error;
-use smccc::psci::system_off;
 use smccc::Hvc;
+use smccc::psci::system_off;
 
 /// Base address of the primary PL011 UART.
 const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
diff --git a/src/bare-metal/aps/examples/src/main_logger.rs b/src/bare-metal/aps/examples/src/main_logger.rs
index e6abb186..b90e5668 100644
--- a/src/bare-metal/aps/examples/src/main_logger.rs
+++ b/src/bare-metal/aps/examples/src/main_logger.rs
@@ -22,9 +22,9 @@ mod pl011;
 
 use crate::pl011::Uart;
 use core::panic::PanicInfo;
-use log::{error, info, LevelFilter};
-use smccc::psci::system_off;
+use log::{LevelFilter, error, info};
 use smccc::Hvc;
+use smccc::psci::system_off;
 
 /// Base address of the primary PL011 UART.
 const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
diff --git a/src/bare-metal/aps/examples/src/main_minimal.rs b/src/bare-metal/aps/examples/src/main_minimal.rs
index b6d2ed68..84dea5b8 100644
--- a/src/bare-metal/aps/examples/src/main_minimal.rs
+++ b/src/bare-metal/aps/examples/src/main_minimal.rs
@@ -24,8 +24,8 @@ use crate::pl011_minimal::Uart;
 use core::fmt::Write;
 use core::panic::PanicInfo;
 use log::error;
-use smccc::psci::system_off;
 use smccc::Hvc;
+use smccc::psci::system_off;
 
 /// Base address of the primary PL011 UART.
 const PL011_BASE_ADDRESS: *mut u8 = 0x900_0000 as _;
diff --git a/src/bare-metal/aps/examples/src/main_rt.rs b/src/bare-metal/aps/examples/src/main_rt.rs
index 792773b6..4f63aedc 100644
--- a/src/bare-metal/aps/examples/src/main_rt.rs
+++ b/src/bare-metal/aps/examples/src/main_rt.rs
@@ -19,14 +19,14 @@
 mod exceptions;
 
 use aarch64_paging::paging::Attributes;
-use aarch64_rt::{entry, initial_pagetable, InitialPagetable};
+use aarch64_rt::{InitialPagetable, entry, initial_pagetable};
 use arm_pl011_uart::{PL011Registers, Uart, UniqueMmioPointer};
 use core::fmt::Write;
 use core::panic::PanicInfo;
 use core::ptr::NonNull;
 use log::error;
-use smccc::psci::system_off;
 use smccc::Hvc;
+use smccc::psci::system_off;
 
 /// Base address of the primary PL011 UART.
 const PL011_BASE_ADDRESS: NonNull<PL011Registers> =
diff --git a/src/bare-metal/microcontrollers/examples/Cargo.toml b/src/bare-metal/microcontrollers/examples/Cargo.toml
index 021848a9..876f8f6f 100644
--- a/src/bare-metal/microcontrollers/examples/Cargo.toml
+++ b/src/bare-metal/microcontrollers/examples/Cargo.toml
@@ -3,7 +3,7 @@
 [package]
 name = "microcontroller-examples"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/bare-metal/microcontrollers/examples/src/bin/hal.rs b/src/bare-metal/microcontrollers/examples/src/bin/hal.rs
index 8e6356fb..3b2f05d0 100644
--- a/src/bare-metal/microcontrollers/examples/src/bin/hal.rs
+++ b/src/bare-metal/microcontrollers/examples/src/bin/hal.rs
@@ -20,7 +20,7 @@ extern crate panic_halt as _;
 
 use cortex_m_rt::entry;
 use embedded_hal::digital::OutputPin;
-use nrf52833_hal::gpio::{p0, Level};
+use nrf52833_hal::gpio::{Level, p0};
 use nrf52833_hal::pac::Peripherals;
 
 #[entry]
diff --git a/src/bare-metal/useful-crates/allocator-example/Cargo.toml b/src/bare-metal/useful-crates/allocator-example/Cargo.toml
index 40fbe5ff..38031194 100644
--- a/src/bare-metal/useful-crates/allocator-example/Cargo.toml
+++ b/src/bare-metal/useful-crates/allocator-example/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "allocator-example"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/bare-metal/useful-crates/tinyvec.md b/src/bare-metal/useful-crates/tinyvec.md
index 9a96f2a2..6e74e657 100644
--- a/src/bare-metal/useful-crates/tinyvec.md
+++ b/src/bare-metal/useful-crates/tinyvec.md
@@ -8,7 +8,7 @@ many elements are used and panics if you try to use more than are allocated.
 <!-- mdbook-xgettext: skip -->
 
 ```rust,editable,compile_fail
-use tinyvec::{array_vec, ArrayVec};
+use tinyvec::{ArrayVec, array_vec};
 
 fn main() {
     let mut numbers: ArrayVec<[u32; 5]> = array_vec!(42, 66);
diff --git a/src/bare-metal/useful-crates/zerocopy-example/Cargo.toml b/src/bare-metal/useful-crates/zerocopy-example/Cargo.toml
index c629fc7b..8d3442c3 100644
--- a/src/bare-metal/useful-crates/zerocopy-example/Cargo.toml
+++ b/src/bare-metal/useful-crates/zerocopy-example/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "zerocopy-example"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/borrowing/Cargo.toml b/src/borrowing/Cargo.toml
index df8e7ff9..38495770 100644
--- a/src/borrowing/Cargo.toml
+++ b/src/borrowing/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "borrowing"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [lib]
diff --git a/src/borrowing/borrowck.md b/src/borrowing/borrowck.md
index 40ae181a..993dd4ba 100644
--- a/src/borrowing/borrowck.md
+++ b/src/borrowing/borrowck.md
@@ -82,5 +82,5 @@ fn main() {
 
 </details>
 
-[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8f5896878611566845fe3b0f4dc5af68
-[2]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f293a31f2d4d0d31770486247c2e8437
+[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=8f5896878611566845fe3b0f4dc5af68
+[2]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f293a31f2d4d0d31770486247c2e8437
diff --git a/src/borrowing/shared.md b/src/borrowing/shared.md
index 268030ad..1e92af5f 100644
--- a/src/borrowing/shared.md
+++ b/src/borrowing/shared.md
@@ -72,4 +72,4 @@ Notes on stack returns and inlining:
 
 </details>
 
-[Playground]: https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=0cb13be1c05d7e3446686ad9947c4671
+[Playground]: https://play.rust-lang.org/?version=stable&mode=release&edition=2024&gist=0cb13be1c05d7e3446686ad9947c4671
diff --git a/src/cargo/rust-ecosystem.md b/src/cargo/rust-ecosystem.md
index c193a5a6..e4de7658 100644
--- a/src/cargo/rust-ecosystem.md
+++ b/src/cargo/rust-ecosystem.md
@@ -32,8 +32,8 @@ Key points:
 - Dependencies can also be resolved from alternative [registries], git, folders,
   and more.
 
-- Rust also has [editions]: the current edition is Rust 2021. Previous editions
-  were Rust 2015 and Rust 2018.
+- Rust also has [editions]: the current edition is Rust 2024. Previous editions
+  were Rust 2015, Rust 2018 and Rust 2021.
 
   - The editions are allowed to make backwards incompatible changes to the
     language.
diff --git a/src/closures/capturing.md b/src/closures/capturing.md
index 024d9687..1393c4a9 100644
--- a/src/closures/capturing.md
+++ b/src/closures/capturing.md
@@ -10,11 +10,7 @@ A closure can capture variables from the environment where it was defined.
 fn main() {
     let max_value = 5;
     let clamp = |v| {
-        if v > max_value {
-            max_value
-        } else {
-            v
-        }
+        if v > max_value { max_value } else { v }
     };
 
     dbg!(clamp(1));
diff --git a/src/concurrency/async-control-flow/select.md b/src/concurrency/async-control-flow/select.md
index e8581649..47908d9a 100644
--- a/src/concurrency/async-control-flow/select.md
+++ b/src/concurrency/async-control-flow/select.md
@@ -17,7 +17,7 @@ the resulting variables. The `statement` result becomes the result of the
 
 ```rust,editable,compile_fail
 use tokio::sync::mpsc;
-use tokio::time::{sleep, Duration};
+use tokio::time::{Duration, sleep};
 
 #[tokio::main]
 async fn main() {
diff --git a/src/concurrency/async-exercises/chat-async/Cargo.toml b/src/concurrency/async-exercises/chat-async/Cargo.toml
index cdb749db..2584dd37 100644
--- a/src/concurrency/async-exercises/chat-async/Cargo.toml
+++ b/src/concurrency/async-exercises/chat-async/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "chat-async"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 futures-util = { version = "0.3.31", features = ["sink"] }
diff --git a/src/concurrency/async-exercises/chat-async/src/bin/client.rs b/src/concurrency/async-exercises/chat-async/src/bin/client.rs
index bc912a28..01e0c8fd 100644
--- a/src/concurrency/async-exercises/chat-async/src/bin/client.rs
+++ b/src/concurrency/async-exercises/chat-async/src/bin/client.rs
@@ -14,8 +14,8 @@
 
 // ANCHOR: solution
 // ANCHOR: setup
-use futures_util::stream::StreamExt;
 use futures_util::SinkExt;
+use futures_util::stream::StreamExt;
 use http::Uri;
 use tokio::io::{AsyncBufReadExt, BufReader};
 use tokio_websockets::{ClientBuilder, Message};
diff --git a/src/concurrency/async-exercises/chat-async/src/bin/server.rs b/src/concurrency/async-exercises/chat-async/src/bin/server.rs
index 46aedbc2..57bc1f7b 100644
--- a/src/concurrency/async-exercises/chat-async/src/bin/server.rs
+++ b/src/concurrency/async-exercises/chat-async/src/bin/server.rs
@@ -19,7 +19,7 @@ use futures_util::stream::StreamExt;
 use std::error::Error;
 use std::net::SocketAddr;
 use tokio::net::{TcpListener, TcpStream};
-use tokio::sync::broadcast::{channel, Sender};
+use tokio::sync::broadcast::{Sender, channel};
 use tokio_websockets::{Message, ServerBuilder, WebSocketStream};
 // ANCHOR_END: setup
 
diff --git a/src/concurrency/async-exercises/dining-philosophers.md b/src/concurrency/async-exercises/dining-philosophers.md
index 6151116e..1af88c1d 100644
--- a/src/concurrency/async-exercises/dining-philosophers.md
+++ b/src/concurrency/async-exercises/dining-philosophers.md
@@ -45,7 +45,7 @@ can use the following `Cargo.toml`:
 [package]
 name = "dining-philosophers-async-dine"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 tokio = { version = "1.26.0", features = ["sync", "time", "macros", "rt-multi-thread"] }
diff --git a/src/concurrency/async-exercises/dining-philosophers.rs b/src/concurrency/async-exercises/dining-philosophers.rs
index 18d1ec65..87caaef7 100644
--- a/src/concurrency/async-exercises/dining-philosophers.rs
+++ b/src/concurrency/async-exercises/dining-philosophers.rs
@@ -15,7 +15,7 @@
 // ANCHOR: solution
 // ANCHOR: Philosopher
 use std::sync::Arc;
-use tokio::sync::{mpsc, Mutex};
+use tokio::sync::{Mutex, mpsc};
 use tokio::time;
 
 struct Chopstick;
diff --git a/src/concurrency/async-pitfalls/async-traits.md b/src/concurrency/async-pitfalls/async-traits.md
index 19cf2123..6f2d52eb 100644
--- a/src/concurrency/async-pitfalls/async-traits.md
+++ b/src/concurrency/async-pitfalls/async-traits.md
@@ -22,7 +22,7 @@ with some caveats:
 ```rust,editable,compile_fail
 use async_trait::async_trait;
 use std::time::Instant;
-use tokio::time::{sleep, Duration};
+use tokio::time::{Duration, sleep};
 
 #[async_trait]
 trait Sleeper {
diff --git a/src/concurrency/async-pitfalls/pin.md b/src/concurrency/async-pitfalls/pin.md
index 27b70a1c..72256682 100644
--- a/src/concurrency/async-pitfalls/pin.md
+++ b/src/concurrency/async-pitfalls/pin.md
@@ -20,7 +20,7 @@ location.
 ```rust,editable,compile_fail
 use tokio::sync::{mpsc, oneshot};
 use tokio::task::spawn;
-use tokio::time::{sleep, Duration};
+use tokio::time::{Duration, sleep};
 
 // A work item. In this case, just sleep for the given time and respond
 // with a message on the `respond_on` channel.
diff --git a/src/concurrency/sync-exercises/Cargo.toml b/src/concurrency/sync-exercises/Cargo.toml
index 7ea21633..69a66347 100644
--- a/src/concurrency/sync-exercises/Cargo.toml
+++ b/src/concurrency/sync-exercises/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "sync-exercises"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/concurrency/sync-exercises/dining-philosophers.md b/src/concurrency/sync-exercises/dining-philosophers.md
index b5e9429e..ccf0a644 100644
--- a/src/concurrency/sync-exercises/dining-philosophers.md
+++ b/src/concurrency/sync-exercises/dining-philosophers.md
@@ -51,7 +51,7 @@ You can use the following `Cargo.toml`:
 [package]
 name = "dining-philosophers"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 ```
 
 <details>
diff --git a/src/concurrency/sync-exercises/dining-philosophers.rs b/src/concurrency/sync-exercises/dining-philosophers.rs
index 6900acdb..d6667ecb 100644
--- a/src/concurrency/sync-exercises/dining-philosophers.rs
+++ b/src/concurrency/sync-exercises/dining-philosophers.rs
@@ -14,7 +14,7 @@
 
 // ANCHOR: solution
 // ANCHOR: Philosopher
-use std::sync::{mpsc, Arc, Mutex};
+use std::sync::{Arc, Mutex, mpsc};
 use std::thread;
 use std::time::Duration;
 
diff --git a/src/concurrency/sync-exercises/link-checker.md b/src/concurrency/sync-exercises/link-checker.md
index 46dc2c34..13744c2f 100644
--- a/src/concurrency/sync-exercises/link-checker.md
+++ b/src/concurrency/sync-exercises/link-checker.md
@@ -34,7 +34,7 @@ The `cargo add` calls will update the `Cargo.toml` file to look like this:
 [package]
 name = "link-checker"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/concurrency/sync-exercises/link-checker.rs b/src/concurrency/sync-exercises/link-checker.rs
index 442add2e..eee665cb 100644
--- a/src/concurrency/sync-exercises/link-checker.rs
+++ b/src/concurrency/sync-exercises/link-checker.rs
@@ -13,12 +13,12 @@
 // limitations under the License.
 
 // ANCHOR: solution
-use std::sync::{mpsc, Arc, Mutex};
+use std::sync::{Arc, Mutex, mpsc};
 use std::thread;
 
 // ANCHOR: setup
-use reqwest::blocking::Client;
 use reqwest::Url;
+use reqwest::blocking::Client;
 use scraper::{Html, Selector};
 use thiserror::Error;
 
diff --git a/src/control-flow-basics/Cargo.toml b/src/control-flow-basics/Cargo.toml
index 8ea0c0bd..4885f71b 100644
--- a/src/control-flow-basics/Cargo.toml
+++ b/src/control-flow-basics/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "control-flow-basics"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/control-flow-basics/functions.md b/src/control-flow-basics/functions.md
index 3cd53280..1dd1fea8 100644
--- a/src/control-flow-basics/functions.md
+++ b/src/control-flow-basics/functions.md
@@ -8,11 +8,7 @@ minutes: 3
 
 ```rust,editable
 fn gcd(a: u32, b: u32) -> u32 {
-    if b > 0 {
-        gcd(b, a % b)
-    } else {
-        a
-    }
+    if b > 0 { gcd(b, a % b) } else { a }
 }
 
 fn main() {
diff --git a/src/error-handling/Cargo.toml b/src/error-handling/Cargo.toml
index 639cfe81..f7f8e2bc 100644
--- a/src/error-handling/Cargo.toml
+++ b/src/error-handling/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "error-handling"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/error-handling/anyhow.md b/src/error-handling/anyhow.md
index fe1bcf2a..cf3c8f5f 100644
--- a/src/error-handling/anyhow.md
+++ b/src/error-handling/anyhow.md
@@ -15,7 +15,7 @@ writing out trait impls explicitly for custom error types.
 [`thiserror`]: https://docs.rs/thiserror/
 
 ```rust,editable,compile_fail
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
 use std::fs;
 use std::io::Read;
 use thiserror::Error;
diff --git a/src/exercises/bare-metal/compass/Cargo.toml b/src/exercises/bare-metal/compass/Cargo.toml
index 9098c192..d0347bc1 100644
--- a/src/exercises/bare-metal/compass/Cargo.toml
+++ b/src/exercises/bare-metal/compass/Cargo.toml
@@ -3,7 +3,7 @@
 [package]
 name = "compass"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/exercises/bare-metal/compass/src/main.rs b/src/exercises/bare-metal/compass/src/main.rs
index 0e790514..ffcb134c 100644
--- a/src/exercises/bare-metal/compass/src/main.rs
+++ b/src/exercises/bare-metal/compass/src/main.rs
@@ -26,12 +26,12 @@ use embedded_hal::digital::InputPin;
 use lsm303agr::{
     AccelMode, AccelOutputDataRate, Lsm303agr, MagMode, MagOutputDataRate,
 };
+use microbit::Board;
 use microbit::display::blocking::Display;
 use microbit::hal::twim::Twim;
 use microbit::hal::uarte::{Baudrate, Parity, Uarte};
 use microbit::hal::{Delay, Timer};
 use microbit::pac::twim0::frequency::FREQUENCY_A;
-use microbit::Board;
 
 const COMPASS_SCALE: i32 = 30000;
 const ACCELEROMETER_SCALE: i32 = 700;
diff --git a/src/exercises/bare-metal/rtc/Cargo.toml b/src/exercises/bare-metal/rtc/Cargo.toml
index aac094a2..2ebaf98b 100644
--- a/src/exercises/bare-metal/rtc/Cargo.toml
+++ b/src/exercises/bare-metal/rtc/Cargo.toml
@@ -3,7 +3,7 @@
 [package]
 name = "rtc"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/src/exercises/bare-metal/rtc/src/exceptions.rs b/src/exercises/bare-metal/rtc/src/exceptions.rs
index fa7286d2..ba9cd225 100644
--- a/src/exercises/bare-metal/rtc/src/exceptions.rs
+++ b/src/exercises/bare-metal/rtc/src/exceptions.rs
@@ -14,8 +14,8 @@
 
 use arm_gic::gicv3::GicV3;
 use log::{error, info, trace};
-use smccc::psci::system_off;
 use smccc::Hvc;
+use smccc::psci::system_off;
 
 // SAFETY: There is no other global function of this name.
 #[unsafe(no_mangle)]
diff --git a/src/exercises/bare-metal/rtc/src/main.rs b/src/exercises/bare-metal/rtc/src/main.rs
index 55b813da..bdf5595e 100644
--- a/src/exercises/bare-metal/rtc/src/main.rs
+++ b/src/exercises/bare-metal/rtc/src/main.rs
@@ -23,20 +23,20 @@ mod logger;
 mod pl031;
 
 use crate::pl031::Rtc;
-use arm_gic::{irq_enable, wfi, IntId, Trigger};
+use arm_gic::{IntId, Trigger, irq_enable, wfi};
 use chrono::{TimeZone, Utc};
 use core::hint::spin_loop;
 // ANCHOR: imports
 use aarch64_paging::paging::Attributes;
-use aarch64_rt::{entry, initial_pagetable, InitialPagetable};
-use arm_gic::gicv3::registers::{Gicd, GicrSgi};
+use aarch64_rt::{InitialPagetable, entry, initial_pagetable};
 use arm_gic::gicv3::GicV3;
+use arm_gic::gicv3::registers::{Gicd, GicrSgi};
 use arm_pl011_uart::{PL011Registers, Uart, UniqueMmioPointer};
 use core::panic::PanicInfo;
 use core::ptr::NonNull;
-use log::{error, info, trace, LevelFilter};
-use smccc::psci::system_off;
+use log::{LevelFilter, error, info, trace};
 use smccc::Hvc;
+use smccc::psci::system_off;
 
 /// Base addresses of the GICv3.
 const GICD_BASE_ADDRESS: *mut Gicd = 0x800_0000 as _;
diff --git a/src/generics/Cargo.toml b/src/generics/Cargo.toml
index 80a38c25..cc851e2a 100644
--- a/src/generics/Cargo.toml
+++ b/src/generics/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "generics"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [lib]
diff --git a/src/generics/generic-functions.md b/src/generics/generic-functions.md
index 2bad39a3..0cbf498c 100644
--- a/src/generics/generic-functions.md
+++ b/src/generics/generic-functions.md
@@ -9,11 +9,7 @@ Rust supports generics, which lets you abstract algorithms or data structures
 
 ```rust,editable
 fn pick<T>(cond: bool, left: T, right: T) -> T {
-    if cond {
-        left
-    } else {
-        right
-    }
+    if cond { left } else { right }
 }
 
 fn main() {
@@ -31,19 +27,11 @@ fn main() {
 
   ```rust
   fn pick_i32(cond: bool, left: i32, right: i32) -> i32 {
-      if cond {
-          left
-      } else {
-          right
-      }
+      if cond { left } else { right }
   }
 
   fn pick_char(cond: bool, left: char, right: char) -> char {
-      if cond {
-          left
-      } else {
-          right
-      }
+      if cond { left } else { right }
   }
   ```
 
diff --git a/src/iterators/Cargo.toml b/src/iterators/Cargo.toml
index c379e76d..2ed59362 100644
--- a/src/iterators/Cargo.toml
+++ b/src/iterators/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "iterators"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [lib]
diff --git a/src/iterators/intoiterator.md b/src/iterators/intoiterator.md
index 2ecee307..781ae236 100644
--- a/src/iterators/intoiterator.md
+++ b/src/iterators/intoiterator.md
@@ -87,4 +87,4 @@ elements of `some_vector`.
 
 </details>
 
-[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=947e371c7295af758504f01f149023a1
+[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=947e371c7295af758504f01f149023a1
diff --git a/src/lifetimes/Cargo.toml b/src/lifetimes/Cargo.toml
index 9f992176..44f73820 100644
--- a/src/lifetimes/Cargo.toml
+++ b/src/lifetimes/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "lifetimes"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
-thiserror = "*"
+thiserror = "2.0.11"
 
 [lib]
 name = "protobuf"
diff --git a/src/lifetimes/lifetime-annotations.md b/src/lifetimes/lifetime-annotations.md
index 8d827999..b80ff854 100644
--- a/src/lifetimes/lifetime-annotations.md
+++ b/src/lifetimes/lifetime-annotations.md
@@ -29,11 +29,7 @@ job.
 struct Point(i32, i32);
 
 fn left_most(p1: &Point, p2: &Point) -> &Point {
-    if p1.0 < p2.0 {
-        p1
-    } else {
-        p2
-    }
+    if p1.0 < p2.0 { p1 } else { p2 }
 }
 
 fn main() {
diff --git a/src/memory-management/Cargo.toml b/src/memory-management/Cargo.toml
index d3d6e0c8..49cc396b 100644
--- a/src/memory-management/Cargo.toml
+++ b/src/memory-management/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "memory-management"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/methods-and-traits/Cargo.toml b/src/methods-and-traits/Cargo.toml
index b04dbde9..781a640a 100644
--- a/src/methods-and-traits/Cargo.toml
+++ b/src/methods-and-traits/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "methods-and-traits"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/modules/Cargo.toml b/src/modules/Cargo.toml
index a86b0386..df29ef89 100644
--- a/src/modules/Cargo.toml
+++ b/src/modules/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "modules"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/modules/encapsulation.md b/src/modules/encapsulation.md
index 98fd8068..b646038b 100644
--- a/src/modules/encapsulation.md
+++ b/src/modules/encapsulation.md
@@ -75,4 +75,4 @@ fn main() {
 
 </details>
 
-[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3e61f43c88de12bcdf69c1d6df9ab3da
+[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=3e61f43c88de12bcdf69c1d6df9ab3da
diff --git a/src/pattern-matching/Cargo.toml b/src/pattern-matching/Cargo.toml
index 35fb9d8b..0af7fa68 100644
--- a/src/pattern-matching/Cargo.toml
+++ b/src/pattern-matching/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "pattern-matching"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [lib]
diff --git a/src/references/Cargo.toml b/src/references/Cargo.toml
index 279efb39..adb49a79 100644
--- a/src/references/Cargo.toml
+++ b/src/references/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "references"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/smart-pointers/Cargo.toml b/src/smart-pointers/Cargo.toml
index c5262152..edb7408f 100644
--- a/src/smart-pointers/Cargo.toml
+++ b/src/smart-pointers/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "smart-pointers"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [lib]
diff --git a/src/std-traits/Cargo.toml b/src/std-traits/Cargo.toml
index bc9dec7f..b0a496e5 100644
--- a/src/std-traits/Cargo.toml
+++ b/src/std-traits/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "std-traits"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [lib]
diff --git a/src/std-types/Cargo.toml b/src/std-types/Cargo.toml
index fe46d703..bcfaadc5 100644
--- a/src/std-types/Cargo.toml
+++ b/src/std-types/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "std-types"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/testing/Cargo.toml b/src/testing/Cargo.toml
index c6abcf21..e5f59ab5 100644
--- a/src/testing/Cargo.toml
+++ b/src/testing/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "testing"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [lints.rust]
diff --git a/src/testing/other.md b/src/testing/other.md
index b361829b..09666639 100644
--- a/src/testing/other.md
+++ b/src/testing/other.md
@@ -44,4 +44,4 @@ pub fn shorten_string(s: &str, length: usize) -> &str {
 - Adding `#` in the code will hide it from the docs, but will still compile/run
   it.
 - Test the above code on the
-  [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0).
+  [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=3ce2ad13ea1302f6572cb15cd96becf0).
diff --git a/src/tuples-and-arrays/Cargo.toml b/src/tuples-and-arrays/Cargo.toml
index 18673731..8068fc01 100644
--- a/src/tuples-and-arrays/Cargo.toml
+++ b/src/tuples-and-arrays/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "tuples-and-arrays"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/tuples-and-arrays/exercise.rs b/src/tuples-and-arrays/exercise.rs
index a84caf85..e9c7d665 100644
--- a/src/tuples-and-arrays/exercise.rs
+++ b/src/tuples-and-arrays/exercise.rs
@@ -40,8 +40,8 @@ fn main() {
 // ANCHOR_END: main
 // ANCHOR_END: solution
 
-// This test does not appear in the exercise, as this is very early in the course, but it verifies
-// that the solution is correct.
+// This test does not appear in the exercise, as this is very early in the
+// course, but it verifies that the solution is correct.
 #[test]
 fn test_transpose() {
     let matrix = [
diff --git a/src/types-and-values/Cargo.toml b/src/types-and-values/Cargo.toml
index 4603dd99..112fa7eb 100644
--- a/src/types-and-values/Cargo.toml
+++ b/src/types-and-values/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "types-and-values"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/unsafe-rust/Cargo.toml b/src/unsafe-rust/Cargo.toml
index d846404d..95710453 100644
--- a/src/unsafe-rust/Cargo.toml
+++ b/src/unsafe-rust/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "unsafe-rust"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
-tempfile = "*"
+tempfile = "3.17.1"
 
 [[bin]]
 name = "listdir"
diff --git a/src/unsafe-rust/mutable-static.md b/src/unsafe-rust/mutable-static.md
index 31ec9bad..d39046d6 100644
--- a/src/unsafe-rust/mutable-static.md
+++ b/src/unsafe-rust/mutable-static.md
@@ -22,6 +22,7 @@ static mut COUNTER: u32 = 0;
 
 fn add_to_counter(inc: u32) {
     // SAFETY: There are no other threads which could be accessing `COUNTER`.
+    #[allow(static_mut_refs)]
     unsafe {
         COUNTER += inc;
     }
@@ -31,6 +32,7 @@ fn main() {
     add_to_counter(42);
 
     // SAFETY: There are no other threads which could be accessing `COUNTER`.
+    #[allow(static_mut_refs)]
     unsafe {
         dbg!(COUNTER);
     }
@@ -40,12 +42,16 @@ fn main() {
 <details>
 
 - The program here is safe because it is single-threaded. However, the Rust
-  compiler is conservative and will assume the worst. Try removing the `unsafe`
-  and see how the compiler explains that it is undefined behavior to mutate a
-  static from multiple threads.
-
-- Using a mutable static is generally a bad idea, but there are some cases where
-  it might make sense in low-level `no_std` code, such as implementing a heap
-  allocator or working with some C APIs.
+  compiler reasons about functions individually so can't assume that. Try
+  removing the `unsafe` and see how the compiler explains that it is undefined
+  behavior to access a mutable static from multiple threads.
+- Rust 2024 edition goes further and makes accessing a mutable static by
+  reference an error by default. We work around this in the example with
+  `#[allow(static_mut_refs)]`. Don't do this.
+- Using a mutable static is almost always a bad idea, you should use interior
+  mutability instead.
+- There are some cases where it might be necessary in low-level `no_std` code,
+  such as implementing a heap allocator or working with some C APIs. In this
+  case you should use pointers rather than references.
 
 </details>
diff --git a/src/unsafe-rust/unsafe-functions/rust.md b/src/unsafe-rust/unsafe-functions/rust.md
index 7d6bb01b..2473fc7e 100644
--- a/src/unsafe-rust/unsafe-functions/rust.md
+++ b/src/unsafe-rust/unsafe-functions/rust.md
@@ -11,9 +11,13 @@ preconditions to avoid undefined behaviour.
 /// The pointers must be valid, properly aligned, and not otherwise accessed for
 /// the duration of the function call.
 unsafe fn swap(a: *mut u8, b: *mut u8) {
-    let temp = *a;
-    *a = *b;
-    *b = temp;
+    // SAFETY: Our caller promised that the pointers are valid, properly aligned
+    // and have no other access.
+    unsafe {
+        let temp = *a;
+        *a = *b;
+        *b = temp;
+    }
 }
 
 fn main() {
@@ -35,9 +39,9 @@ fn main() {
 We wouldn't actually use pointers for a `swap` function --- it can be done
 safely with references.
 
-Note that unsafe code is allowed within an unsafe function without an `unsafe`
-block. We can prohibit this with `#[deny(unsafe_op_in_unsafe_fn)]`. Try adding
-it and see what happens. This will
-[change in the 2024 Rust edition](https://github.com/rust-lang/rust/issues/120535).
+Note that Rust 2021 and earlier allow unsafe code within an unsafe function
+without an `unsafe` block. This changed in the 2024 edition. We can prohibit it
+in older editions with `#[deny(unsafe_op_in_unsafe_fn)]`. Try adding it and see
+what happens.
 
 </details>
diff --git a/src/user-defined-types/Cargo.toml b/src/user-defined-types/Cargo.toml
index 10655db3..8f44b4c4 100644
--- a/src/user-defined-types/Cargo.toml
+++ b/src/user-defined-types/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "user-defined-types"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [[bin]]
diff --git a/src/user-defined-types/const.md b/src/user-defined-types/const.md
index 08b4c742..d7d1b4be 100644
--- a/src/user-defined-types/const.md
+++ b/src/user-defined-types/const.md
@@ -14,11 +14,7 @@ const DIGEST_SIZE: usize = 3;
 const FILL_VALUE: u8 = calculate_fill_value();
 
 const fn calculate_fill_value() -> u8 {
-    if DIGEST_SIZE < 10 {
-        42
-    } else {
-        13
-    }
+    if DIGEST_SIZE < 10 { 42 } else { 13 }
 }
 
 fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {
diff --git a/theme/book.js b/theme/book.js
index 66e86989..7d53c735 100644
--- a/theme/book.js
+++ b/theme/book.js
@@ -137,6 +137,8 @@ function playground_text(playground, hidden = true) {
             edition = "2018";
         } else if(classes.contains("edition2021")) {
             edition = "2021";
+        } else if(classes.contains("edition2024")) {
+            edition = "2024";
         }
         var params = {
             backtrace: true,
diff --git a/third_party/cxx/blobstore/build.rs b/third_party/cxx/blobstore/build.rs
index 41158115..c3b0031e 100644
--- a/third_party/cxx/blobstore/build.rs
+++ b/third_party/cxx/blobstore/build.rs
@@ -1,6 +1,7 @@
 fn main() {
     // Find target directory, either from CARGO_TARGET_DIR or in-tree if unset.
-    let mut src_dir = std::env::var_os("CARGO_TARGET_DIR").unwrap_or("../../../target".into());
+    let mut src_dir =
+        std::env::var_os("CARGO_TARGET_DIR").unwrap_or("../../../target".into());
     src_dir.push("/cxxbridge/demo/src");
 
     cxx_build::bridge("src/main.rs")
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index a012f45a..da177eb4 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "xtask"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 publish = false
 
 [dependencies]
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index e355b6e0..0e43de38 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -19,7 +19,7 @@
 //! `cargo xtask install-tools` and the logic defined here will install
 //! the tools.
 
-use anyhow::{anyhow, Ok, Result};
+use anyhow::{Ok, Result, anyhow};
 use clap::{Parser, ValueEnum};
 use std::path::Path;
 use std::{env, process::Command};
@@ -79,10 +79,10 @@ fn install_tools() -> Result<()> {
     let install_args = vec![
         // The --locked flag is important for reproducible builds. It also
         // avoids breakage due to skews between mdbook and mdbook-svgbob.
-        vec!["mdbook", "--locked", "--version", "0.4.44"],
-        vec!["mdbook-svgbob", "--locked", "--version", "0.2.1"],
-        vec!["mdbook-pandoc", "--locked", "--version", "0.9.3"],
-        vec!["mdbook-i18n-helpers", "--locked", "--version", "0.3.5"],
+        vec!["mdbook", "--locked", "--version", "0.4.48"],
+        vec!["mdbook-svgbob", "--locked", "--version", "0.2.2"],
+        vec!["mdbook-pandoc", "--locked", "--version", "0.10.4"],
+        vec!["mdbook-i18n-helpers", "--locked", "--version", "0.3.6"],
         vec!["i18n-report", "--locked", "--version", "0.2.0"],
         // Mdbook-exerciser and mdbook-course are located in this repository.
         // To make it possible to install them from any directory we need to