Preskoči na vsebino

Rust glossary — terms in the colibri codebase

index

Non-exhaustive glossary of Rust terms and crates you’ll meet in the code. Assumes the reader is a competent programmer in another language, not a Rustacean.

Serde = serialization + deserialization. The standard Rust framework for converting structs to and from text formats (JSON, YAML, TOML, bincode, etc.).

Appears as #[derive(Serialize, Deserialize)] on structs:

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillChunk {
pub skill_id: String,
pub artifact_relative_path: String,
pub chunk_type: ChunkType,
pub content: String,
}

Without it, you’d write manual to/from code for every type. Serde auto-generates it from the field names and types.

Where it shows up:

  • colibri-skills — every public type (Skill, SkillChunk, ImportSummary)
  • colibri-ledger — TaskEval, TaskCost (SQL ↔ Rust)
  • colibri-contracts — API types (JSON over the wire)

A derive macro — Rust auto-implements traits for a struct based on the list. Common ones in Colibri:

TraitWhat it does
Debug{:?} formatting (print structs for debugging)
Clone.clone() — deep copy
Default::default() — zero-value constructor
PartialEq, Eq==, != comparison
Serialize, DeserializeSerde (see above)
Errorthiserror — makes a type usable as an error return
Parserclap — CLI argument parsing

Example:

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum SkillStatus {
#[default]
Active,
Archived,
Superseded,
}

This one line gives the enum debug printing, cloning, default value (Active), equality checks, and JSON conversion. Without derives you’d write six impl ... for SkillStatus { ... } blocks by hand.

Rust’s error-handling type. Every fallible function returns a Result:

pub fn write_task_eval(&self, te: &TaskEval) -> Result<()>

Result<()> is shorthand for Result<(), Error> — success returns nothing useful (()), failure returns an error. The caller must handle the error or propagate it with ?:

let store = Store::open(&config.db_path)?; // propagate error up
let eval = store.read_task_eval(task_id)?; // propagate error up
Ok(()) // explicit "no error"

No exceptions. No null. Errors are value, tracked by the type system.

A value that might be absent. Like null but type-safe — you can’t use the inner value without checking:

pub quality_score: Option<f64>, // might be None if task wasn't verified

Pattern matches:

if let Some(score) = eval.quality_score {
// use score here
}
let display = eval.quality_score.unwrap_or(0.0); // fallback if None

Shows up heavily in colibri-skills (optional manifest fields) and SQLite rows (nullable columns).

Atomic Reference Counted — a thread-safe shared pointer. When multiple threads need to read the same data without copying:

Arc<DaemonConfig>

The data lives as long as any Arc points to it. Frees automatically when the last reference drops. Like shared_ptr in C++, but thread-safe.

A heap-allocated value. Used when the size isn’t known at compile time, or when recursing (a type that contains itself):

Box<dyn Provider> // trait object — any type implementing Provider

The dyn keyword means “I don’t know the concrete type, but it implements this trait.” Boxed because trait objects have no fixed size.

The error derive crate. Defines error types with human-readable messages:

#[derive(Debug, thiserror::Error)]
pub enum VaultError {
#[error("tenant {0} not found")]
TenantNotFound(String),
#[error("env file write failed: {0}")]
WriteFailed(String),
}

Integrates with ? — the error type can be returned from Result.

CLI argument parser. #[derive(Parser)] turns a struct into a command-line interface:

#[derive(Parser)]
pub enum Subcommand {
IndexSkills,
ListSkills,
Search { query: String },
}

Becomes colibri index-skills, colibri list-skills, colibri search <query> with —help auto-generated.

The async runtime. Colibri’s daemon, network code, and JSONL streaming run on tokio. async fn + .await is cooperative multitasking:

async fn handle_client(stream: UnixStream) -> Result<()> {
let mut reader = BufReader::new(stream);
let mut line = String::new();
reader.read_line(&mut line).await?;
// ...
}

.await yields the thread — it’s a “pause here, run other tasks, come back when data arrives.”

Turbofish ::<T> — explicit type parameter when Rust can’t infer it:

serde_json::from_str::<TaskCost>(&json)?

? operator — propagate errors or unwrap success:

let value = function_that_returns_result()?;
// if Ok, value is the inner; if Err, return early

match — exhaustive pattern matching:

match status {
SkillStatus::Active => { /* handle */ }
SkillStatus::Archived => { /* handle */ }
SkillStatus::Superseded => { /* handle */ }
}

The compiler requires all cases — missed cases are a compile error, not a runtime bug.

The Rust Book: <https://doc.rust-lang.org/book/> (free, covers everything above)

Rust by Example: <https://doc.rust-lang.org/rust-by-example/> (cookbook)

Serde docs: <https://serde.rs/> (derive macros, format support)