pub struct Nfa {
states: Vec<Vec<Transition>>,
start: usize,
accept: usize,
reach_accept: Vec<bool>,
min_match_rows: usize,
max_match_rows: Option<usize>,
}Expand description
A Thompson-construction NFA with a single start and single accept state.
Fields§
§states: Vec<Vec<Transition>>§start: usize§accept: usize§reach_accept: Vec<bool>Per state: whether accept is reachable from it, with every predicate assumed satisfiable.
This is the static half of Nfa::may_extend: a consuming transition at the row boundary
whose target cannot reach accept can never contribute a longer match, no matter what rows
arrive. Computed once at compile with one reverse BFS.
min_match_rows: usizeFewest rows any accepting path consumes, with every predicate assumed satisfiable — the
shortest match the pattern admits (0 when it accepts the empty match). A start with fewer
rows than this before the boundary cannot complete, so the finder skips it without a walk
(see Nfa::next_match). Computed once at compile with one 0-1 BFS.
max_match_rows: Option<usize>Most rows any path from start consumes, when the automaton is acyclic (no *, + or
unbounded range) — None when it has a cycle and a path can consume without bound. From a
position with MORE rows than this before the boundary, no path can reach the boundary while
still inside the automaton: the position is dead without a walk (see
Nfa::reaches_boundary_alive). Computed once at compile with one DFS.
Implementations§
Source§impl Nfa
impl Nfa
Sourcepub fn max_match_rows(&self) -> Option<usize>
pub fn max_match_rows(&self) -> Option<usize>
See the max_match_rows field.
Sourcefn compute_max_match_rows(
states: &[Vec<Transition>],
start: usize,
) -> Option<usize>
fn compute_max_match_rows( states: &[Vec<Transition>], start: usize, ) -> Option<usize>
See the max_match_rows field: the longest path from start counting consuming edges,
over ANY path (not only accepting ones — a path that dies still consumed its rows), or
None if a cycle is reachable. One iterative DFS with a tri-state mark, so a 100k-state
automaton does not recurse.
Sourcepub fn min_match_rows(&self) -> usize
pub fn min_match_rows(&self) -> usize
See the min_match_rows field.
Sourcefn compute_min_match_rows(
states: &[Vec<Transition>],
start: usize,
accept: usize,
) -> usize
fn compute_min_match_rows( states: &[Vec<Transition>], start: usize, accept: usize, ) -> usize
See the min_match_rows field: shortest path from start to accept where ε-edges cost
nothing and consuming edges cost one row (0-1 BFS, linear in states + transitions).
Sourcefn compute_reach_accept(states: &[Vec<Transition>], accept: usize) -> Vec<bool>
fn compute_reach_accept(states: &[Vec<Transition>], accept: usize) -> Vec<bool>
See the reach_accept field. Linear in states + transitions (one reverse BFS).
Source§impl Nfa
impl Nfa
Sourcepub async fn find_matches_dynamic(
&self,
n_rows: usize,
matcher: &(impl CandidateMatcher + Sync),
skip: &SkipMode,
) -> StreamExecutorResult<Vec<LabeledMatch>>
pub async fn find_matches_dynamic( &self, n_rows: usize, matcher: &(impl CandidateMatcher + Sync), skip: &SkipMode, ) -> StreamExecutorResult<Vec<LabeledMatch>>
Like find_matches_labeled, but membership is decided by an async CandidateMatcher
instead of precomputed satisfied-sets, so DEFINE predicates with row-pattern navigation can
be evaluated against the running match. n_rows is the number of (sorted) rows to scan. This
is the only matcher the streaming executor uses.
Sourcepub async fn next_match(
&self,
scan: &mut MatchScan,
n_rows: usize,
matcher: &(impl CandidateMatcher + Sync),
skip: &SkipMode,
budget: &mut ScanBudget,
memoize: bool,
) -> StreamExecutorResult<Option<LabeledMatch>>
pub async fn next_match( &self, scan: &mut MatchScan, n_rows: usize, matcher: &(impl CandidateMatcher + Sync), skip: &SkipMode, budget: &mut ScanBudget, memoize: bool, ) -> StreamExecutorResult<Option<LabeledMatch>>
Pull the next match at or after scan’s cursor, advancing the cursor by the skip mode.
Returns None once the cursor passes n_rows.
This is the streaming form of Nfa::find_matches_dynamic, which is a thin collect over
it. The executor’s emit loop pulls instead of collecting so that stopping — at the first
boundary match that must be held for maximality — stops the scan, not just the emission:
nothing past the held match is computed (it would be recomputed from scratch on the next
watermark anyway) and at most one match is resident at a time. Collecting is worst-case
quadratic in live rows: under an overlapping skip mode a greedy (a+) over n qualifying
rows yields n matches whose label vectors sum to O(n^2) strings, all materialized before
the first one is examined.
Sourcepub async fn reaches_boundary_alive(
&self,
pos: usize,
n_rows: usize,
matcher: &(impl CandidateMatcher + Sync),
budget: &mut ScanBudget,
memoize: bool,
) -> StreamExecutorResult<bool>
pub async fn reaches_boundary_alive( &self, pos: usize, n_rows: usize, matcher: &(impl CandidateMatcher + Sync), budget: &mut ScanBudget, memoize: bool, ) -> StreamExecutorResult<bool>
Whether a match starting at pos is still live at the safe boundary n_rows: there exists
a path that consumes the safe rows pos..n_rows and reaches the boundary while still inside
the automaton (not yet accepted), so a future row could extend it into a complete match. Used
to evict rows that can no longer be part of any match.
This is strictly stronger than “can pos begin the pattern”: for (a b) over [a, x] where
x matches neither, the a can begin the pattern, but every path dies on x before the
boundary, so the start is dead and must be evictable. A lone [a] (boundary right after a),
in contrast, is kept because a future b may still complete it.
Sourcepub fn is_linear(&self) -> bool
pub fn is_linear(&self) -> bool
Whether the automaton is a fixed-length linear chain: every state has at most one
outgoing transition (plain concatenations like (a b c) — no alternation, no
quantifiers, no PERMUTE). For such patterns an accepted match consumed the only path there
is, so no more-preferred extension can exist and Nfa::may_extend is statically false
— the emission gate can skip the probe entirely.
Sourcepub async fn may_extend(
&self,
start: usize,
end: usize,
matcher: &(impl CandidateMatcher + Sync),
budget: &mut ScanBudget,
memoize: bool,
) -> StreamExecutorResult<bool>
pub async fn may_extend( &self, start: usize, end: usize, matcher: &(impl CandidateMatcher + Sync), budget: &mut ScanBudget, memoize: bool, ) -> StreamExecutorResult<bool>
Whether the finder’s preferred result for the match starting at start could change if
more rows arrived past the boundary end.
The finder (Nfa::next_match) returns the first accepting path in transition
order: greedy quantifiers try their consume edge before their exit edge, reluctant ones the
reverse, and ordered alternation tries branches as listed. A lower-priority path can
therefore NEVER override an accepting higher-priority one, no matter what rows arrive — for
PATTERN (A (B | B C)) the first-listed B alternative wins even if a C shows up later.
So the question is not “could any NFA path consume more” but “could a path the finder
prefers over the current result become accepting”.
This walk mirrors the finder’s own traversal exactly and stops at its first accept — the
preferred result. It answers true iff, strictly before that accept in preference order,
some consuming transition was blocked by the row boundary while its target can still reach
accept (Nfa::reach_accept, unknown rows assumed satisfiable): exactly the paths that
arriving rows could turn into a more-preferred accepting result. Everything explored after
the accept is lower-priority and irrelevant.
false means the preferred result is terminal: it cannot change, so a boundary match is
final and must be emitted — holding it would starve an idle partition forever (the frontier
recompute finds neither a future row nor, without WITHIN, a deadline, and drops the
partition). true means the standard maximality wait applies.
Sourceasync fn walk(
&self,
goal: Goal<'_>,
start_pos: usize,
matcher: &(impl CandidateMatcher + Sync),
budget: &mut ScanBudget,
memo: Option<&mut Memo>,
) -> StreamExecutorResult<Option<(usize, Vec<String>)>>
async fn walk( &self, goal: Goal<'_>, start_pos: usize, matcher: &(impl CandidateMatcher + Sync), budget: &mut ScanBudget, memo: Option<&mut Memo>, ) -> StreamExecutorResult<Option<(usize, Vec<String>)>>
The one traversal behind the finder, the liveness check and the extension probe: a
depth-first search from self.start at row start_pos, in transition order, with DEFINE
predicates evaluated over the running match (path, the labels bound so far, is threaded to
the matcher). Returns the row position at which goal was met together with the labels of
the path that met it; None is “no such path” — or, with budget.hit latched, “undecided”.
Iterative, over an explicit heap stack. The recursive walkers this replaced — boxed
async_recursion frames, polled through the real thread stack once per consumed row and
once per ε-edge — needed a hard depth cap to avoid overflowing it, and the cap made any
match spanning more than a couple of hundred rows permanently undecidable: unlike the
budget it did not reset between visits, so every refresh died at the same depth. Every push
is charged to the budget, so the stack holds at most budget frames — at the executor’s
2^20 steps, about 32 megabytes of 32-byte frames in the worst case — and the budget is the
only bound on a walk.
The discipline, identical for all three goals:
- transitions are tried in the order the builder emitted them, and the FIRST verdict wins — greedy quantifiers list their consume edge before their exit edge, reluctant ones the reverse, alternation as written;
- a
Visitedscope per row position cuts ε-cycles: ε-edges keep the position and the scope, a consumed row opens a fresh one, so distinct label assignments may reach the same state at the next row; - the budget is charged once per predicate evaluation and once per edge taken, and a spent budget ends the walk without a verdict and without recording anything;
- failures are memoized only at consumption boundaries — a consumed frame starts with a fresh
scope, so its outcome is context-free — and never for a budget-aborted walk, which would
turn a transient abort into a permanent wrong verdict (see
Memo).
Sourcefn pop_failed(
stack: &mut Vec<Frame>,
scopes: &mut [Visited],
depth: &mut usize,
path: &mut Vec<String>,
memo: Option<&mut Memo>,
budget: &ScanBudget,
)
fn pop_failed( stack: &mut Vec<Frame>, scopes: &mut [Visited], depth: &mut usize, path: &mut Vec<String>, memo: Option<&mut Memo>, budget: &ScanBudget, )
Pop the top frame as a failure: unmark its state in its scope, and if it was entered by consuming a row, close that scope, drop its label, and memoize the failure.
A budget-aborted walk never gets here — every latch site returns from walk directly — so
the failure being recorded is always a proven one, by construction rather than by a guard.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Nfa
impl RefUnwindSafe for Nfa
impl Send for Nfa
impl Sync for Nfa
impl Unpin for Nfa
impl UnsafeUnpin for Nfa
impl UnwindSafe for Nfa
Blanket Implementations§
impl<T> Allocation for T
impl<T> Allocation for T
§impl<U> As for U
impl<U> As for U
§fn as_<T>(self) -> Twhere
T: CastFrom<U>,
U: Sized,
fn as_<T>(self) -> Twhere
T: CastFrom<U>,
U: Sized,
self to type T. The semantics of numeric casting with the as operator are followed, so <T as As>::as_::<U> can be used in the same way as T as U for numeric conversions. Read more§impl<T> AsAny for T
impl<T> AsAny for T
§fn any_ref(&self) -> &(dyn Any + Sync + Send + 'static)
fn any_ref(&self) -> &(dyn Any + Sync + Send + 'static)
dyn Any reference to the object: Read more§fn as_any(self: Arc<T>) -> Arc<dyn Any + Sync + Send> ⓘ
fn as_any(self: Arc<T>) -> Arc<dyn Any + Sync + Send> ⓘ
Arc<dyn Any> reference to the object: Read more§fn into_any(self: Box<T>) -> Box<dyn Any + Sync + Send>
fn into_any(self: Box<T>) -> Box<dyn Any + Sync + Send>
Box<dyn Any>: Read more§fn type_name(&self) -> &'static str
fn type_name(&self) -> &'static str
std::any::type_name, since Any does not provide it and
Any::type_id is useless as a debugging aid (its Debug is just a mess of hex digits).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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> Downcast for Twhere
T: AsAny + ?Sized,
impl<T> Downcast for Twhere
T: AsAny + ?Sized,
§fn downcast_ref<T>(&self) -> Option<&T>where
T: AsAny,
fn downcast_ref<T>(&self) -> Option<&T>where
T: AsAny,
Any.§fn downcast_mut<T>(&mut self) -> Option<&mut T>where
T: AsAny,
fn downcast_mut<T>(&mut self) -> Option<&mut T>where
T: AsAny,
Any.§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSend for T
impl<T> DowncastSend for T
§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§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<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>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> 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<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
type Err = Infallible
fn into_result(self) -> Result<T, <T as IntoResult<T>>::Err>
§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> LruValue for T
impl<T> MaybeSend for Twhere
T: Send,
impl<T> MaybeSend for Twhere
T: Send,
Source§impl<M> MetricVecRelabelExt for M
impl<M> MetricVecRelabelExt for M
Source§fn relabel(
self,
metric_level: MetricLevel,
relabel_threshold: MetricLevel,
) -> RelabeledMetricVec<M>
fn relabel( self, metric_level: MetricLevel, relabel_threshold: MetricLevel, ) -> RelabeledMetricVec<M>
RelabeledMetricVec::with_metric_level.Source§fn relabel_n(
self,
metric_level: MetricLevel,
relabel_threshold: MetricLevel,
relabel_num: usize,
) -> RelabeledMetricVec<M>
fn relabel_n( self, metric_level: MetricLevel, relabel_threshold: MetricLevel, relabel_num: usize, ) -> RelabeledMetricVec<M>
RelabeledMetricVec::with_metric_level_relabel_n.Source§fn relabel_debug_1(
self,
relabel_threshold: MetricLevel,
) -> RelabeledMetricVec<M>
fn relabel_debug_1( self, relabel_threshold: MetricLevel, ) -> RelabeledMetricVec<M>
RelabeledMetricVec::with_metric_level_relabel_n with metric_level set to
MetricLevel::Debug and relabel_num set to 1.§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> Scope for T
impl<T> Scope for T
§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.