Rust - Variable and Mutability
2021-12-31 07:00:32
목차
모든 언어들이 변수(Variables)를 다룬다. 하지면 Rust에서는
Mutability라는 새로운 개념을 다룬다. Mutability는 "변할 수 있는"이라는 의미인데, Rust에서 Mutability가 의미하는 바를 짚어야 할 것 같다. 아래 코드를 실행해보자.
fn main() {
let foo = 5;
println!("{}",foo);
foo = 10;
println!("{}",foo);
}
변수 foo에 5를 대입하고, 다시 10을 대입했다. 특별히 문제 될게 없어 보이는 코드이지만 빌드하면 아래와 같은 에러가 발생한다.
$ cargo run
Compiling hello_world v0.1.0 (/home/yundream/workspace/rust/HelloWorld)
error[E0384]: cannot assign twice to immutable variable `foo`
--> src/main.rs:4:5
|
2 | let foo = 5;
| ---
| |
| first assignment to `foo`
| help: consider making this binding mutable: `mut foo`
3 | println!("{}",foo);
4 | foo = 10;
| ^^^^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.
error: could not compile `hello_world` due to previous error
"cannot assign twice to immutable variable" 해석하자면 "불변변수에 두번 할당 할 수 없습니다." 이다.
불변(immutable) 변수에 주목하자.
Rust에서 변수는 다른 언어에서의 변수와 마찬가지로 메모리에 저장된 값을 가리키는 이름이다. 그런데 코드를 보면 foo 변수를 선언할 때
let을 사용했다. let은 Rust의 키워드로 변수에 값을
바인딩한다. 이렇게 바인딩된 변수는
불변성(immutable)을 가지며 값을 변경 할 수 없다.
바인딩된 변수의 값을 바꾸고 싶다면
mut를 이용해서 변경가능한 변수(가변성을 가진 변수)로 만들 수 있다. 예제코드가 컴파일되도록 수정했다.
fn main() {
let mut foo = 5;
println!("{}",foo);
foo = 10;
println!("{}",foo);
}
불변성으로 선언된 변수의 값을 바꾸려는 시도를 하면 컴파일 시간에 에러를 받게 된다. 프로그래머는 변수가 불변성을 가지고 있는지 가변성을 가지고 있는지를 명확히 알 수 있으며, 해당 값이 의도치 않게 변경됐을 가능성을 걱정하거나 추적할 필요가 없다.
이는 코드를 효과적으로 관리 할 수 있게 만들어준다.
Rust에서 새 변수를 만드는 가장 간단한 방법은 "let" 키워드를 사용하는 것이다.
fn main() {
let my_num = 5;
println!("{}", my_num);
}
let은 현재 범위(scope)에 새 변수를 도입한다. 이 변수는 기본적으로 불변성을 가진다.
Rust에서 변수는 타입을 설정 할 수 있다. 예제코드의 my_num의 타입을 가져와보자.
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let my_num = 5;
print_type_of(&my_num);
}
실행해보자.
$ cargo run
Compiling hello_world v0.1.0 (/home/yundream/workspace/rust/HelloWorld)
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/hello_world`
i32
Rust에서 따로 타입을 설정하지 않으면, 기본 타입으로 설정된다. "i8"로 타입을 설정해보자.
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let my_num: i8 = 5;
print_type_of(&my_num);
}
아래 코드가 컴파일 될지 안될지를 예상해보자.
fn main() {
let my_num = 32;
let my_num = my_num+32;
println!("{}", my_num);
}
실행된다.
$ cargo run
Compiling hello_world v0.1.0 (/home/yundream/workspace/rust/HelloWorld)
Finished dev [unoptimized + debuginfo] target(s) in 0.18s
Running `target/debug/hello_world`
64
분명히 rust의 변수는
불변성을 가진다고 했는데, 값이 변경되었다. 그 전에 같은 이름의 변수를 두 번 선언했다.
Rust에서
let을 이용해서 변수를 선언하면, 같은 이름이라고 하더라도 새로 변수를 할당하게 된다. 이름이 같은 경우에는 이를
shadowing 변수라고 한다. 아래 코드를 실행해보자.
fn main() {
let my_num = 32;
println!("{:p}", &my_num);
let my_num = my_num+32;
println!("{:p}", &my_num);
println!("{}", my_num);
}
주소 값이 다르게 나오는 걸 확인 할 수 있다. 서로 다른 변수라는 의미다.
0x7ffd8876907c
0x7ffd887690d4
서로 다른 변수이기 때문에 불변성을 해치는게 아니고, 실행하는데 문제가 없다.
서로 다른 변수이기 때문에 타입이 달라져도 상관 없다.
fn main() {
let my_age = 25;
let my_age = format!("{} year", my_age);
println!("{}",my_age);
}
그렇다면 shadow를 언제 사용하는 이유가 뭘까 ? 위의 코드를 다른 언어로 작성 할 때는 my_age와 함께 포맷팅된 값을 저장하기 위한 my_age_str 같은 변수를 따로 선언해서 사용해야 한다. Rust에서는 shadow를 이용해서 일관성 있는 변수이름의 관리가 가능하다.
상수는 다른 대부분의 언어가 가지고 있는 개념으로 "변경 할 수 없는 값"이다. 불변변수(immutable variables)와 상수는 값을 바꿀 수 없다는 측면은 동일하지만 몇 가지 차이가 있다.
- 상수는 mut키워드와 함께 사용 할 수 없다. 상수는 불변성(immutable)가 기본이며, 항상 불변성을 유지한다.
- 상수는 글로벌 영역(global scope)에 선언하는 값이므로, 코드의 여러 부분에서 알아야 하는 값이 있을 때 유용하다.
- 상수는 상수표현식으로만 선언할 수 있다. 컴파일 시간에 리터럴로 변환되기 때문에, 런타임에 실행되는 함수의 반환값이나 연산 값을 대입할 수 없다.
- 상수이름은 대문자만 사용할 수 있다.
const MAX_AGE: u8 = 150;
fn main() {
println!("{}", MAX_AGE);
}
실행해보자.
$ cargo run
Compiling hello_world v0.1.0 (/home/yundream/workspace/rust/HelloWorld)
Finished dev [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/hello_world`
150
혹시라도 상수값을 변경하려고 하면 컴파일 실패한다.
const MAX_AGE: u8 = 150;
fn main() {
MAX_AGE = 4;
println!("{}", MAX_AGE);
}
실행해보자.
$ cargo run
Compiling hello_world v0.1.0 (/home/yundream/workspace/rust/HelloWorld)
error[E0070]: invalid left-hand side of assignment
--> src/main.rs:3:13
|
3 | MAX_AGE = 4;
| ------- ^
| |
| cannot assign to this expression
For more information about this error, try `rustc --explain E0070`.
반면 let은 범위(scope)안에서 정의해야 한다. 아래의 코드는 허용되지 않는다.
let MAX_AGE: u8 = 150;
fn main() {
println!("{}", MAX_AGE);
}
실행해보자.
$ cargo run
Compiling hello_world v0.1.0 (/home/yundream/workspace/rust/HelloWorld)
error: expected item, found keyword `let`
--> src/main.rs:1:1
|
1 | let MAX_AGE: u8 = 150;
| ^^^ expected item
error: could not compile `hello_world` due to previous error
- Rust의 변수는 let 키워드를 이용해서 바인딩 한다.
- Rust의 변수는 기본적으로 immutable 하며 mut 키워드로 mmutable 하게 만들 수 있다.
- Rust는 shadow 변수를 제공한다. 이름이 같은 변수도 서로 다른 변수로 취급한다.