#0 시작하기

Pt J·2020년 8월 10일
8

[完] Rust Programming

목록 보기
1/41
post-thumbnail

이 시리즈는 Rust 공식문서를 통해 공부한 흔적임을 밝힙니다.

올해도 개발자들의 가장 큰 사랑을 받은 언어, Rust.

그것을 개발자들의 가장 큰 사랑을 받은 플랫폼, Linux에서

공식 문서를 통해 알아가는 시간을 가지려 한다.

Rust language

그래서 대체 Rust란 언어는 무엇인가, 라는 이야기부터 시작하자.

Rust
Mozilla가 Firefox web browser의 렌더링 엔진을 새로 작성하기 위해 개발한 언어로, 기존의 C/C++ 언어가 지니는 메모리 관리의 어려움을 언어 차원에서 해소하면서 최신의 멀티 코어 프로세스를 활용한 동시성에 대한 지원, 비용 없는 추상화 등 강력한 기능을 탑재한 시스템 프로그래밍 언어

Rust는 이와 같이 시스템 프로그래밍 언어를 표방하고 있지만
단지 그것만을 위한 언어는 아니며 CLI 앱, 웹 서버 등 다양한 프로그램을 구현할 수 있다.
Rust가 가지고 있는 몇 가지 특징을 소개하자면,

  • 컴파일러가 차람이 찾아내기 힘든 버그를 유발하는 코드의 컴파일 자체를 거부함으로써 버그의 발생 가능성을 감소시킨다. 이는 개발자로 하여금 버그를 추적하는 데 소비하는 시간을 프로그램 로직 작성에 활용할 수 있게 한다.
  • 운영체제와 같은 시스템 프로그램부터 CLI tool, 웹 서비스, DevOps 도구, 임베디드 장치, 암호학, 검색엔진, IoT 애플리케이션, 머신러닝, 그리고 Firewox web browser의 주요 기능 구현 등 다양한 분야로 폭넓게 이용된다.
  • C 언어와 같은 직접적인 메모리 할당 해제 작업을 하지 않으면서도 Java의 garbage collector 같은 overhead 없이 메모리를 관리할 수 있어 메모리 사용에 있어서의 효율성과 편의성 사이의 trade-off issue를 효과적으로 처리한다.

소개는 간단히 이 정도 하고, 그 외 세부적인 것들은 앞으로 차차 알아가도록 하자.
프로그래밍에 있어서 직접 실습을 해보는 것만큼 효과적인 학습 방법은 없다는 게 피터의 주장이다.
Rust 언어 자체에 대한 추가적인 이론적 배경이 궁금하다면
검색을 통해 알아보고 오는 것이 효율적일 것이다.

실습 환경 구축

Rust에 대해 공부하기 위해 우리가 가장 먼저 해야 할 것은 Rust를 설치하는 것이다.
Rust를 설치하는 방법은 공식 홈페이지에도 나와 있듯이 rustup을 이용하면 된다.

rustup
Rust의 버전과 관련된 도구를 관리하는 CLI tool

앞서 언급했듯이 우리는 Linux에서 Rust를 사용하도록 할 것이며,
Linux에서 rustup과 Rust를 설치하려면 터미널에서 다음 명령어를 입력하면 된다.
// 터미널은 peter@hp-laptop:~$와 같은 프롬프트로 시작하거나
// 사용자 및 호스트를 생략한 ~$와 같은 프롬프트로 시작하도록 작성될 예정이다.

~$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

이것은 MacOS와 같은, 다른 UNIX-like OS에서도 마찬가지다.
구체적인 문자열은 다를 수 있지만 다음과 같이 출력되는 것을 볼 수 있는데,

peter@hp-laptop:~$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.

Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:

  /home/peter/.rustup

This can be modified with the RUSTUP_HOME environment variable.

The Cargo home directory located at:

  /home/peter/.cargo

This can be modified with the CARGO_HOME environment variable.

The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:

  /home/peter/.cargo/bin

This path will then be added to your PATH environment variable by
modifying the profile file located at:

  /home/peter/.profile

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:


   default host triple: x86_64-unknown-linux-gnu
     default toolchain: stable (default)
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>

1을 입력하고 엔터를 눌러 설치를 진행하도록 하자.
혹은, 아무것도 입력하지 않고 엔터를 누르면 default로 1번이 선택된다.
다음과 같이 마저 출력되면 성공적으로 설치된 것이다.

info: profile set to 'default'
info: default host triple is x86_64-unknown-linux-gnu
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2020-08-03, rust version 1.45.2 (d3fb005a3 2020-07-31)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-std'
 15.9 MiB /  15.9 MiB (100 %)  15.9 MiB/s in  1s ETA:  0s
