Split a string and store it in a hashmap

use std::io;
use std::collections::HashMap;

fn main() {
    let mut words = HashMap::new();
    let mut t: u32 = input().trim().parse().unwrap();
    let mut n: u32;

    while t > 0 {
        n = input().trim().parse().unwrap();

        while n > 0 {
            let line = input();
            let line = line.trim().split(' ');
            let word: Vec<&str> = line.collect();
            words.insert(word[0], word[1]);
            n -= 1;
        }
        t -= 1;
    }
}

fn input () -> String {
    let mut read_line = String::new();
    io::stdin().read_line(&mut read_line).unwrap();
    read_line
}

I am trying to perform a simple task where I am reading a string like word 1 and then trying to split it in two different variables and store it in HashMap like key: word, value: 1. But the above program is throwing error about borrowing elements. What should I do?

The type &str does not own the contents of the string, so variables of that type may not exist for longer than the String they borrowed the string slice from. In this case you are using trim followed by split to create the string slices, which results in borrows of the original line variable.

Since they borrow from the line variable, and since the line variable is destroyed when it goes out of scope at the end of the inner while loop, the string slice cannot exist after the while loop either.

It looks like you want to take ownership of the split strings, so you should probably use a String that owns its data instead.

1 Like

As an aside, I recommend writing your loops as for loops, and I don't recommend reusing the n variable like that. You can do it like this instead:

use std::io;
use std::collections::HashMap;

fn main() {
    let mut words = HashMap::new();
    let t: u32 = input().trim().parse().unwrap();

    for _ in 0..t {
        let n: u32 = input().trim().parse().unwrap();

        for _ in 0..n {
            let line = input();
            let line = line.trim().split(' ');
            let word: Vec<&str> = line.collect();
            words.insert(word[0].to_string(), word[1].to_string());
        }
    }
}

fn input () -> String {
    let mut read_line = String::new();
    io::stdin().read_line(&mut read_line).unwrap();
    read_line
}

Note how the vector in this case can have &str as the type. This is ok because the call to to_string converts them into owned Strings before putting them into words, and the vector is destroyed at the end of the inner loop, which is before line is destroyed. (variables are destroyed in opposite order to their declaration)

Thank you so much for the explanation.

1 Like

@alice What if I want to store the value as an integer in the hashmap?

Integers implement the Copy trait. So they aren't generally borrowed, but rather are passed by value. So you don't have to do anything special. If you are asking how to parse the integer from the string:

let i: i32 = s.parse().unwrap_or(0);

(assuming you want to use the value 0 if the string does not parse into an i32.)

@alice @tkaitchuck

words.insert("abc", (0, 1));
...
let (i, j) = words.get("abc").unwrap();
^^^^^^ expected enum `std::option::Option`, found tuple

words is a HashMap and I am storing a tuple as value. This code works fine on my local machine but as soon as I use it on an online compile this above error comes up. Any help?

I'd need more context and a full error message to answer your question.

Hi @alice

error[E0308]: mismatched types
  --> prog.rs:20:21
   |
20 |                 let (i, j) = words.get(&word).unwrap();
   |                     ^^^^^^ expected reference, found tuple
   |
   = note: expected type `&_`
   = note:    found type `(_, _)`

error[E0308]: mismatched types
  --> prog.rs:40:17
   |
40 |             let (s, ns) = v;
   |                 ^^^^^^^ expected &(i32, i32), found tuple
   |
   = note: expected type `&(i32, i32)`
   = note:    found type `(_, _)`

error: aborting due to 2 previous errors

Whole program is following:

use std::io;
use std::collections::HashMap;

fn main() {
    let mut result: [i32; 10] = [0; 10];
    let t: i32 = input().trim().parse().unwrap();

    for i in 0..t {
        let mut words = HashMap::new();
        let n: u32 = input().trim().parse().unwrap();

        for _ in 0..n {
            let line = input();
            let word: Vec<&str> = line.trim().split(' ').collect();
            let spam: u32 = word[1].parse().unwrap();
            let word = word[0].to_string();
            let mut s: i32 = 0;
            let mut ns: i32 = 0;
            if words.contains_key(&word) {
                let (i, j) = words.get(&word).unwrap();

                if spam == 0 {
                    s = i + 1;
                    ns = j + 0;
                } else {
                    s = i + 0;
                    ns = j + 1;
                }
            } else {
                if spam == 0 {
                    s = 1;
                } else {
                    ns = 1;
                }
            }
            words.insert(word, (s, ns));
        }

        for (_, v) in  &words {
            let (s, ns) = v;
            if s > ns {
                result[i as usize] += *s;
            } else {
                result[i as usize] += *ns;
            }
        }
    }

    for i in 0..t {
        println!("{:?}", result[i as usize]);
    }
}

fn input () -> String {
    let mut read_line = String::new();
    io::stdin().read_line(&mut read_line).unwrap();
    read_line
}

Check your code again, it doesn't .unwrap()

@Hyeonu I am sorry I have updated output.

Both error messages tell you, that the expressions return references (to tuples) and you're storing owned tuples. Try dereferencing with * and see if that helps.

1 Like

Thanks solved!

Sounds like that online compiler is using 2015 edition, which does not have match ergonomics