본문 바로가기

Rust/pure

[Rust] 함수(function)

러스트에서 함수는 다음과 같은 구조를 가진다.

fn function_name(arg1: type1, arg2: type2 ...) -> return_type { statements [expression] }

함수는 fn으로 시작된다. 파라미터는 각각 타입을 명시해야 하며, 반환값이 있는 경우에도 -> 뒤에 타입을 명시한다.

러스트 함수에서 독특한 점은 함수 본문 마지막에 expression을 두고, 해당 값을 반환할 수 있다는 점이다. 이에 대해 설명하기 전에, 우선 expression 과 statement에 대해 알아보자.

컴파일러가 특정 소스코드를 생성하는 과정을 생각해보자. 소스코드는 Lex에 의해 어휘 분석을 거쳐 일련의 토큰 스트림을 이루게 된다. 이후 해당 토큰 스트림은 해당 언어에 대해 미리 정의된 문법에 기반하여 특정 구조 ( 보통 AST ) 를 가지게 된다. 이때 syntactic analyzer 단계에서 사용하는, 프로그래밍 언어의 뼈대를 이루는 부분이 syntax이다. 앞서 언급한 expression 및 statement는 이러한 syntax의 일종으로, 문법적으로 정해진 구조가 존재한다.

 

  • Expression : 특정 값으로 평가될 수 있는 식. 평가 결과로 하나 이상의 value를 생성한다.
  • Statement : 자연어와 동일한 의미를 가진다. 완전한 실행의 한 단위로, 세미콜론( ; ) 으로 끝난다.

예를 들어, x + y / z 는 expression이다. x, y, z 의 값에 따라 전체 식은 특정한 하나의 값으로 평가된다.반면  x + y / z ; 는 세미콜론으로 끝나므로 expression이 아니라 expression-statement이다.

잘 와닿지 않을 수도 있지만, 우리가 흔히 사용하는 수학적 연산(+, -, *, /, %), 관계 연산(>, >=, <, <=, ==, !=), 논리 연산자(&&, ||) 을 포함한 식이 expression이다.

아무튼 타 언어와 마찬가지로 러스트 역시 expression 과 statement를 구분한다. 사실 대부분의 언어에서는 이런 개념조차도 몰라도 거의 문제가 발생하지 않는다. 그렇다면, 러스트 공식문서에서는 왜 이 둘의 구분을 강조할까?

답은 expression의 작동 방식에 있다. 우리가 쉽게 접할 수 있는 언어들은 대부분 expression이 단일로 작동하지 않는다.

if (x > 20) : 조건 부분에서 expression 사용.
int x = y + 13; // 대입에 이용
...
k + 26;	// 단순 expression-statement를 지원하는 언어도 존재
return x > 13; // 값을 직접 반환하더라도 return statement로 반환

우리는 expression을 연산에 사용하고 대입하거나. 조건 부분에 사용하기도 하고, return을 통해 평가된 값을 반환하기도 한다. 이들의 공통점은 expression 자체를 어떻게 사용하는 것이 아니라, 전부 다른 문법적 요소의 일부분으로 사용한다는 점이다. 그러나, 러스트에서는 expression이 단일로 작동할 수 있다.

러스트는 함수, 조건문 등에서 특정 값을 "반환" 하는 상황이 존재할 때 expression을 그대로 사용할 수 있도록 구현된다. 예를 들어 C 언어는 return statement를 통해 값을 반환할 수는 있지만, expression 자체를 반환할 수는 없다. 반면 위와 같은 일이 러스트에서는 가능하다.

int something()
{
	13
    //C 코드. 오류를 일으킨다.
}
fn something() {
	13
    //러스트 코드. 오류를 일으키지 않는다
}

러스트에서는 많은 것들이 expression에 해당한다. 앞서 언급한 다양한 표현식들 말고도 함수의 호출, 스코프 블럭 ( {} ), 매크로의 호출 등이 속하는데, 세미콜론으로 끝나지 않는, 값을 가지는 것들은 expression인 수준이다. 물론, 상식적으로 생각해보면 대부분 당연한 이야기가 대부분이다.

fn main()
{
	let x = 13 + 12; // 13, 12, 13 + 12 : expression
    
    let y = { // block은 expression
    		let z = 13;
            z + x // z, x, z + x : expression
    	};
        
   let z = something(); // something이 특정 값을 반환하면 expression
}

 

개인적으로 block을 expression으로 간주할 수 있는 것은 정말 신기한 것 같다. 이런 특징에 의해 match, let if 구문 등에서 직관적으로 값을  제공할 수 있는데, 이것도 정말 독특한 부분이다.

 

'Rust > pure' 카테고리의 다른 글

[rust] 숫자 parse 할 때 주의점  (0) 2023.04.04
[rust] 문자열 문자 단위로 나누기  (0) 2023.03.26
[Rust] 변수  (0) 2021.10.28