diff --git a/src/concurrency.md b/src/concurrency.md
index de6809721530..18f8f1f8d891 100644
--- a/src/concurrency.md
+++ b/src/concurrency.md
@@ -11,3 +11,13 @@ channels.
The Rust type system plays an important role in making many concurrency bugs
compile time bugs. This is often referred to as _fearless concurrency_ since you
can rely on the compiler to ensure correctness at runtime.
+
+
+
+- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc.
+- The type system gives us safety for concurrency without any special features.
+- The same tools that help with "concurrent" access in a single thread (e.g., a
+ called function that might mutate an argument or save references to it to read
+ later) save us from multi-threading issues.
+
+
diff --git a/src/concurrency/threads.md b/src/concurrency/threads.md
index 2f7456ab65b8..432231c876d1 100644
--- a/src/concurrency/threads.md
+++ b/src/concurrency/threads.md
@@ -27,19 +27,48 @@ fn main() {
-Key points:
+- Rust thread APIs look not too different from e.g. C++ ones.
-- Notice that the thread is stopped before it reaches 10 --- the main thread is
- not waiting.
+- Run the example.
+ - 5ms timing is loose enough that main and spawned threads stay mostly in
+ lockstep.
+ - Notice that the program ends before the spawned thread reaches 10!
+ - This is because main ends the program and spawned threads do not make it
+ persist.
+ - Compare to pthreads/C++ std::thread/boost::thread if desired.
+
+- How do we wait around for the spawned thread to complete?
+- [`thread::spawn`] returns a `JoinHandle`. Look at the docs.
+ - `JoinHandle` has a [`.join()`] method that blocks.
- Use `let handle = thread::spawn(...)` and later `handle.join()` to wait for
- the thread to finish.
+ the thread to finish and have the program count all the way to 10.
+
+- Now what if we want to return a value?
+- Look at docs again:
+ - [`thread::spawn`]'s closure returns `T`
+ - `JoinHandle` [`.join()`] returns `thread::Result`
+
+- Use the `Result` return value from `handle.join()` to get access to the
+ returned value.
+
+- Ok, what about the other case?
+ - Trigger a panic in the thread. Note that this doesn't panic `main`.
+ - Access the panic payload. This is a good time to talk about [`Any`].
-- Trigger a panic in the thread, notice how this doesn't affect `main`.
+- Now we can return values from threads! What about taking inputs?
+ - Capture something by reference in the thread closure.
+ - An error message indicates we must move it.
+ - Move it in, see we can compute and then return a derived value.
-- Use the `Result` return value from `handle.join()` to get access to the panic
- payload. This is a good time to talk about [`Any`].
+- If we want to borrow?
+ - Main kills child threads when it returns, but another function would just
+ return and leave them running.
+ - That would be stack use-after-return, which violates memory safety!
+ - How do we avoid this? see next slide.
[`Any`]: https://doc.rust-lang.org/std/any/index.html
+[`thread::spawn`]: https://doc.rust-lang.org/std/thread/fn.spawn.html
+[`.join()`]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join