squi
squi

Reputation: 13

How can I prevent an Option<T> from being moved when I reassign it?

I have the following code:

use std::env;
use std::fs;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::stdin;
use std::io::stdout;
use std::io::ErrorKind;
use std::io::Write;
use std::io::{self, prelude::*, BufReader};
use std::path::Path;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use std::str;

fn process_line(input: &str) -> String {
    let mut commands = input.trim().split(" | ").peekable();
    let mut previous_command = None;
    let mut resultat = String::new();

    while let Some(command) = commands.next() {
        let mut parts = command.trim().split_whitespace();
        let mut command = parts.next().unwrap();
        let args = parts;

        match command {
            //[...]
            command => {
                if commands.peek().is_some() {
                    let stdin = match previous_command {
                        None => Stdio::inherit(),
                        Some(o) => o,
                    };
                    let stdout = Stdio::piped();

                    let output = Command::new(command)
                        .args(args)
                        .stdout(stdout)
                        .stdin(stdin)
                        .spawn();

                    match output {
                        Ok(o) => previous_command = Some(Stdio::from(o.stdout.unwrap())),
                        Err(e) => {
                            previous_command = None;
                            eprintln!(" Err: {}", e);
                        }
                    };
                } else {
                    let stdin = match previous_command {
                        None => Stdio::inherit(),
                        Some(o) => o,
                    };
                    let output = Command::new(command)
                        .args(args)
                        .stdin(previous_command.unwrap())
                        .output()
                        .expect("???");
                    &mut resultat.push_str(
                        str::from_utf8(&output.stdout).expect("Could not convert command to str"),
                    );
                }
            }
        };
    }
    return resultat;
}

The goal is to take a string containing a shell command that my or may not contain pipes, execute them and return the result.

I use previous_command in the case of a pipe to store the output of the previous command and reuse it; however, I can't seem to reassign it without ending up with "use of moved value":

error[E0382]: use of moved value
  --> src/lib.rs:59:34
   |
30 |         let mut previous_command = None;
   |             -------------------- move occurs because `previous_command` has type `Option<Stdio>`, which does not implement the `Copy` trait
...
59 |                             Some(o) => o,
   |                                  ^ value used here after move
...
83 |                             .stdin(previous_command.unwrap())
   |                                                     -------- `previous_command` moved due to this method call, in previous iteration of loop

I have tried using as_mut() to reassign value, but to no avail... Any idea?

I have tried using as_mut() such as:

Ok(o) => previous_command.as_mut() = &mut Some(Stdio::from(o.stdout.unwrap())),

but end up having "cannot assign to this expression".

Also tried wrapping it up in a vector, but could not get to manage.

Upvotes: 1

Views: 109

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 71300

You can see that each time previous_command is moved from it is also set back, except in the last iteration of the loop; but the compiler cannot distinguish the last iteration from previous iterations, and it complains that you may enter the if after it was moved from by the else in the previous iteration.

The fix is simple: just to make the compiler happy, assign previous_command = None; at the end of the else block.

This will reveal another (unrelated) borrow error, but it is pretty easy to solve. I will leave it as an exercise to you :)

Upvotes: 1

Related Questions