[Rust] Guessing 게임 만들기_2

Rust에서 오픈소스 활용하기

Kwoncheol Shin
7 min readMar 10, 2019

Guessing 게임 만들기_1

Guessing 게임 만들기_2 (현재글)

지난 글에서는 사용자 인풋을 받는 코드를 통해 모듈 가져오기, 변수의 특징, Result 타입 을 살펴보았다.

이번 글에서는 외부에서 crate를 가져와 이를 프로젝트에서 사용할 수 있도록 해보자.

Photo by Steve Krohn on Flickr

랜덤넘버 생성하기

std 라이브러리에는 기본적인 프로그래밍을 위한 다양한 라이브러리들이 들어있다. 하지만 아쉽게도 랜덤넘버를 생성하는 기능은 아직 존재하지 않는다. 하지만 Rust팀에서 이를 crate로 제공하기 때문에 이를 가져와 사용할 것이다.

외부 Crate 가져오기

Crate는 Rust 소스코드 파일의 집합으로 ‘Rust 프로젝트’ 는 실행 가능한 binary crate 이다. 하지만 랜덤넘버를 생성하는 rand crate는 다른 프로그램에서 사용할 수 있도록 만든 library crate이다.

외부 crate를 가져오기 위해서는 Cargo.toml 파일을 수정해야 한다. 해당 파일을 열고 [dependencies] 섹션에 다음과 같이 추가하자.

[dependencies]
rand = "0.3.14"

Cargo.toml 파일의 [dependencies] 섹션은 해당 프로젝트에서 사용할 외부 crate의 이름과 버전들을 가지고 있는 섹션이다.

위와 같이 추가했다면 cargo build 를 이용해 다시한번 프로젝트를 빌드해보자. 정상적으로 추가됐을 경우 cargo가 rand crate를 다운받을 것이다.

    Updating crates.io index
