백엔드/Rust

[Rust] Rust 추리 게임 만들기

Koras02 2023. 1. 11. 21:47

이번 시간에서는 Rust로 

추리 게임을 만들어보는 시간을 

가져보도록 하겠습니다.

 

새로운 프로젝트 시작

새로운 프로젝트를 만들기 위해

이전에 Hello Cargo를 하던 프로젝트 폴더

 

새로운 프로젝트를 생성하도록 하겠습니다.

Cargo를 이용해 새로운 폴더를 만들어 줍니다.

$ cargo new reasoning_game --bin
$ cd reasoning_game

첫 명령 문 cargo new는 프로젝트의 이름을 

첫번째 인자로 받습니다. 

 

--bin 플래그는 Cargo가 바이너리용 프로젝트를 

생성하도록 도와주는 명령어입니다.

 

두번째 명령문은 작업 디렉토리를 새로운 

디렉토리로 이동하는 명령어 입니다.

 

생성한 프로젝트에 Cargo.toml 파일을 

살펴봅시다.

[package]
name = "reasoning_game"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

만약 Cargo가 환경변수에서 가져온

author 정보가 잘못되었다면 

 

파일을 수정후 저장해주면 됩니다.

추리값 처리하기

프로그램의 첫 부분은 사용자 입력 요청,

입력값의 처리 후 입력값이 

 

기대하던 형식인지를 검증합니다.

첫 시작으로 플레이어가 추리한 값을 

입력 받을 수 있도록 합니다.

 

src 폴더에 main.rs 파일에 다음과 같이 

작성해 줍니다.

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);
}

Listing 2-1: 사용자가 추리한 값을 입력 받아서 그대로 출력하는 코드

 

이 코드에 담긴 다양한 정보를 하나씩 살펴보면

사용자 입력을 받고 결과값을 표시하기 위해

 

io(input/output) 라이브러리를 스코프 연산자로

가져와야 합니다. io 라이브러리는 std 라고 

불리는 표준 라이브러리에 있습니다.

use std::io;

Rust에서는 모든 프로그램의 스코프에 

prelude 내의 타입들을 가져옵니다.

 

만약에 여러분이 원하는 타입이 

prelude에 없다면 use문을 사용해

명시적으로 타입값을 가져와야 합니다.

 

std::io는 사용자의 입력을 받는 것을 

포함하여 io와 관련된 기능들을 제공합니다.

fn main() {

main 함수는 프로그램의 진입점입니다.

fn문법은 새로운 함수를 선언하면 

 

( ) 부분은 인자가 없음을 나타내고 

{ 는 함수 본문의 시작을 나타냅니다.

 

println! 은 string을 화면에 표시하는 매크로입니다.

println!("Guess the number!");
println!("Please input your guess.");

이 코드는 게임에 대한 설명과

사용자의 입력을 요청하는 글자를 표시합니다.

 

값을 변수에 저장하기

다음으로 다음 코드처럼 사용자의 입력값을 

저장할 공간을 생성할 수 잇습니다.

let mut guess = String::new();

여기서 let 문을 주목하자면 다음 코드도 

변수를 생성하는 예시입니다.

let foo = bar;

이 라인은 foo라는 변수를 선언하고 bar이라는

값과 묶습니다. 러스트에서 변수는 

 

기본적으로 불변입니다. 

다음 예시에서는 변수 앞에 mut를 사용해

가변변수를 만드는 방법입니다.

let foo = 5 // immutable 
let mut bar = 5; // mutable

이제 let mut guess가 guess라는 이름의 

가변변수임을 알 수 있습니다.

 

=의 반대편의 값은 guess와 묶이는데

이번 예시에서는 함수 String::new

 

결과값인 새로운 String 인스턴스가 

묶이는 대상이 됩니다.

 

String은 표준 라이브러리에서 제공하는

확장 가능한 UTF-8 인코딩의 

문자열 타입입니다.

 

::new 에 있는 :: 부분은 newString 타입의

연관함수임을 나타냅니다.

 

연관함수란 하나의 타입을 위한 함수이며 

이 경우 하나의 String 인스턴스가 아닌

String 타입을 위한 함수이며

 

몇몇 언어에서는 이것을 정적 메소드라 

부릅니다. new 함수는 새로운 빈

 

String을 생성하고, new함수는 새로운 값을 

생성하기 위한 일반적인 이름이므로 

많은 타입에서 찾아볼 수 있습니다.

 

요약하면 

let mut guess = String::new();

위 라인은 새로운 빈 String 인스턴스와

연결된 가변변수를 생성하고

 

프로그램에 첫번째 라인에 

use std::io;

를 이용해 표준 라이브러리의 

input/output 기능을 포함한 것입니다.

 

이제 우리는 io의 연관함수인

stdin을 호출합니다.

io::stdin().read_line(&mut guess)
    .expect("Failed to read line");

만약 프로그램 시작점에 

use std::io

가 없다면 함수 호출시 

std::io::stdin

위처럼 작성해야 합니다.

stdin 함수는 터미널 표준 입력의 

 

핸들(handle) 타입인 std::io::Stdin

인스턴스를 돌려줍니다.

 

코드의 다음 부분인 .read_line(&mut guess)

사용자로부터 입력을 받기 위해 표준 입력 핸들에서

.read_line(&mut guess) 메소드를 호출합니다.

 

또한 read_line에 &mut guess를 인자로 하나 

넘겨주고 read_line은 사용자가 표준 입력할 

때마다 입력된 문자들을 하나의 문자열에 

 

저장하므로 인자로 값을 저장할 문자열이

필요합니다. 그 문자열 인자는 

 

사용자 입력을 추가하면서 변경되므로 

가변이어야 합니다.

 

&는 코드의 여러 부분에서 데이터를 여러번

메모리로 복사하지 않고 

 

접근하기 위한 방법을 제공하는 참조자임을 

나타냅니다. 참조자는 복잡한 특성으로서

 

러스트의 큰 이점 중 하나가 참조자를 사용함으로써

얻는 안정성과 용이성입니다.

 

이 프로그램을 작성하기 위해서 

참조자의 자세한 내용을 알 필요는 없습니다.

 

지금 당장은 참조자가 변수처럼 기본적으로

불변임을 알기만 하면 되고 

 

따라서 가변으로 바꾸기 위해 

&guess가 아닌 &mut guess로 

작성해야 합니다.

 

두번쨰 라인은 다음 메소드입니다.

.expect("Faild to read line");

.foo() 형태의 문법으로 메소드를 호출할 경우

긴 라인을 나누기 위해서 다음 줄과 여백을 

넣는 것은 바람직한 방법입니다.

 

위 코드를 아래처럼 쓸 수도 있습니다.

io::stdin().read_line(&mut guess)
  .expect("Failed to read line");

하지만 하나의 긴 라인은 가독성이 떨어지므로 

두 개의 메소드 호출을 위한 라인으로 

 

나누는 것이 좋습니다. 이제 이 라인이

무엇인지에 대해 살펴봅시다.

 

참고 자료

 

추리 게임 튜토리얼 - The Rust Programming Language

실습 프로젝트를 통해 러스트를 사용해 봅시다. 이번 장은 실제 프로젝트에서 몇몇 일반적인 Rust 개념이 어떻게 활용되는지를 소개하려 합니다. 이 과정에서 let, match, 메소드, 연관함수(associated

rinthel.github.io