2

I am implementing a simple library system to keep track of my pdfs.

I have a Subject enum and a Entry struct defined as follows:

pub enum Subject {

    Math,
    Programming,
    CompSci,
    Language,
    Misc,
    None
}

pub struct Entry {

    pub subject: Subject

}

I am trying to implement a function that will operate on a vector of Entry's and return a Vec<&Entry> whose entries match a given Subject.

I have a simple Library struct that is a wrapper around a Vec<Entry>:

pub struct Library {

    pub entries: Vec<Entry>

}

In order to do so, I need to iterate through entries and filter only the elements whose .subject field correspond to the desired subject. To accomplish this I have created a function that will return a predicate function.

Here is the get_subject function:

impl Library {
    
    pub fn get_subject(&self, subject: Subject) -> Vec<&Entry> {

        let pred = subject_pred(subject);
        self.entries.iter().filter(pred).collect::<Vec<&Entry>>()
    }
}

which calls the function subject_pred to create the correct predicate function:

// Return a PREDICATE that returns true when
// the passed ENTRY matches the desired SUBJECT
fn subject_pred(subject_UNUSED: Subject) -> impl FnMut(&&Entry) -> bool {
    |e: &&Entry| if matches!(&e.subject, subject_UNUSED) {
        true
    } else {
        false
    }
}

Here's the problem. This syntax compiles just fine but apparently the subject_UNUSED local variable in subject_pred is "unused". I am flabbergasted as my syntax clearly shows intent to match with the passed subject_UNUSED. When I test out this function on a vector of entries, the predicate always returns true (hence why I am receiving the "unused" warning) but I have literally no idea why.

If anyone could explain why the match statement is always matched, that would be greatly appreciated. I tried using a regular match statement but the same warning is popping up, and this is not the behavior that I am trying to code. If I don't include the subject_UNUSED in a traditional match statement, the compiler tells me that I have to cover the Math, Programming, CompSci, Language, Misc and None variants of my enum, which indicates to me that everything up until that point is good.

ejovo13
  • 37
  • 6
  • 1
    This signatue - `pub fn get_subject(entries: Vec, subject: Subject) -> Vec<&Entry>` - is not going to work: you cannot take an owned `Entry` and produce a borrowed one. – Chayim Friedman Jun 07 '22 at 21:17
  • I actually have a `Library` structure that is simply a wrapper around a `Vec` but I didn't want to include it to complicate things. I'll include them in an edit for transparency – ejovo13 Jun 07 '22 at 21:21
  • Does this answer your question? [Why is this match pattern unreachable when using non-literal patterns?](https://stackoverflow.com/questions/28225958/why-is-this-match-pattern-unreachable-when-using-non-literal-patterns) – Jmb Jun 08 '22 at 06:49

1 Answers1

6

You cannot match against a variable. What you've done is equivalent to

matches!(&e.subject, some_subject)

That matches any Subject, just like a wildcard (_), except it also captures it in the some_subject variable (can be used in a guard like matches!(&e.subject, subject_UNUSED if subject_UNUSED == ...)). Neither the captured variable nor the parameter (which is shadowed by it) are used.

What you need to do is to #[derive(PartialEq)] then use ==:

if e.subject == subject_UNUSED { ... }

By the way, your code also has other problems: you don't move into the closure and you're taking owned entries but produce borrowed.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Thank you for your reply. I started off with a simple `==` but moved to a `match` because that's what I've seen done when "comparing" enums. I'll use `#[derive(PartialEq)]` as a solution. I also had a `move` statement previously in the closure but since that "didn't change anything" (from my ignorant standpoint) I removed it. I'm just kinda stumbling through the language and finding it far more difficult then anything I've learned in the past. "You cannot match against a variable" isn't mentioned in the book. – ejovo13 Jun 07 '22 at 21:29
  • @ejovo13 Well, it's been long since I learned Rust, but looking at the book, it does mention that you match against patterns and I don't think it says a variable is a pattern. – Chayim Friedman Jun 07 '22 at 21:33
  • I get your point but my counter is that the value stored in my variable will very much be an enum variant so it's not at all unreasonable to assume that I can match against something that is effectively a pattern. It's like saying that I can't expect `let x = 10; println!("{}", x);` to print out the value `10` because I'm not actually passing the value `10` rather I'm passing a variable... The point is I'm not hating on the authors of the book - it's amazing - it's just one of those things that you only learn the limitations of the language when you try something tthat doesnt work. – ejovo13 Jun 08 '22 at 00:57
  • @ejovo13 Well what you're storing in the variable is not a pattern ([you can't do that](https://stackoverflow.com/questions/42793606/how-can-i-store-a-pattern-in-a-variable-in-rust)) but a value, and the `10` (or the enum variant) you're assigning in the variable declaration is not a pattern but an expression. However, arguing about that is pretty redundant. Now you understand and that is the important thing. – Chayim Friedman Jun 08 '22 at 01:00
  • Not intending to argue! It's a learning process and yes now I understand and that's what's important :). Just giving in to my primitive desire to defend myself ;). Thanks again for the help. – ejovo13 Jun 08 '22 at 01:03