pub struct SavepointId<'t> { /* private fields */ }alloc or std only.Expand description
An opaque handle to one savepoint inside a StackedTransaction.
Returned by savepoint and consumed by
rollback_to and
release. It is a small Copy token that holds no
runtime borrow, so it can be stashed in a list of candidates or returned up the call
stack while the transaction stays open.
§Not a durable position token
A SavepointId is branded with the lifetime of the transaction that issued it, so
it cannot outlive that transaction or the input loan behind it — keeping one past the
parse is a compile error, not a dangling handle. For a position that must survive
beyond the transaction, capture a Cursor or a span instead.
§How a misused id is caught
Identity is layered, from compile time down to a runtime check, with no global state — no counter, no atomic — behind any of it:
- Temporal misuse — using an id after its transaction ended, or holding one across
the next
begin_stackedon the same input — is a compile error: the id’s invariant lifetime brand keeps the input loan open while the id is live, so the borrow checker rejects reopening it. - Cross-parser misuse — an id from another, simultaneously-live transaction over a different input — panics in every build: each id carries the address of an Input-owned field, and two live inputs occupy distinct addresses.
- Intra-transaction staleness — an id destroyed by an earlier
rollback_to/releaseon the same transaction, or one whose checkpoint a raw restore below it invalidated (see the mixing rules onStackedTransaction) — panics in every build via a membership scan of the live savepoints and their lineage (seerollback_to). State surgery is transactional and does not invalidate a savepoint.
§Compile-time rejections
The temporal and nesting misuses never reach a runtime check — the borrow checker rejects them. Each snippet below fails to compile.
Reusing an id after its transaction ended (here, across the next begin_stacked on the
same input) — the id keeps the first transaction’s loan on input open, so the second
begin_stacked cannot re-borrow it:
use tokora::{InputRef, Lexer, ParseContext};
fn temporal_misuse<'inp, L, Ctx>(input: &mut InputRef<'inp, '_, L, Ctx>)
where
L: Lexer<'inp>,
L::State: Clone,
Ctx: ParseContext<'inp, L>,
{
let sp = {
let mut txn = input.begin_stacked();
let sp = txn.savepoint();
txn.commit();
sp
};
let mut txn2 = input.begin_stacked(); // error[E0499]: `input` is still borrowed by `sp`
txn2.rollback_to(sp);
}Storing an id past the parse (returning it out of the transaction) — the brand cannot outlive the input loan:
use tokora::{InputRef, Lexer, ParseContext, SavepointId};
fn durable_misuse<'inp, 'closure, L, Ctx>(
input: &mut InputRef<'inp, 'closure, L, Ctx>,
) -> SavepointId<'closure>
where
L: Lexer<'inp>,
L::State: Clone,
Ctx: ParseContext<'inp, L>,
{
let mut txn = input.begin_stacked();
let sp = txn.savepoint();
txn.commit();
sp // error: the id's brand does not outlive the transaction's loan
}Passing a parent savepoint into a nested child transaction — the parent’s brand region strictly contains the child’s (the parent is used before the child exists), so the invariant brands cannot unify:
use tokora::{InputRef, Lexer, ParseContext};
fn parent_id_in_child<'inp, L, Ctx>(input: &mut InputRef<'inp, '_, L, Ctx>)
where
L: Lexer<'inp>,
L::State: Clone,
Ctx: ParseContext<'inp, L>,
{
let mut parent = input.begin_stacked();
let sp_parent = parent.savepoint();
let mut child = parent.begin_stacked();
child.rollback_to(sp_parent); // error[E0597]: brands cannot unify parent with child
}The mirror — keeping a child savepoint to use in the parent after the child ends — also fails: the child id keeps the child’s borrow of the parent alive, so the parent cannot be used:
use tokora::{InputRef, Lexer, ParseContext};
fn child_id_in_parent<'inp, L, Ctx>(input: &mut InputRef<'inp, '_, L, Ctx>)
where
L: Lexer<'inp>,
L::State: Clone,
Ctx: ParseContext<'inp, L>,
{
let mut parent = input.begin_stacked();
let sp_child = {
let mut child = parent.begin_stacked();
let s = child.savepoint();
child.commit();
s
};
parent.rollback_to(sp_child); // error: `parent` is still borrowed by the child id
}Trait Implementations§
Source§impl<'t> Clone for SavepointId<'t>
impl<'t> Clone for SavepointId<'t>
Source§fn clone(&self) -> SavepointId<'t>
fn clone(&self) -> SavepointId<'t>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more