#[non_exhaustive]pub struct Snapshot<'a> { /* private fields */ }Expand description
Borrowed view over a captured FailureDumpReport for typed
traversal of BTF-rendered map values, per-CPU entries, and
scalar variables.
Constructed from a FailureDumpReport reference (typically
obtained via super::SnapshotBridge::drain); the view is cheap to
build — it does not copy the underlying report. Accessor
methods all return further borrowed views that walk the report
in place.
Implementations§
Source§impl<'a> Snapshot<'a>
impl<'a> Snapshot<'a>
Sourcepub fn new(report: &'a FailureDumpReport) -> Self
pub fn new(report: &'a FailureDumpReport) -> Self
Build a borrowed view over report with no active-scheduler
filter. Every map-walking accessor sees every captured map.
Sourcepub fn report(&self) -> &'a FailureDumpReport
pub fn report(&self) -> &'a FailureDumpReport
Underlying FailureDumpReport borrowed back to the caller.
Escape hatch. Most consumers should reach for the typed
accessors on Snapshot / SnapshotMap / SnapshotEntry
/ SnapshotField, which route through SnapshotError and
compose with the crate::assert::temporal patterns via
SeriesField. Use
report() only when a FailureDumpReport field has no
typed accessor yet:
vcpu_regs— per-vCPU register snapshot captured at the freeze instant.vcpu_perf_at_freeze— per-vCPU hardware perf counter snapshot captured at the freeze instant.dump_truncated_at_us— microseconds-into-the-dump at which the soft deadline tripped.sdt_allocations,scx_static_ranges— SDT allocator and scx static memory layout snapshots used by the arena / pointer-renderer pipelines.schema— wire-format metadata (Self::is_placeholderalready wraps the boolean form).
All other fields documented as escape-only on
FailureDumpReport above now have first-class accessors on
Snapshot (event_counter_timeline, rq_scx_states,
dsq_states, scx_sched_state, per_cpu_time,
per_node_numa, task_enrichments, prog_runtime_stats,
probe_counters) and on SnapshotMap (ringbuf,
arena, fd_array, stack_trace, map_error).
Five *_unavailable diagnostic accessors cover the subset of
walker-backed fields the dump pipeline writes a reason string
for: Self::scx_walker_unavailable (shared by
rq_scx_states / dsq_states / scx_sched_state — the scx
walker writes one reason for the whole group),
Self::task_enrichments_unavailable,
Self::prog_runtime_stats_unavailable,
Self::per_node_numa_unavailable, and
Self::sdt_alloc_unavailable (for the still-escape-only
sdt_allocations field above). The remaining accessors
(event_counter_timeline, per_cpu_time, probe_counters)
have no companion diagnostic — empty / None is their only
“no capture” signal.
Caveats of the bypass:
- No
SnapshotErrorrouting — call-site is on its own to handle missing fields / type mismatches / per-CPU narrowing. - No
SeriesFieldintegration — temporal patterns (nondecreasing,rate_within, etc.) cannot consume rawFailureDumpReportfield values. - No placeholder-sample short-circuit
(
Self::is_placeholdercheck is the caller’s responsibility).
Sourcepub fn map(&self, name: &str) -> SnapshotResult<SnapshotMap<'a>>
pub fn map(&self, name: &str) -> SnapshotResult<SnapshotMap<'a>>
Look up a BPF map by exact name. Respects the
Self::active filter when set — only maps the filter
admits are considered. Returns SnapshotError::MapNotFound
(with the captured map names in available) when no match
is found among the admitted maps, or
SnapshotError::PlaceholderSnapshot when the snapshot’s
underlying FailureDumpReport is a placeholder (freeze
rendezvous failed; no maps to walk).
Sourcepub fn var(&self, name: &str) -> SnapshotField<'a>
pub fn var(&self, name: &str) -> SnapshotField<'a>
Walk the BTF-rendered fields of every *.bss / *.data /
*.rodata global-section map for a top-level variable
named name. Convenience for .var("nr_cpus_onln") style
scalar reads without naming the section explicitly.
Returns SnapshotField::Value on a unique match;
SnapshotField::Missing with
SnapshotError::VarNotFound (and the union of every
global-section map’s top-level member names in available)
when no map exposes the name; OR — when more than one
global-section map exposes the name — auto-falls-back to
Self::live_var semantics (delegates to
Self::active and re-projects) before yielding
SnapshotError::AmbiguousVar.
§Auto-fallback contract
When the raw scan finds 2+ hits AND the snapshot is not
already narrowed by Self::active (i.e.
self.active_obj is None), var() calls
Self::active: on Ok it returns active.var(name)
directly — whether SnapshotField::Value,
SnapshotError::VarNotFound, or
SnapshotError::AmbiguousVar persisting after the
live filter narrowed; on Err it falls through to the
pre-filter SnapshotError::AmbiguousVar (see next
section). The fallback exists so post-
crate::scenario::ops::Op::ReplaceScheduler callers
who name a global by string don’t have to know about
Self::live_var explicitly — the principled
active-scheduler walker is consulted automatically when
the raw lookup is ambiguous. Self::live_var remains
the explicit-opt-in form for callers who want the live
filter unconditionally (skip the raw-scan path).
§When AmbiguousVar STILL fires
After the auto-fallback. The raw scan found 2+ hits AND
active() failed (no scheduler attached, multi-obj
without principled walker resolution, etc.). The
found_in list names every map the raw scan saw — the
operator needs all of them to reason about which obj
they want to address via Self::map.
Sourcepub fn vars(
&self,
name: &str,
) -> impl Iterator<Item = (&'a str, SnapshotField<'a>)> + '_
pub fn vars( &self, name: &str, ) -> impl Iterator<Item = (&'a str, SnapshotField<'a>)> + '_
Iterate every global-section copy that carries a top-level
member named name. Yields (owning_map_name, field) pairs
in capture order. Use when Self::var errors
SnapshotError::AmbiguousVar and the caller needs to
reason across every observed copy explicitly (e.g. summing
counter deltas across two scheduler instances loaded
back-to-back in the same scenario).
Respects the Self::active filter when set, so chained
snapshot.active()?.vars(name) is well-defined — it iterates
only the active scheduler’s copies (typically exactly one,
since active() filters to one obj_name).
Yields nothing on placeholder snapshots (the underlying
report.maps is empty by construction so nothing matches
anyway — callers needing “is this a placeholder?” use the
Snapshot::is_placeholder accessor explicitly).
Sourcepub fn active(&self) -> SnapshotResult<Snapshot<'a>>
pub fn active(&self) -> SnapshotResult<Snapshot<'a>>
Project the snapshot to the currently-active scheduler’s
maps. Returns a filtered Snapshot whose Self::map /
Self::var / Self::vars see only the maps whose name
shares the <obj>. prefix of the active scheduler’s BPF
object. Composable: snapshot.active()?.var(name).
§When to use
Tests that swap schedulers mid-scenario (via
crate::scenario::ops::Op::ReplaceScheduler) reach for
.active() after the swap so the per-phase post-swap
snapshots resolve the live scheduler’s bss without hitting
SnapshotError::AmbiguousVar across both schedulers’
captured copies. Single-scheduler tests never need
.active() — there is no ambiguity to resolve.
§Signal source
“Active” comes from two fields the freeze coordinator populates at capture time:
crate::monitor::dump::FailureDumpReport::active_obj_name– set by the target-freeprog_idrwalker (noscx_rootdependency; works pre-6.16) that finds the live struct_ops prog’s obj prefix (seemonitor/dump/mod.rsidentify_active_obj_from_struct_ops).crate::monitor::dump::FailureDumpReport::active_map_kvas– the live scheduler’sprog.aux->used_mapsKVA set that the same walker publishes. Non-empty iff the walker resolved a global-section-bearing prog (the same-binary disambiguation case).
When the walker resolved both fields, active() uses them
directly and the obj-prefix scan below is a sanity cross-
check against the captured map set. When the walker was
unavailable (placeholder dump, transient swap window before
the accessor-init worker republished, or kernel built
without struct_ops support), the obj-prefix scan with
per-section count fallback decides.
§Failure cases
SnapshotError::PlaceholderSnapshot: the snapshot is a freeze-rendezvous-failure placeholder.SnapshotError::NoActiveScheduler(no global-section maps): the snapshot has no<obj>.bss/.data/.rodata— either no scheduler is attached, or the capture missed the global sections entirely.SnapshotError::NoActiveScheduler(multiple distinct obj prefixes, walker unavailable): two scheduler instances with DIFFERENT obj names coexist (back-to-back load of distinct binaries, or one scheduler composed of multiple BPF objects) AND the walker did not publishactive_obj_name. UseSelf::varsto enumerate every copy orSelf::mapto address a specific scheduler’s bss directly.SnapshotError::NoActiveScheduler(multi-copy same-prefix, walker unavailable): ancrate::scenario::ops::Op::ReplaceSchedulerswap between two builds of the SAME binary left two<obj>.bss(or.data/.rodata) copies with identical names AND the walker did not publishactive_map_kvasto disambiguate. The obj-prefix filter alone cannot pick the live copy without admitting both. UseSelf::live_var_via/Self::live_vars_viawithcrate::scenario::snapshot::pickers::max_by_sum_u64to pick by counter activity.
§Lifetime
Pure projection over the frozen FailureDumpReport;
multiple calls return equivalent views. Caching the result
in a let active = snapshot.active()?; binding is fine but
not required.
Sourcepub fn live_var(&self, name: &str) -> SnapshotField<'a>
pub fn live_var(&self, name: &str) -> SnapshotField<'a>
Read a single live counter from the active scheduler — the
default for single-variable reads. Convenience for
self.active()?.var(name).
For multi-variable arithmetic on multiple counters —
fractions, ratios, deltas computed across more than one
named field — use Self::live_vars_via instead.
live_vars_via resolves the picker ONCE across a name set
so independent per-name picks cannot corrupt the
cross-variable computation by selecting different bss
copies for different names. Repeatedly calling live_var
for two counters from the same scheduler is correct in the
walker-resolved case (both reads land in the same scheduler’s
bss) but loses that guarantee on the picker-fallback path
— silent corruption of ratios.
Returns a SnapshotField carrying either
SnapshotError::NoActiveScheduler (no scheduler
identifiable) or the standard Self::var error variants
(SnapshotError::VarNotFound / SnapshotError::TypeMismatch
from the inner var lookup).
Sourcepub fn live_var_via(
&self,
name: &str,
picker: impl FnOnce(&[(&'a str, SnapshotField<'a>)]) -> Option<usize>,
) -> SnapshotField<'a>
pub fn live_var_via( &self, name: &str, picker: impl FnOnce(&[(&'a str, SnapshotField<'a>)]) -> Option<usize>, ) -> SnapshotField<'a>
Caller-supplied disambiguator for the multi-bss case where
Self::live_var cannot resolve a single live copy by itself.
Self::live_var delegates to Self::active to filter the
snapshot to one scheduler’s maps. When Self::active cannot
pick a single scheduler — multiple BPF objects with
global-section maps are present AND the principled
prog_idr → prog aux->used_maps → global-section map → obj prefix
walker did not
identify the live one — it errors with
SnapshotError::NoActiveScheduler (the exact reason field
is the long-form message constructed at the bail site listing
the observed obj_names + the walker’s failure cause), and
Self::live_var propagates that as SnapshotField::Missing.
live_var_via is the escape hatch: it skips the Self::active
filter entirely, enumerates every observed copy of name via
Self::vars, and hands the slice to the caller-supplied
picker to pick one by index. Common case: an
Op::ReplaceScheduler swap between two builds of the same
scheduler that leaves two <obj>.bss maps in the snapshot
sharing one obj_name prefix.
For multi-variable arithmetic (ratios, fractions, deltas
computed across more than one named field), use
Self::live_vars_via instead — it resolves the picker once
across a name set so independent per-name picks cannot
corrupt the cross-variable computation by selecting different
bss copies for different names.
picker receives every observed copy of the named variable
(one entry per <obj>.bss/.data/.rodata map carrying it,
per Self::vars) and returns the index the caller wants
(typically chosen by inspecting each candidate’s value via
SnapshotField::as_u64 / as_str and applying a liveness
or activity fingerprint — see
crate::scenario::snapshot::pickers for predefined
pickers such as max_by_counter_value).
Returns SnapshotField::Missing when:
- the snapshot’s underlying
FailureDumpReportis a placeholder (carryingSnapshotError::PlaceholderSnapshot— matches the siblingSelf::var/Self::mapplaceholder-first contract so callers pattern-matching on the error variant distinguish “freeze rendezvous failed” from “name absent from a real capture”), - the snapshot has no copies of
name(carryingSnapshotError::VarNotFoundwith the list of available global-section maps), pickerreturnsNone(carryingSnapshotError::ProjectionFailednaming the picker as the source), ORpickerreturnsSome(idx)outside the candidate range (carryingSnapshotError::ProjectionFailedwith the bad index and the candidate count).
Sourcepub fn live_vars_via<P>(
&self,
names: &[&str],
picker: P,
) -> SnapshotResult<Vec<SnapshotField<'a>>>
pub fn live_vars_via<P>( &self, names: &[&str], picker: P, ) -> SnapshotResult<Vec<SnapshotField<'a>>>
Caller-supplied disambiguator for the multi-bss case where
multiple variables from the same scheduler instance must
be read consistently — e.g. computing
nr_mig_cross_dispatch / (nr_mig_same_dispatch + nr_mig_cross_dispatch)
as a cross-LLC dispatch fraction from one scheduler’s BPF
counters.
§Why a separate primitive
Calling Self::live_var_via N times independently risks
picking a DIFFERENT bss copy per call: the picker resolves
each name’s candidate set independently, so two consecutive
live_var_via("a", picker) + live_var_via("b", picker)
calls can land on bss copy A for a and bss copy B for b,
corrupting any cross-variable arithmetic (ratio, fraction,
delta). live_vars_via resolves the picker ONCE across the
candidate set for all N names jointly so every returned
SnapshotField reads from the same source map.
§Mechanism
Per global-section map, look up each name in input order;
keep the map as a candidate row iff it has ALL the names
(intersection semantics — partial-coverage maps are absent
from the picker’s input). The picker receives
&[(map_name, fields_in_input_order)] and returns the
chosen row’s index. The returned Vec<SnapshotField> is
positional, keyed by the input names order — result[0]
is names[0]’s field from the picked map, result[1] is
names[1]’s field, etc.
Single-section constraint. All names must reside in
the SAME global-section map — typically the scheduler’s
<obj>.bss. A bss counter co-picked with a data
constant from the same scheduler obj lands in DIFFERENT
candidate rows (the obj’s .bss map carries the first
name, its .data map carries the second, neither row has
both), the intersection collapses to empty, and the helper
returns SnapshotError::VarNotFound. If the test reads
from multiple sections, issue separate live_vars_via
calls (one per section’s name group) and compose the
per-call results caller-side.
§See also
Self::live_var_viafor single-variable disambiguation.crate::scenario::snapshot::pickers::max_by_sum_u64for the “max-activity bss” heuristic over co-picked u64 counters.
§Errors
SnapshotError::PlaceholderSnapshot— the underlyingFailureDumpReportis a placeholder; matches the siblingSelf::live_var_via/Self::var/Self::mapplaceholder-first contract.SnapshotError::ProjectionFailed—namesis empty (caller bug: nothing to co-pick),pickerreturnsNone(no candidate matched), orpickerreturns an out-of-range index.SnapshotError::VarNotFound— no global-section map has ALL the requested names.requestedcarries the joined name list,availablecarries the global-section map names that were scanned.
Sourcepub fn map_count(&self) -> usize
pub fn map_count(&self) -> usize
Number of maps the current view exposes — every captured
map when unfiltered; only maps the Self::active filter
admits when set.
Sourcepub fn is_placeholder(&self) -> bool
pub fn is_placeholder(&self) -> bool
True when the underlying FailureDumpReport is a
placeholder produced by FailureDumpReport::placeholder
— i.e. the freeze-rendezvous capture pipeline could not
produce real data. Periodic-sample temporal patterns use
this to skip the BPF axis on a placeholder sample (the
stats axis, when present, may still be valid). Bypassing
the projection-error path keeps the sample’s diagnostic
distinct from “field missing on a real capture”.
Sourcepub fn event_counter_timeline(&self) -> &'a [EventCounterSample]
pub fn event_counter_timeline(&self) -> &'a [EventCounterSample]
Per-monitor-tick SCX_EV_* event counter samples. Each entry is
the cross-CPU sum of the 13 SCX event counters at one monitor
tick. Empty when no EventCounterCapture ran, or every sample
was suppressed (event-stat offsets unresolved, scx_root unset).
Unlike the walker-backed accessors below, this field carries
no *_unavailable companion: an empty timeline is the only
signal for “no capture / no events”.
Sourcepub fn rq_scx_states(&self) -> &'a [RqScxState]
pub fn rq_scx_states(&self) -> &'a [RqScxState]
Per-CPU rq->scx snapshots — one per CPU walked by
crate::monitor::scx_walker. Empty when the
ScxWalkerCapture was absent or every CPU’s translate
failed (see FailureDumpReport::scx_walker_unavailable).
Sourcepub fn dsq_states(&self) -> &'a [DsqState]
pub fn dsq_states(&self) -> &'a [DsqState]
Per-DSQ snapshots — local, bypass, global, and user DSQs
reachable from *scx_root. Each entry carries nr (depth),
seq (BPF-iter counter), and the queued task KVAs. Empty
when the ScxWalkerCapture was absent (see
FailureDumpReport::scx_walker_unavailable).
Sourcepub fn scx_sched_state(&self) -> Option<&'a ScxSchedState>
pub fn scx_sched_state(&self) -> Option<&'a ScxSchedState>
Top-level scx_sched state captured from *scx_root:
aborting flag, bypass_depth, exit_kind. None when no
scheduler is attached or *scx_root was unreadable (see
FailureDumpReport::scx_walker_unavailable).
Sourcepub fn per_cpu_time(&self) -> &'a [PerCpuTimeStats]
pub fn per_cpu_time(&self) -> &'a [PerCpuTimeStats]
Per-CPU CPU-time / softirq / IRQ counter rows. One row per
CPU enumerated by crate::monitor::dump::CpuTimeCapture.
Empty when the capture was not wired or symbol/BTF
resolution failed.
Sourcepub fn per_cpu_time_at(&self, cpu: u32) -> Option<&'a PerCpuTimeStats>
pub fn per_cpu_time_at(&self, cpu: u32) -> Option<&'a PerCpuTimeStats>
Per-CPU CPU-time row for CPU cpu, looked up by the cpu
field on each PerCpuTimeStats (not by vec position).
Returns None when no row matches — typical when the
walker skipped that CPU, the capture didn’t run, or cpu
exceeded the topology. Returns the first match in walker
enumeration order if cpu appears more than once.
Sourcepub fn cgroup_psi(&self) -> &'a [CgroupPsiStat]
pub fn cgroup_psi(&self) -> &'a [CgroupPsiStat]
Per-cgroup PSI-irq rows for the test’s workload cgroups, host-walked
from the cgroup hierarchy at this freeze (Phase A). One row per
workload-root leaf cgroup with per-cgroup PSI accounting enabled. Empty
when the capture was not wired, the workload root isn’t present yet, or
psi_cgroups_enabled is off — loud-absent. RAW values; decoded + folded
at the metric layer (see
crate::monitor::cgroup_walk::CgroupPsiStat).
Sourcepub fn per_node_numa(&self) -> &'a [PerNodeNumaStats]
pub fn per_node_numa(&self) -> &'a [PerNodeNumaStats]
Per-NUMA-node event counter rows captured from
pglist_data->node_zones[]->vm_numa_event[]. Empty until
the host-side NUMA walker lands (see
FailureDumpReport::per_node_numa_unavailable).
Sourcepub fn per_node_numa_at(&self, node: u32) -> Option<&'a PerNodeNumaStats>
pub fn per_node_numa_at(&self, node: u32) -> Option<&'a PerNodeNumaStats>
Per-NUMA-node event-counter row for node, looked up by
the node field on each PerNodeNumaStats. Returns
None when no row matches. Returns the first match in
walker enumeration order if node appears more than once.
Sourcepub fn task_enrichments(&self) -> &'a [TaskEnrichment]
pub fn task_enrichments(&self) -> &'a [TaskEnrichment]
Per-task failure-dump enrichments — identity (pid, tgid,
comm), process tree, scheduling priority, sched_class name,
context-switch counters, watchdog disambiguation, lock
slowpath stack matches. Empty when no task walker ran (see
FailureDumpReport::task_enrichments_unavailable).
Sourcepub fn task_enrichment_by_pid(&self, pid: i32) -> Option<&'a TaskEnrichment>
pub fn task_enrichment_by_pid(&self, pid: i32) -> Option<&'a TaskEnrichment>
Look up the enrichment for pid. The returned reference
matches the first task whose task_struct.pid equals pid
in walker enumeration order. Returns None when no task with
that pid was captured. Production captures dedupe by task_kva
before push, so duplicate-pid rows do not occur in real
dumps.
Sourcepub fn prog_runtime_stats(&self) -> &'a [ProgRuntimeStats]
pub fn prog_runtime_stats(&self) -> &'a [ProgRuntimeStats]
Per-program BPF runtime stats — invocation count, total ns,
recursion misses. One entry per struct_ops program reached
by the prog walker. Empty when no struct_ops programs are
loaded or the prog accessor was unavailable (see
FailureDumpReport::prog_runtime_stats_unavailable).
Sourcepub fn prog_runtime_stats_by_name(
&self,
name: &str,
) -> Option<&'a ProgRuntimeStats>
pub fn prog_runtime_stats_by_name( &self, name: &str, ) -> Option<&'a ProgRuntimeStats>
Look up the runtime stats for the program registered with
name (kernel-side bpf_prog->aux->name). Returns None
when no program with that name was captured. Returns the
first match in walker enumeration order if name appears
more than once — struct_ops programs in real captures use
distinct callback names (select_cpu, enqueue, etc.) so
duplicates do not occur in production.
Sourcepub fn probe_counters(&self) -> Option<&'a ProbeBssCounters>
pub fn probe_counters(&self) -> Option<&'a ProbeBssCounters>
Probe BPF program’s per-CPU diagnostic counter snapshot.
None when the probe’s .bss map isn’t enumerated (probe
not loaded), the program BTF can’t be parsed, or the
array’s offset doesn’t resolve. A populated
trigger_count > 0 is the structural signal that the
tp_btf/sched_ext_exit handler fired during the run.
Diagnostic reason recorded when Self::rq_scx_states /
Self::dsq_states / Self::scx_sched_state could not
be populated. None when the walker fully succeeded;
otherwise Some(reason) (e.g. "scx_root null",
"no scx walker", or a partial-degradation string from the
dump pipeline).
Diagnostic reason recorded when Self::task_enrichments
could not be populated. None when the walker yielded at
least one enrichment; otherwise Some(reason)
(e.g. "no task walker available",
"task walker yielded zero tasks").
Diagnostic reason recorded when Self::prog_runtime_stats
could not be populated. None when the walker yielded at
least one program; otherwise Some(reason)
(e.g. "prog accessor unavailable",
"no struct_ops programs loaded").
Diagnostic reason recorded when Self::per_node_numa
could not be populated — typically "no NUMA walker" until
the host-side walker lands.
Diagnostic reason recorded when the SDT allocator snapshot
(still escape-only via Self::report) could not be
populated.
Trait Implementations§
Auto Trait Implementations§
impl<'a> Freeze for Snapshot<'a>
impl<'a> RefUnwindSafe for Snapshot<'a>
impl<'a> Send for Snapshot<'a>
impl<'a> Sync for Snapshot<'a>
impl<'a> Unpin for Snapshot<'a>
impl<'a> UnwindSafe for Snapshot<'a>
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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§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> 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