this post was submitted on 19 Mar 2024
1 points (100.0% liked)

Learn Programming

1805 readers
1 users here now

Posting Etiquette

  1. Ask the main part of your question in the title. This should be concise but informative.

  2. Provide everything up front. Don't make people fish for more details in the comments. Provide background information and examples.

  3. Be present for follow up questions. Don't ask for help and run away. Stick around to answer questions and provide more details.

  4. Ask about the problem you're trying to solve. Don't focus too much on debugging your exact solution, as you may be going down the wrong path. Include as much information as you can about what you ultimately are trying to achieve. See more on this here: https://xyproblem.info/

Icon base by Delapouite under CC BY 3.0 with modifications to add a gradient

founded 2 years ago
MODERATORS
 

cross-posted from: https://lemmy.ml/post/13353225

Quick little confusion or even foot-gun I ran into (while working on the challenge I posed earlier).

TLDR

My understanding of what I ran into here:

  • Matching on multiple variables simultaneously requires assigning them to a tuple (?),
  • which happens more or less implicitly (?),
  • and which takes ownership of said variables.
  • This ownership doesn't occur when matching against a single variable (?)
  • Depending on the variables and what's happening in the match arms this difference can be the difference between compiling and not.

Anyone got insights they're willing to share??

Intro

I had some logic that entailed matching on two variables. Instead of having two match statements, one nested in the other, I figured it'd be more elegant to match on both simultaneously.

An inline tuple seemed the obvious way to do so and it works, but it seems that the tuple creates some ownership problems I didn't anticipate, mostly because I was thinking of it as essentially syntax and not an actual tuple variable.

As you'll see below, the same logic with nested match statements each on a single variable doesn't suffer from the same issues.

Demo Code

fn main() {

    // # Data structures
    enum Kind {
        A,
        B
    }
    struct Data {
        kind: Kind
    }

    // # Implementation
    let data = vec![Data{kind: Kind::A}];

    // ## Basic idea: process two adjacent data points
    let prev_data = data.last().unwrap();
    let new_data = Data{kind: Kind::B};

    //

MATCH STATEMENTS


// ## This works: match on one then the other
let next_data = match prev_data.kind {
    Kind::A => match new_data.kind {
        Kind::A => 1,
        Kind::B => 2,
    },
    Kind::B => match new_data.kind {
        Kind::A => 3,
        Kind::B => 4,
    },
};

// ## This does NOT work: match on both
let next_data2 = match (prev_data.kind, new_data.kind) {
    (Kind::A, Kind::A) => 1,
    (Kind::A, Kind::B) => 2,
    (Kind::B, Kind::A) => 3,
    (Kind::B, Kind::B) => 4,
};

}


### The Error

The error is on the line `let next_data = match (prev_data.kind, new_data.kind)`, specifically the tuple and its first element `prev_data.kind`, with the error:

error[E0507]: cannot move out of prev_data.kind which is behind a shared reference

move occurs because prev_data.kind has type Kind, which does not implement the Copy trait


### The Confusion

So `prev_data.kind` needs to be moved.  That's ok.  Borrowing it with `(&prev_data.kind, ...)` fixes the problem just fine, though that can cause issues if I then want to move the variable within the match statement, which was generally the idea of the logic I was trying to write.

What got me was that the same logic but with nested match statements works just fine.

I'm still not clear on this, but it seems that the inline tuple in the second tuple-based approach is a variable that takes ownership of the variables assigned to it.  Which makes perfect sense ... my simple mind just thought of it as syntax for interleaving multiple match statements I suppose. In the case of nested match statements however, I'm guessing that each match statement is its own scope.

**The main thing I haven't been able to clarify is what are the ownership dynamics/behaviours of match statements??**  It seems that there's maybe a bit happening implicitly here??  I haven't looked super hard but it does seem like something that's readily glossed over in the materials I've seen thus far??

### General Implications

AFAICT:
* if you want to match on two or more variables simultaneously, you'll probably need borrow them in the match statement if they're anything but directly owned variables.
* If you want to then use or move them in the match arms you may have to wrangle with ownership or just use nested match statements instead (or refactor your logic/implementation).
* **So probably don't do multi-variable matching unless the tuple of variables is a variable native to the logic**?
no comments (yet)
sorted by: hot top controversial new old
there doesn't seem to be anything here