📌 "The Rust Programming Language"를 읽고 남긴 기록
% cargo new guessing_game
Created binary (application) `guessing_game` package
% cd guessing_game
% cargo run
Compiling guessing_game v0.1.0 (/Users/jison/projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.20s
Running `target/debug/guessing_game`
cargo는 rust의 빌드 시스템이자 패키지 관리자이다. 새로운 프로젝트를 생성, 실행하고 외부 패키지를 설치하는 등 rust 프로젝트를 관리하는 유용한 유틸리티를 제공한다.
랜덤한 숫자를 맞히는 간단한 게임을 작성해보자. 이를 위해 사용자의 입력을 받고 출력하는 기능을 먼저 구현하자.
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
}
use 키워드로 사용할 패키지를 명시한다. fn 키워드로 함수를 정의하고, rust 프로그램의 첫 실행점은 main함수이다.
println!("Guess the number!");
println!("Please input your guess.");
println!은 문자열을 표준 출력 스트림에 출력하는 매크로이다. 함수와 달리 매크로는 호출할 때 !를 이용한다.
let mut guess = String::new();
rust의 변수는 let 키원드로 정의하며, 모든 변수는 기본적으로 immutable하므로 mutable한 변수를 정의하고 싶다면 mut 키워드를 이용한다. rust 는 타입 추론 기능을 제공하며, 위의 문장에서 guess 변수는 새로 생성되는 String 인스턴스의 타입으로 바인딩된다.
io::stdin().read_line(&mut guess).expect("Failed to read line");
stdin() 함수는 표준 입력 스트림을 제어하는 std::io::Stdin 인스턴스를 반환한다. read_line() 함수는 표준 입력 스트림의 입력을 전달받은 문자열의 뒤에 추가한다. &mut은 함수 호출에 mutable한 레퍼런스를 전달한다. read_line() 함수의 반환값은 Result enum 타입으로 동작의 성공 여부와 실패했다면 실패의 원인과 정보를 제공한다. enum은 variant라 불리는 특정한 상태만을 가지는 타입이며, read_line() 함수가 반환하는 값은 Ok 또는 Err라는 variant만을 가진다. Result 타입은 expect 메소드를 가지며, Err variant일 경우 실행된다. 이를 통해 동작이 실패한 경우의 행동을 정의할 수 있다.
println!("You guessed: {guess}");
마찬가지로 문자열을 출력하는 매크로이지만, 변수의 값을 포함해 출력한다.
crate란 rust의 코드 모듈 단위이다. rust는 랜덤 기능을 지원하지 않으므로, 게임의 기능을 완성하기 위해 random crate를 프로젝트의 Cargo.toml 파일에 추가하자. Cargo.toml는 프로젝트의 메타 데이터를 기록하기 위해 이용하며, 프로젝트의 버전과 이름, 의존 패키지 버전 등을 기록한다.
[dependencies]
rand = "0.8.5"
crate를 추가하고 프로젝트를 빌드하면, cargo는 아래와 같이 변경된 의존 패키지를 감지하고 프로젝트에 이를 설치한다.
% cargo build
Compiling cfg-if v1.0.0
Compiling ppv-lite86 v0.2.17
Compiling libc v0.2.147
Compiling getrandom v0.2.10
Compiling rand_core v0.6.4
Compiling rand_chacha v0.3.1
Compiling rand v0.8.5
Compiling guessing_game v0.1.0 (/Users/jison/projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.90s
랜덤 수를 생성하고 사용자의 입력과 비교하여 동작을 분기하는 기능을 구현하자.
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("The secret number is {secret_number}");
loop {
println!("Enter the number");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
},
}
}
}
match문은 전달된 값과 일치하는 첫 패턴에 대응하는 동작을 실행한다.
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
},
}
guess.cmp(&secret_number)는 비교 결과를 Ordering::Less, Ordering::Greater, Ordering::Equal을 variant로 가지는 Result enum으로 반환한다. 각 비교 결과에 따라 다른 동작을 정의한다.