rust-boundarieslisted
Install: claude install-skill AkashPriyadarshii/cookbook
# Rust Boundary Lessons
Lessons from a production Android IME (Rust engine via JNI). Each one broke
something real.
## 1. panic=abort + FFI = host process death
With `panic = "abort"` in the release profile, any panic inside a JNI call kills
the entire host process — the IME crash-loops on device. `.unwrap()` on a mutex
lock is the classic trigger (poisoned lock → unwrap panics → dead).
**Fix:** never `.unwrap()` on locks or FFI paths. Recover from poison:
```rust
fn engine() -> MutexGuard<'static, Predictor> {
match ENGINE.get_or_init(|| Mutex::new(Predictor::new())).lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(), // data still valid; recover
}
}
```
**Check:** grep `unwrap()` in every `extern` fn before release.
## 2. Global state: OnceLock<Mutex<T>>, init-once
Engine state as `static ENGINE: OnceLock<Mutex<T>>`. Init once from the host,
reuse across calls. OnceLock can't be cleared — "destroy" just drops on process
exit; don't pretend otherwise in a destroy() function.
## 3. Composite keys aren't JSON map keys
`HashMap<(String, String), V>` won't serialize to JSON ("key must be a string").
Flatten to row structs:
```rust
#[derive(Serialize, Deserialize)]
struct Row { k1: String, k2: String, v: u32 }
```
**Check (mandatory for any persistence):** roundtrip test (save → load → state
equal) + corrupt-input test (`from_json(b"{corrupt") == false`, state unchanged).
## 4. Cross-compile artifact discipline
- Targets configured i