Skip to content

Commit

Permalink
Matching on structopt commands and using values after move
Browse files Browse the repository at this point in the history
We can match on `opt.cmd` to determine which `Command`
variants was called. This lets us specify the variants
as match criteria and also destructure the values out
to use them.

```rust
    let opt = Opt::from_args();
    dbg!(&opt);
    match opt.cmd {
        Command::Write { title } => todo!(),
    }
```

As written, we run into two issues with this code.
One is that we aren't using `title` yet. In
destructuring, we can use the placeholder (`_`) in our
[pattern](https://doc.rust-lang.org/reference/patterns.html).

```
warning: unused variable: `title`
  --> src/main.rs:32:26
   |
32 |         Command::Write { title } => todo!(),
   |                          ^^^^^ help: try ignoring the field: `title: _`
   |
   = note: `#[warn(unused_variables)]` on by default
```

```rust
fn main() -> Result<()> {
    color_eyre::install()?;

    let opt = Opt::from_args();
    dbg!(opt);
    match opt.cmd {
        Command::Write { title: _ } => todo!(),
    }
}
```

The second issue is that the `opt` variable is moved
before we try to destructure it. If we look at the
compiler output before we fixed the `title` issue we
see the value use after move pointing at `title`
specifically.

```
error[E0382]: use of moved value
m  --> src/main.rs:32:26
   |
29 |     let opt = Opt::from_args();
   |         --- move occurs because `opt` has type `Opt`, which does not implement the `Copy` trait
30 |     dbg!(opt);
   |     ---------- value moved here
31 |     match opt.cmd {
32 |         Command::Write { title } => todo!(),
   |                          ^^^^^ value used here after move
```

If we look at it after fixing the unused `title`,
we see the compiler point at `opt.cmd`.

```
error[E0382]: use of moved value: `opt.cmd`
  --> src/main.rs:31:11
   |
29 |     let opt = Opt::from_args();
   |         --- move occurs because `opt` has type `Opt`, which does not implement the `Copy` trait
30 |     dbg!(opt);
   |     ---------- value moved here
31 |     match opt.cmd {
   |           ^^^^^^^ value used here after move
```

This is because opt is moved into the `dbg` call because
it doesn't implement the `Copy` trait. Now, we could
implement or derive `Copy` for `Opt` if `Command` can
implement `Copy`, but we can not implement `Copy` for
`Command`. This is because `String` does not, and can not,
implement `Copy`, and we have a `String` in our `title`.

Without diving into the depths of `Copy`, [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html),
and allocation, there is something we have that already
implements `Copy`, so we don't need to.

Shared references implement `Copy`.

So instead of passing `opt` into `dbg!`, and thus using
move semantics (which are the default in Rust), we can
pass a shared reference in: `dgb!(&opt)` which lets us
use copy semantics.

The difference between move semantics and copy semantics
in this case is that we can access `opt` after passing it
to `dbg!`.

Under the hood, both a copy and a move can result in bits
being copied in memory, although this is sometimes
optimized away. -- [Copy](https://doc.rust-lang.org/std/marker/trait.Copy.html)

```rust
fn main() -> Result<()> {
    let opt = Opt::from_args();
    dbg!(&opt);
    match opt.cmd {
        Command::Write { title: _ } => todo!(),
    }
}
```
  • Loading branch information
ChristopherBiscardi committed Jan 16, 2021
1 parent caf3327 commit 150a6db
Showing 1 changed file with 4 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/main.rs
Expand Up @@ -27,6 +27,8 @@ fn main() -> Result<()> {
color_eyre::install()?;

let opt = Opt::from_args();
dbg!(opt);
todo!()
dbg!(&opt);
match opt.cmd {
Command::Write { title: _ } => todo!(),
}
}

0 comments on commit 150a6db

Please sign in to comment.