pub struct FlowStack { /* private fields */ }Expand description
The main flow plus its digressions, with push-on-trigger and resume-on-completion.
While a digression is active, governance — tool admission, postures and
grounds, explain() — delegates to the active layer, and the main
flow’s marking is untouched, so Resume::Previous resumes exactly where
it left off. Driven by State and guards: model-free and deterministic.
A digression stays the active layer through the turn on which it
completes, so that turn’s projection carries its closing instruction (the
terminal stage’s posture — “hand off to a human now”), and its
Resume policy applies at the next turn boundary. Without that, a
digression that completes on its entry turn would never be seen at all.
Implementations§
Source§impl FlowStack
impl FlowStack
Sourcepub fn new(main: CompiledFlow, mode: Enforcement) -> FlowStack
pub fn new(main: CompiledFlow, mode: Enforcement) -> FlowStack
A stack over a compiled main flow with no digressions yet.
Sourcepub fn from_monitor(main: FlowMonitor) -> FlowStack
pub fn from_monitor(main: FlowMonitor) -> FlowStack
A stack whose main layer is an existing monitor (keeps its on_enter
actions and mode). This is how a bare governed flow becomes the one
governance object the control plane drives.
Sourcepub fn with_overlay(self, overlay: Overlay) -> FlowStack
pub fn with_overlay(self, overlay: Overlay) -> FlowStack
Add a digression.
Sourcepub fn with_overlays(
self,
overlays: impl IntoIterator<Item = Overlay>,
) -> FlowStack
pub fn with_overlays( self, overlays: impl IntoIterator<Item = Overlay>, ) -> FlowStack
Add several digressions, in trigger-priority order.
Sourcepub fn with_ambient_tools(
self,
tools: impl IntoIterator<Item = String>,
) -> FlowStack
pub fn with_ambient_tools( self, tools: impl IntoIterator<Item = String>, ) -> FlowStack
Admit installed service tools in the main flow and every digression. Tool-specific constraints still apply. Call this after adding overlays when constructing a stack, before it starts processing events.
Sourcepub fn with_repair(
self,
step: impl Into<String>,
policy: RepairPolicy,
) -> FlowStack
pub fn with_repair( self, step: impl Into<String>, policy: RepairPolicy, ) -> FlowStack
Attach a repair policy to a main-flow step.
Sourcepub fn with_repairs(
self,
policies: impl IntoIterator<Item = (String, RepairPolicy)>,
) -> FlowStack
pub fn with_repairs( self, policies: impl IntoIterator<Item = (String, RepairPolicy)>, ) -> FlowStack
Attach repair policies keyed by main-flow step.
Sourcepub fn with_correction(
self,
slot: impl Into<String>,
clear: impl IntoIterator<Item = String>,
) -> FlowStack
pub fn with_correction( self, slot: impl Into<String>, clear: impl IntoIterator<Item = String>, ) -> FlowStack
Watch slot for corrections: when its value changes from one captured
value to another, correction_flag(slot) is raised for that turn
and clear keys are removed from state.
Raising the flag does nothing by itself. The main flow reacts through
a Constraint::Reset gated on it, which
un-latches the stages downstream of the slot so they run again with
the corrected value. A Conversation lowers exactly that for every
collected slot, clearing the confirmation of any commit stage it
re-opens.
Sourcepub fn with_corrections(
self,
corrections: impl IntoIterator<Item = (String, Vec<String>)>,
) -> FlowStack
pub fn with_corrections( self, corrections: impl IntoIterator<Item = (String, Vec<String>)>, ) -> FlowStack
Watch several slots; see with_correction.
Sourcepub fn correction_policies(&self) -> &BTreeMap<String, Vec<String>>
pub fn correction_policies(&self) -> &BTreeMap<String, Vec<String>>
The watched slots and the keys each correction clears.
Sourcepub fn with_verbatim(
self,
step: impl Into<String>,
text: impl Into<String>,
) -> FlowStack
pub fn with_verbatim( self, step: impl Into<String>, text: impl Into<String>, ) -> FlowStack
Require step to say text word for word; see
verbatim. The stack publishes the requirement
while the step is active. Its completion guard must also require
verbatim_flag(step), which a
Conversation lowers for you.
Sourcepub fn with_verbatims(
self,
texts: impl IntoIterator<Item = (String, String)>,
) -> FlowStack
pub fn with_verbatims( self, texts: impl IntoIterator<Item = (String, String)>, ) -> FlowStack
Verbatim requirements keyed by step.
Sourcepub fn verbatim_policies(&self) -> &BTreeMap<String, String>
pub fn verbatim_policies(&self) -> &BTreeMap<String, String>
The verbatim requirements keyed by step.
Sourcepub fn with_timing(
self,
step: impl Into<String>,
timing: VoiceTiming,
) -> FlowStack
pub fn with_timing( self, step: impl Into<String>, timing: VoiceTiming, ) -> FlowStack
Attach voice timing to a step (main flow or digression).
Sourcepub fn with_timings(
self,
timings: impl IntoIterator<Item = (String, VoiceTiming)>,
) -> FlowStack
pub fn with_timings( self, timings: impl IntoIterator<Item = (String, VoiceTiming)>, ) -> FlowStack
Attach voice timing keyed by step.
Sourcepub fn timing_policies(&self) -> &BTreeMap<String, VoiceTiming>
pub fn timing_policies(&self) -> &BTreeMap<String, VoiceTiming>
The voice timing keyed by step.
Sourcepub fn active_timing(&self, state: &State) -> VoiceTiming
pub fn active_timing(&self, state: &State) -> VoiceTiming
The merged timing of the steps active right now in the driving layer (empty when none of them has timing, or the conversation is over).
Sourcepub fn publish_timing(&self, state: &State)
pub fn publish_timing(&self, state: &State)
Publish active_timing to
VOICE_TIMING_KEY in state, where the runtime’s audio path, timers
and turn lifecycle read it. Writes only on change; removes the key
when no timing applies. The stack calls this itself after every turn
and tool call; call it once when installing the stack.
Wrap in a SharedFlowStack for shared ownership between the control
lane (which advances it) and runtime accessors (which snapshot it).
Sourcepub fn mode(&self) -> Enforcement
pub fn mode(&self) -> Enforcement
The enforcement mode (shared by every layer).
Sourcepub fn overlays(&self) -> &[Overlay]
pub fn overlays(&self) -> &[Overlay]
The digressions this stack can enter, in trigger-priority order.
Sourcepub fn overlays_mut(&mut self) -> &mut [Overlay]
pub fn overlays_mut(&mut self) -> &mut [Overlay]
Mutable access to the digressions, for connect-time merges.
Sourcepub fn repair_policies(&self) -> &BTreeMap<String, RepairPolicy>
pub fn repair_policies(&self) -> &BTreeMap<String, RepairPolicy>
The repair policies keyed by main-flow step.
Sourcepub fn main(&self) -> &FlowMonitor
pub fn main(&self) -> &FlowMonitor
The main flow’s monitor, whether or not it is currently driving.
Sourcepub fn current(&self) -> &FlowMonitor
pub fn current(&self) -> &FlowMonitor
The monitor currently driving — the active digression if any, else the main flow.
Sourcepub fn active_overlay(&self) -> Option<&str>
pub fn active_overlay(&self) -> Option<&str>
The name of the driving digression, if one is suspending the main flow.
Sourcepub fn overlay_path(&self) -> Vec<&str>
pub fn overlay_path(&self) -> Vec<&str>
Every active digression, outermost first: a digression can itself be interrupted by another, which then drives until it completes.
Sourcepub fn is_complete(&self) -> bool
pub fn is_complete(&self) -> bool
Whether the conversation is finished (main complete, or a Terminate
digression ran).
Sourcepub fn is_terminated(&self) -> bool
pub fn is_terminated(&self) -> bool
Whether a Terminate digression ended the conversation. From then on
the stack governs nothing: no active steps or postures, every tool
denied. The runtime does not close the session by itself; the
application reads this (or TERMINATED_STATE_KEY) and hangs up.
Sourcepub fn on_interrupted(&mut self, state: &State)
pub fn on_interrupted(&mut self, state: &State)
Count a barge-in against every active main step and escalate those
whose policy’s escalate_after_interruptions is reached. Called by
the control plane when the user interrupts the model.
Sourcepub fn on_tool_failed(&mut self, state: &State)
pub fn on_tool_failed(&mut self, state: &State)
Count a failed (or timed-out) tool call against every active main step
and escalate those whose policy’s escalate_after_tool_failures is
reached.
Sourcepub fn on_turn(&mut self, state: &State)
pub fn on_turn(&mut self, state: &State)
Advance one turn.
A digression that completed on the previous turn has had its closing turn projected; its resume policy applies now, and the turn then proceeds as if the main flow had been driving all along (a new digression may trigger, or the main flow advances). Otherwise: advance the active digression, enter a triggered one (suspending the main flow), or advance the main flow.
Sourcepub fn on_turn_counted(&mut self, state: &State, counted: bool)
pub fn on_turn_counted(&mut self, state: &State, counted: bool)
Advance one turn, saying whether it counts toward repair thresholds.
A turn the caller had no part in, such as the greeting or the model
speaking a tool’s result, is not a stall: with counted false the
flow advances as usual but no step’s reprompt_after /
escalate_after count moves.
Sourcepub fn on_tool_ok(&mut self, tool: &str, state: &State)
pub fn on_tool_ok(&mut self, tool: &str, state: &State)
Record a successful tool call against the active layer. A no-op once the
conversation has been terminated: nothing is governing, so there is no
marking for the call to advance. (In Enforce the call is denied before
it runs; in Observe it runs but must not move a flow that has ended.)
A tool can itself fire a reset (reset(..).when(called_ok(..))), so the
main layer sheds the repair signals of whatever that un-latches, exactly
as the main layer does at a turn boundary. Repair
is tracked for the main flow only, so a digression just delegates.
Sourcepub fn observe_tool(&mut self, tool: &str, ok: bool, state: &State)
pub fn observe_tool(&mut self, tool: &str, ok: bool, state: &State)
Observe a tool call for conformance against the active layer (see
FlowMonitor::observe_tool).
After termination nothing is advanced, but in Observe the call is
still recorded as a deviation: a tool used after the conversation ended
is exactly what that mode exists to catch, and the monitor cannot see it
for itself because the denial is the stack’s, not the flow’s.
Sourcepub fn admits_tool(&self, tool: &str, state: &State) -> Result<(), String>
pub fn admits_tool(&self, tool: &str, state: &State) -> Result<(), String>
Whether tool is admitted right now (delegates to the active layer).
Every tool is denied once the conversation has been terminated.
Sourcepub fn offers_tool(&self, tool: &str, state: &State) -> bool
pub fn offers_tool(&self, tool: &str, state: &State) -> bool
Whether the active layer would admit tool once its
never(tool).until(..) guards hold (see
FlowMonitor::offers_tool). Nothing is offered after termination.
Sourcepub fn blocking_keys(&self, tool: &str, state: &State) -> BTreeSet<String>
pub fn blocking_keys(&self, tool: &str, state: &State) -> BTreeSet<String>
The state keys read by the guards that refuse tool in the active
layer right now (see FlowMonitor::blocking_keys).
Sourcepub fn explain(&self, state: &State) -> FlowExplanation
pub fn explain(&self, state: &State) -> FlowExplanation
Explain the active layer’s control-plane state. After termination:
nothing active, nothing admitted, every tool blocked with the reason —
the conversation is over, so it is waiting for nothing. To ask instead
what it never finished, read main(): its marking and
unmet_requirements() are kept intact for exactly that audit.
Sourcepub fn snapshot(&self, state: &State) -> FlowSnapshot
pub fn snapshot(&self, state: &State) -> FlowSnapshot
Observe the current layer and whole-stack lifecycle together.
Callers holding a shared stack lock get one consistent marking and
overlay path. Guard evaluation reads State, which may change
independently; this is not a transaction across all session state.
Sourcepub fn decision_scope(&self, state: &State) -> DecisionScope
pub fn decision_scope(&self, state: &State) -> DecisionScope
What a decision model should be asked now: the questions named by the driving layer’s guards and by the triggers of the digressions that can still be entered, and the state keys the driving layer could act on (for decisions that write a key). Empty after termination.
Sourcepub fn gate_scope(&self, tools: &[String], state: &State) -> DecisionScope
pub fn gate_scope(&self, tools: &[String], state: &State) -> DecisionScope
What decides whether tools are admitted now: the questions in the
driving layer’s never(tool).until(..) guards on them (a stage’s
commit), in the way of the frontier
steps that allow one, and in the triggers of the digressions that
would admit one of them, with the state keys those guards read. A call
no decision governs gets an empty scope, so it is not held up by a
decision round.
Sourcepub fn on_decisions(&mut self, state: &State)
pub fn on_decisions(&mut self, state: &State)
Apply fresh decisions mid-turn: enter a digression whose trigger now holds, or re-evaluate the driving layer. Counts no turn and advances no repair policy, so it can run before a tool call is admitted: a caller who asks for a person and a model that transfers in the same turn then see the transfer counted inside the handoff digression.
Sourcepub fn relatch(&mut self, state: &State)
pub fn relatch(&mut self, state: &State)
Re-evaluate the current layer after directly setting state.
This does not count a turn, trigger or resume digressions, or advance
repair policies. Use on_turn for a user turn.
Sourcepub fn marking(&self) -> &Marking
pub fn marking(&self) -> &Marking
The active layer’s marking (the last driving layer’s, after termination — kept for audit).
Sourcepub fn active_steps(&self, state: &State) -> Vec<&Step>
pub fn active_steps(&self, state: &State) -> Vec<&Step>
Steps of the active layer that are eligible but not yet done. Empty after termination.
Sourcepub fn active_postures(&self, state: &State) -> Vec<String>
pub fn active_postures(&self, state: &State) -> Vec<String>
Postures to project this turn: the active layer’s active steps’, or —
on the turn a digression completes — its closing steps’ (see
FlowMonitor::closing_postures). Empty after termination.
Sourcepub fn active_grounds(&self, state: &State) -> Vec<String>
pub fn active_grounds(&self, state: &State) -> Vec<String>
Grounding lines to project this turn, chosen like
active_postures.
Sourcepub fn unmet_requirements(&self) -> Vec<String>
pub fn unmet_requirements(&self) -> Vec<String>
The active layer’s unmet requirements. Empty after termination.
Sourcepub fn take_newly_active(&mut self, state: &State) -> Vec<String>
pub fn take_newly_active(&mut self, state: &State) -> Vec<String>
Steps of the active layer that became active since the last call.
Sourcepub fn enter_action(&self, step: &str) -> Option<&StepAction>
pub fn enter_action(&self, step: &str) -> Option<&StepAction>
The on_enter action registered for a step of the active layer.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for FlowStack
impl !UnwindSafe for FlowStack
impl Freeze for FlowStack
impl Send for FlowStack
impl Sync for FlowStack
impl Unpin for FlowStack
impl UnsafeUnpin for FlowStack
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
§impl<T> Conv for T
impl<T> Conv for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> Pointable for T
impl<T> Pointable for T
§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.