Retry loops need an explicit budget so failure remains bounded and inspectable.

Program

Play the program to choose a retry budget and watch the deterministic status list.

max_attempts
retry_budget_model.rs
Replay: real traced execution (multi-file project)
fn main() {
    let max_attempts = 3;
    let statuses = ["fail", "fail", "ok"];
    let result = first_success(&statuses, max_attempts);
    println!("{result}");
}

fn first_success(statuses: &[&str], max_attempts: usize) -> String {
    let mut attempts = 0;
    for status in statuses.iter().take(max_attempts) {
        attempts += 1;
        if *status == "ok" {
            return format!("success after {attempts}");
        }
    }
    format!("failed after {attempts}")
}
fn main() {
    let max_attempts = 1;
    let statuses = ["fail", "fail", "ok"];
    let result = first_success(&statuses, max_attempts);
    println!("{result}");
}

fn first_success(statuses: &[&str], max_attempts: usize) -> String {
    let mut attempts = 0;
    for status in statuses.iter().take(max_attempts) {
        attempts += 1;
        if *status == "ok" {
            return format!("success after {attempts}");
        }
    }
    format!("failed after {attempts}")
}
fn main() {
    let max_attempts = 2;
    let statuses = ["fail", "fail", "ok"];
    let result = first_success(&statuses, max_attempts);
    println!("{result}");
}

fn first_success(statuses: &[&str], max_attempts: usize) -> String {
    let mut attempts = 0;
    for status in statuses.iter().take(max_attempts) {
        attempts += 1;
        if *status == "ok" {
            return format!("success after {attempts}");
        }
    }
    format!("failed after {attempts}")
}
  1. max_attempts ← 3, statuses ← ["fail", "fail", "ok"]

    1fn main() {2    let max_attempt→ 3s = 3; //@max_attempts=3, 2, 13    let statuse→ ["fail", "fail", "ok"]s = ["fail", "fail", "ok"];4    let result = first_success(&statuse["fail", "fail", "ok"]s, max_attempt3s);5    println!("{result}");
  2. attempts ← 0

    8fn first_success(statuses: &[&str], max_attempts: usize) -> String {9    let mut attempt→ 0s = 0;10    for status in statuses.iter().take(max_attempts) {
  3. attempts ← 1

    pass 1 of 3
    9let mut attempts = 0;10for statu"fail"s in statuses.iter().take(max_attempts) {11    attempt→ 1s += 1;12    if *status == "ok" {
    All 3 passes — pass 1 is the card above
    passstatusattempts
    1"fail"0 1
    2"fail"1 2
    3"ok"2 3
  4. if *status == "ok"

    11attempts += 1;12if *statu"ok"s == "ok" {13    return format!("success after {attempts}");14}
  5. result ← "success after 3"

    3    let statuses = ["fail", "fail", "ok"];4    let resul→ "success after 3"t = first_success(&statuse["fail", "fail", "ok"]s, max_attempt3s);5    println!("{result}");6}
    outputsuccess after 3
  1. max_attempts ← 1, statuses ← ["fail", "fail", "ok"]

    1fn main() {2    let max_attempt→ 1s = 1;3    let statuse→ ["fail", "fail", "ok"]s = ["fail", "fail", "ok"];4    let result = first_success(&statuse["fail", "fail", "ok"]s, max_attempt1s);5    println!("{result}");
  2. attempts ← 0

    8fn first_success(statuses: &[&str], max_attempts: usize) -> String {9    let mut attempt→ 0s = 0;10    for status in statuses.iter().take(max_attempts) {
  3. attempts ← 1

    9let mut attempts = 0;10for statu"fail"s in statuses.iter().take(max_attempts) {11    attempt→ 1s += 1;12    if *status == "ok" {
  4. format!("failed after {attempts}")

    15    }16    format!("failed after {attempts}")17}
  5. result ← "failed after 1"

    3    let statuses = ["fail", "fail", "ok"];4    let resul→ "failed after 1"t = first_success(&statuse["fail", "fail", "ok"]s, max_attempt1s);5    println!("{result}");6}
    outputfailed after 1
  1. max_attempts ← 2, statuses ← ["fail", "fail", "ok"]

    1fn main() {2    let max_attempt→ 2s = 2;3    let statuse→ ["fail", "fail", "ok"]s = ["fail", "fail", "ok"];4    let result = first_success(&statuse["fail", "fail", "ok"]s, max_attempt2s);5    println!("{result}");
  2. attempts ← 0

    8fn first_success(statuses: &[&str], max_attempts: usize) -> String {9    let mut attempt→ 0s = 0;10    for status in statuses.iter().take(max_attempts) {
  3. attempts ← 1

    pass 1 of 2
    9let mut attempts = 0;10for statu"fail"s in statuses.iter().take(max_attempts) {11    attempt→ 1s += 1;12    if *status == "ok" {
  4. attempts ← 2

    pass 2 of 2
    9let mut attempts = 0;10for statu"fail"s in statuses.iter().take(max_attempts) {11    attempt→ 2s += 1;12    if *status == "ok" {
  5. format!("failed after {attempts}")

    15    }16    format!("failed after {attempts}")17}
  6. result ← "failed after 2"

    3    let statuses = ["fail", "fail", "ok"];4    let resul→ "failed after 2"t = first_success(&statuse["fail", "fail", "ok"]s, max_attempt2s);5    println!("{result}");6}
    outputfailed after 2
budget `take(max_attempts)` bounds the retry loop.
status list The statuses are fixed data so replay is deterministic.
outcome The returned message records whether success happened before the budget ended.