Skip to main content

Study

Struct Study 

pub struct Study { /* private fields */ }
Expand description

A study: storage, sampler, scheduler, clock and configuration, bound.

A handle is one worker. Several handles — threads, processes, machines — may drive the same study through the same storage at the same time; they coordinate through storage alone and never through each other (several workers, one study).

use atune_core::clock::ManualClock;
use atune_core::space::Scale;
use atune_core::study::{Budget, Study, StudyConfig};
use std::sync::Arc;

let study = Study::builder()
    .clock(Arc::new(ManualClock::new(0)))
    .budget(Budget::new_with_trials(16))
    .create(StudyConfig::new("quadratic").with_seed(7))?;

study.optimize(|ctx| {
    let x = ctx.suggest_f64("x", -5.0..=5.0, Scale::Linear)?;
    Ok((x * x).into())
})?;

assert_eq!(study.trial_count()?, 16);
assert!(study.best_trial()?.is_some());

Implementations§

§

impl Study

pub fn trial_fan(&self, trial: TrialId) -> Result<Option<ReplicateFan>>

The per-seed replicate fan of a multi-seed trial, if it has one.

Ok(None) for a single-seed trial before optional re-evaluation (no fan was stored). The fan carries both the tune replicates the sampler’s objective aggregates over and — if the final reevaluate stage has run — the disjoint test replicates. A fan-free study may therefore have a test-only fan after re-evaluation.

§Errors

Error::Conflict if a same-version fan violates the producer’s row shape; Error::Storage if the blob cannot be decoded or the backend faults.

pub fn trial_with_fan(&self, trial: TrialId) -> Result<FrozenTrial>

A trial’s snapshot with its per-seed fan attached.

get_trial leaves FrozenTrial::replicates empty — storage is fan-agnostic and the sampler never needs it — so this reader fills it from the trial’s fan blob for a caller that does want the per-seed sub-results (a report, a dashboard, the Wilcoxon pruner). It carries the tune replicates; the test replicates stay in trial_fan.

§Errors

Error::NotFound if the trial does not exist; Error::Storage on a backend fault.

§

impl Study

pub fn register_policy(&self, policy: &SpacePolicy) -> Result<()>

Persists policy into this study’s per-parameter slots — the create-time half of declaring open parameters, shared by StudyBuilder::policy and every surface that creates its study through StudyLocator rather than StudyBuilder::create (the CLI does).

Each Open is validated (Open::validate), and a policy naming a declared parameter must seed exactly the declared distribution — the range has one home, not two. Writing is per-parameter and replace-on-write, so re-registering an equal policy is idempotent; refusing a disagreement with an already-stored slot is the suggest-time and resume-time comparison’s job, not this method’s.

An empty policy writes nothing at all — the property that keeps every existing journal golden byte-identical.

§Errors

Error::InvalidSpace/Error::Unsupported from validation, or Error::Storage on a backend fault.

pub fn live_distribution( &self, name: &str, seed: &Distribution, ) -> Result<Distribution>

The distribution name would be asked under right now: seed widened by the replayed growth rule, or seed itself when no policy names name (the scripted path — join_and_sample has no lease to interpose on, so it asks the handle directly and must record the widened range, not the caller’s seed).

Emits the same growth-decision events an ordinary ask would, so a scripted study’s decisions are observable too.

§Errors

As the ask-path widening: Error::Unsupported for a stateful sampler or a multi-objective study under a policy, or a storage fault from the on-demand policy reads.

pub fn policy_for(&self, name: &str) -> Result<Option<Open>>

Reads the stored policy slot for name, if one was ever declared — the read half of register_policy, for the CLI’s resume comparison and atune doctor’s policy reporting.

§Errors

A foreign blob kind or a future version is Error::Incompatible; Error::Storage on a backend fault or an undecodable payload.

§

impl Study

pub fn backing_paths(&self) -> Option<Vec<PathBuf>>

Local source files protected from export replacement.

See crate::storage::Storage::backing_paths for the unknown-provenance contract.

pub fn builder() -> StudyBuilder

Starts building a study.

Storage is in-memory by default; call storage for anything durable.

pub const fn id(&self) -> StudyId

The study’s identity in its backend.

pub const fn config(&self) -> &StudyConfig

The configuration the study was created with.

Read from storage when the study was loaded, so a resumed study reproduces the original seed rather than a new one.

pub fn user_attrs(&self) -> Result<UserAttrs>

Returns the user-owned JSON metadata attached to this study.

The backing storage is authoritative, so the result is shared across handles and survives a reload.

§Errors

Returns the backing storage’s error if the study does not exist or its metadata cannot be read.

pub fn set_user_attr(&self, key: impl Into<String>, value: Value) -> Result<()>

Inserts or replaces one user-owned JSON attribute on this study.

Reserved-key validation and persistence are delegated to the backing storage, which keeps this handle a coordination facade.

§Errors

Returns crate::error::Error::Conflict for a reserved key, or the backing storage’s error if the study does not exist or the value cannot be stored.

pub const fn budget(&self) -> &Budget

What the study is allowed to spend.

pub const fn worker(&self) -> WorkerId

This handle’s worker identity.

Set by StudyBuilder::worker, and stamped onto FrozenTrial::worker by every lease this handle claims.

pub const fn retry_policy(&self) -> RetryPolicy

The retry policy failed trials are subject to.

pub const fn seed_protocol(&self) -> SeedProtocol

The multi-seed protocol this study evaluates trials under.

The default is single-seed (SeedProtocol::new): no fan, and every trial is one evaluation.

§

impl Study

pub fn ask(&self) -> Result<Option<TrialCtx<'_>>>

Creates the next trial and samples it.

The sequence is fixed:

  1. check the budget — deadline against the injected clock, fidelity against the synced view, trial count against a local reservation;
  2. one storage sync folded into the handle’s view through its cursor, with reads beside it only for the features in use: a full snapshot when the study has a target, each newly reporting trial’s atune.lineage record once under a fidelity budget, and constraint state for newly synced completed and pruned trials when the sampler reads constraints;
  3. create the trial (storage assigns the contiguous, race-safe number);
  4. derive sampler_seed = seed_for(study_seed, number, Stream::Sampler);
  5. infer the relative space and sample it jointly;
  6. persist the whole sample as one batch write;
  7. transition Waiting -> Running and hand back the context.

Ok(None) means the budget is spent, the handle’s StopHandle was stopped, or its target_value is met — a normal stop, not a failure.

§Budgets under parallelism

The handle first claims a local slot with an atomic compare-and-swap, preventing its own threads from needlessly racing for the last slot. The authoritative check and creation are one TrialLifecycleStorage::reserve_and_create_if_budget operation, so independently opened handles and processes cannot overshoot either. max_trials counts durable trial records already in the study, including records whose post-create start later fails.

§What the batch write costs a define-by-run study

Persisting the joint sample up front is what makes a declared-space trial cost one parameter write instead of one per suggest. It has one consequence worth stating: a parameter that the inferred relative space contains but the objective does not go on to suggest — a conditional branch not taken — is still recorded on the trial. For a study with a declared SpaceSchema that is exactly right (the space is the space). For a define-by-run study the inferred space depends on what had completed when the trial was asked, so under parallelism two runs may record different unsuggested extras on the same trial number. The values actually suggested are unaffected: a relative draw is used only when it was made under exactly the distribution the objective asks for (step 4 of the chain on TrialCtx), and otherwise the independent path — keyed on (sampler seed, name, distribution) — answers. Determinism promise 2 therefore holds for everything the objective read.

§Errors

Error::SpaceExhausted when an exhaustive sampler has handed out every configuration — a normal stop condition that optimize treats as the end of the study. The trial it was discovered on keeps its number, becomes durably Failed with the reason recorded, and is invisible to samplers; Error::LostRace if another worker claimed the freshly created trial before this handle could start it; plus anything storage or the sampler reports.

A failed ask does not consume a queued template. Every error path below returns the template to the front of the queue, so an enqueued configuration or a pending retry survives a lost race, a backend fault or an exhausted sampler and is used by the next successful ask.

§

impl Study

pub fn optimize<F>(&self, objective: F) -> Result<()>
where F: Fn(&mut TrialCtx<'_>) -> Result<Outcome> + Send + Sync,

Runs trials until the budget is spent.

A convenience loop over ask/tell and nothing more — anything it can do, a caller can do by hand with ask and tell.

Sequential by default. With parallelism(n) and n > 1 the trials run on std::thread::scope, which needs the system feature; with n == 1 the code path contains no thread spawn at all, which is what keeps optimize usable on wasm32-unknown-unknown.

§What stops the loop
ConditionResult
budget spentOk(())
Error::SpaceExhausted from the samplerOk(()) — a grid search ends this way
a failing objectivethe trial is recorded Failed (and maybe retried); the loop continues
a panicking objectiveunwinds sequentially; parallel joins return Err(..)
a storage or sampler faultErr(..), after the trial in flight is recorded

“Recorded” holds for successful terminal writes and for finalization fallbacks that can commit the trial’s state without replacement effects. If the backend refuses every terminal write, the trial remains Running for stale-lease fail-over.

A terminal that overlapped another sampler or scheduler call commits without draining its callbacks and effects. Once the workers join, optimize recovers those finalizations; when that creates a retry or fork child, the workers run again, under the same stop conditions, to evaluate it.

§Panics in the objective

A sequential objective panic unwinds through optimize. In parallel, it unwinds its worker; optimize joins every worker and returns an error if any worker panicked. A panicking objective is not converted into a trial outcome, so its trial remains Running for durable stale-lease fail-over. A profile compiled with panic = "abort" still aborts the process.

§Determinism

Trial-number-indexed determinism — trial n gets the same parameters however many workers ran — holds for a stateless sampler with an empty template queue. Both enqueue and a non-zero RetryPolicy make the number-to-template mapping order-dependent, because when a template is queued is itself thread-scheduling dependent; such a study is replayable rather than pre-determined. Under parallelism(1) the mapping is fixed in both cases.

§Errors

The first fatal error any worker reports; the other workers finish the trial they hold first and then stop, rather than running out the remaining budget. A Error::LeaseLost while committing one trial is local to that trial, so its worker moves on. Error::Conflict if parallelism > 1 was requested from a build without the system feature.

pub fn optimize_with(&self, objective: &dyn Objective) -> Result<()>

optimize for an Objective that is not a closure.

The general entry point, and the one both forms share: a hand-written impl Objective, a boxed trait object, an objective chosen at run time. optimize exists alongside it purely so that a closure needs no type annotation — a bound of impl Objective gives the compiler nothing to infer a closure’s argument type from, whereas a function-trait bound does.

§Errors

As optimize.

§

impl Study

pub fn view(&self) -> Result<StudyView>

A fresh snapshot of the study.

Syncs once — incrementally, from the handle’s cursor — and returns the folded view. This is the read path every convenience method below goes through, and the one a caller should use too: re-reading the whole study per question is exactly the chattiness the cursor exists to kill. When the sampler reads_constraints, each newly synced completed or pruned trial also carries its recorded constraint values.

§Errors

Error::NotFound if the study vanished from storage; Error::Storage on a backend fault or a poisoned internal lock.

pub fn snapshot(&self) -> Result<StudySnapshot>

A canonical hydrated observation of the study.

Constraints, replicate fans, space policies, and pending terminal finalizations are read with the trials through one storage snapshot request. Built-in backends provide one read epoch; compatible custom backends expose crate::storage::SnapshotConsistency::BestEffort through the returned snapshot instead of making an atomicity claim. Samplers and schedulers continue to use the lighter raw view.

§Errors

A storage read or side-state decoding error.

pub fn best_trial(&self) -> Result<Option<FrozenTrial>>

The best trial of a single-objective study, if there is one — feasibility first.

None for a multi-objective study, whose honest answer is pareto_front — see StudyView::best.

Like pareto_front this attaches each visible trial’s recorded constraint values before ranking (storage cannot carry them on the record), so a trial that violated its constraints never wins while a feasible one exists, whatever the objectives say. That is the same relation the Pareto front uses, so the two answers cannot disagree about feasibility. An unconstrained study is unaffected: with nothing recorded the ranking is the plain objective comparison it always was.

§Cost

A reporting call through one storage snapshot request. It is not a per-suggest path.

§Errors

As view, plus Error::Storage if a constraint blob cannot be decoded.

pub fn stop_handle(&self) -> StopHandle

This handle’s cooperative stop flag; see crate::study::StopHandle.

Every clone shares the flag, so an objective, a signal handler or a monitoring thread can stop the search while optimize runs.

pub fn trial_count(&self) -> Result<usize>

How many trials the study has, in any state.

§Errors

As view.

pub fn trial_checkpoints( &self, trial: TrialId, ) -> Result<Option<TrialCheckpoints>>

The checkpoint references a trial recorded, if any.

atune stores checkpoint references, never bytes (population-based training): each entry maps a resource step to an opaque reference string that an objective wrote through TrialCtx::record_checkpoint. Ok(None) when the trial recorded none.

§Errors

Error::Storage if the blob cannot be decoded or the backend faults.

pub fn latest_checkpoint(&self, trial: TrialId) -> Result<Option<String>>

A trial’s most advanced checkpoint reference, if it recorded one.

This is the reference a Fork child or a resumed trial starts from.

§Errors

As trial_checkpoints.

pub fn trial_constraints(&self, trial: TrialId) -> Result<Option<Vec<f64>>>

The constraint values a trial recorded, if it recorded any.

≤ 0 is satisfied, > 0 is violated (pareto::constraint). Ok(None) when the trial declared none. Calling TrialCtx::record_constraints with an empty list is also undeclared: it is a no-op and creates no canonical state. An undeclared trial is distinct from a declared trial whose values are all satisfied.

§Errors

Error::Storage if the blob cannot be decoded or the backend faults.

pub fn pareto_front(&self) -> Result<Vec<FrozenTrial>>

The study’s Pareto front: the non-dominated set of completed trials.

The multi-objective analogue of best_trial, which correctly answers None for a multi-objective study because a front has no total order (read the front, not the best). Trials are returned in trial-number order, so the answer does not depend on completion order, and a single-objective study gets the (possibly tied) best trials — the degenerate case, not a special case.

Ranking is constrained_dominates: if trials recorded constraint values, a feasible trial outranks an infeasible one whatever their objectives say, and the front is the feasible one whenever any trial is feasible.

§Cost

This is a reporting call through one storage snapshot request, not a hot path. Ranking itself is O(M·N²).

§Errors

As view, plus Error::Storage if a constraint blob cannot be decoded.

pub fn nadir_point(&self) -> Result<Option<Vec<f64>>>

The nadir point of the study’s comparable trials: the componentwise worst objective value observed.

The conventional default reference for hypervolume — see nadir_point for why a hypervolume history must nonetheless be measured against one fixed reference rather than a moving nadir. Ok(None) when no trial has a comparable objective vector yet.

§Errors

As view.

pub fn hypervolume(&self, reference: &[f64]) -> Result<f64>

The hypervolume the study’s Pareto front covers, bounded by reference.

The standard scalar quality measure of a multi-objective run: it rewards a front for converging and for spreading out, which no single number derived from one objective can. Larger is better whatever the directions are. Only feasible front members count.

§Errors

As view, plus everything hypervolume reports — notably Error::InvalidSpace above MAX_EXACT_HYPERVOLUME_DIM objectives, where an exact computation would take exponential time and is refused rather than silently attempted.

pub fn lineage(&self) -> Result<Vec<LineageEntry>>

The study’s fork genealogy — the population tree a forking scheduler (such as Pbt) builds.

Reads one hydrated snapshot and reconstructs, per trial, its parent and the recovered explore perturbation (the parameter difference from its parent). This is the inspectable lineage the design calls the headline PBT artifact and the tree the GUI renders (read the genealogy); the pure reconstruction is fork_lineage. Entries are in trial-number order, so the answer does not depend on completion order.

§Errors

As snapshot.

pub fn recipe(&self, leaf: TrialId) -> Result<Recipe>

The schedule that produced leaf, as a versioned Recipe.

Reads one hydrated snapshot and walks from leaf back through each trial’s atune.lineage record while it inherited a checkpoint: the segments run from the start of the leaf’s training (a root, a fresh restart, or a child that started without a checkpoint) to the leaf, each with its full typed configuration, the step range it trained, and the fork or restart event that began it. Recipe::to_json is the deterministic atune.recipe version-1 export.

§Errors

Error::NotFound if leaf is not in the study; Error::Lineage naming the trial when a trial on the path is a fork without an atune.lineage record (written before the record existed) or names a donor the study does not contain — nothing is inferred from reports; otherwise as snapshot.

pub fn resource_ledger(&self) -> Result<ResourceLedger>

The study’s ResourceLedger: per trial, the resource it was charged for, the resource its lineage represents, its training age since initialization or the last restart, and each stored evaluation stage’s replicate count, plus the study total — the fidelity the budget gate has charged.

§Errors

As snapshot.

§

impl Study

pub fn reevaluate<F>(&self, objective: F, top_k: usize) -> Result<FinalReport>
where F: Fn(&mut TrialCtx<'_>) -> Result<Outcome> + Send + Sync,

The final re-evaluation stage: re-rank the top configurations on the held-out test seeds (re-evaluate on held-out seeds).

This is the guard against the ICML-2023 failure mode — configurations that overfit their tuning seeds. After the optimization budget is spent, it takes the top_k completed trials by the canonical constraint-aware tune ranking (feasibility first, then aggregate), re-evaluates each one’s configuration under the protocol’s disjoint test seeds, and reports:

  • the test-seed-ranked best (which is not necessarily the tune-ranked best — that is the whole point), ranked feasibility-first on the componentwise worst of the constraints its test replicates recorded, then by test value;
  • per configuration, its tune value, its test value, and the signed overfit_gap between them.

The test replicates are stored on each re-evaluated trial’s fan (readable through trial_fan); the trial’s sampler-visible objective — its values, and therefore the sampler’s observation — is left untouched, so the tune aggregate remains the objective used by the sampler. Study-owned ranking continues to apply the recorded constraints first. In other words:

  • before / without reevaluate: best_trial is the canonical constraint-aware tune-ranked best.
  • after reevaluate: best_trial is still the canonical constraint-aware tune-ranked best; the test-ranked best is FinalReport::best, which is the study’s honest answer.

Nothing else changes about a study, and calling it is optional — it runs only when the caller invokes it, and only if the protocol declares test seeds.

§A stored test set is reused

A candidate whose fan already holds a complete test set from an earlier call — one replicate per test seed, in index order, each under exactly the seed the protocol derives now, each with a finite objective vector of the study’s arity, and each with a recorded constraint vector when the trial declares tune-stage constraints — is ranked from that set without running the objective again, and its fan is not rewritten. Any other stored set (missing, partial, failed, or recorded under other seeds) is discarded and rerun in full, so a resumed study repeats only the held-out work an interruption lost.

§Errors

Error::Conflict if the study is multi-objective (there is no total order to rank a test best by) or if the protocol declares no test seeds; plus anything storage or the objective reports.

pub fn reevaluate_with( &self, objective: &dyn Objective, top_k: usize, ) -> Result<FinalReport>

reevaluate for an Objective that is not a closure (the Subprocess executor, a boxed trait object).

§Errors

As reevaluate.

pub fn evaluate_checkpoints<F>( &self, objective: F, top_k: usize, ) -> Result<FinalReport>
where F: Fn(&mut TrialCtx<'_>) -> Result<Outcome> + Send + Sync,

The checkpoint estimand: evaluate each of the top_k tune-ranked trials’ latest checkpoint under the held-out test seeds, without training.

The candidates, test seeds, eligibility gate, ranking, and report are exactly reevaluate’s. Each replicate’s context reports Evaluation::Checkpoint from TrialCtx::evaluation, carries the trial’s latest checkpoint as parent_checkpoint and its step as parent_step, and suggest returns the trial’s recorded configuration; a Subprocess child sees ATUNE_EVALUATE=checkpoint and ATUNE_CHECKPOINT. The objective loads that checkpoint and evaluates it; it must not train. A trial that recorded no checkpoint is an ineligible entry (test_value NaN) and the objective does not run for it.

The replicates are stored in the trial’s atune.estimate record (StudySnapshot::estimate) beside the checkpoint they evaluated; the trial’s values and its fan’s test replicates are untouched. A stored set is reused under the same conditions as reevaluate’s, and only when it evaluated the same checkpoint step and reference; otherwise it is rerun and this estimand’s record replaced.

§Errors

As reevaluate.

pub fn evaluate_checkpoints_with( &self, objective: &dyn Objective, top_k: usize, ) -> Result<FinalReport>

evaluate_checkpoints for an Objective that is not a closure.

§Errors

As reevaluate.

pub fn replay_recipes<F>( &self, objective: F, top_k: usize, ) -> Result<FinalReport>
where F: Fn(&mut TrialCtx<'_>) -> Result<Outcome> + Send + Sync,

The recipe estimand: train each of the top_k tune-ranked trials afresh following its recipe under the held-out test seeds.

The candidates, test seeds, eligibility gate, ranking, and report are exactly reevaluate’s. Each replicate’s context reports Evaluation::Recipe from TrialCtx::evaluation and the trial’s Recipe from TrialCtx::recipe; suggest returns the recipe’s first-segment configuration, the one training starts with, and there is no parent checkpoint. The objective trains from scratch and switches configuration at each segment boundary (Recipe::params_at). A Subprocess child sees ATUNE_EVALUATE=recipe and the recipe as compact JSON in ATUNE_RECIPE. The result is reported as measured; it is not assumed to equal the checkpoint’s.

The replicates are stored in the trial’s atune.estimate record beside the recipe’s exact JSON; a stored set is reused only for the same recipe bytes and otherwise rerun, as for evaluate_checkpoints.

§Errors

As reevaluate, plus the recipe error (Error::Lineage or Error::NotFound) of any candidate, which fails the whole call before any objective runs.

pub fn replay_recipes_with( &self, objective: &dyn Objective, top_k: usize, ) -> Result<FinalReport>

replay_recipes for an Objective that is not a closure.

§Errors

As replay_recipes.

§

impl Study

pub fn retry_record(&self, trial: TrialId) -> Result<Option<RetryRecord>>

What a trial records about being a retry, if it is one.

§Errors

Error::Storage if the blob cannot be decoded or the backend faults.

pub fn enqueue(&self, template: TrialTemplate) -> Result<()>

Queues a trial to be created with something already decided.

The enqueued parameters outrank every sampler: they are step 2 of the priority chain documented on TrialCtx. Templates are consumed in FIFO order by ask; once the queue is empty, trials are sampled normally again. An ask that takes a template but then fails before the trial is running puts it back at the front of the queue, so an explicitly requested configuration is never lost.

§Determinism: an enqueued study is replayable, not pre-determined

Trial-number-indexed determinism holds for a stateless sampler with an empty template queue. Enqueuing makes the number-to-template mapping order-dependent: which trial number a template lands on is decided by which ask claims it, and under parallelism(n) with n > 1 that is thread scheduling. The values a template fixes are of course exactly what was enqueued; it is the pairing with a trial number that floats. Such a study is replayable (its history explains itself) rather than pre-determined (computable from the seed before it runs).

§Errors

Error::Storage if the internal queue lock is poisoned.

§

impl Study

pub fn tell(&self, ctx: TrialCtx<'_>, outcome: Outcome) -> Result<()>

Completes a trial with its objective values.

Values and the terminal state are committed atomically under the context’s owner/epoch fence. A stale worker therefore cannot complete a trial after another worker has reclaimed it.

This and the other terminal tell_* methods consume the context, so the handle of a finished or suspended trial cannot stay usable.

§Errors

Error::Conflict if the value count does not match the study’s directions or the trial has already finished; Error::Scheduler if the scheduler asks to resume a trial that is not paused; plus anything storage or a stateful seam reports.

pub fn tell_pruned(&self, ctx: TrialCtx<'_>) -> Result<()>

Records a trial as pruned.

No values are written. A pruned trial’s objective value is its last intermediate report (FrozenTrial::objective_value), and writing a copy of it would only create a second source of truth that could disagree. A trial pruned before its first report therefore has no objective value at all, which is the honest answer.

§Errors

As tell, minus the value-count conflict.

pub fn tell_failed(&self, ctx: TrialCtx<'_>, message: &str) -> Result<()>

Records a trial as failed, keeping message.

The error text and terminal state are committed atomically under the context’s owner/epoch fence. The record, its parameters and the message all survive; only the sampler’s view of the trial does not (StudyView::visible filters failures out).

§Errors

As tell.

pub fn tell_paused(&self, ctx: TrialCtx<'_>) -> Result<()>

Suspends a trial, keeping its checkpoint — the execution of Decision::Pause.

The trial moves Running -> Paused, a non-terminal state: no values are written, its recorded checkpoint reference is left in place, and it can be woken later with Command::ResumeTrial. Because a paused trial is not finished, the sampler’s after_trial and the scheduler’s on_trial_end — both defined as terminal-state callbacks — do not run here; the seam state that a pause decision may have mutated (in on_report) is persisted so a resume across a reopen is exact.

§Errors

When another worker of this handle is inside a sampler or scheduler call, the pause commits without that snapshot: the seams are not touched, and the next owned finalization persists their current state.

Error::Storage on a backend fault; anything a stateful sampler’s or scheduler’s state reports while its blob is persisted.

Trait Implementations§

§

impl Debug for Study

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

The identity and the knobs; the seam handles are opaque by design.

Auto Trait Implementations§

§

impl !Freeze for Study

§

impl !RefUnwindSafe for Study

§

impl !UnwindSafe for Study

§

impl Send for Study

§

impl Sync for Study

§

impl Unpin for Study

§

impl UnsafeUnpin for Study

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more