diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index ff7b6239941a..3c2b54db37ba 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -207,6 +207,9 @@
- [Mutable Static Variables](unsafe-rust/mutable-static.md)
- [Unions](unsafe-rust/unions.md)
- [Unsafe Functions](unsafe-rust/unsafe-functions.md)
+ - [Unsafe External Functions](unsafe-rust/unsafe-extern-c-functions.md)
+ - [Unsafe Rust Functions](unsafe-rust/unsafe-rust-functions.md)
+ - [Calling Unsafe Functions](unsafe-rust/calling-unsafe-functions.md)
- [Unsafe Traits](unsafe-rust/unsafe-traits.md)
- [Exercise: FFI Wrapper](unsafe-rust/exercise.md)
- [Solution](unsafe-rust/solution.md)
diff --git a/src/unsafe-rust/calling-unsafe-functions.md b/src/unsafe-rust/calling-unsafe-functions.md
new file mode 100644
index 000000000000..9a932894d2c5
--- /dev/null
+++ b/src/unsafe-rust/calling-unsafe-functions.md
@@ -0,0 +1,47 @@
+---
+minutes: 5
+---
+
+# Calling Unsafe Functions
+
+Failing to uphold the safety requirements breaks memory safety!
+
+```rust,editable
+#[derive(Debug)]
+#[repr(C)]
+struct KeyPair {
+ pk: [u16; 4], // 8 bytes
+ sk: [u16; 4], // 8 bytes
+}
+
+const PK_BYTE_LEN: usize = 8;
+
+fn log_public_key(pk_ptr: *const u16) {
+ let pk: &[u16] = unsafe { std::slice::from_raw_parts(pk_ptr, PK_BYTE_LEN) };
+ println!("{pk:?}");
+}
+
+fn main() {
+ let key_pair = KeyPair { pk: [1, 2, 3, 4], sk: [0, 0, 42, 0] };
+ log_public_key(key_pair.pk.as_ptr());
+}
+```
+
+Always include a safety comment for each `unsafe` block. It must explain why the
+code is actually safe. This example is missing a safety comment and has UB.
+
+
+
+Key points:
+
+- The second argument to `slice::from_raw_parts` is the number of _elements_,
+ not bytes! This example demonstrate undefined behavior by reading past the end
+ of one array and into another.
+
+- The standard library contains many low-level unsafe functions. Prefer the safe
+ alternatives when possible!
+
+- If you use an unsafe function as an optimization, make sure to add a benchmark
+ to demonstrate the gain.
+
+
diff --git a/src/unsafe-rust/unsafe-extern-c-functions.md b/src/unsafe-rust/unsafe-extern-c-functions.md
new file mode 100644
index 000000000000..8b65bdceb9f3
--- /dev/null
+++ b/src/unsafe-rust/unsafe-extern-c-functions.md
@@ -0,0 +1,33 @@
+---
+minutes: 3
+---
+
+# Unsafe External Functions
+
+All functions implemented in a foreign language are considered unsafe in Rust:
+
+```rust,editable
+extern "C" {
+ fn abs(input: i32) -> i32;
+}
+
+fn main() {
+ // SAFETY: `abs` doesn't deal with pointers and doesn't have any safety
+ // requirements.
+ unsafe {
+ println!("Absolute value of -3 according to C: {}", abs(-3));
+ }
+}
+```
+
+
+
+`abs` is unsafe because it is an external function (FFI). Calling external
+functions is usually only a problem when those functions do things with pointers
+which might violate Rust's memory model, but in general any C function might
+have undefined behaviour under any arbitrary circumstances.
+
+The `"C"` in this example is the ABI;
+[other ABIs are available too](https://doc.rust-lang.org/reference/items/external-blocks.html).
+
+
diff --git a/src/unsafe-rust/unsafe-functions.md b/src/unsafe-rust/unsafe-functions.md
index 6503506f8fc7..aba15850afae 100644
--- a/src/unsafe-rust/unsafe-functions.md
+++ b/src/unsafe-rust/unsafe-functions.md
@@ -1,100 +1,19 @@
---
-minutes: 5
+minutes: 1
---
# Unsafe Functions
-## Calling Unsafe Functions
-
A function or method can be marked `unsafe` if it has extra preconditions you
-must uphold to avoid undefined behaviour:
-
-```rust,editable
-extern "C" {
- fn abs(input: i32) -> i32;
-}
-
-fn main() {
- let emojis = "🗻∈🌏";
-
- // SAFETY: The indices are in the correct order, within the bounds of the
- // string slice, and lie on UTF-8 sequence boundaries.
- unsafe {
- println!("emoji: {}", emojis.get_unchecked(0..4));
- println!("emoji: {}", emojis.get_unchecked(4..7));
- println!("emoji: {}", emojis.get_unchecked(7..11));
- }
-
- println!("char count: {}", count_chars(unsafe { emojis.get_unchecked(0..7) }));
-
- // SAFETY: `abs` doesn't deal with pointers and doesn't have any safety
- // requirements.
- unsafe {
- println!("Absolute value of -3 according to C: {}", abs(-3));
- }
-
- // Not upholding the UTF-8 encoding requirement breaks memory safety!
- // println!("emoji: {}", unsafe { emojis.get_unchecked(0..3) });
- // println!("char count: {}", count_chars(unsafe {
- // emojis.get_unchecked(0..3) }));
-}
-
-fn count_chars(s: &str) -> usize {
- s.chars().count()
-}
-```
-
-## Writing Unsafe Functions
+must uphold to avoid undefined behaviour.
-You can mark your own functions as `unsafe` if they require particular
-conditions to avoid undefined behaviour.
+There are two main categories:
-```rust,editable
-/// Swaps the values pointed to by the given pointers.
-///
-/// # Safety
-///
-/// The pointers must be valid and properly aligned.
-unsafe fn swap(a: *mut u8, b: *mut u8) {
- let temp = *a;
- *a = *b;
- *b = temp;
-}
-
-fn main() {
- let mut a = 42;
- let mut b = 66;
-
- // SAFETY: ...
- unsafe {
- swap(&mut a, &mut b);
- }
-
- println!("a = {}, b = {}", a, b);
-}
-```
+- Foreign functions in `extern "C"` blocks.
+- Rust functions declared unsafe with `unsafe fn`.
-## Calling Unsafe Functions
-
-`get_unchecked`, like most `_unchecked` functions, is unsafe, because it can
-create UB if the range is incorrect. `abs` is incorrect for a different reason:
-it is an external function (FFI). Calling external functions is usually only a
-problem when those functions do things with pointers which might violate Rust's
-memory model, but in general any C function might have undefined behaviour under
-any arbitrary circumstances.
-
-The `"C"` in this example is the ABI;
-[other ABIs are available too](https://doc.rust-lang.org/reference/items/external-blocks.html).
-
-## Writing Unsafe Functions
-
-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 likely change in a future Rust edition.
+We will look at the two kinds of unsafe functions next.
diff --git a/src/unsafe-rust/unsafe-rust-functions.md b/src/unsafe-rust/unsafe-rust-functions.md
new file mode 100644
index 000000000000..4f2f5d8c972e
--- /dev/null
+++ b/src/unsafe-rust/unsafe-rust-functions.md
@@ -0,0 +1,44 @@
+---
+minutes: 3
+---
+
+# Unsafe Rust Functions
+
+You can mark your own functions as `unsafe` if they require particular
+conditions to avoid undefined behaviour.
+
+```rust,editable
+/// Swaps the values pointed to by the given pointers.
+///
+/// # Safety
+///
+/// The pointers must be valid and properly aligned.
+unsafe fn swap(a: *mut u8, b: *mut u8) {
+ let temp = *a;
+ *a = *b;
+ *b = temp;
+}
+
+fn main() {
+ let mut a = 42;
+ let mut b = 66;
+
+ // SAFETY: ...
+ unsafe {
+ swap(&mut a, &mut b);
+ }
+
+ println!("a = {}, b = {}", a, b);
+}
+```
+
+
+
+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 likely change in a future Rust edition.
+
+