프로그래밍 언어/[Rust]

[Rust] Variable의 특성

swc0317 2024. 12. 30. 19:40
728x90
반응형
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 된 블럭을 나가기 전까지

 

위의 구문처럼 리터럴의 값을 변경하는

 

구문을 사용할 수 없습니다.

728x90
반응형

'프로그래밍 언어 > [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