site logo
github logotwitter logolinkedin logo

String pattern matching

June 20, 2021

It’s been quite some time since I coded in a lower level language than JavaScript. Distributed systems recently piqued my interest thus I decided to pick-up a lower level compiled language. I chose Rust which I knew nothing about. Inspired by @swyx’s “learn in public” I decided to document my journey learning Rust.

Matching a string against another one is a pattern you often use in development. For example let’s say we’d want to match a string representing a status against the different possible statuses. In JavaScript it could look like this:

const status = 'start';

switch (status) {
case 'ready':
  /** Do something **/
  break;

case 'start':
  /** Do something **/
  break;

case 'stop':
  /** Do something **/
  break;

default:
  throw new Error('Unknown status');
}

Coming to Rust as a JS engineer you’d be tempted to write:

// Let's imagine this string comes from somewhere else
// and you'd get a String and not a string literal (&str)
let status = String::from("start");

match status {
  "ready" => /** Do something **/,
  "start" => /** Do something **/,
  "stop" => /** Do something **/,
  _ => /** Do something **/,
};

The compiler however will not agree as this code triggers an error:

error[E0308]: mismatched types
 --> src/main.rs:5:5
  |
4 | match status {
  |       ------ this expression has type `String`
5 |     "ready" => (),
  |     ^^^^^^^ expected struct `String`, found `&str`

For those wondering I just replaced the comment blocks /** Do something **/ above by the unit type () so the compiler would not complain.

From what the compiler tells us, we understand that we gave the match operator a String but the pattern branches have &str (string literals). Rust does not know how to match these two types. We could try to rewrite it as followed:

let status = String::from("start");

match status {
  String::from("ready") => /** Do something **/,
  String::from("start") => /** Do something **/,
  String::from("stop") => /** Do something **/,
  _ => /** Do something **/,
};

Sadly this won’t cut it:

error[E0164]: expected tuple struct or tuple variant, found associated function `String::from`
 --> src/main.rs:5:5
  |
5 |     String::from("ready") => (),
  |     ^^^^^^^^^^^^^^^^^^^^^ `fn` calls are not allowed in patterns
  |
  = help: for more information, visit https://doc.rust-lang.org/book/ch18-00-patterns.html

Function calls are not allowed in patterns (as the compiler says).

Actually the quickest way to make that code works is to cast the status String into a string literal. We can use status.as_str() or &status[..]:

let status = String::from("start");

match status.as_str() {
  "ready" => /** Do something **/,
  "start" => /** Do something **/,
  "stop" => /** Do something **/,
  _ => /** Do something **/,
};

This now compiles properly 🎉. Hope it helps.


Written by Jonas who lives in Lyon, France and he's passionate about Open Source and web development in general. Feel free to ping him on Twitter.