24년 11월 이전/Rust

보편적인 프로그래밍 개념 - 변수와 상수

Gurumee 2022. 2. 26. 19:46
반응형

개발 환경

이 문서에서 진행한 필자의 개발 환경은 다음과 같다.

  • desktop: macbook pro 13 2020
  • cpu: Intel Core i7 4core
  • memory: 32GB
  • rustup v1.24.3
  • cargo v1.58.0

 

이 문서는 여러분이 cargo가 설치되어 있다고 가정한다. 만약 cargo를 설치하지 않았다면, 이 문서를 참고하여 설치 및 설정을 진행하길 바란다.

 

cargo 설치가 되었다면 이번 장을 위한 프로젝트를 생성한다.

# 프로젝트 생성
$ cargo new --bin variables

# 프로젝트 디렉토리로 이동
$ cd varialbes

let

letrust에서 변수를 선언하기 위한 키워드이다. 변수를 선언하려면 이런 형태로 선언해야 한다.

let 변수_이름: 타입 = 값;

 

예를 들어, 정수형 값 5를 갖는 변수 num을 선언하고 싶다면 다음과 같이 선언할 수 있다.

let num: u32 = 5;

 

이 때 u32는 정수형 타입 중 하나로 부호가 없는 32bit 정수형 타입이다. 이는 추후 진행할 "데이터 타입"에서 자세히 다룰 것이다. 이 문서에서는 정수형 타입 선언을 위해 쓰였다 정도로만 이해하자.

 

위 변수 선언은 이렇게 줄여 쓸 수도 있다.

let num = 5;

 

이제 본격적으로 프로그램을 만들어보자. main.rs를 다음 내용으로 수정한다.

 

variables/src/main.rs

fn main() {
    let num = 5;
    println!("num: {}", 5)
}

 

이후 cargo run 명령어를 이용해서 프로그램을 실행시켜보자.

$ cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.28s
     Running `target/debug/variables`
num: 5

 

정수 5를 값으로 갖는 num이란 변수를 선언하고 출력하는 매우 간단한 프로그램이다. 보편적으로 변수라하면 "변하는 수"이다. 한 번 이제 main.rs에서 num의 값을 변경해보자.

 

variables/src/main.rs

fn main() {
    let num = 5;
    println!("num: {}", num);

    num = 6;
    println!("num: {}", num);
}

 

위 프로그램은 어떻게 동작할까? 우리의 예상대로 변수의 값이 변경될까? 한 번 프로그램을 실행시켜보자.

let mut

다음은 위에서 만든 프로그램을 cargo run을 통해 실행한 결과이다.

$ cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
error[E0384]: cannot assign twice to immutable variable `num`
 --> src/main.rs:5:5
  |
2 |     let num = 5;
  |         ---
  |         |
  |         first assignment to `num`
  |         help: consider making this binding mutable: `mut num`
...
5 |     num = 6;
  |     ^^^^^^^ cannot assign twice to immutable variable

For more information about this error, try `rustc --explain E0384`.

 

실행이 되지 않는다. 왜 그런지는 컴파일러가 잘 설명해준다. 이유는 기본적으로 rust의 변수는 불변성을 가지고 있기 때문이다. 왜 변수인데 불변성을 가져야 할까? rust는 다른 프로그래밍 언어보다 안정성을 더 생각하고 설계된 언어이기 때문이다. 이는 다른 문서에서 더 자세히 다루도록 하자. 지금은 안전한 프로그래밍을 위해 기본적으로는 변수는 변하지 않는다 정도로만 이해하자.

 

그렇다면, 위 프로그램을 동작시키기 위해서는 어떻게 해야할까? 역시 컴파일러가 친절하게 알려주고 있다.

...
2 |     let num = 5;
  |         ---
  |         |
  |         first assignment to `num`
  |         help: consider making this binding mutable: `mut num`
...

 

변수를 프로그램 실행 시 변하게 하고 싶다면 let mut으로 변수를 선언하면 된다. main.rs를 다음과 같이 수정한다.

 

variables/src/main.rs

fn main() {
    let mut num = 5;
    println!("num: {}", num);

    num = 6;
    println!("num: {}", num);
}

 

이제 다시 프로그램을 실행시켜보자.

$ cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.24s
     Running `target/debug/variables`
num: 5
num: 6

const

보편적으로 프로그래밍 언어에서 "변수"와 "상수"란 개념은 쌍이다. 변수는 변할 수 있는 값이라면, 상수는 절대 변하지 않는 값이다. rust에서도 상수를 위한 키워드가 있다. 상수를 선언하려면 다음과 같이 할 수 있다.

const 상수_이름: 상수 타입 = 값;

 

정수 5를 값는 상수 NUM을 선언하고 싶으면 다음과 같이 선언할 수 있다. (상수는 대문자로 이름 짓는 것이 관례이다.)

const NUM: u32 = 5;

 

