minikin
presence-rs
Rust

A Rust library providing a tri-state type for representing value presence in schemas and data structures.

Last updated Jun 6, 2026
39
Stars
0
Forks
0
Issues
0
Stars/day
Attention Score
23
Language breakdown
No language data available.
โ–ธ Files click to expand
README

Presence

CI crates.io License: MIT

A Rust library providing a tri-state type for representing value presence
in schemas and data structures.
[!TIP]
If you what to read more about the motivation behind this crate, check out
Stop Losing Intent: Absent, Null, and Value in Rust
- Overview - Cardinality - Why Not Option<Option<T>>? - Usage - Examples - Basic Usage - Practical Example: API Update Request - Use Cases - Contributing - License

Overview

Presence<T> extends the traditional Option<T> two-state model (Some/None) with an additional distinction between "absent" and "null". This is particularly useful when working with serialization formats like JSON where the following states are semantically different:

  • Absent: Field not present in the data structure: {}
  • Null: Field present but explicitly set to null: {"field": null}
  • Some: Field present with a concrete value: {"field": value}

Cardinality

The Presence type increases the cardinality (number of possible states) of any wrapped type by adding two states: Absent and Null.

| Type | Valid States | Cardinality | | ---------------- | --------------------------------------------- | ----------- | | bool | true, false | 2 | | Option<bool> | None, Some(true), Some(false) | 3 | | Presence<bool> | Absent, Null, Some(true), Some(false) | 4 |

This distinction is particularly important in schema design and APIs where the semantic difference between "field not present" and "field explicitly set to null" has meaning.

Why Not Option<Option<T>>?

While Option<Option<T>> can technically represent three states, Presence<T> offers several advantages:

  • Clarity: Presence::Absent, Presence::Null, and Presence::Some(value)
are self-documenting. Compare this to None, Some(None), and Some(Some(value)) where the meaning of nested None values is ambiguous.
  • Ergonomics: Method names like isabsent(), isnull(), and is_present()
clearly express intent, versus checking option.is_none() or option == Some(None).
  • Type Safety: The compiler understands the three distinct states,
making pattern matching more explicit and reducing cognitive load.
  • Semantics: Presence models the domain concept directly rather than
forcing a tri-state model into a two-level optional structure.
// With Presence - clear and explicit
match value {
    Presence::Absent => println!("Field not in payload"),
    Presence::Null => println!("Field explicitly null"),
    Presence::Some(v) => println!("Value: {}", v),
}

// With Option<Option<T>> - confusing match value { None => println!("Field not in payload"), Some(None) => println!("Field explicitly null"), // Wait, which None? Some(Some(v)) => println!("Value: {}", v), }

Usage

Add this to your Cargo.toml:

[dependencies]
presence-rs = "0.2.0"

Examples

Basic Usage

use presence_rs::Presence;

// Create Presence values let absent: Presence<i32> = Presence::Absent; let null: Presence<i32> = Presence::Null; let some: Presence<i32> = Presence::Some(42);

// Query the state assert!(absent.is_absent()); assert!(null.is_null()); assert!(some.is_present());

Practical Example: API Update Request

use presence_rs::Presence;

#[derive(Debug)] struct UserUpdate { name: Presence<String>, email: Presence<String>, age: Presence<u32>, }

fn applyupdate(currentname: &str, update: UserUpdate) -> String { match update.name { Presence::Absent => { // Field not provided - keep current value println!("Name unchanged: {}", current_name); current_name } Presence::Null => { // Field explicitly set to null - clear it println!("Name cleared"); String::new() } Presence::Some(new_name) => { // Field has a new value - update it println!("Name updated to: {}", new_name); new_name } } }

// Example: Partial update where only email is provided let update = UserUpdate { name: Presence::Absent, // Not in request payload email: Presence::Some("new@example.com".to_string()), age: Presence::Null, // Explicitly set to null };

applyupdate("Alice".tostring(), update); // Output: "Name unchanged: Alice"

Use Cases

This type is particularly useful in:

  • API clients/servers where you need to distinguish between a field not
being sent vs. being explicitly set to null
  • Partial updates where absence means "don't change" vs. null means "clear the value"
  • Schema validation where field presence has semantic meaning
  • GraphQL implementations where null and undefined are distinct concepts
  • Database operations where you need to differentiate between "not provided"
and "set to NULL"

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ”— More in this category

ยฉ 2026 GitRepoTrend ยท minikin/presence-rs ยท Updated daily from GitHub