[Rust] 함수

RedBean·2024년 7월 24일
0

Rust 배우기

목록 보기
4/4
post-thumbnail

함수

러스트 코드는 온통 함수로 가득 차 있다. 이미 우리는 함수 중 하나를 봤다. main함수 말이다. 여기서 우리는 새로운 함수를 만들도록 해 주는 fn 키워드도 보았다

러스트 코드는 함수나 변수 이름을 위한 관례로 스네이크 케이스(snake case)를 사용하는데, 이는 모든 글자를 소문자로 쓰고 밑줄 (underscore)로 단어를 구분하는 방식을 말한다.

다음은 함수의 예시 프로그램이다.

fn main() {
    println!("Hello, world!");

    another_function();
}

fn another_function() {
    println!("Another function.");
}

러스트에서는 fn 뒤에 함수 이름과 괄호를 붙여서 함수를 정의한다. 중괄호는 함수의 시작과 끝을 알려준다.

함수의 이름 뒤에 괄호를 쓰면 정의한 어떤 함수든 호출할 수 있다.

그런데 여기서 이상한 점이 있다.
main 함수 뒤에 another_function이 구현되어 있다. 과연 이 코드는 제대로 작동할까?

다음은 cargo run 명령어로 코드를 실행시킨 결과다.

$ cargo run
...
Hello, world!
Another function.

매우 정상적으로 작동한다. 러스트는 우리가 함수를 어디 정의했는지는 신경 쓰지 않으며, 어딘가에 정의만 되어 있으면 된다.

매개변수

함수는 매개변수(parameter)를 가지도록 정의될 수 있으며, 이는 함수 시그니처의 일부인 특별한 변수다. 함수가 매개변수를 가지고 있으면 우리는 이에 대한 구체적인 값을 전달할 수 있는데, 이를 인수(argument)라고 부른다.

다음은 매개변수가 추가된 버전의 예시이다.

fn main() {
    another_function(5);
}

fn another_function(x: i32) {
    println!("The value of x is: {x}");
}

이 코드를 실행해보자.

$ cargo run
...
The value of x is: 5

another_function 선언을 보면, x라는 이름의 매개변수를 하나 가지고 있다. 이것의 타입은 i32로 명시되어 있는데, 이렇게 매개변수를 선언하면 그것의 타입을 반드시 명시해야 한다.

쉼표로 구분해서 매개변수를 더 만들 수도 있다.

fn main() {
    another_function(5);
}

fn another_function(x: i32, y: i32) {
    println!("The value of x and y are: {x}{y}");
}

구문과 표현식

함수 본문은 필요에 따라 표현식으로 끝나는 구문의 나열로 구성된다. 지금까지 우리가 다룬 함수는 표현식으로 끝나지는 않았지만 표현식이 구문의 일부분으로 쓰인 것은 보았다.

구문과 표현식은 무슨 차이가 있을까?

구문은 어떤 동작을 수행하고 값을 반환하지 않는 명령이다.
표현식은 결괏값을 평가한다. 예시를 살펴보자.

fn main() {
    let y = 6;
}

또한 함수 정의도 구문이다.

구문은 값을 반환하지 않는다. 그러므로 let 구문을 다른 변수에 할당하려고 하면 에러가 발생한다.

fn main() {
    let x = (let y = 6);
}
$ cargo run
...
error: expected expression, found `let` statement
...
error: expected expression, found statement (`let`)
...
error[E0658]: `let` expressions in this position are unstable
...
warning: unnecessary parentheses around assigned value
...
warning: `functions` (bin "functions") generated 1 warning

error: could not compile `functions` due to 3 previous errors; 1 warning emitted

let y = 6 구문은 값을 반환하지 않으므로, x에 바인딩할 수 없다.
다른 언어들은 값을 반환하기에 x = y = 6 같은 것들이 작동하지만, 러스트에서는 그렇지 않다.

작성하는 러스트 코드의 대부분은 표현식이며, 이는 어떤 값을 평가한다. 5 + 6과 같은 간단한 수학 연산을 살펴보자. 이 수식은 11이라는 값을 평가하는 표현식이다.

let y = 6;이라는 구문에서 66이라는 값을 평가하는 표현식입니다. 함수를 호출하는 것도, 매크로를 호출하는 것도 표현식이다. 아래 예제처럼 중괄호로 만들어진 새로운 스코프 블록도 표현식이다.

fn main() {
    let y = {
        let x = 3;
        x + 1
    };

    println!("The value of y is: {y}");
}
{
    let x = 3;
    x + 1
}

이 같은 경우는 4를 평가하는 코드 블록이다. 이 값은 let의 일부로서 y에 바인딩된다. 표현식은 종결을 나타네는 세미콜론을 쓰지 않는다는 것을 기억하자. 만약 세미콜론을 추가하면, 이는 구문으로 변해서 아무 값도 반환하지 않는다.

반환 값을 가지는 함수

함수는 호출한 코드에 값을 반환할 수 있다. 굳이 이걸 명명해야 할 필요는 없지만, 그 값의 타입은 화살표(->) 뒤에 선언되어야 한다. 러스트에서 함수의 반환 값은 마지막 표현식의 값과 동일하다. (return을 사용할 수도 있다.)

fn five() -> i32 {
    5
}

fn main() {
    let x = five();

    println!("The value of x is: {x}");
}

five 함수에는 함수 호출, 매크로, 심지어 let 구문도 없이 그저 5라는 숫자 하나가 있다. 러스트에서는 놀랍게도 이게 유효한 함수다.

$ cargo run
...
The value of x is: 5

five()의 반환 값이 5이기 때문에, let x = five();let x = 5;와 동일하다.

다른 표현식들도 가능하다.

fn six() -> i32 {
    5 + 1
}

fn main() {
    let x = six();

    println!("The value of x is: {x}");
}
$ cargo run
...
The value of x is: 6

다음 글은 주석과 제어 흐름문에 대해 다룰 것이다.

profile
Rust를 배우고 있는

0개의 댓글

관련 채용 정보