RustArena

Step 3 of 7

Associated functions and Self: constructors

A function inside an impl block that does not take self is an associated function: it belongs to the type, but isn't called on a value. (Strictly, every function in an impl block is an associated function and methods are the ones that take self, but in everyday speech "associated function" means one without self.) You call it with the type's name and ::. You've used them since the first modules: String::from("hi"), String::new(), Vec::new().

Their most common job is building values. By convention, the main one is called new:

struct Item {
    name: String,
    price_cents: u32,
    quantity: u32,
}

impl Item {
    fn new(name: &str, price_cents: u32) -> Self {
        Self {
            name: name.to_string(),
            price_cents,
            quantity: 0,
        }
    }

    fn free_sample(name: &str) -> Self {
        Self::new(name, 0)
    }

    fn summary(&self) -> String {
        format!("{} x{} at {} cents", self.name, self.quantity, self.price_cents)
    }
}

fn main() {
    let mut tea = Item::new("Tea", 450);
    tea.quantity = 12;
    let sticker = Item::free_sample("Sticker");
    println!("{}", tea.summary());
    println!("{}", sticker.summary());
}
Tea x12 at 450 cents
Sticker x0 at 0 cents

Why constructors?

new is not special syntax: Rust has no built-in constructors, just this convention. It's still worth it. Callers don't need to know every field (quantity starts at 0 without them thinking about it), the defaults live in one place, and later, with private fields (the modules module), a constructor becomes the only way to create a value, so it can enforce rules like "a name is never empty". Clippy even checks the convention: a new that doesn't return Self gets a new_ret_no_self warning.

:: or .?

You write Meaning
Item::new("Tea", 450) associated function, called on the type
tea.summary() method, called on a value
Item::summary(&tea) the same method, called the long way

Common mistakes

struct Rect {
    width: u32,
    height: u32,
}

impl Rect {
    fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }

    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let r = Rect.new(2, 3);
    println!("{}", r.area());
}
error[E0423]: cannot find value `Rect` in this scope

Your task

Write Player::new(name: &str) -> Self. A new player has the given name, level 1, 100 hp and a score of 0. Build the value with Self { ... }.

Challenge 2

Give Rect two constructors and a method:

Challenge 3

Write an impl block for Timer, which stores a duration in seconds, with:

Previous: &mut self: methods that change the value Next: self: methods that take ownership