hyunsu blog

홈강좌Rust 입문·중급 47편 중 29편째

Rust HashMap — 이름(키)으로 값을 찾는 표

목차 · 3
  1. 01use 로 불러온다
  2. 02넣고 찾는다
  3. 03실습 — 단어 수 세기

Rust 의 HashMap 은 키와 값을 짝지어 담고, 키로 값을 찾는 표다. use std::collections::HashMap; 으로 불러와야 하고, 빠뜨리면 E0433 이 난다. insert 로 넣고, get 은 Option 을 돌려준다. entry(…).or_insert(0) 으로 단어 수를 셀 수 있다.

01 use 로 불러온다

과일 재고를 이름으로 찾는 표를 만들어 보자. 먼저 use 없이 써 본다.

rust
fn main() {
    let mut stock = HashMap::new();
    stock.insert("apple", 3);
    println!("{:?}", stock.get("apple"));
}
text
error[E0433]: cannot find type `HashMap` in this scope
 --> src/main.rs:2:21
  |
2 |     let mut stock = HashMap::new();
  |                     ^^^^^^^ use of undeclared type `HashMap`
  |
help: consider importing this struct
  |
1 + use std::collections::HashMap;
  1. 무엇 — 이 범위에서 HashMap 이라는 타입을 찾을 수 없다.
  2. 어디 — 2번째 줄의 HashMap.
  3. 고치는 법 — help: 가 1번째 줄에 use std::collections::HashMap; 을 더하라고 한다(+ 줄).

Vec·String·Option 은 워낙 자주 써서 처음부터 불러와져 있다. HashMap 은 아니라서 use 로 "표준 라이브러리(std)의 collections 안에 있는 HashMap" 을 불러온다. 파일 맨 위에 둔다.

02 넣고 찾는다

rust
use std::collections::HashMap;

fn main() {
    let mut stock = HashMap::new();
    stock.insert("apple", 3);
    stock.insert("pear", 5);
    stock.insert("apple", 7);

    println!("{:?}", stock.get("apple"));
    println!("{:?}", stock.get("grape"));
    println!("kinds: {}", stock.len());
}
text
   Compiling stock v0.1.0 (~/projects/stock)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
     Running `target/debug/stock`
Some(7)
None
kinds: 2
  • insert(키, 값) 으로 넣는다. 고치는 것이니 let mut 다.
  • 같은 키로 다시 넣으면 덮어쓴다. apple 은 3 이었다가 7 이 됐다. 그래서 종류(len())는 2 다.
  • get(키) 는 28편의 Vec::get 처럼 Option 을 돌려준다. 있으면 Some(7), 없는 grape 는 None 이다.

HashMap 은 넣은 순서를 기억하지 않는다. 통째로 찍으면 실행할 때마다 순서가 달라질 수 있다. 순서가 중요하면 Vec 을 쓴다.

03 실습 — 단어 수 세기

문장에서 단어마다 몇 번 나왔는지 세어 보자. 34편에서 만들 프로그램의 핵심이다.

rust
use std::collections::HashMap;

fn main() {
    let text = "the cat and the dog and the bird";
    let mut counts = HashMap::new();

    for word in text.split_whitespace() {
        let count = counts.entry(word).or_insert(0);
        *count = *count + 1;
    }

    println!("the: {:?}", counts.get("the"));
    println!("and: {:?}", counts.get("and"));
    println!("cat: {:?}", counts.get("cat"));
    println!("fish: {:?}", counts.get("fish"));
}
text
   Compiling words v0.1.0 (~/projects/words)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/words`
the: Some(3)
and: Some(2)
cat: Some(1)
fish: None
  • text.split_whitespace() 는 문장을 빈칸 기준으로 잘라 단어를 하나씩 꺼낸다. for 로 돈다.
  • counts.entry(word) 는 그 단어의 칸을 찾는다. .or_insert(0) 은 칸이 없으면 0 으로 새로 만든다. 그리고 그 칸의 값을 고칠 수 있는 참조(&mut)를 돌려준다.
  • *count = *count + 1; 은 28편의 *s 처럼, 참조가 가리키는 값에 1 을 더한다.

the 는 3 번, and 는 2 번, cat 은 1 번이고, 문장에 없는 fish 는 None 이다. entry 를 쓰지 않으면 "이미 있나 → 있으면 더하고 없으면 넣는다" 를 match 로 길게 써야 한다. text 를 다른 문장으로 바꿔 돌려 보자. 다음 편부터는 에러를 다룬다. 첫 번째는 panic! 과 Result 다.