Downloaded rand v0.3.23
Downloaded rand v0.4.6
Downloaded libc v0.2.50
Compiling libc v0.2.50
Compiling rand v0.4.6
Compiling rand v0.3.23
Compiling guessing_game v0.1.0 (/user/Rust_Tutorials/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 18.73s

libcrand 라이브러리의 dependency이기 때문에 자동으로 다운로드된다.

매번 이렇게 다운로드 받는 것은아니다. 한번 다운받은 crate는 프로젝트에 이후 해당 파일을 불러와 사용한다.

Carte 업데이트하기

우리가 Cargo.toml 파일에 버전을 명시했기 때문에 cargo는 업데이트 된 버전의 crate가 있더라도 해당 버전을 그대로 사용할 것이다. 만약 지금 사용하고 있는 버전의 crate에 버그가 있어 새로운 버전의 crate가 출시됐다면 이를 업데이트 할 수 있어야 한다. crate 업데이트는 cargo update 명령어를 통해 가능하다.

업데이트된 crate들의 버전은 Cargo.toml 파일에 자동으로 입력된다.

랜덤넘버 생성

Crate를 가져왔으니 이를 사용해보자. 불러오는 방법은 std 를 불러왔을 때와 같다.

rand::Rng 는 랜덤넘버 생성기를 가지고 있는 라이브러리다. 먼저 thread_rng 함수를 이용해 OS가 제공하는 현재 쓰레드에서 사용할 수 있는 랜덤넘버 생성기를 불러왔다. 랜덤넘버 생성기의 gen_range 메서드를 호출했다.

해당 crate의 사용법을 잘 모른다면 cargo doc --open 명령어를 이용해 crate의 도큐먼트를 볼 수 있다. 명령어 입력 후 도큐먼트 페이지에서 원하는 crate를 골라 해당 crate의 명세를 살펴 볼 수 있다.

랜덤넘버와 사용자입력 값을 비교하기

이제 랜덤하게 생성한 정수를 사용자의 입력과 비교하기만 하면 된다. 이를 위해서는 std 에 있는 또다른 라이브러리를 불러와야 한다.

Might 컴파일 에러 발생…

Ordering 타입은 Result 타입과 같은 enum 형식의 타입이다. Ordering 의 반환값은 Less , Greater , 그리고 Equal 세 가지가 있다.

비교가 이루어지는 코드를 살펴보면 맨 앞에 match 라고 쓰여져있다. match 는 arm이라고 부르는 표현식으로 arm을 명시해준 코드에서는 그 arm에 맞는 패턴의 동작이 이루어져야 한다. 여기서는 match 라는 arm에 맞는 패턴으로 코드를 짜야하는 것이다.

그런데 위 코드를 실행해보면 다음과 같은 에러를 마주치게 된다.

error[E0308]: mismatched types
--> src/main.rs:19:21
|
19 | match guess.cmp(&secret_number) {
| ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integer
|
= note: expected type `&std::string::String`
found type `&{integer}`

Rust는 타입을 엄격하게 구분한다. 우리는 guess 변수를 생성할 때 이를 String::new 함수로 생성했다. 하지만 gen_range 함수가 반환한 secret_number 의 타입은 i32 이다.

Rust에서 정수의 기본타입은 i32이다. 다른 숫자타입(e.g. u32 , i64) 을사용하고 싶다면 직접 명시해줘야한다.

let num: i64

이 에러를 해결하기 위해서 입력받은 guess 문자열을 정수형으로 형변환 해주어야 한다.

문제는 해결됐다지만 14번째 줄을 보면 이미 선언된 guess 변수를 다시 선언한 것이 보인다.

이것은 Rust의 shadow 라는 기능으로 변수의 타입을 바꾸고 싶을 때 새로운 이름의 변수를 만들 필요가 없도록 해준다.( e.g. guess , guess_str)

guess 는 문자열 타입에 존재하는 trim 메서드를 이용해 공백과 개행문자를 제거하고 parse 메서드를 이용해 명시된 타입으로 형변환을 진행했다. 그리고 인풋을 받을 때와 마찬가지로 expect 메서드로 에러핸들링을 해주었다.

반복문으로 여러번 시도하게 만들기

반복문 생성은 loop 를 이용해 가능하다.

이제 사용자가 계속해서 숫자를 입력하며 게임을 할 수 있다. 하지만 루프를 나가는 부분이 없다. ctrl-c 를 누르거나 숫자가 아닌 값을 입력해서 강제로 루프를 빠져나갈 수 있지만 이는 정상적인 방법이 아니기 때문에 루프에서 나가는 부분을 추가해줘야 한다.

이제 정답을 맞춰 “You win”이 출력된 이후에는 프로그램이 자동으로 종료된다.

사용자 입력 관리

사용자가 숫자가 아닌 문자를 입력했을 때 에러를 일으키게 하지 말고 이를 그냥 무시하도록 만들어보자.

guess 의 형변환을 해주는 코드에서 expect를 사용하지 않고 match arm을 사용해 결과 값을 비교하도록 만들었다. 이전 글에서 이야기했듯 expectOk , Err 두 가지 값을 가질 수 있다. 형변환이 성공해 Ok 를 반환한다면 그대로 진행하고 Err 를 받는다면 반복문의 처음으로 다시 돌아가는 continue 를 실행한다.

또한 Ok 는 파라미터로 num 을 받았는데 이는 형변환된 값을 받게된다. Ok(num) => num 은 성공시 그 값을 그대로 반환한다는 의미이다. 실패시에는 값이 필요없으므로 Err(_) 으로 표현해줬다.

Guessing game 완성이다!

간단한 프로젝트이지만 Rust의 기본 개념인 let , match , crate , reference 등 다양하게 살펴보았다.

하지만 unmanaged 언어인 만큼 소개할 기능들이 아직 많이 남아있다. 앞으로도 Rust의 다양한 기능들을 나누어 볼 예정이다! (Flickr의 동물 사진들과 함께🐶)

See you 👋

--

--