Struggling with borrows

Newbie question. I think I understand why the code below fails. I have a function that gets a Vec of items (not a ref, the vector itself) and I'd like to do some things based on what is in the vector.
It is complaining because it returns something that references items, which is borrowed in the for loop.

How do I get "around" that?

impl TryFrom<Vec<Item>> for Ledger<'_> {
    type Error = Error;

    fn try_from(items: Vec<Item>) -> Result<Self, Self::Error> {
        let mut ledger = Ledger::new();

        // 1. Populate the lists
        for item in items.iter() {
            match item {
                Item::Comment(_) => {}
                Item::Transaction(parsed) => {
                    for p in parsed.postings.iter() {
                        let account = Account::from(p.account.clone());
                        ledger.accounts.push(account);

                        // Currencies
                        if let Some(c) = &p.money_currency {
                            ledger.currencies.push(Currency::from(c.as_str()));
                        }
                        if let Some(c) = &p.cost_currency {
                            let currency = Currency::from(c.as_str());
                            ledger.currencies.push(currency);
                        }
                        if let Some(c) = &p.balance_currency {
                            let currency = Currency::from(c.as_str());
                            ledger.currencies.push(currency);
                        }
                    }
                }
                Item::Directive => {}
            }
        }

        return Ok(ledger);
}

If you want to create a Ledger consisting of references into collection of Items, the Items are naturally going to have to be alive longer than your references. Rust isn't garbage collected, and instead uses a system of ownership to manage memory. To get "around" your situation, you're going to have to approach things in such a way that there's an owner of the Items which sticks around for at least as long as any references.

Moreover, self-referential structs are another pattern in Rust which can not be achieved safely and is almost never what you actually want. So for your example, storing the Vec<Item> in the Ledger isn't likely to work out either.

The two most direct adjustments left are:

  • Store Items in the Ledger, not references.
    • You'll probably drop the <'_> part of Ledger<'_> in this approach.
  • impl<'a> TryFrom<&'a [Item]> for Ledger<'a>
    • The Vec (or whatever owns the Items) will have to "stick around" at least as long as the resulting Ledger.
2 Likes