RustArena

Step 6 of 7

Method chaining

You've chained calls before: line.trim().to_uppercase() calls to_uppercase on whatever trim returned. Your own methods can be designed for chaining too, by returning the value itself.

Builders: take self, return Self

A method that takes mut self and returns Self changes the value and hands it back, ready for the next call:

struct Request {
    url: String,
    method: String,
    timeout_secs: u32,
    retries: u32,
}

impl Request {
    fn get(url: &str) -> Self {
        Self {
            url: url.to_string(),
            method: String::from("GET"),
            timeout_secs: 30,
            retries: 0,
        }
    }

    fn timeout(mut self, secs: u32) -> Self {
        self.timeout_secs = secs;
        self
    }

    fn retries(mut self, n: u32) -> Self {
        self.retries = n;
        self
    }

    fn describe(&self) -> String {
        format!("{} {} (timeout {}s, {} retries)", self.method, self.url, self.timeout_secs, self.retries)
    }
}

fn main() {
    let req = Request::get("https://example.com/api")
        .timeout(5)
        .retries(3);
    println!("{}", req.describe());
    let quick = Request::get("https://example.com/health");
    println!("{}", quick.describe());
}
GET https://example.com/api (timeout 5s, 3 retries)
GET https://example.com/health (timeout 30s, 0 retries)

Changing in place: take &mut self, return &mut Self

When you want to keep a variable and change it several times, return &mut Self instead:

struct Tally {
    total: u32,
    count: u32,
}

impl Tally {
    fn add(&mut self, value: u32) -> &mut Self {
        self.total += value;
        self.count += 1;
        self
    }
}

fn main() {
    let mut t = Tally { total: 0, count: 0 };
    t.add(5).add(10).add(20);
    println!("{} values, total {}", t.count, t.total);
}
3 values, total 35

Here self is already a &mut Tally, so returning self hands the same mutable borrow to the next call.

Common mistakes

Your task

Write a builder for alert messages. Message::new(to) starts with an empty subject and urgent false. subject(text) sets the subject and urgent() marks the message as urgent; both take mut self and return Self. preview builds a one-line summary:

Challenge 2

Write Stats, which records numbers in place with chainable calls:

Challenge 3

Write a pizza order builder from scratch:

Previous: Getters, invariants and multiple impl blocks Next: Putting it together: a bank account program