1.4.0으로 업데이트

sarojaba authored
revision 8969ab1022116382d49c66f3288facaeb752f136
functions
# 5.2. 함수 (Functions) - 1090%

모든 Rust 프로그램은 최소한 하나의 함수를 가집니다. 바로 `main` 함수죠.

```rust
fn main() {
}
```

위의 코드는 가장 간단한 함수 선언입니다. 전에 언급했듯이 `fn`는 ‘이것은 함수이다’라는 뜻입니다. 그 다음엔 '이름'이 오고, 이 함수는 인자를 취하지 않기 때문에 '빈 괄호'가 오고, 몸체를 나타내는 '중괄호'가 옵니다. 다음 함수의 이름은 `foo` 입니다.

```rust
fn foo() {
}
```

이제 인자를 받아볼까요? 숫자를 출력하는 `print_number` 함수를 만들어봅시다.

```rust
fn print_number(x: i32) {
println!("x is: {}", x);
}
```

아래는 `print_number` 함수를 사용하는 완전한 프로그램입니다.

```rust
fn main() {
print_number(5);
}

fn print_number(x: i32) {
println!("x is: {}", x);
}
```

보시다시피 함수 인자는 `let` 선언과 아주 유사하게 동작합니다. 인자 이름 다음 `:`(콜론)을 적고 타입을 적습니다.

다음은 두 수를 더하고 출력하는 완전한 프로그램입니다.

```rust
fn main() {
print_sum(5, 6);
}

fn print_sum(x: i32, y: i32) {
println!("sum is: {}", x + y);
}
```

함수를 호출하거나 선언할 때는 쉼표로 인자를 분리합니다.

`let`과는 다르게 함수 인자의 타입을 _꼭_ 선언해야 합니다. 다음 예제는 동작하지 않습니다.

```rust,ignore
fn print_sum(x, y) {
println!("sum is: {}", x + y);
}
```

이 에러가 표시될 것입니다.

```text
expected one of `!`, `:`, or `@`, found `)`
fn print_number(x, y) {
```

이는 신중하게 결정한 설계입니다. Haskell처럼 전체 프로그램의 추론이 가능한 언어라면 종종 타입을 명시적으로 기록하는 것이 좋습니다. Rust는 함수 선언에선 타입을 명시해야 하지만, 함수의 내부에서는 추론을 허용합니다(즉, 명시하지 않아도 됩니다). 이것은 추론과 명시 사이에서 적절한 합의점이 됩니다.

'반환'에 대해 알아볼까요? 다음은 정수에 1을 더하는 함수입니다.

```rust
fn add_one(x: i32) -> i32 {
x + 1
}
```

Rust의 함수는 정확히 하나의 값만 반환하고, 대시 (`-`) 뒤에 초과 기호 (`>`)인 ‘화살표’ 다음에 타입을 선언합니다. 함수의 마지막 줄이 반환값을 결정합니다. 세미콜론이 빠졌다는 것을 눈치채셨나요? 추가해 보겠습니다.

```rust,ignore
fn add_one(x: i32) -> i32 {
x + 1;
}
```

세미콜론을 추가하면 에러가 납니다.

```text
error: not all control paths return a value
fn add_one(x: i32) -> i32 {
x + 1;
}

help: consider removing this semicolon:
x + 1;
^
```

이는 Rust의 두가지 독특한 성질을 보여줍니다. '표현식' 기반의 언어라는 점과, 세미콜론이 '중괄호와 세미콜론' 기반의 언어에서의 세미콜론과 다르다는 것입니다. 이 두 가지는 연관이 있습니다.

## 표현식 vs. 구문 (Expressions vs. Statements)

Rust은 기본적으로 표현식 기반 언어입니다. 구문은 오직 두 종류만 있고, 나머지는 모두 표현식입니다.

구문과 표현식은 무엇이 다를까요? 표현식은 값을 반환하고, 구문은 반환값이 없습니다. 이것은 결국 '모든 제어 흐름이 값을 반환하지 않습니다(not all control paths return a value)'가 에러 메세지로 나타난 이유입니다: `x + 1;` 구문은 값을 반환하지 않습니다. Rust에는 두 종류의 구문이 있습니다. ‘선언문’ 과 ‘표현문’ 입니다. 나머지는 전부 표현식입니다. 우선 선언문에 대해 이야기 해볼까요?

Ruby와 같은 언어에서는 변수 바인딩은 구문이 아닌, 표현식으로 작성될 수 있습니다.

```ruby
x = y = 5
```

그러나 Rust에서는, 바인딩이 표현식이 _아니다_는 것을 알려주기 위해 `let`을 사용합니다. 다음은 컴파일시에 에러를 발생시킬 것입니다.

```ignore
let x = (let y = 5); // expected identifier, found keyword `let`
```

컴파일러는 표현식의 시작을 기대했으나, `let`은 오직 구문만이 될 수 있습니다.

비록 그 값이 쓰이진 않더라도 변수에 값을 할당하는 것(e.g. `y = 5`)이 여전히 표현식이라는 것에 주목하세요. 할당하는 행위 자체가 할당된 값을 평가하는(e.g. 이전 예제에서 `5`) 다른 언어들과 달리, 러스트에서 값의 할당은 빈 튜플인 `()`이 됩니다. 할당된 값은 [단 하나의 소유자](ownership.md)를 가지기 때문입니다. 따라서 반환된 값은 매우 놀라울 것입니다:

```rust
let mut y = 5;

let x = (y = 6); // x has the value `()`, not `6`
```

