Why use `match` in function no error but in main happened?

I want to parse command args. and match args.path to do some different:

use clap::Parser;

#[derive(Parser)]
struct Cli {
    #[arg(value_name = "FILE", default_value = "-")]
    path: String,
}

fn main() {
    let args = Cli::parse();
    open(&args.path);
}

fn open(filename: &str) -> () {
    match filename {
        "-" => {
            println!("{:?}", filename);
        }
        _ => {
            println!("{:?}", filename);
        }
    }
}

But if I want inline open function to main. error happened:

fn main() {
    let args = Cli::parse();

    match &args.path {
        "-" => {
      // ^ expected reference `&std::string::String`, found reference `&'static str` 
            println!("hello world");
        }
        _ => {
            println!("hello world");
        }
    }
}

I have no idea why it failed. and If I use if, it no error too:

fn main() {
    let args = Cli::parse();

    if &args.path == "-" {
        println!("hello world");
    } else {
        println!("hello world");
    }
}

It let me feel very incomprehensible. Does anybody can help me :frowning:

The match statement lets you pattern match on &str, but the type of &args.path is &String.

Try changing it to match args.path.as_str() { ... } to force &args.path to be a string.

The if-statement works because a &String can be compared with &str (i.e. &String: PartialEq<&str>).

2 Likes

Does the function or if statement auto do the convert?

fn main() {
    let args = Cli::parse();
    open(&args.path) // open require a &str as first param
    if &args.path == "-" {} // "-" is &str
}

fn open(filename: &str) {
    println!("{}", filename)
}

When you call open(&args.path), where &args.path is a &String and open() accepts a &str and String can dereference to a str, the compiler will automatically dereference the &String for you. This is one of the few implicit type coersions in Rust, and is also referred to as "auto deref".

This thread on auto deref even mentions how the compiler won't automatically dereference when doing pattern matching (i.e. your match situation):

The equality check in your if-statement isn't doing any special conversions.

It's just combining impl PartialEq<str> for String with the general impl<A, B> PartialEq<&B> for &A where B: PartialEq<A> to infer that &String: PartialEq<&str>, and therefore we can use == with a &String on the left-hand side and &str on the right.

4 Likes

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.