스코프

스코프(scope)란 변수에 접근할 수 있는 범위를 의미합니다. 먼저 파이썬에서는 스코프를 기본적으로 함수 단위로 구분합니다.

실제로는 파이썬은 LEGB 룰이라고 불리는 좀더 복잡한 스코프 규칙을 가지고 있지만, 여기서는 단순화해서 함수 기준으로 설명합니다.

def hello(name: str):
    num = 3
    print(f"Hello {name}")


if __name__ == '__main__':
    my_name = "buzzi"

    if True:
        print("My name is", my_name)
        my_name = "mellon"

    hello(my_name)

    # print(num) # error

실행 결과

My name is buzzi
Hello mellon

코드 실행 부분을 먼저 보면, my_name 변수에 "buzzi" 라는 문자열을 할당합니다. 그 다음 if 문에서 변수 값을 프린트해보면 "buzzi"가 프린트됩니다. 하지만 그 다음 라인에서 my_name = "mellon" 으로 변수의 값을 바꿔 버렸습니다. 파이썬은 스코프를 함수 단위로만 구분하고 있기 때문에 이제 코드 전체에서 값이 바뀌게 됩니다. 따라서 hello(my_name)의 출력은 Hello mellon이 됩니다. 마지막으로 # print(num) # error를 주석 해제하고 실행해 보면 에러가 발생합니다. hello 함수 안에서 선언된 num 이라는 변수를 프린트하기 때문입니다. 즉, num 의 스코프가 hello 함수이기 때문에 함수 바깥에서 참조할 수 없는 것입니다.

이번에는 러스트의 스코프를 살펴보겠습니다.

fn hello(name: String) {
    let num = 3;
    println!("Hello {}", name);
}

fn main() {
    let my_name = "buzzi".to_string();

    {
        println!("My name is {}", my_name);
        let my_name = "mellon";
    }

    hello(my_name);

    // println!("{}", num); // error
}

실행 결과

My name is buzzi
Hello buzzi

러스트에서는 스코프를 중괄호 "{}" 기준으로 구분합니다. 먼저 my_name 변수를 "buzzi"로 할당했습니다. 그 다음, 중괄호 안에서 my_name 을 프린트해보면 "buzzi"가 프린트됩니다. 중괄호 안에서 my_name 을 "mellon"으로 할당하더라도, 중괄호를 벗어나면 중괄호 안에서 선언된 my_name 의 스코프가 끝나게 되므로 중괄호 바깥에서는 my_name 의 값은 원래대로 "buzzi"가 됩니다. 따라서 hello(my_name)의 실행 결과는 "Hello buzzi"가 됩니다. 파이썬에서와 마찬가지로, hello 안에서 선언된 변수인 num은 함수 바깥에서 참조할 수 없기 때문에 println!("{}", num); 을 주석 해제한 다음 코드를 실행하면 에러가 발생합니다.

러스트의 스코프는 나중에 배울 소유권 모델과 밀접한 연관이 있기 때문에 중괄호를 기준으로 스코프가 변경된다는 사실을 꼭 기억해 두세요.

러스트의 주석은 // 로 표기합니다.