rustmove-semanticsownership

Error: Use of moved value, while it cannot be used


Consider the following code:

fn print_or(opt: Option<()>, tail: Vec<i32>) -> Vec<i32> {
    opt.map_or(tail, |_| {
        println!("{:?}", tail);
        tail
    })
}

Playground

The error message says that:

error[E0382]: use of moved value: `tail`
 --> src/lib.rs:2:22
  |
1 | fn print_or(opt: Option<()>, tail: Vec<i32>) -> Vec<i32> {
  |                              ---- move occurs because `tail` has type `Vec<i32>`, which does not implement the `Copy` trait
2 |     opt.map_or(tail, |_| {
  |                ----  ^^^ value used here after move
  |                |
  |                value moved here
3 |         println!("{:?}", tail);
4 |         tail
  |         ---- use occurs due to use in closure
  |
help: consider cloning the value if the performance cost is acceptable
  |
2 |     opt.map_or(tail.clone(), |_| {
  |                    ++++++++

And it's clear since I use owned Vec<i32> type here. But the problem is that it's pretty obvious that it cannot be used after move since Option cannot be Some and None at the same time. Is there a way to overcome the problem without clone or Rc?


Solution

  • Rust doesn't really have a way to say that only one argument of a function will be used, but it does know that only one branch of a control flow statement will run, so you can use match:

    match opt {
        None => tail,
        Some(()) => {
            println!("{:?}", tail);
            tail
        }
    }
    

    Similarly, you can use if let:

    if let Some(()) = opt {
        println!("{:?}", tail);
        tail
    } else {
        tail
    }
    

    And in this case, you can just check if opt is Some since both branches end in the same thing:

    if opt.is_some() {
        println!("{:?}", tail);
    }
    tail