# Rust Coding Guidelines ## Core Stack | Component | Required | Forbidden | |-----------|----------|-----------| | Error handling | `thiserror` (lib), `anyhow` (bin) | `.unwrap()`, `.expect()`, `panic!` | | Serialization | `rkyv` (zero-copy) | `serde_json` for hot paths | | Hashing | `blake3` | `sha256`, `md5` | | KV Store | `sled` (MVP), trait-abstracted | Direct `rocksdb` coupling | | Async | `tokio` (if needed) | `async-std` | | Logging | `tracing` | `log`, `println!` | | Testing | `proptest`, `rstest` | Mocking core types | ## Error Handling ```rust // Library errors: use thiserror #[derive(Debug, thiserror::Error)] pub enum StemeError { #[error("assertion not found: {0}")] NotFound(Hash), #[error("invalid signature for agent {agent}")] InvalidSignature { agent: AgentId }, #[error("storage error: {0}")] Storage(#[from] sled::Error), } // Always add context fn load_assertion(hash: &Hash) -> Result { let bytes = self.store .get(hash) .context("failed to read from store")? .ok_or(StemeError::NotFound(*hash))?; // ... } ``` ## Type Safety Use newtypes for domain IDs: ```rust // Good: Strong types pub struct EntityId(pub String); pub struct RelationId(pub String); pub struct Hash(pub [u8; 32]); pub struct AgentId(pub [u8; 32]); // Bad: Stringly-typed fn query(subject: String, predicate: String) -> ... ``` ## Formatting - Use underscores in numbers: `1_000_000` not `1000000` - Use inline interpolation: `println!("{hash:?}")` not `println!("{:?}", hash)` - Max line length: 100 characters - Run `cargo fmt` before commit ## Testing ```rust #[cfg(test)] mod tests { use super::*; use proptest::prelude::*; // Property test for critical invariants proptest! { #[test] fn assertion_round_trips(bytes in prop::collection::vec(any::(), 0..1000)) { let assertion = /* create from bytes */; let serialized = rkyv::to_bytes(&assertion)?; let deserialized = rkyv::from_bytes(&serialized)?; assert_eq!(assertion, deserialized); } } // Descriptive test names #[test] fn test_lens_consensus_returns_highest_vote_count() { // Arrange let assertions = vec![/* ... */]; // Act let result = ConsensusLens.resolve(&assertions, &ctx); // Assert assert_eq!(result.value, expected); } } ``` ## Module Structure ``` crates/stemedb-core/ src/ lib.rs # Public API, re-exports assertion.rs # Assertion struct types.rs # EntityId, RelationId, Hash, etc. store.rs # Storage trait error.rs # StemeError tests/ integration.rs # Cross-module tests ``` ## Documentation ```rust /// Resolves conflicting assertions using weighted voting. /// /// # Arguments /// * `candidates` - All assertions matching the query /// * `context` - Query context including agent reputations /// /// # Returns /// The assertion with highest weighted support, or None if empty. /// /// # Example /// ``` /// let lens = ConsensusLens::default(); /// let result = lens.resolve(&assertions, &ctx); /// ``` pub fn resolve(&self, candidates: &[Assertion], context: &QueryContext) -> Option { // ... } ``` ## Clippy Must pass with zero warnings: ```bash cargo clippy --workspace -- -D warnings ``` Common fixes: - `needless_return`: Remove explicit `return` when implicit works - `redundant_clone`: Check if clone is actually needed - `missing_docs`: Add doc comments to public items