Adding an item to the manager, then return it

Hey there. I'm still learning Rust, and what I'm trying to accomplish is fairly simple (I think).

Imagine we had two structs: ItemManager and Item

    struct ItemManager {
        items: Vec<Item>,
    }

    impl ItemManager {
        fn add(&mut self, item: Item) {
            self.items.push(item);
        }
    }

    struct Item {
        id: u64,
        desc: String,
    }

    impl Item{
        fn new(id: u64, desc: String, manager: &mut ItemManager) -> Self {
            let item = Self {
                id,
                desc,
            };
            // Uncomment the line below to make the compiler very angry:
            // manager.add(item);

            item
        }
    }
}

I know where the error is coming from: I'm moving the value item, and then I'm trying to return it. I know there must be an easy way to accomplish this, maybe using references, but I don't exactly know how.

Another question: I don't know if it's "Idiomatic Rust" to pass ItemManager to the "new" method of "Item".

I mean, I don't know if adding things to the manager should be a side-effect of calling "new" on a new item. Should I be passing the ItemManager as an argument, or is this not a good practice?

Thank you and apologies if this has an easy answer.

I think you'd have to get the item from items after pushing it there, as Vec doesn't currently offer any way to push and get a reference to the pushed object in one function as far as I know. It does feel a bit clunky though.

    fn new(id: u64, desc: String, manager: &mut ItemManager) -> &Self {
        let item = Self { id, desc };
        manager.add(item);
        manager.items.last().unwrap()
    }

You have to return a reference because ItemManager owns the Item, if you wanted to return an owned Item you'd have to take it out of the Vec you just put it in.
I do think passing ItemManager to Item::new looks strange, even more so if you change it to return a reference. Personally I'd rather make it a method of ItemManager.

1 Like

Note also that you will run into trouble because manager is a mutable reference, and something can only be mutably borrowed once at a time.

3 Likes

Depending on what ItemManager does with the items it’s managing, some combination of Arc/Rc/Weak might also be appropriate. For example, if ItemManager needs access to all the Items, but doesn’t need to keep them alive, you can do this:


struct ItemManager {
    items: Vec<Weak<Item>>,
}

impl Item {
    fn new(id: u64, desc: String, manager: &mut ItemManager) -> Rc<Self> {
        let item = Rc::new(Self { id, desc });
        manager.add(Rc::clone(item).downgrade());
        item
    }
}
2 Likes

Thank you all for your answers! I think that, at least for my own problem, the solution is to return a reference from Item as Heliozoa suggested.

Alice's heads up is also very insightful; I think I should make the manager parameter a reference (not a mutable reference).

As for 2e71828: Arc/Rc/Weak are some advanced terms I haven't gotten into yet! But I will re-read your answer as soon as I get there. I'm sure it makes a lot of sense. Thank you, too :slight_smile:

Cheers!

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.