컬렉션 문서의 검증 코드다.
//! 컬렉션 문서의 주장을 확인한다: Vec 용량이 늘어나는 모습(관찰값)과 with_capacity, 원소 참조를
//! 쥔 채 push하면 E0502, 인덱스와 get의 차이, HashMap entry로 단어 세기, HashMap 순회 순서가
//! 맵마다 다를 수 있음, BTreeMap 정렬 순회와 range, HashSet insert의 반환값, VecDeque 큐, clippy map_entry.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/3.개념집/13_collections_verify.rs"
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
fn compile_with(code: &str, tool: &str, tag: &str) -> String {
let dir = std::env::temp_dir().join(format!("vr13_{}_{}", std::process::id(), tag));
std::fs::create_dir_all(&dir).unwrap();
let file = dir.join("snippet.rs");
std::fs::write(&file, code).unwrap();
let out = std::process::Command::new(tool)
.args([
"--edition",
"2024",
"--crate-type=lib",
"--emit=metadata",
"--out-dir",
])
.arg(&dir)
.arg(&file)
.output()
.expect("rustc 실행");
String::from_utf8_lossy(&out.stderr).into_owned()
}
fn word_count(text: &str) -> HashMap<&str, usize> {
let mut map = HashMap::new();
for word in text.split_whitespace() {
*map.entry(word).or_insert(0) += 1;
}
map
}
fn main() {
// 1. Vec 용량: 늘어나는 모습은 보장이 아니라 관찰값
let mut v: Vec<u64> = Vec::new();
let mut caps = vec![v.capacity()];
let mut reallocs = 0;
for i in 0..1000u64 {
let before = v.capacity();
v.push(i);
if v.capacity() != before {
reallocs += 1;
caps.push(v.capacity());
}
}
println!("Vec<u64> 1000번 push: 용량 변화 {caps:?}, 다시 잡은 횟수 {reallocs}");
assert_eq!(caps, vec![0, 4, 8, 16, 32, 64, 128, 256, 512, 1024]);
// with_capacity: 그만큼은 다시 잡지 않는다
let mut w: Vec<u64> = Vec::with_capacity(10);
assert!(w.capacity() >= 10);
let c0 = w.capacity();
for i in 0..10 {
w.push(i);
}
assert_eq!(w.capacity(), c0);
w.push(10);
assert!(w.capacity() > c0);
println!(
"with_capacity(10): 처음 {c0}, 11번째 push 뒤 {}",
w.capacity()
);
let mut z: Vec<u64> = Vec::with_capacity(1000);
let zc = z.capacity();
for i in 0..1000 {
z.push(i);
}
assert_eq!(z.capacity(), zc);
// 원소 참조를 쥔 채 push: E0502
let e = compile_with(
"pub fn f() -> i32 { let mut v = vec![1, 2, 3]; let first = &v[0]; v.push(4); *first }",
"rustc",
"e0502",
);
assert!(e.contains("E0502"), "{e}");
println!("참조를 쥔 채 push: E0502");
// 2. 인덱스는 패닉, get은 None
let small = [1, 2, 3, 4, 5];
assert_eq!(small.get(100), None);
assert_eq!(small.get(2), Some(&3));
std::panic::set_hook(Box::new(|_| {}));
let idx = std::hint::black_box(100usize);
let p = std::panic::catch_unwind(|| small[idx]);
let _ = std::panic::take_hook();
assert!(p.is_err());
// 3. HashMap entry로 단어 세기
let counts = word_count("hello world wonderful world");
assert_eq!(counts["world"], 2);
assert_eq!(counts["hello"], 1);
assert_eq!(counts.get("rust"), None);
// String 키 맵을 &str로 찾는다
let owned: HashMap<String, u32> = HashMap::from([("ann".to_string(), 1)]);
assert_eq!(owned.get("ann"), Some(&1));
// 4. HashMap 순회 순서는 정해지지 않았다: 맵마다 씨앗이 달라 순서가 달라진다(관찰)
let mut orders = HashSet::new();
for _ in 0..20 {
let m: HashMap<u32, ()> = (0..50).map(|k| (k, ())).collect();
let order: Vec<u32> = m.keys().copied().collect();
orders.insert(order);
}
println!(
"같은 키 50개를 넣은 HashMap 20개의 서로 다른 순회 순서: {}",
orders.len()
);
assert!(orders.len() > 1);
// 5. BTreeMap: 키 순서로 순회, range, 가장 작은 키
let mut scores = BTreeMap::new();
for (name, s) in [("kim", 70), ("lee", 95), ("ahn", 88), ("park", 60)] {
scores.insert(name, s);
}
let names: Vec<&str> = scores.keys().copied().collect();
assert_eq!(names, vec!["ahn", "kim", "lee", "park"]);
let mid: Vec<&str> = scores.range("b".."m").map(|(k, _)| *k).collect();
assert_eq!(mid, vec!["kim", "lee"]);
assert_eq!(scores.first_key_value(), Some((&"ahn", &88)));
// 6. HashSet insert는 새로 넣었는지를 돌려준다
let mut seen = HashSet::new();
assert!(seen.insert("a@x.com"));
assert!(!seen.insert("a@x.com"));
assert_eq!(seen.len(), 1);
// 7. VecDeque를 큐로
let mut q = VecDeque::new();
q.push_back(1);
q.push_back(2);
q.push_front(0);
assert_eq!(q.pop_front(), Some(0));
assert_eq!(q.pop_front(), Some(1));
assert_eq!(q.len(), 1);
// 8. 확인 문제 C3 실행 추적
let mut stock: BTreeMap<&str, u32> = BTreeMap::new();
for item in ["pen", "cup", "pen", "bag", "pen", "cup"] {
*stock.entry(item).or_insert(0) += 1;
}
stock.entry("bag").and_modify(|n| *n += 10).or_insert(100);
stock.entry("box").and_modify(|n| *n += 10).or_insert(100);
let line: Vec<String> = stock.iter().map(|(k, v)| format!("{k}={v}")).collect();
println!("C3: {}", line.join(" "));
assert_eq!(line.join(" "), "bag=11 box=100 cup=2 pen=3");
// 9. clippy: contains_key 뒤 insert는 map_entry (기본 켜짐)
let e = compile_with(
"use std::collections::HashMap;\npub fn f(m: &mut HashMap<u32, u32>, k: u32) { if !m.contains_key(&k) { m.insert(k, 1); } }",
"clippy-driver",
"mapentry",
);
assert!(e.contains("clippy::map_entry"), "{e}");
println!("clippy::map_entry 기본으로 경고");
println!("ALL CHECKS PASSED");
}