Recallable
recallable is a no_std-friendly crate for Memento-pattern state recovery in Rust.
It gives a type a companion memento type and a way to apply that memento to an
already initialized value.
This is useful when your runtime struct contains a mix of:
- durable state that should be restored or updated
- runtime-only fields that must stay alive across recall
Typical examples are caches, handles, closures, connection ids, metrics state, or other non-persisted fields that should not be reconstructed from serialized input.
What It Provides
Recallable: declares a companionMementotypeRecall: applies that memento infalliblyTryRecall: applies it fallibly with validation#[derive(Recallable)]: generates the companion memento type#[derive(Recall)]: generates recall logic#[recallable_model]: convenience attribute for the common model path; withserdeenabled it also removes extra derive boilerplate
The crate intentionally does not force one universal memento shape for
container-like field types. A field type can choose whole-value replacement,
selective inner updates, zipped updates, or any other domain-specific behavior
through its own Recallable::Memento and Recall::recall implementations.
Why Not Just Deserialize?
Normal Deserialize constructs a brand-new value.
That is awkward when you already have a live runtime object with fields that
should survive updates unchanged.
Recallable lets you deserialize or construct a memento and apply it to an
existing value instead:
- durable state changes
- skipped runtime fields stay untouched
- nested
#[recallable]fields use their own recall behavior
Quickstart
With the default serde feature, the easiest path is #[recallable_model].
[]
= "0.2"
= "1"
use ;
serde_json is used here because it is easy to read in documentation.
For no_std + serde deployments, prefer a no_std-compatible format such as
postcard.
#[recallable_model] injects:
#[derive(Recallable, Recall)]#[derive(serde::Serialize)]when the defaultserdefeature is enabled#[serde(skip)]for fields marked#[recallable(skip)]
Features
serde(default): enables macro-generated serde support; generated mementos deriveserde::Deserialize, and#[recallable_model]also adds the source-side serde behavior described above. This feature remains compatible withno_stdas long as your serde stack is configured forno_std.impl_from: generatesFrom<Struct>for<Struct as Recallable>::Mementofull: convenience feature forserde+impl_fromdefault-features = false: disables recallable's default serde integration. It is useful for non-serde setups, but it is not what makesno_stdpossible.
Example dependency sets:
[]
# Readable std example
= "0.1"
= { = "1", = ["derive"] }
= "1"
[]
# no_std + serde example
= { = "0.1", = false, = ["serde"] }
= { = "1", = false, = ["derive"] }
= { = "1", = false, = ["heapless"] }
= { = "0.9.2", = false }
[]
# In-memory snapshots
= { = "0.1", = ["impl_from"] }
Two Common Workflows
Persistence and restore
This is the default path when state crosses process boundaries or is written to
disk. It works in both std and no_std environments; only the serialization
format and serde configuration differ.
- Serialize the source struct.
- Deserialize into
<Type as Recallable>::Memento. - Apply the memento with
recallortry_recall.
This flow is especially convenient with #[recallable_model], because the
source struct's serialized shape already matches the generated memento shape.
In-memory snapshots
Enable impl_from when you want an owned memento inside the same process for
checkpoint/rollback, undo stacks, or test fixtures.
use ;
Manual trait implementations
You do not need the macros to use the traits. Manual implementations work whether or not serde is enabled.
Disable default features only when you want recallable itself to stop enabling serde support by default:
[]
= { = "0.1", = false }
Define the memento type and recall behavior manually:
use ;
Important Notes
#[recallable_model]must appear before the attributes it needs to inspect.- Direct
#[derive(Recallable, Recall)]does not modify source serde behavior. If you needSerializeor#[serde(skip)], add them yourself. - There is intentionally no
#[derive(TryRecall)]; fallible recall is where application-specific validation belongs. - Generated mementos are meant to be named through
<Type as Recallable>::Memento. - Generated memento fields remain private.
Current Limitations
- Derive macros currently support structs only: named, tuple, and unit structs
- Borrowed state fields are rejected unless they are skipped
#[recallable]is path-only: it supports type parameters, path types, and associated types, but not tuple/reference/slice/function syntax directly- Serde attributes are not forwarded to the generated memento
Examples
Runnable examples live under recallable/examples/:
More Documentation
- Full guide and reference: GUIDE.md
- API docs: docs.rs/recallable
- Contribution guide: CONTRIBUTING.md
- Changelog: CHANGELOG.md
License
Licensed under either MIT or Apache-2.0, at your option.