홈강좌Rust 입문·중급 47편 중 28편째
Rust Vec — 길이가 늘었다 줄었다 하는 목록
Rust 의 Vec 은 같은 타입 값을 담는, 길이가 바뀌는 목록이다. Vec::new() 나 vec![…] 로 만들고 push 로 더한다. v[n] 처럼 범위 밖 번호를 쓰면 실행 중에 패닉하고, get(n) 은 Option 을 돌려줘서 없는 경우를 안전하게 다룬다.
01 만들고 더한다
할 일 목록을 만들어 보자. 9편의 배열은 길이가 정해져 있어서 나중에 더할 수 없었다.
fn main() {
let mut todo = Vec::new();
todo.push("wash");
todo.push("cook");
todo.push("read");
println!("{todo:?}");
println!("count: {}", todo.len());
println!("first: {}", todo[0]);
let nums = vec![3, 1, 2];
println!("{nums:?}");
} Compiling todo v0.1.0 (~/projects/todo)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.18s
Running `target/debug/todo`
["wash", "cook", "read"]
count: 3
first: wash
[3, 1, 2]Vec::new()는 빈Vec을 만든다. 더할 것이니let mut다.push는 맨 뒤에 하나를 더한다. 처음 넣은 값이&str이라 이Vec의 타입이 정해진다. 다른 타입을 넣으면 오류다.len()·todo[0]·{:?}는 배열과 똑같이 쓴다.vec![3, 1, 2]는 값을 넣은 채로 만드는 매크로다.println!처럼 느낌표가 붙는다.
02 for 로 돌고 고친다
fn main() {
let mut scores = vec![72, 85, 90];
for s in &mut scores {
*s = *s + 5;
}
for s in &scores {
println!("{s}");
}
scores.sort();
scores.reverse();
println!("{scores:?}");
} Compiling todo v0.1.0 (~/projects/todo)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
Running `target/debug/todo`
77
90
95
[95, 90, 77]for s in &mut scores는 원소를 하나씩 고칠 수 있게 빌려 온다(19편).*s는 "s가 가리키는 값" 이다. 참조 자체가 아니라 그 값을 고치므로*를 붙인다.for s in &scores는 읽기만 하게 빌려 온다(18편).&를 빼고for s in scores라고 쓰면Vec이for로 이동해 버려서(15편), 그 뒤에scores를 쓸 수 없다.sort()는 작은 것부터 늘어놓고,reverse()는 순서를 뒤집는다.
03 범위 밖 번호는 패닉한다
9편의 배열에서는 범위 밖 번호를 컴파일러가 미리 막았다. Vec 은 길이가 실행 중에 바뀌니 미리 알 수 없다.
fn main() {
let todo = vec!["wash", "cook", "read"];
let n = todo.len();
println!("{}", todo[n]);
} Compiling todo v0.1.0 (~/projects/todo)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/todo`
thread 'main' (213063879) panicked at src/main.rs:4:24:
index out of bounds: the len is 3 but the index is 3
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace길이가 3 인데 번호 3 을 썼다(index out of bounds). 번호는 0 부터라 마지막은 2 다. 컴파일은 됐지만 실행 중에 멈췄다.
04 실습 — get 은 Option 을 돌려준다
번호가 범위 안인지 확신할 수 없으면 get 을 쓴다.
fn main() {
let todo = vec!["wash", "cook", "read"];
match todo.get(1) {
Some(item) => println!("2nd: {item}"),
None => println!("no 2nd"),
}
match todo.get(10) {
Some(item) => println!("11th: {item}"),
None => println!("no 11th"),
}
} Compiling todo v0.1.0 (~/projects/todo)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.18s
Running `target/debug/todo`
2nd: cook
no 11thtodo.get(1) 은 값이 있으니 Some(…), todo.get(10) 은 없으니 None 이다. 26편의 Option 이라 match 로 두 경우를 나눴다. 패닉 없이 "없다" 를 다룬다. 27편의 if let 으로 바꿔 써 보자.
v[n] | v.get(n) | |
|---|---|---|
| 돌려주는 것 | 값 | Option |
| 범위 밖이면 | 패닉 | None |
| 쓰는 때 | 범위 안이 확실할 때 | 확실하지 않을 때 |
다음 편은 이름으로 값을 찾는 목록, HashMap 이다.