Skip to content

Ownership: move & borrow

Here is the idea that makes Rust feel different — and the one that makes a data engine fast. Every value has exactly one owner. Hand it to someone else and you’ve moved it; you can’t use it anymore. Or lend it out with & — a borrow — and keep ownership. Clinker leans hard on borrowing, and you can see why right in the code that reads a field.

You’ll be able to: distinguish move, borrow, and clone, and explain why FieldResolver hands out a borrow of a field rather than a copy.

When CXL evaluates status == "active", it has to read the status field of the record. The trait that resolves a field name to its value is FieldResolver:

clinker-record ·resolver.rs ·FieldResolver trait @47d2e12
pub trait FieldResolver {
fn resolve(&self, name: &str) -> Option<&Value>;
// ^^^^^^^^^^^^^^^^
// a BORROW of the value, not a copy
}

That return type — Option<&Value> — is the whole lesson. &Value is a reference: a borrow of the value living inside the record. The resolver doesn’t hand back a Value (which would mean copying it out); it hands back a window onto the one that’s already there. CXL can read status, compare it, and move on without ever duplicating it. (We’ll cover the Option part — what the None means — in the next lesson.)

Why does this matter so much? Because copying a Value isn’t always cheap. A Value::Integer is a few bytes, sure — but a Value::String owns its text and a Value::Array owns a whole Vec. Cloning those allocates. Now multiply by every field access, of every record, in a million-row file. Borrowing is free; cloning is a tax. So the engine’s rule is: borrow to read, and only pay for a clone at the one spot that genuinely needs to keep a value (a topic we finish in the zero-copy lesson, 2.8).

rust // editable

Run it, then uncomment the read(&status) line after the move and watch the borrow checker explain that status no longer holds anything.

// quick check

Why does FieldResolver::resolve return Option<&Value> instead of Option<Value>?

You understand why records flow on borrows. Next: the two types that encode “maybe absent” and “maybe failed” — Option and Result — which you already half-met in that Option<&Value>.