Lifetime issue when creating vector elements inside a closure

Hello folks! I know the problem I have is quite common among beginners. I also understand why it happens. What I don't get is how I should solve it.

I have the following code (see full example on Github):

let ads_listing: Vec<Ad> = (0..9).into_iter().filter_map(|i| {
    // first, a seller needs to create supply
    let mut supply = seller.creates_supply(i);
    // later, a marketer will pick up this supply and put it on the market
    // FIXME: borrowing error
    marketer.makes_market(&mut supply)
}).collect();

// further in this scope:
println!("{:?}", ads_listing);

My question is: how do we fix this kind of problem, when we have a variable declared in some inner scope, and we pass it as a reference to be used outside of the inner scope? Should I be looking into the Smart Pointers (for example, the Box type)?

Regarding, Box, the answer is no. Boxing does not change anything when it comes to lifetimes or the borrow checker. In the given code example, I would probably eliminate every lifetime in your code. Replace every &'a str with a String, and either remove or don't use a reference for the fields that contain your own types.

For example, you could define your types in this way:

struct Market {
    sellers: HashMap<SellerId, Seller>,
    marketers: HashMap<MarketerId, Marketer>,
    buyers: HashMap<BuyerId, Buyer>,
}

struct SellerId(String);
struct Seller {
    id: SellerId,
    name: String,
}

struct MarketerId(String);
struct Marketer {
    id: MarketerId,
    name: String,
}

struct BuyerId(String);
struct Buyer {
    id: BuyerId,
    name: String,
}

struct Supply {
    available_items: u32,
    provided_by: SellerId,
    state: SupplyState,
}

enum SupplyState {
    Created,
    Marketed,
    Consumed,
}

struct Ad {
    marketer: MarketerId,
    supply: Supply,
}

struct Transaction {
    ad: Ad,
    taker: BuyerId,
}

Then any methods that need to access the seller, marketer or buyer can go on the Market struct, which can use the ids to look up in the HashMap.

1 Like

Oh, nice! I'll try it out :slight_smile: So the bottom line is to avoid hierarchical structure, and prefer composition. Did I get it correctly?

The main idea is to be clear about who owns who. Try to make sure every value has a single owner. References should not be used to get shared ownership.

3 Likes

Rule of the thumb is never use references in structs. struct <'a> means trouble.

References are inherently temporary and always permanently tied to the scopes they borrow from. That makes your structs temporary and scope-limited too, and 99% of the time that's not what you want.

3 Likes

Thank you all for the hints: no lifetime issues anymore. Now, it's all about the borrow checker and partially moved values. I pushed my most recent code to Github, and I prepared an example on the Rust Playground.

I started going down the road of adding .clone() in the spots the move would occur, but I'm not sure if that the right approach. Also, I did not manage to make my example in test working. Please let me know what are the patterns that help solving the move/partial move problem. Thank you in advance!

I feel I need to shift my mind a lot from the dynamic languages :upside_down_face:

Your makes_market method takes self as a parameter. That means it'll consume the instance of the Marketer struct it's used on and destroy it when the function ends. As such you can't use it afterwards. You'll want to use &self (to take a reference to it) or &mut self (if you want to mutate it).

You can avoid all the clones if you make structs like ProviderId take a number rather than a string, so that they can be Copy.

You can also avoid cloning Buyer and friends. Insert it into the hashmap (without cloning), then borrow a reference of them from the hashmap and use that to do the transactions.

Just clone the ids. As for the marketer objects, you can insert them into the map, then use the ID to look them up in the map.

Thank you, all kind people, for bearing with me here :slight_smile:

I spent some time trying to wrap my head around the advice from this topic, and from what I get now, I should always keep a single source of a particular value. Whenever I need to update the value, I use a function that would take &mut reference and make the required changes.

In case I have a dictionary of values, I understand that cloning the ID is the preferred way of accessing the dictionary items from any place in the nested scopes. Thanks to that I don't have to worry about passing lifetime annotations, and I can keep data integrity (due to not cloning dictionary items).

Thank you once again, everyone! I'll post the working example of my code tomorrow, so other beginners (as myself) could see "where it started" vs. "how's it going" :rocket:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.