1

I'm having the problem shown in the sample case here:

struct Deck<'a> {
    cards: Vec<&'a str>
}
impl Deck<'_> {
    fn top_in_deck(&self) -> &str {
        self.cards[0]
    }
    fn remove_top_in_deck_if_is_card(&mut self, card: &str) {
        if self.cards[0] == card {
            self.cards.remove(0);
        }
    }
}

fn main() {
    let mut deck = Deck { cards: vec!["9-H", "K-D"] };
    let top_card = deck.top_in_deck();
    deck.remove_top_in_deck_if_is_card(top_card);
}
error[E0502]: cannot borrow `deck` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:5
   |
17 |     let top_card = deck.top_in_deck();
   |                    ------------------ immutable borrow occurs here
18 |     deck.remove_top_in_deck_if_is_card(top_card);
   |     ^^^^^-----------------------------^^^^^^^^^^
   |     |    |
   |     |    immutable borrow later used by call
   |     mutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.

I do understand the problem, I get a read-only reference from top_in_deck, so then I can't call remove_top_in_deck_if_is_card which needs a mutable reference. But I'm not sure what would be the best way to make something like this work. I tried cloning the string returned by top_in_deck, thinking that maybe the compiler would understand that the return of the function is not a reference to the struct, but it didn't work. Does anyone know what to do in a case like this?

Marc Garcia
  • 3,287
  • 2
  • 28
  • 37
  • 4
    Cloning a `&str` is meaningless. If you want an owned `String`, you need to call `.to_owned()` or `.to_string()` on it. This is one way to resolve the problem. Another is to return the index of the card instead of its content. – Chayim Friedman Jul 12 '23 at 10:54
  • without context it's hard to answer, you can look at entry api of hashmap std that a very interesting way to solve such problem. – Stargateur Jul 12 '23 at 13:56

1 Answers1

3

There are multiple solutions:

1. Simple fix: use &'static str.

The type of"9-H" is actually &'static str. Use that type directly instead of &'a str.

[Smartass mode on:] Your code casts Vec<&'static str> into Vec<&'a str> and it's possible because Vec is covariant on the lifetime of its type parameter. Sounds complicated? Yes it is, avoid it if you can, this solution comes with its own can of worms.

Playground

2. Preferred design: don't store borrowed references on the heap.

Use value types to represent your cards. Anything that implements Copy will work better on the long term because lifetimes need additional attention and references have lifetimes. If you use references, your Deck won't own the cards it stores.

There are multiple simple ways: type Card = i32, or an enum Card, or maybe a custom struct with simple fields.

aedm
  • 5,596
  • 2
  • 32
  • 36
  • Thanks, this is helpful. I made the example with static strings and cards to make it simpler, but what I'd store in the vector in practice would be arbitrary strings. I guess 2 solves and answers my problem, I guess I should rather use `String` in the struct instead of `&str`. Thanks a lot for the hint, I think your answer is correct for my question, but this is the exact answer I needed for the actual problem: https://stackoverflow.com/questions/25754863/how-to-create-a-rust-struct-with-string-members – Marc Garcia Jul 13 '23 at 05:59