key-paths-core 2.0.0

Trait-only keypath surface (Readable, Writable, KpTrait, AccessorTrait) for building custom keypath libraries.
Documentation

key-paths-core

Trait-only keypath contracts for Rust. No proc-macros, no locks, no async runtime — only the interfaces your library can implement so callers share a common read/write surface.

Use this crate when you want to build a custom keypath library (or integrate keypaths into an existing framework) without pulling in rust-key-paths.

For a full reference implementation (derive macros, Kp, sync/async locks, composition), see rust-key-paths in the same repository.

Traits

Trait Role
Readable<Root, Value> Getter path: root → optional Value
Writable<MutRoot, MutValue> Setter path: mut root → optional mutable Value
KeyPath<Root, Value, MutRoot, MutValue> Marker: both read and write
KpTrait<R, V, Root, Value, MutRoot, MutValue> Above + TypeId helpers + then
KeyPathValueTarget Maps &T / &mut TT for generic chaining
AccessorTrait Optional-root and or_else helpers (default methods)

Architecture: adapting keypaths in your crate

1. Pick root and value types

  • Root — what the caller passes into get (often &MyStruct or &mut MyStruct).
  • Value — what the keypath returns on success (often &Field or &mut Field).
  • MutRoot / MutValue — usually the same shapes as Root/Value for mutation paths.

Keep logical types R and V in mind when you implement [KpTrait] (for TypeId and documentation).

2. Implement Readable and Writable

use key_paths_core::{Readable, Writable};

pub struct NameKp;

impl Readable<&Person, &str> for NameKp {
    fn get(&self, root: &Person) -> Option<&str> {
        Some(&root.name)
    }
}

impl Writable<&mut Person, &mut str> for NameKp {
    fn set(&self, root: &mut Person) -> Option<&mut str> {
        Some(&mut root.name)
    }
}

3. Optional: AccessorTrait

Blanket-style ergonomics without new methods on your type:

use key_paths_core::{AccessorTrait, Readable, Writable};

impl AccessorTrait<&Person, &str, &mut Person, &mut str> for NameKp {}

4. Composition with [KpTrait::then]

Implement then on your keypath type; return type Out is inferred at the call site (see rust-key-paths Kp::then for a reference impl).

Lock traversal and async chaining stay in rust-key-paths (SyncKp, AsyncLockKp, ChainExt).

5. Async keypaths

Define async methods on your type (e.g. async fn get(&self, root: Root) -> Option<Value>), and optionally a blocking adapter that implements Readable/Writable for background threads (see rust-key-paths block_async pattern). The core traits stay synchronous so they stay runtime-agnostic.

6. Lock / shared state

Do not fake &mut T through Arc or lock-free snapshots. Either:

  • return owned snapshots / guards from your own API, or
  • use rust-key-paths lock keypaths (SyncKp, AsyncLockKp) that encode the right semantics.

Cargo.toml

[dependencies]
key-paths-core = "2"

No default features. #![no_std] with alloc not required.

Migration from 1.x

Version 2.0 is a breaking rewrite: the old dynamic KeyPaths enum and container helpers were removed in favor of these traits. Existing code that used key_paths_core::KeyPaths should stay on 1.x or migrate to rust-key-paths 3.x.

Related crates

Crate Purpose
rust-key-paths Reference Kp, derive, sync/async locks, ChainExt, HOF helpers
key-paths-derive #[derive(Kp)] proc-macro (depends on rust-key-paths, not this crate)