info: downloading component 'rustc'
 47.1 MiB /  47.1 MiB (100 %)  12.6 MiB/s in  3s ETA:  0s
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: Defaulting to 500.0 MiB unpack ram
info: installing component 'clippy'
info: installing component 'rust-docs'
 12.2 MiB /  12.2 MiB (100 %)   8.7 MiB/s in  1s ETA:  0s
info: installing component 'rust-std'
 15.9 MiB /  15.9 MiB (100 %)  14.7 MiB/s in  1s ETA:  0s
info: installing component 'rustc'
 47.1 MiB /  47.1 MiB (100 %)  14.2 MiB/s in  3s ETA:  0s
info: installing component 'rustfmt'
info: default toolchain set to 'stable'

  stable installed - rustc 1.45.2 (d3fb005a3 2020-07-31)


Rust is installed now. Great!

To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH
environment variable. Next time you log in this will be done
automatically.

To configure your current shell run source $HOME/.cargo/env
peter@hp-laptop:~$ 

구체적인 내용은 달라도 Rust is installed now. Great!이라는 문자열을 확인할 수 있다.

이 문자열이 출력되어도 당장은 Rust를 사용할 수 없을 것이다.
이는 rustup이 등록한 환경변수가 시스템에 적용되지 않았기 때문인데
사용자가 터미널에 다시 로그인 하는 시점에 등록될 것이다.
터미널 종료 없이 바로 사용하기 위해서는
다음 명령어를 통해 수동으로 등록할 수 있다.

~$ source $HOME/.cargo/env

이제 Rust를 이용한 프로그래밍을 할 수 있게 되었다.

// 경우에 따라서는 여기까지 해도 Rust를 사용하는 데 무리가 있을 수 있다.
// 이는 일부 Rust package들이 C 언어에 의존성을 가지고 있기 때문인데
// gcc와 같은 C 컴파일러를 설치하면 이 issue는 금방 해결된다.

Rust를 설치한지 한참 지나 그것을 최신 버전으로 업데이트하고 싶다면
다음 명령어를 통해 쉽게 업데이트 할 수 있다.

~$ rustup update

그리고 더이상 Rust 프로그래밍을 하지 않겠다며 Rust를 제거할 땐
다음 명령어를 사용할 수 있긴 한데... 시작도 하기 전에 제거할 생각부터 하는 건 아니겠지?

~$ rustup self uninstall

아무튼 Rust가 잘 설치되었는지 확인하기 위해서는 Rust 버전을 출력해보면 된다.
버전 정보가 정상적으로 출력된다면 잘 설치된 것이다.
Rust의 버전은 다음 명령어를 통해 출력할 수 있다.

~$ rustc --version

Rust의 버전 정보는 rustc x.y.z (<commit hash> yyyy-mm-dd) 형태로
버전 번호, 커밋 해시, 최신 안정 버전 커밋 날짜가 표시된다.

peter@hp-laptop:~$ rustc --version
rustc 1.45.2 (d3fb005a3 2020-07-31)
peter@hp-laptop:~$ 

Rust 프로그램 작성해보기

자, 그러면 Rust가 설치되었으니 이제 Rust 프로그램을 작성해보자.
Hello, World! 정도는 찍어줘야 프로그래밍을 해봤다고 할 수 있지 않겠는가.
어떤 편집기를 사용하든 그것은 자유이며 Rust를 지원하는 IDE를 사용해도 된다.
vscode를 사용할 경우 rust-analyzer 확장을 사용하는 것을 추천한다.
이 시리즈에서는 개발도구에 대한 종속성을 배제하기 위해
터미널에서 Vim을 사용하는 것을 기준으로 작성하겠다.
먼저, 프로젝트를 위한 디렉토리를 생성한다.
이번 한 번만 할 것은 아니니 Rust 프로그래밍을 위한 디렉토리를 만들고
그 안에 챕터별로 또 디렉토리를 만들도록 하겠다.

~$ mkdir -p rust-practice/chapter01/hello_world
~$ cd rust-practice/chapter01/hello_world
~/rust-practice/chapter01/hello_world$

Rust의 소스 파일은 .rs 확장자를 가지며
여러 단어로 이루어져 있을 경우 _을 이용해 단어를 구분한다.
main.rs라는 이름의 파일을 만들고 다음의 소스 코드를 작성해보자.

~/rust-practice/chapter01/hello_world$ vi main.rs

main.rs

