1

First time poster and very new to Rust. I have been slowly toying with Rust for the past few months and I am just recently making a solid effort to learn the language. To help me with this I'm planning to do all the problems for this years Advent of Code in Rust. Currently I'm stuck on the fourth day. If you would like to read about it here is the link. Obviously I might be spoiling this problem for some of you so be warned! Anyways on to the problem!

For this problem I decided to use a HashMap to solve part one. I come from Java and Python, so I figured Rust's HashMap would work similarly to Java and Python's dictionary. I realize that this might not be the best solution but I wanted to try it anyway to learn about HashMaps. Here is the code I came up with. I tried to comment the code so it was easier to understand.

use std::fs::File;
use std::io::{BufRead, BufReader};
use std::collections::HashMap;

fn check_passport_valid(passport: HashMap<&str, &str>) -> bool {
    return true;
}

fn main() {
    // set up a bufReader to read the data
    let data = "data/data.txt";
    let file = File::open(data).unwrap();
    let reader = BufReader::new(file);

    // init a mutable HashMap 
    let mut passport: HashMap<&str, &str> = HashMap::new();
    let mut count = 0;

    // read the data in line by line
    for line in reader.lines() {
        // not sure if i should &line.unwrap() or do this
        // seems to work the same in both situations
        let s = line.unwrap();

        // if line is blank check password and init new HashMap
        if "" == s {
            if check_passport_valid(passport) { count += 1; }
            passport = HashMap::new();
        } 
        
        // else put values into HashMap
        else {
            // first split into items
            let items: Vec<&str> = s.split(' ').collect();
            for item in items {
                // split into key/value pair
                let pair: Vec<&str> = item.split(':').collect();
                passport.insert(pair[0], pair[1]);
            }
        }
    }

    println!("{}", count);
}

As you can see its not done, but with this I get the following error:

error[E0597]: `s` does not live long enough
  --> src/main.rs:34:36
   |
27 |             if check_passport_valid(passport) { count += 1; }
   |                                     -------- borrow later used here
...
34 |             let items: Vec<&str> = s.split(' ').collect();
   |                                    ^ borrowed value does not live long enough
...
41 |     }
   |     - `s` dropped here while still borrowed

I have tried played around with borrowing different values but I can't seem to get this to work. Any help would be greatly appreciated!

Endzeit
  • 4,810
  • 5
  • 29
  • 52
  • It's best to read the whole input into a `String` first. This will avoid the borrow checker error: `let data = std::fs::read_to_string("data/data.txt").unwrap();` – Aloso Dec 08 '20 at 00:51
  • P.S. [Here's](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f94e5730c143a813b21b245e15c72fd9) a solution that minimizes memory allocations. – Aloso Dec 08 '20 at 01:37

1 Answers1

1

The error you're getting is that the values you get from s.split(' ') are slices of the lines variable, which essentially mean that they reference the lines variable. At the end of the loop, the lines variable is dropped and replaced by the next line, which invalidates all the references to it. The easiest way is to convert the &str to a String (an owned type) with .to_owned(), so it copies the data to a static memory location which is no longer dependent on the lines variable:

use std::fs::File;
use std::io::{BufRead, BufReader};
use std::collections::HashMap;

// this is now HashMap<String, String> instead of HashMap<&str, &str>
fn check_passport_valid(passport: HashMap<String, String>) -> bool {
    return true;
}

fn main() {
    let data = "data/data.txt";
    let file = File::open(data).unwrap();
    let reader = BufReader::new(file);

    // this is now HashMap<String, String> instead of HashMap<&str, &str>
    let mut passport: HashMap<String, String> = HashMap::new();
    let mut count = 0;

    for line in reader.lines() {
        let s = line.unwrap();

        if "" == s {
            if check_passport_valid(passport) { count += 1; }
            passport = HashMap::new();
        } 
        
        else {
            let items: Vec<&str> = s.split(' ').collect();
            for item in items {
                let pair: Vec<&str> = item.split(':').collect();
                // call to_owned on both elements
                passport.insert(pair[0].to_owned(), pair[1].to_owned());
            }
        }
    }

    println!("{}", count);
}

Read more about str vs String here.

Aplet123
  • 33,825
  • 1
  • 29
  • 55