-1

I have a function that can take a &[&str] or a &[String] and return different values based on the slice. It works fine using == if I add a PartialEq constraint:

pub fn info1<'a, S>(path: &[S]) -> Option<String>
where
  S: PartialEq<&'a str>,
{
  if path == ["a", "b"] {
    return Some("It's a-b!".to_string())
  }
  None
}

But it doesn't work if I use match:

pub fn info2<'a, S>(path: &[S]) -> Option<String>
where
  S:  PartialEq<&'a str>,
{
  match path {
    ["a", "b"] => Some("It's a b!".to_string()),
    _ => None,
  }
}
error[E0308]: mismatched types
  --> src/lib.rs:16:6
   |
11 | pub fn info2<'a, S>(path: &[S]) -> Option<String>
   |                  - this type parameter
...
15 |   match path {
   |         ---- this expression has type `&[S]`
16 |     ["a", "b"] => Some("It's a b!".to_string()),
   |      ^^^ expected type parameter `S`, found `&str`
   |
   = note: expected type parameter `S`
                   found reference `&'static str`

Playground

Is there any way to make it work? Ideally without requiring something like ["a".as_foo(), "b".as_foo()].

Timmmm
  • 88,195
  • 71
  • 364
  • 509

2 Answers2

1

You can't use match for this.

There is nothing wrong in your first version. You can make it more general using a higher-ranked trait bound (for<'a>):

pub fn info1<S>(path: &[S]) -> Option<String>
where
    for<'a> S: PartialEq<&'a str>,
{
    if path == ["a", "b"] {
        Some("It's a-b!".to_string());
    } else {
        None
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Stargateur
  • 24,473
  • 8
  • 65
  • 91
0

I got a helpful answer from the Rust Forum. It isn't possible to use match because patterns in match can only consist of structs, enums, slices and literals. The best description of that I found is here:

A pattern consists of some combination of the following:

  • Literals
  • Destructured arrays, enums, structs, or tuples
  • Variables
  • Wildcards
  • Placeholders

In this case, a string literal like "a" is a &str, so that's really the only kind of string you can match against (and you can see people having basically the same problem but without the slice here). In that case you can work around it without allocation because you don't need to construct a slice of references - you only need one.

Since you definitely need a slice of &strs there's really no other way than to allocate a Vec<&str> as suggested by Skysch:

pub fn info1<'a, S>(path: &[S]) -> Option<String>
where
  S: AsRef<str>,
{
  match &path.iter().map(|s| s.as_ref()).collect::<Vec<_>>()[..] {
    ["a", "b"] => Some("It's a b!".to_string()),
    _ => None,
  }
}

I think even with macro_rules! you couldn't solve it using match. However would be possible to make a match-like macro that uses == instead.

Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • we may be "antagonistic" but I least we known that "Since you definitely need a slice of &strs there's really no other way than to allocate a Vec<&str>" is a bad solution – Stargateur May 20 '21 at 20:38
  • 1
    Allocating a `Vec` is a perfectly fine solution in 99% of cases. Not every bit of code has to be optimised to the nth degree. – Timmmm May 20 '21 at 20:39