fn main() {
    println!("Hello, world!");
}

작성한 소스 코드를 저장하고 다음과 같은 명령어를 입력해보자.

~/rust-practice/chapter01/hello_world$ rustc main.rs
~/rust-practice/chapter01/hello_world$ ./main
Hello, world!

방금 우리는 정상적으로 작동하는 하나의 Rust 프로그램을 만들었다.

우리가 작성한 코드는...

우리가 작성한 작고 단순한 Rust 프로그램을 훑어보자.
우리는 fn main() {} 코드블록을 작성했다.
fn은 Rust에서 함수를 정의할 때 사용되는 키워드다.
그리고 main은 프로그래밍 경험이 있다면 알겠지만 가장 먼저 실행되는 특별한 함수의 이름이다.
우리가 작성한 main 함수는 매개변수도 없고 반환값도 없다.
매개변수와 반환값에 대해서는 추후에 좀 더 자세히 알아보도록 하자.

fn main()으로 함수를 선언한 직후,
띄어쓰기 한 칸을 사이에 두고 중괄호를 여는데,
여기서부터 그것이 닫힐 때까지가 함수의 본문이다.
우리가 작성한 함수 본문에는 println!("Hello, world!");라는 코드가 작성되어 있다.
이 코드 앞에는 띄어쓰기 네 칸이 존재하는데,
Rust의 들여쓰기는 TAB이 아니라 띄어쓰기 네 칸이라는 것을 기억하자.
그리고 println!처럼 !가 붙어 있는 건 함수와 비슷하지만 다른, 매크로라는 녀석인데
일단 그냥 함수와 비슷하게 사용되는 녀석이라고만 생각하고 넘어가자.
이것에 대해서는 한참 뒤에 다루게 될 것이다.
println! 매크로에 인자로 전달된 문자열이 찍힌다는 것은 직관적으로 알 수 있을 것이다.
그리고 코드의 맨 마지막에는 세미콜론(;)이 붙어야 한다는 것을 잊지 말자.

우리는 Rust 프로그램을 작성하고 rustc라는 Rust 컴파일러를 통해 컴파일하고
그것을 실행하는 실습을 해보았다.
그런데 일반적으로 Rust 프로그램은 이렇게 작성하지 않는다.
Rust의 빌드 시스템이자 패키지 관리자인 Cargo를 이용하여 Rust 프로젝트를 관리하는 게 일반적이다.
이것을 통해 우리는 프로젝트 디렉토리를 생성하고, 의존성을 가진 라이브러리를 내려 받고,
프로젝트 코드를 컴파일하고, 실행할 수 있다.

본격적으로 작성해보기

그렇다면 Cargo를 이용하는 방식으로 Rust 프로그램을 작성해보자.
Cargo는 앞서 설명한 방식으로 설치했다면 우리가 Rust를 설치할 때 함께 설치되었다.
다음 명령어를 통해 Cargo의 버전 정보를 출력하여 설치 여부를 확인할 수 있다.

~$ cargo --version

정상적으로 설치되어 있다면 다음과 같이 버전 정보가 출력된다.

peter@hp-laptop:~/rust-practice/chapter01/hello_world$ cargo --version
cargo 1.45.1 (f242df6ed 2020-07-22)
peter@hp-laptop:~/rust-practice/chapter01/hello_world$ 

Cargo가 설치되어 있다는 것이 확인되었으면 명령어를 통해 Rust 프로젝트를 생성한다.

peter@hp-laptop:~/rust-practice/chapter01/hello_world$ cd ..
peter@hp-laptop:~/rust-practice/chapter01$ cargo new hello_cargo
     Created binary (application) `hello_cargo` package
peter@hp-laptop:~/rust-practice/chapter01$ cd hello_cargo
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$

cargo new 명령어 다음에 입력한 것이 Rust 프로젝트의 이름이 되며
그것과 같은 이름을 가진 디렉토리가 생성된다.
tree 명령어를 사용해보면 디렉토리 외에도 무언가 추가적으로 생성되었음을 알 수 있다.

peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ tree
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$

git 저장소가 아닌 디렉토리에 Rust 프로젝트를 생성했을 경우
프로젝트 디렉토리를 git 저장소로 만들고 .gitignore 파일도 생성한다.

자동으로 생성된 Cargo.toml 파일을 열어보면 다음과 같은 코드를 볼 수 있다.

Cargo.toml

