1. Mutability
먼저 모든 mut 예약어를 제외하고 정의된 식별자들은
Immutable, 즉 한 번 정의된 후 그 식별자의 값은 바뀔 수 없습니다.
fn main() {
let _immutable_binding = 1;
let mut mutable_binding = 1;
println!("Before mutation: {}", mutable_binding);
// Ok
mutable_binding += 1;
println!("After mutation: {}", mutable_binding);
// Error! Cannot assign a new value to an immutable variable
_immutable_binding += 1;
}
이렇게 정의된 두 개의 식별자,
" _immutable_binding " 과 " mutable_binding " 가 있습니다.
이때 정의할 때, mut 예약어를 통해 정의한 mutable_binding만
값을 바꾸는 게 허락됩니다.
_immutable_binding += 1;
이 구문이 에러를 일으킵니다.
2. Shadowing
C, C++에서의 Shadowing은
nested block 간 식별자의 Shadowing 만 허용한다.
C++, C 등에선 같은 "범위" 내 정의된 같은 이름의 식별자를
정의할 수 없습니다.
int main() {
int x = 10;
{
int x = 20; // 내부 스코프에서 x를 다시 선언 (허용됨)
std::cout << x << std::endl; // 출력: 20
}
std::cout << x << std::endl; // 출력: 10
int x = 30; // 같은 블록 내에서 x를 다시 선언 (컴파일 오류)
std::cout << x << std::endl;
return 0;
}
보시면 먼저 outer block에서
x를 10으로 정의한 뒤, 내부 block에선 20으로 정의하는 게 문제가 없으나,
x = 10을 정의한 같은 블럭 내에서 x를 재정의하는 게 문제가 됩니다.
즉, nested block 끼리의 shadowing을 허용하나,
같은 블럭 내 shadowing을 허용하지 않습니다.
Rust에서의 Shadowing은
nested block 간 식별자의 Shadowing 뿐만 아니라,
같은 블럭 내 식별자의 Shadowing 도 허용한다.
Rust의 경우
C, C++에서의 nested block 끼리의 shadowing 뿐만 아니라,
같은 블럭에서의 Shadowing도 허용합니다.
fn main() {
let shadowed_binding = 1;
{
println!("before being shadowed: {}", shadowed_binding);
// This binding *shadows* the outer one
let shadowed_binding = "abc";
println!("shadowed in inner block: {}", shadowed_binding);
}
println!("outside inner block: {}", shadowed_binding);
// This binding *shadows* the previous binding
let shadowed_binding = "cda";
println!("shadowed in outer block: {}", shadowed_binding);
}
먼저 외부 블럭에서 식별자의 리터럴을 1로 정의합니다.
이후 블럭에서 다른 타입형인 문자열 리터럴 "abc"로 정의합니다.
여기까진 C++에서도 허용하는 nested block 간 shadowing 입니다.
그러나 이뿐만 아니라
식별자를 1로 정의한 뒤, 같은 블럭에서 문자열 리터럴 "cda"로
재정의합니다. 즉 Rust는 같은 블럭 내 Shadowing 도 허용합니다.
3. First Declare, Initialize Later
C에서의 리터럴 값 없이 식별자 정의
#include <stdio.h>
int main() {
int a; // 초기화되지 않은 변수
printf("a: %d\n", a); // 쓰레기 값 출력
a = 10; // 변수 초기화
printf("a: %d\n", a); // 10 출력
return 0;
}
이렇게 리터럴 값 없이 식별자 a를 정의해 봅시다.
이후 이 값을 출력하면 예상하지 못한 값이 튀어나옵니다.
그러나 C에선 이를 "예방"하지 않고요.
Rust에서의 리터럴 값 없이 식별자 정의
Rust 또한 리터럴 값 없이 식별자를 정의하는 것 자체는
허용은 합니다.
fn main() {
let a_binding;
}
또 immutable하게 정의되었다 한들
이후의 초기화, 즉 단 한번 값을 정할 수 있습니다.
물론 여러 번 값을 변경해야 한다면 mutable하게 정의되어야 합니다.
fn main() {
let a_binding;
let x = 2;
// Initialize the binding
a_binding = x * x;
println!("a binding: {}", a_binding);
}
즉, immutable하게 정의된 식별자라도
사용하기 전에만 초기화한다면 사용할 수 있습니다.

만약 초기화 전에 사용한다면?
fn main() {
let another_binding;
// Error! Use of uninitialized binding
println!("another binding: {}", another_binding);
// FIXME ^ Comment out this line
another_binding = 1;
println!("another binding: {}", another_binding);
}
앞선 코드에서 순서를 조금 바꾼 코드입니다.
식별자를 리터럴 없이 정의한 뒤,
초기화 전, 해당 식별자를 사용합니다.
(물론 소유권이 첫 번째 println!에서 넘어가 뒤의 println!은 어차피
사용하지 못하겠지만)
초기화 전 해당 식별자를 사용하기 때문에 다음과 같은
에러를 일으킵니다.

4. Freezing
Rust에서 freezing은 변수가 불변(immutable) 상태로 "고정"되는 것을 의미합니다.
특히 Shadowing을 통해 정의됩니다.
fn main() {
let mut _mutable_integer = 7i32;
{
// Shadowing by immutable `_mutable_integer`
let _mutable_integer = _mutable_integer;
// Error! `_mutable_integer` is frozen in this scope
// _mutable_integer = 50;
let mut _mutable_integer = _mutable_integer;
_mutable_integer = 50;
// FIXME ^ Comment out this line
println!("_mutable_inter: {}", _mutable_integer);
// `_mutable_integer` goes out of scope
}
println!("_mutable_inter: {}", _mutable_integer);
// Ok! `_mutable_integer` is not frozen in this scope
_mutable_integer = 3;
}
먼저 외부 블럭에서 _mutable_integer을 Mutable하게
정의합니다. 이후 내부 블럭에서
Shadowing을 통해
immutable한 _mutable_integer을 정의합니다.
이는 내부 블럭에서는 해당 식별자의 리터럴 값이
변하지 않게 설정됩니다.
_mutable_integer = 50;
그래서 다시 Mutable하게 정의하거나,
해당 Freeze 된 블럭을 나가기 전까지
위의 구문처럼 리터럴의 값을 변경하는
구문을 사용할 수 없습니다.
'프로그래밍 언어 > [Rust]' 카테고리의 다른 글
| [Rust] Type Conversion (0) | 2025.01.07 |
|---|---|
| [Rust] Types (1) | 2025.01.03 |
| [Rust] enum (0) | 2024.12.30 |
| [Rust] 구조체 (0) | 2024.12.30 |
| [Rust] Ownership과 Reference (0) | 2024.12.30 |