Google Codejam 2016 Round 3 Problem A I'm learning Rust by completing a variety of coding challenges, such as the Google Codejam problem stated in the title.  I completed a Python 3 implementation that I liked, which I then tried to port over to Rust.  

To my surprise, the Python implementation appeared to be considerably faster: when I ran time python3 ../a.py ../A-large-practice.in &gt; /dev/null, I got times of roughly .075s for each Python run as compared to when I ran time cargo run --release ../A-large-practice.in &gt; /dev/null for which I got roughly .210s for each Rust run.  These times come from averaging the sums of the usr and sys outputs generated by running the time command around 10 times per language.

How can I improve my Rust implementation so that it better conforms to best practices and also ends up being faster (at least to a level comparable with the Python implementation)?

use std::env::args;
use std::fs::File;
use std::io::Read;

fn main() {
    let args: Vec&lt;String&gt; = args().collect();
    let mut input = String::new();
    let mut f = File::open(&amp;args[1]).unwrap();
    f.read_to_string(&amp;mut input).unwrap();

    let mut input = input.lines();
    let num_inputs: u32 = match input.next() {
        None =&gt; panic!("No contents."),
        Some(num) =&gt; {
            match num.trim().parse() {
                Ok(num) =&gt; num,
                Err(_) =&gt; panic!("First line in input not a number."),
            }
        }
    };

    for i in 0..num_inputs {
        let mut score = 0;
        let moods: Vec&lt;char&gt; = match input.next() {
            None =&gt; panic!("Missing content."),
            Some(mood) =&gt; mood.chars().collect(),
        };
        let mut problems: Vec&lt;&amp;char&gt; = Vec::new();
        for (j, mood) in moods.iter().enumerate() {
            /*
             * the assumption is that you'll only request problems that're the same mood that the grader is in
             * reason being that you can receive a maximum of 10 points and a minimum of 5,
             * whereas for the other mood it'll be a max of 5 and a min of 0
             */
            if problems.is_empty() {
                problems.push(mood);
            } else if problems.len() &gt;= moods.len() - j {
                if problems.last() == Some(&amp;mood) {
                    score += 10;
                } else {
                    score += 5;
                }
                problems.pop();
            } else if problems.last() == Some(&amp;mood) {
                score += 10;
                problems.pop();
            } else {
                problems.push(mood);
            }
        }
        println!("Case #{}: {}", i+1, score);
    }
}
There's no need to collect all the arguments into a vector to ignore all but one. Just grab that one via nth.
Remove most of the explicit types and prefer inferring them instead.
Use expect instead of unwrap to provide a message when it fails. It took me 10 minutes to realize that the first argument had to be a filename and not the number of lines to read from standard input.
It's shorter to use expect instead of a panic! when you can.
Don't provide full type for collect; let the inner type be inferred with Vec&lt;_&gt;.
Use the iterator in the for loop instead of calling next manually.
Extract functions to give structure, add names, and isolate mutability.




use std::env::args;
use std::fs::File;
use std::io::Read;

fn read_input_file(name: &amp;str) -&gt; String {
    let mut input = String::new();
    let mut f = File::open(&amp;name).expect("Unable to open the input file");
    f.read_to_string(&amp;mut input).expect("Unable to read the input file");
    input
}

fn main() {
    let filename = args().nth(1).expect("Provide an input file as an argument");

    let input = read_input_file(&amp;filename);
    let mut input = input.lines();

    let num_inputs = input.next().expect("No contents");
    let num_inputs = num_inputs.trim().parse().expect("First line in input not a number");

    for (i, line) in input.take(num_inputs).enumerate() {
        let mut score = 0;
        let moods: Vec&lt;_&gt; = line.chars().collect();
        let mut problems = Vec::new();

        for (j, &amp;mood) in moods.iter().enumerate() {
            // the assumption is that you'll only request problems
            // that're the same mood that the grader is in reason
            // being that you can receive a maximum of 10 points and a
            // minimum of 5, whereas for the other mood it'll be a max
            // of 5 and a min of 0

            if problems.is_empty() {
                problems.push(mood);
            } else if problems.len() &gt;= moods.len() - j {
                if problems.last() == Some(&amp;mood) {
                    score += 10;
                } else {
                    score += 5;
                }
                problems.pop();
            } else if problems.last() == Some(&amp;mood) {
                score += 10;
                problems.pop();
            } else {
                problems.push(mood);
            }
        }
        println!("Case #{}: {}", i+1, score);
    }
}




When profiling, it is very important to compile in release mode. You will also want to make sure you aren't including compilation time. I tend to split the compile and execute steps into two parts to be sure nothing extra is included:

$ cargo build --release
$ time ./target/release/gc3 A-large-practice.in

# ...snipped output...

real    0m0.022s
user    0m0.010s
sys     0m0.010s