This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Use the helper scripts in scripts/ for development. These handle platform differences and set correct flags.
# Run all checks (fmt, check, clippy, tests, docs) for both native and wasm32
./scripts/check_all
# Individual commands (run for both native and wasm32)
./scripts/check # cargo check
./scripts/clippy # cargo clippy
./scripts/tests # cargo test
./scripts/docs # cargo doc
./scripts/fmt # cargo fmt --check
# Native-only or wasm32-only
./scripts/native/tests
./scripts/wasm32/tests
# Run a single test
cargo test test_name
# Use --relaxed to disable -D warnings (useful during development)
./scripts/tests --relaxed- Rust 1.95.0+ (Rust 2024 edition)
- For WASM testing:
cargo +nightly, thewasm_literunner (cargo install wasm_lite), and a browser with its WebDriver on PATH (Firefox/geckodriver or Chrome/chromedriver).WASM_LITE_BROWSERselects between them.
send_cells is a Rust library that provides cell types for safely sending and sharing non-Send/non-Sync types across thread boundaries. The library has two main categories:
SendCell<T>(src/send_cell.rs): Wraps non-Send types with runtime thread checking. Panics if accessed from wrong thread.SyncCell<T>(src/sync_cell.rs): Wraps non-Sync types with mutex-based synchronization for safe concurrent access.SendFuture<T>(src/send_cell.rs): Wraps non-Send futures with runtime thread checking.
UnsafeSendCell<T>(src/unsafe_send_cell.rs): No runtime checks, requires unsafe blocks for access.UnsafeSyncCell<T>(src/unsafe_sync_cell.rs): No runtime checks for Sync types.UnsafeSendFuture<T>(src/unsafe_send_cell.rs): No runtime checks for futures.
src/sys.rs: Platform-specific thread ID implementation- Special support for
wasm32-unknown-unknownwith web workers via thewasm_lite_stddependency
-
Thread Safety Model: Safe wrappers store the creation thread ID and check it on every access. Unsafe wrappers bypass these checks for performance.
-
API Design:
- Safe wrappers provide
get(),with(),with_mut()methods - Unsafe wrappers require explicit
unsafeblocks - All types implement
Sendand/orSyncas appropriate
- Safe wrappers provide
-
Error Handling: Safe wrappers panic with descriptive messages when accessed from wrong thread. Include thread IDs in panic messages for debugging.
-
Documentation: Every module has comprehensive rustdoc comments with examples. Documentation emphasizes safety requirements and use cases.
- This is a low-level concurrency library - safety is paramount
- All unsafe code must have clear SAFETY comments explaining invariants
- Tests should verify both correct usage and panic cases for wrong-thread access
- Consider platform differences, especially WASM support with web workers
Write each test once and let it run on both targets. #[wasm_lite::wasm_lite_test]
is an ordinary libtest #[test] off wasm32 and a browser test on it — do not add a
literal #[test] beside it (above the macro it registers twice), and do not reach for
the old #[cfg_attr(target_arch = "wasm32", …)] pairing.
Four shapes cover everything here:
| the test needs to… | write it as |
|---|---|
| nothing special | #[wasm_lite_test] fn |
| use another thread | #[wasm_lite_test] async fn + wasm_lite_std::spawn + .join_async().await |
| assert something panics | #[wasm_lite_test] #[should_panic(expected = "…")] |
block (join, lock_block, park) |
#[wasm_lite_test(worker)] |
Never call the blocking join from a test body: it traps on the browser's main
thread, and worker creation is delegated to that thread, so blocking there
deadlocks before the worker it waits on can start. join_async reports a
panicking worker as Err on both targets — natively by unwinding into
catch_unwind, on wasm32 through wasm_lite_std's panic hook, which publishes
the message before the trap. So "did it panic?" needs no unwinding.
catch_unwind is the one thing that genuinely does. Reach for #[should_panic]
instead wherever the test only cares that it panicked. What is left is a short
list of legitimately native-only tests — observing what happens on a thread after
it panics, and native mutex poisoning — each gated with
#[cfg(not(target_arch = "wasm32"))] and a comment saying which of the two it is.
Gate the item, never the attribute: an ungated test now runs on both targets.
Doctests that join a thread must be wrapped in wasm_lite_std::worker_doctest!,
which moves the body off the browser's main thread and is a plain call natively.