Skip to content

Instantly share code, notes, and snippets.

@framp
Created May 30, 2025 11:21
Show Gist options
  • Save framp/32cb26023eb94d2fc45d440d2b3ce9ba to your computer and use it in GitHub Desktop.
Save framp/32cb26023eb94d2fc45d440d2b3ce9ba to your computer and use it in GitHub Desktop.

What is covariance?

Covariance describes how lifetimes behave in type substitution. A type is covariant over a lifetime if it can accept shorter lifetimes than originally specified.

📌 Example:

let short: &'short i32 = &5;
let long: &'long i32 = short; // allowed if &'long is covariant over 'short

#### In `ouroboros`:

When you annotate a field with:

#[covariant]

You're telling the macro:

> “This field’s type behaves covariantly over 'this (the synthetic lifetime tied to the struct). It's safe to allow borrowing access.”

For instance:

#[covariant]
fields: Vec<&'this str>,

This is safe because Vec<&'this str> is covariant—each element is a reference that can have a shorter lifetime than 'this.

Contrast with:

callback: Box<dyn Fn(&'this str)>

This is not covariant, since function traits are *invariant*. If the macro isn’t sure, it’ll ask you to annotate the field with either:

* #[covariant], or
* #[not_covariant]

The macro will then generate or skip the .borrow_*() method accordingly.

---
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment