Expand description
Small, runtime-agnostic retry helpers for fallible operations.
reliakit-retry turns a Backoff schedule and an attempt limit into a
RetryPolicy, then drives a fallible operation against it — synchronously
or asynchronously. It is deliberately minimal: it decides whether to retry
and how long the gap should be, but it never sleeps, spawns, or assumes an
async runtime. You inject the waiting.
It has no third-party dependencies, forbids unsafe code, and is
no_std-friendly (it needs no allocation and no clock).
§Why it does not sleep
Blocking the current thread (std::thread::sleep) or awaiting a runtime
timer is hidden runtime behavior, and it ties a small helper to one
execution model. Instead:
retryruns attempts back-to-back and never waits.retry_with_sleephands each backoffDurationto asleepclosure you provide (e.g. one that calls your timer).retry_asyncawaits asleepfuture you provide, so it works under any executor without depending on Tokio, async-std, orfutures.
§Attempt counting
RetryPolicy::max_attempts is the total number of attempts, including
the first:
max_attempts = 1→ try once, never retry (the backoff is never used).max_attempts = 3→ the first try plus up to two retries.max_attempts = 0is rejected byRetryPolicy::new(returnsNone).
The attempt count is the single authority for how many times the operation
runs. The Backoff is consulted only for the delay before each retry
(retry 0 is the first retry, zero-based); if it yields no delay, the gap is
Duration::ZERO. The two limits therefore
never conflict.
§Retry predicate
Every helper takes a should_retry: FnMut(&E) -> bool classifier. Returning
false stops immediately — use it to retry only transient errors and fail
fast on permanent ones. It is consulted only when another attempt is actually
possible (so it is never called when max_attempts is already reached).
§Example — sync, no sleeping
use core::time::Duration;
use reliakit_retry::{retry, Backoff, RetryError, RetryPolicy};
let policy = RetryPolicy::new(3, Backoff::constant(Duration::from_millis(10))).unwrap();
let mut calls = 0;
let result: Result<u32, RetryError<&str>> = retry(
&policy,
|| {
calls += 1;
if calls < 2 { Err("temporary") } else { Ok(42) }
},
|_error| true, // retry every error
);
assert_eq!(result.unwrap(), 42);
assert_eq!(calls, 2);§Example — sync, with an injected sleeper
use core::time::Duration;
use reliakit_retry::{retry_with_sleep, Backoff, RetryError, RetryPolicy};
let policy = RetryPolicy::new(4, Backoff::exponential(Duration::from_millis(1), 2)).unwrap();
// Record the delays instead of really sleeping (a real caller would wait).
let mut waited: Vec<Duration> = Vec::new();
let mut attempts = 0;
let result: Result<(), RetryError<&str>> = retry_with_sleep(
&policy,
|| { attempts += 1; Err("always fails") },
|_error| true,
|delay| waited.push(delay),
);
assert!(matches!(result, Err(RetryError::Exhausted { attempts: 4, .. })));
// Three gaps before retries 2, 3, 4: 1ms, 2ms, 4ms.
assert_eq!(waited, [Duration::from_millis(1), Duration::from_millis(2), Duration::from_millis(4)]);For the async helper, see retry_async and the async_retry example,
which drives it without any runtime.
§Feature flags
std(default) addsimpl std::error::Error for RetryError. With--no-default-featuresthe crate is purecore: no allocation, no clock, no runtime.
§What this is not
This is a small retry helper, not a framework, middleware stack, async runtime, or a Tower replacement. It does not log, spawn, time, or schedule on your behalf.
Structs§
- Backoff
- Re-exported from
reliakit-backoffso the backoff schedule is reachable without a separate dependency line. A retry backoff policy. - Retry
Policy - How many times to attempt an operation and how long to wait between tries.
Enums§
- Retry
Error - The error returned when a retried operation does not succeed.
Functions§
- retry
- Retries
opaccording topolicy, without ever sleeping. - retry_
async - Retries an async
opaccording topolicy, awaitingsleepbefore each retry. - retry_
with_ sleep - Retries
opaccording topolicy, callingsleepwith the backoff delay before each retry.