RustArena

Step 4 of 7

self: methods that take ownership

The third kind of receiver is plain self: the method takes ownership of the value. The value moves into the method, and the caller can't use it afterwards. That sounds harsh, but it's exactly right for methods that turn a value into something else:

struct Order {
    customer: String,
    items: Vec<String>,
}

struct Receipt {
    customer: String,
    item_count: usize,
}

impl Order {
    fn add(&mut self, item: &str) {
        self.items.push(item.to_string());
    }

    fn into_receipt(self) -> Receipt {
        Receipt {
            customer: self.customer,
            item_count: self.items.len(),
        }
    }
}

fn main() {
    let mut order = Order { customer: String::from("Grace"), items: Vec::new() };
    order.add("coffee");
    order.add("bagel");
    let receipt = order.into_receipt();
    println!("{} bought {} items", receipt.customer, receipt.item_count);
}
Grace bought 2 items

Because into_receipt owns the order, it can move the customer String into the receipt instead of copying the text with .clone(). And once the order has become a receipt, the compiler makes sure nobody keeps adding coffee to it:

struct Order {
    items: Vec<String>,
}

impl Order {
    fn into_receipt(self) -> String {
        format!("{} items", self.items.len())
    }
}

fn main() {
    let order = Order { items: vec![String::from("tea")] };
    let receipt = order.into_receipt();
    println!("{}", receipt);
    println!("{}", order.items.len());
}
error[E0382]: borrow of moved value: `order`

The full message even says note: `Order::into_receipt` takes ownership of the receiver `self`, which moves `order` . The standard library names such methods into_... (like String::into_bytes), so readers know the value is consumed.

Choosing the receiver

Receiver The method... Caller writes Use it for
&self reads the value x.m() on any value most methods: queries, calculations, printing
&mut self changes the value in place x.m() on a mut value updates: deposit, push, take damage
self consumes the value x.m(), then x is gone conversions and "finishing" steps

Start with &self, use &mut self when the method must change something, and self only when the old value really should be gone afterwards.

Small types: derive Copy

For a small struct made only of Copy fields (numbers, bool, char), you can derive Clone and Copy (Copy needs Clone, so they come as a pair): #[derive(Clone, Copy)]. Then the struct behaves like an integer, as you saw in the ownership module: using it by value copies it, so self methods leave the original usable.

#[derive(Debug, Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn moved(self, dx: i32, dy: i32) -> Point {
        Point { x: self.x + dx, y: self.y + dy }
    }
}

fn main() {
    let start = Point { x: 0, y: 0 };
    let next = start.moved(2, 3);
    println!("{:?} -> {:?}", start, next);
}
Point { x: 0, y: 0 } -> Point { x: 2, y: 3 }

A struct with a String or a Vec inside can't be Copy: copying it bit by bit would give two owners of the same heap memory.

Your task

A support ticket can get notes while it's open. Closing it produces a ClosedTicket, which has no add_note method at all, so the type system guarantees nobody edits a closed ticket. Write:

Challenge 2

Point is small and made of integers, so make it Copy: add Clone and Copy to its derive list. Then write two methods that take self by value and return a new point:

The tests keep using the original point after calling the methods, which only compiles if Point is Copy.

Challenge 3

Write an impl block for Playlist with one constructor and three methods, choosing the right receiver for each:

Don't clone the Vec in into_songs.

Previous: Associated functions and Self: constructors Next: Getters, invariants and multiple impl blocks