Table of Contents
· 2 min read

Unexpected pattern-matching in Rust

First, a question. What is this code in the wild doing?

async fn v1_conversations_list_items(
State(state): State<Arc<AppState>>,
Path(conversation_id): Path<String>,
Query(ListItemsQuery {
limit,
order,
after,
}): Query<ListItemsQuery>,
) -> Response { ... }

Huh?”, I can hear you say. Those look nothing like function arguments. What are those weird wrappers?

This syntax has a surprisingly satisfying answer.

Pattern-matching is everywhere

If you’ve used Rust, you’re probably familiar with its umpteen ways to destructure values. Destructuring, for the uninitiated, looks like this:

let (a, b) = (1, 2);
// a and b are auto-initialized to 1 and 2

but is most frequently encountered using match statements:

match result {
Ok(value) => { /* use value here */}
Err(error) => {/* use error here */}
}

You can also use if-let and while-let expressions:

if let Some(x) = maybe {
println!("{x}");
}
while let Some(x) = iter.next() {
println!("{x}");
}

or destructure references

let &x = &5;

and so on.

Function arguments can be pattern-matched too

Yes, you read that correctly:

Function arguments can be pattern-matched too.

Let’s revisit our first example:

async fn v1_conversations_list_items(
State(state): State<Arc<AppState>>,
Path(conversation_id): Path<String>,
Query(ListItemsQuery {
limit,
order,
after,
}): Query<ListItemsQuery>,
) -> Response { ... }

Here, the function is simply destructuring variables for use in the function body. It accepts a State<Arc<AppState>> and extracts the inner state as a variable. Here’s the full body:

async fn v1_conversations_list_items(
State(state): State<Arc<AppState>>,
Path(conversation_id): Path<String>,
Query(ListItemsQuery {
limit,
order,
after,
}): Query<ListItemsQuery>,
) -> Response {
conversations::list_conversation_items(
&state.context.conversation_storage,
&state.context.conversation_item_storage,
&conversation_id,
limit,
order.as_deref(),
after.as_deref(),
)
.await
}

Notice how the body can simply reference limit, order, etc.

The slightly more cumbersome equivalent in more familiar syntax:

async fn v1_conversations_list_items(
app_state: State<Arc<AppState>>,
path_string: Path<String>,
query: Query<ListItemsQuery>
) -> Response {
let State(state) = app_state;
let Path(conversation_id) = path_string;
let Query(ListItemsQuery {
limit,
order,
after,
}) = query;
conversations::list_conversation_items(
&state.context.conversation_storage,
&state.context.conversation_item_storage,
&conversation_id,
limit,
order.as_deref(),
after.as_deref(),
)
.await
}

Isn’t that neat?

Upvoting temporarily disabled

Found this helpful? Share on Hacker News, subscribe to my RSS feed, or email me.