Rust 첫걸음 연습 문서의 문제 2 풀이 코드다.
//! 04 Rust 첫걸음 연습 p2: FizzBuzz를 if 사슬 없이 match 하나로, 결과를 값으로 돌려받게 쓴다.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/4.연습문제/04_rust-basics-practice_p2.rs"
/// n을 3과 5로 나눈 나머지의 짝으로 갈래를 고른다. match 전체가 이 함수의 값이다.
fn fizzbuzz(n: u32) -> String {
match (n % 3, n % 5) {
(0, 0) => "FizzBuzz".to_string(),
(0, _) => "Fizz".to_string(),
(_, 0) => "Buzz".to_string(),
_ => n.to_string(),
}
}
/// 갈래 순서를 바꾼 틀린 판: (0, _)가 먼저 와서 15도 Fizz가 된다.
/// 컴파일러는 unreachable_patterns 경고를 낸다. 여기서는 실행 결과를 보려고 그 경고만 끈다.
#[allow(unreachable_patterns)]
fn fizzbuzz_wrong_order(n: u32) -> String {
match (n % 3, n % 5) {
(0, _) => "Fizz".to_string(),
(_, 0) => "Buzz".to_string(),
(0, 0) => "FizzBuzz".to_string(),
_ => n.to_string(),
}
}
const WRONG_ORDER: &str = "pub fn f(n: u32) -> &'static str {\n match (n % 3, n % 5) {\n (0, _) => \"Fizz\",\n (_, 0) => \"Buzz\",\n (0, 0) => \"FizzBuzz\",\n _ => \"\",\n }\n}\n";
fn compile_stderr(code: &str) -> String {
let dir = std::env::temp_dir().join(format!("vr_{}_{}", std::process::id(), code.len()));
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("rustc")
.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 main() {
let got: Vec<String> = (1..=15).map(fizzbuzz).collect();
let want = [
"1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14",
"FizzBuzz",
];
assert_eq!(got, want);
println!("{}", got.join(" "));
// 순서를 바꾸면 컴파일은 되지만 15가 Fizz가 된다. (0, 0) 갈래에는 영영 닿지 않는다.
assert_eq!(fizzbuzz_wrong_order(15), "Fizz");
assert_eq!(fizzbuzz_wrong_order(30), "Fizz");
println!("갈래 순서를 바꾸면 15 -> {}", fizzbuzz_wrong_order(15));
// 경고 끄기 없이 컴파일하면 컴파일러가 닿지 않는 갈래를 알려 준다.
let warn = compile_stderr(WRONG_ORDER);
assert!(warn.contains("warning: unreachable pattern"), "{warn}");
assert!(warn.contains("no value can reach this"), "{warn}");
println!("컴파일러: warning: unreachable pattern");
// 1..=100에서 FizzBuzz는 15의 배수 6개, Fizz는 33 - 6 = 27개, Buzz는 20 - 6 = 14개.
let all: Vec<String> = (1..=100).map(fizzbuzz).collect();
let count = |w: &str| all.iter().filter(|s| *s == w).count();
assert_eq!(
(count("FizzBuzz"), count("Fizz"), count("Buzz")),
(6, 27, 14)
);
println!(
"1..=100: FizzBuzz {}, Fizz {}, Buzz {}",
count("FizzBuzz"),
count("Fizz"),
count("Buzz")
);
println!("ALL CHECKS PASSED");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn example() {
assert_eq!(fizzbuzz(9), "Fizz");
assert_eq!(fizzbuzz(10), "Buzz");
assert_eq!(fizzbuzz(45), "FizzBuzz");
assert_eq!(fizzbuzz(7), "7");
}
}