[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Peter J <peter.j@kakao.com>"]
edition = "2018"

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

[dependencies]

이것은 Cargo의 설정파일로, 패키지 관리에 이용된다.
프로젝트 이름, 버전, 작성자 등의 정보가 자동 입력되며, 필요에 따라 추가할 수 있다.
작성자 이름 및 이메일은 시스템 환경에서 읽어오는데,
.gitconfig와 같은 파일에서 비롯된 정보일 것이다.
에디션은 일단 2015와 2018이 존재하며 현재는 default로 2018을 사용하게 된다는 것 정도만 알아두자.
[dependencies] 아래로는 의존성을 갖는 라이브러리를 관리하는데
이것을 사용하게 될 때 좀 더 자세히 이야기해보도록 하자.

Cargo는 소스 코드를 src 디렉토리에 보관한다.
그리고 프로젝트의 최상위 디렉토리는 README, LICENSE, 설정 파일 등을 보관한다.
src 디렉토리에 자동 생성된 main.rs를 열어보면 다음과 같다.

src/main.rs

fn main() {
    println!("Hello, world!");
}

우리가 앞서 작성했던 것과 같은 Hello, world! 코드다.
이것을 컴파일하기 위해서는 앞서 수행한 것처럼 rustc를 이용할 수도 있지만
Cargo를 통해 컴파일 하려면 다음 명령어를 사용한다.

peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ cargo build
   Compiling hello_cargo v0.1.0 (/home/peter/rust-practice/chapter01/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.55s
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ 

컴파일 결과 실행 파일은 src 디렉토리가 아닌 target/debug 디렉토리에 생성된다.
따라서 이를 실행하고자 한다면 다음과 같이 실행할 수 있다.

peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ ./target/debug/hello_cargo 
Hello, world!
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$

cargo build 명령이 최초로 실행될 때 프로젝트 최상위 디렉토리에 Cargo.lock 파일이 생성된다.
이는 의존성을 가진 라이브러리의 정확한 버전을 추적하기 위한 파일인데
의존성을 가진 라이브러리를 사용할 때 좀 더 자세히 이야기하도록 하자.
지금은 그저 우리가 관리하는 게 아니라 Cargo가 관리하는 파일이라는 것만 알고 넘어가자.

컴파일을 하고 바로 실행까지 하고 싶다면 cargo run 명령어를 사용할 수 있다.
cargo buildcargo run은 소스코드에 변화가 없다면 다시 컴파일하지 않는다.

peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/hello_cargo`
Hello, world!
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ 

소스코드를 일부 수정하고 cargo run을 수행하면 컴파일 후 실행하는 걸 볼 수 있다.

peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ vi src/main.rs

src/main.rs

fn main() {
    println!("Hello, RUST!");
}
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ cargo run
   Compiling hello_cargo v0.1.0 (/home/peter/rust-practice/chapter01/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.18s
     Running `target/debug/hello_cargo`
Hello, RUST!
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ 

Cargo는 실행 파일을 생성하지 않고 컴파일 가능 여부만 신속하게 검사하는 명령어도 존재한다.
이것은 실행 파일을 생성하는 cargo build 보다 빠르게 실행되어
구현 도중에 작업 결과에 오류가 없는지 확인하는 데 유용하다.

peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ cargo check
    Checking hello_cargo v0.1.0 (/home/peter/rust-practice/chapter01/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$

우리가 cargo build 또는 cargo run을 통해 생성한 실행파일의 경로를 보면
debug라는 디렉토리에 생성되었음을 알 수 있다.
이는 실행 파일이 디버그 모드로 생성되었음을 의미한다.
디버그 모드로 컴파일을 할 경우 배포 모드에 비해 컴파일 시간이 짧고 실행 시간이 길다.
그만큼 최적화가 덜 된 채 생성되는 것이다.
반면, 배포를 할 때에는 컴파일이 조금 오래 걸리더라도 짧은 실행 시간이 보장될 필요가 있다.
따라서 Rust는 컴파일 방식을 디버그 모드와 배포 모드로 구분지었다.
배포 모드로 컴파일 하기 위해서는 컴파일 할 때 --release 옵션을 붙이면 된다.

peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$ cargo build --release
   Compiling hello_cargo v0.1.0 (/home/peter/rust-practice/chapter01/hello_cargo)
    Finished release [optimized] target(s) in 0.18s
peter@hp-laptop:~/rust-practice/chapter01/hello_cargo$

배포를 위한 실행 파일은 target/release 디렉토리에 생성된다.

Cargo에 대해 더 자세히 알고 싶다면 공식문서를 참고하자.

이 포스트의 내용은 공식문서의 Introduction & 1장 Getting Started에 해당합니다.

profile
Peter J Online Space - since July 2020

0개의 댓글