Moving ownership out of mutable/scratchpad variable

I'm somewhat new to Rust and am having a hard time figuring out an idiomatic way to handle a very common kind of situation when parsing or de-serializing a file. I want to build up a struct as data comes in, and when it's finished, pass ownership to somewhere else (like a Vec) and start on a new one. Maybe I'm thinking about this all wrong, and needing a completely different methodology. Here's a miniature version of the issue -- imagine you're a retailer building up complete orders out of a sequence of messages. In this distilled version, an "order" consists of a customer's name and the total price. I think that's enough to exhibit the issue.

#[derive(Debug)]
struct Order {
    name: String,
    // ...
    amount: f32,
}

enum Message<'a> {
    MyName(&'a str),
    Price(f32),
}

fn main() {
    let messages = vec![Message::MyName("Cust1"), Message::Price(15.0),
    Message::MyName("Cust2"), Message::Price(20.0)];

    let mut parsed_orders: Vec<Order> = Vec::new();

    let mut order: Option<Order> = None;
    for message in &messages {
        match message {
            Message::MyName(name) => {
                if let Some(finished) = order {
                    // Fail -- does not move ownership from order into parsed_orders
                    parsed_orders.push(finished); 
                    order = None;
                }
                order = Some(Order{name: name.to_string(), amount: 0.});
            },
            Message::Price(amount) => {
                order.unwrap().amount = *amount;
            },
        }
    }
    if let Some(finished) = order {
        parsed_orders.push(finished);
    }

    println!("{:?}", orders);
}

This won't compile. It's broken, but maybe not so broken that you can't see what I'm attempting. I've tried many variations on this with various uses of references and other re-structurings. But I can't get anything to work.

Maybe I need to process the messages twice, first breaking it into orders? That way the scratchpad variable "order" can be in its own scope entirely inside a loop? But that's less efficient and much more complicated than what I'd do in any other language. And I find myself using this general approach or pattern in a lot of languages for a lot of different problems. So I think I'd really learn something important about Rust if I could figure this out properly.

if let Some(finished) = order {

You can use Option::take to take out the content without moving the option variable:

if let Some(finished) = order.take() {
order.unwrap().amount = *amount;

You can use Option::as_mut to get a mutable reference to the content without moving the option variable:

order.as_mut().unwrap().amount = *amount;
5 Likes

Thanks! This solved it. I was worried I was getting into some situation of dynamic ownership that might require a more complex approach. This is much simpler than I was expecting!