역시 변수처럼 축약할 수 있다.

const NUM = 5;

 

이번엔 main.rs를 다음과 같이 변경해보자.

 

variables/src/main.rs

fn main() {
    const NUM: u32 = 5;
    println!("NUM: {}", NUM);

    NUM = 6;
    println!("NUM: {}", NUM);
}

 

이제 프로그램을 실행시켜보자. 잘 될까?

$ cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
error[E0070]: invalid left-hand side of assignment
 --> src/main.rs:5:9
  |
5 |     NUM = 6;
  |     --- ^
  |     |
  |     cannot assign to this expression

For more information about this error, try `rustc --explain E0070`.
error: could not compile `variables` due to previous error

 

당연히 안된다. 상수는 절대 변할 수 없는 값이라 했다. 따라서 값을 바꾸면 에러가 뜬다. 조금 특이한 점은 let만 썼을 때와는 에러 출력문이 다르다는 것이다.

let만 썼을 때는 consider making this binding mutable: `mut num`문구를 출력하면서 어떻게 하면 에러를 고칠 수 있는지 알려줬다면 const는 이 표현식은 사용할 수 없다는 에러가 뜬다. 

 

이 에러를 고치려면 상수 값을 재 할당하는 부분을 삭제하면 된다. 다음과 같이 수정하자.

 

variables/src/main.rs

fn main() {
    const NUM: u32 = 5;
    println!("NUM: {}", NUM);
}

 

이제 프로그램을 실행시켜보자.

$  cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.24s
     Running `target/debug/variables`
NUM: 5

let shaddowing

이제 shaddowing이란 개념을 알아보자. let은 기본적으로 불변이라고 했다. 여기서 let mut으로 바꾸면 가변성을 갖는다. rust에서 변수를 바꾸는 방법은 1가지가 더 있는데 바로 shaddowing이란 기법을 이용하는 것이다. shaddowing을 사용하면 불변성을 유지하면서 변수를 바꿀 수가 있다.

 

main.rs를 다음과 같이 수정해보자.

 

variables/src/main.rs

fn main() {
    let spaces = "    ";
    spaces = spaces.len();
    println!("The value of spaces is: {}", spaces);
}

 

위 프로그램은 " "(공백 4칸) 문자열 값을 갖는 spaces 변수를 선언한다. 그 이후 문자열 타입의 len 메소드를 호출하여 문자열 길이를 다시 spaces에 할당하고 출력하는 프로그램이다. 위 프로그램은 과연 잘 동작할까?

$ cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
2 |     let spaces = "    ";
  |                  ------ expected due to this value
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` due to previous error

 

당연히 잘 동작하지 않는다. 기본적으로 rust에서 변수는 불변하다. 때문에 값을 변경하기 때문에 에러가 발생한다. 그럼 let mut으로 변경해보면 어떨까?

 

variables/src/main.rs

fn main() {
    let mut spaces = "    ";
    spaces = spaces.len();
    println!("The value of spaces is: {}", spaces);
}

 

잘 동작할 것 같은가? 프로그램을 실행시켜보자.

$ cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
2 |     let mut spaces = "    ";
  |                      ------ expected due to this value
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` due to previous error

 

같은 컴파일 오류를 나타내고 있다. let mut을 통해 변수의 값을 변경할 수 있을텐데 왜 안되는 것일까? 일반적으로 프로그래밍에서는 변수를 한 번 선언하면 그 타입은 절대 바뀌지 않는다. 근데 위 프로그램은 " " 값을 갖는 문자열 변수 spaces를 선언해놓고 spaces.len()을 통해 문자열 길이를 값으로 갖는 정수형 값을 변수에 넣으려고 하기 때문에 이런 오류가 발생하는 것이다.

 

이 때 shaddowing 기법을 이용하면 이를 해결할 수 있다. main.rs를 다시 다음과 같이 수정하자.

 

variables/src/main.rs

fn main() {
    let spaces = "    ";
    let spaces = spaces.len(); # 위 문자열 타입의 spaces를 이용한다. 여기서부터 spaces는 문자열 길이를 값으로 갖는 정수형 변수이다.
    println!("The value of spaces is: {}", spaces);
}

프로그래밍 경험이 있다면 해괴 망측한 코드가 아닐 수 없다. 같은 이름을 갖는 변수를 다시 선언하다니... 하지만 rust에서는 같은 이름을 가지는 변수를 사용한다는 조건 하에서 같은 이름을 갖는 변수를 다시 선언할 수 있다. 이 때 타입도 변경할 수 있는 것이다. 이게 바로 shaddowing 기법이다.

 

이제 프로그램을 다시 한 번 실행해보자.

$ cargo run
   Compiling variables v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.25s
     Running `target/debug/variables`
The value of spaces is: 4

문자열 길이 4가 출력되는 것을 확인할 수 있다. 

728x90
반응형