How match works with parameter reference?

Hi,

sorry to post here frequently, as I learn more about rust, I am getting glued to it and this forum is the only place to rescue when I am stuck. I have to strip my code to make the question shorter.

Iteration 1(No compilation error, all good)

pub fn setbaud (instance: u8, clk: Clock, baud: Baud){

    unsafe {
        match(clk, baud){
            
            (Clock::F2Mhz, Baud::R31250) => (*uartx).div = 63,
            (Clock::F2Mhz, Baud::R115200) => (*uartx).div = 16,

Iteration 2 - tried to take in as reference and dereferencing in match (compilation error)

pub fn setbaud (instance: u8, clk: &Clock, baud: &Baud){

    unsafe {
        match(*clk, *baud){
            
            (Clock::F2Mhz, Baud::R31250) => (*uartx).div = 63,
            (Clock::F2Mhz, Baud::R115200) => (*uartx).div = 16,

Throws following error and could n't understand.
match(clk, baud){
** | ^^^^^ move occurs because *baud has type Baud, which does not implement the Copy trait

Iteration 3 (NO compilation error without dereferecing)

pub fn setbaud (instance: u8, clk: &Clock, baud: &Baud){

    unsafe {
        match(clk, baud){
            
            (Clock::F2Mhz, Baud::R31250) => (*uartx).div = 63,
            (Clock::F2Mhz, Baud::R115200) => (*uartx).div = 16,

My main question here is how iteration 3 did NOT throw any compilation error?
Coz, parameters are reference, but matched without dereferencing it.

In old versions of Rust, the 3rd iteration wouldn't have worked, because the types are different. However the match ergonomics RFC added some rules to make matching through references less finicky.

Your third iteration is basically equivalent to

pub fn setbaud (instance: u8, clk: &Clock, baud: &Baud){

    unsafe {
        match(clk, baud){
            
            (&Clock::F2Mhz, &Baud::R31250) => (*uartx).div = 63,
            (&Clock::F2Mhz, &Baud::R115200) => (*uartx).div = 16,

In this explicit version the code requires clk and baud to be references, as the match arm patterns all specify that they should match a reference to the value.


As for second iteration error, the compile error is ultimately pretty simple. When you use the deref operator you are creating a copy of the data behind the reference. In the case of simple enums, it would probably be fine to implement Copy so that was allowed, but it very often isn't what you want.

2 Likes

Recommended reading: Patterns Are Not Expressions (Because They Are Duals).

The article predates match binding modes (aka match ergonomics). The binding modes can be convenient, but can also be confusing -- as witnessed by your question![1] Therefore it can be nice to have a tool to tell you when the binding modes are doing something invisible / when your pattern is no longer acting in a purely dual-like manner.

The Clippy lint pattern_type_mismatch can do this for you. In this playground, I've set up variations of your three examples. The first one compiles; not too interesting. In the second one, I've stopped derefing in the tuple and am instead utilizing the dual nature of patterns without using match binding modes. Try bouncing between the article above and the code to see if you understand how it works.

The third example is just like yours. But if you run Clippy (under Tools, top-right) you'll get warnings where match bindings is implicitly acting like the second example. Following the Clippy help one by one would eventually result in the third example looking like the second example.


Finally, note that I've also disabled a warn-by-default Clippy lint, explicit_auto_deref. You can comment that line out and run Clippy again to see what it suggests. It is in effect suggesting changes like this to the code:

-        (Clock::F2Mhz, Baud::R31250) => (*uartx).div = 63,
+        (Clock::F2Mhz, Baud::R31250) => uartx.div = 63,

It's a good idea to run Clippy on your own code, especially when learning. It can be a bit opinionated at times, but on the whole, it will point out unidiomatic code and help you improve.


  1. They also have some buggy corner-cases. ↩ī¸Ž

6 Likes

The RFC is overwhelming to me especially the ref and ref mut as I haven't heard/used these yet. So if match expression is provided with reference, it matches against the reference of each value. Got it.

But, since my code in third iteration don't have the reference for all match patterns in match arms, should n't it throw error?
will match expression if passed a reference, automatically matches with refernce?

Thanks. In your playground code, clippy clearly throws the message "
warning: type of pattern does not match the expression type.

Will find out how i can run Clippy on my code. But still wonder why don't compiler show the same message.

Discrepancies in clippy warning can be due to mismatched versions of Rust, or because you have disabled some rules.

The compiler has a number of built in lints. Clippy is an extended and more opinionated set of lints, and also sort of a "proving ground" for lints. Occasionally Clippy lints are uplifted into the compiler.

Here's how you can add the Clippy subcommand so you can run it yourself. The pattern_type_mismatch lint is allow by default, so you'd also have to enable it when you want to see it in action.

1 Like

The language has this inference of types from patterns as a feature. IMO it was a mistake to allow mismatching patterns and values, but it's intentional. Thus the compiler will never treat it as an error.

Since it's a feature, Clippy used to accept it as well. I had some rather heated discussions on IRLO and my request to have a lint for it was repeatedly rejected by some developer of Clippy with the rather arrogant reasoning of "we don't lint against accepted features". But one day, the lint somehow appeared anyway (thankfully). Still, it's allow-by-default.

1 Like