두 번째 문장은 러스트에서 *표현문(expression statement)*이 됩니다. 어떤 표현식이든 구문으로 바꾸기 위해 존재합니다. 현실적인 조건으로, 러스트의 문법에서 구문은 다른 구문을 따르기를 기대합니다. 각각에서 표현식을 분리하려면 세미콜론을 사용합니다. 러스트는 다른 언어들과 같이 줄의 끝마다 세미콜론을 필요로 하는 것을 뜻하고, 실제로 러스트 코드의 줄마다 대부분 세미콜론을 볼 수 있을 것입니다.

"대부분"이라면 그 예외가 무엇일까요? 이미 보았듯이, 이러한 코드입니다:

```rust
fn add_one(x: i32) -> i32 {
x + 1
}
```

우리의 함수는 `i32`를 반환한다고 했지만, 세미콜론을 쓴다면 `()`를 반환할 것입니다. 그렇기 때문에 러스트는 아마 이것이 우리가 원하는 결과가 아님을 알고, 세미콜론을 제거해보라는 제안을 한 것입니다.

## 미리 반환 (Early returns)

근데 미리 반환하려면 어떻게 해야하나요? Rust는 이를 위한 키워드인 `return`을 가지고 있습니다.

```rust
fn foo(x: i32) -> i32 {
return x;

// we never run this code!
x + 1
}
```

함수의 마지막 줄에 `return`을 사용할 수도 있지만, 일반적으로 좋은 방법은 아닙니다.

```rust
fn foo(x: i32) -> i32 {
return x + 1;
}
```

이전에 표현식 기반의 언어로 작업해보지 않았다면, 앞서 나온 `return` 없는 정의는 좀 이상하게 보일 수 있습니다. 그러나 때로는 더 직관적일 수 있습니다.

## Diverging functions

러스트는 리턴을 하지 않는, '발산하는 함수(diverging functions)'를 위한 특별한 문법을 가지고 있습니다.

```rust
fn diverges() -> ! {
panic!("This function never returns!");
}
```

`panic!()`은 `println!()`과 비슷한 매크로입니다. `println!()`과 다른점은 `panic!()`은 현재 실행하고 있는 스레드를 충돌시키고 메세지를 전달합니다.

함수가 충돌되었기 때문에 어떠한 것도 반환하지 않고, 그렇기 때문에 "벗어났다(diverges)"라고 읽는 `!` 타입을 가집니다. 발산하는 함수는 어떤 타입으로도 쓸 수 있습니다:

```should_panic
# fn diverges() -> ! {
# panic!("This function never returns!");
# }
let x: i32 = diverges();
let x: String = diverges(
Because this function will cause a crash, it will never
return, and so it has the type ‘`!`’, which is read ‘diverges’.

If you add a main function that calls `diverges()` and run it, you’ll get
some output that looks like this:

```text
thread ‘
’ panicked at ‘This function never returns!’, hello.rs:2
```

If you want more information, you can get a backtrace by setting the
`RUST_BACKTRACE` environment variable:

```text
$ RUST_BACKTRACE=1 ./diverges
thread '
' panicked at 'This function never returns!', hello.rs:2
stack backtrace:
1: 0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r
2: 0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w
3: 0x7f402773960e - rt::unwind::begin_unwind_inner::h2844b8c5e81e79558Bw
4: 0x7f4027738893 - rt::unwind::begin_unwind::h4375279447423903650
5: 0x7f4027738809 - diverges::h2266b4c4b850236beaa
6: 0x7f40277389e5 - main::h19bb1149c2f00ecfBaa
7: 0x7f402773f514 - rt::unwind::try::try_fn::h13186883479104382231
8: 0x7f402773d1d8 - __rust_try
9: 0x7f402773f201 - rt::lang_start::ha172a3ce74bb453aK5w
10: 0x7f4027738a19 - main
11: 0x7f402694ab44 - __libc_start_main
12: 0x7f40277386c8 -
13: 0x0 -
```

`RUST_BACKTRACE` also works with Cargo’s `run` command:

```text
$ RUST_BACKTRACE=1 cargo run
Running `target/debug/diverges`
thread '
' panicked at 'This function never returns!', hello.rs:2
stack backtrace:
1: 0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r
2: 0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w
3: 0x7f402773960e - rt::unwind::begin_unwind_inner::h2844b8c5e81e79558Bw
4: 0x7f4027738893 - rt::unwind::begin_unwind::h4375279447423903650
5: 0x7f4027738809 - diverges::h2266b4c4b850236beaa
6: 0x7f40277389e5 - main::h19bb1149c2f00ecfBaa
7: 0x7f402773f514 - rt::unwind::try::try_fn::h13186883479104382231
8: 0x7f402773d1d8 - __rust_try
9: 0x7f402773f201 - rt::lang_start::ha172a3ce74bb453aK5w
10: 0x7f4027738a19 - main
11: 0x7f402694ab44 - __libc_start_main
12: 0x7f40277386c8 -
13: 0x0 -
```

발산하는 함수는 어떤 타입으로도 쓸 수 있습니다.

```should_panic
# fn diverges() -> ! {
# panic!("This function never returns!");
# }
let x: i32 = diverges();
let x: String = diverges();
```

## Function pointers

We can also create variable bindings which point to functions:

```rust
let f: fn(i32) -> i32;
```

`f` is a variable binding which points to a function that takes an `i32` as
an argument and returns an `i32`. For example:

```rust
fn plus_one(i: i32) -> i32 {
i + 1
}

// without type inference
let f: fn(i32) -> i32 = plus_one;

// with type inference
let f = plus_one;
```

We can then use `f` to call the function:

```rust
# fn plus_one(i: i32) -> i32 { i + 1 }
# let f = plus_one;
let six = f(5);
```