• B6b364a4920a7ba8220a449635ca0c59?s=80&d=mm

    Changes from SoonBin

    SoonBin - about 4 years ago (Sep 15, 2015, 1:13 PM)
    리턴값이 없다는 의미의 Diverging function을 우선 발산하는 함수로 번역했습니다. 난해한 부분이 워낙많아 고수분들의 수정이 많이 필요할듯..
  • Changes have been accepted Merged
      Looks like something's not quite right here.. We've been notified of the issue, and will get back to you soon.
      functions
      # 5.2. 함수 (Functions) - 50%

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

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

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

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

      이제, 인자를 받아볼까요? 여기 숫자를 출력하는 함수가 있습니다.

      ```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 처럼, 전체 프로그램의 추론이 가능한 언어라면, 종종 타입을 명시적으로 기록하는 것이 최선책입니다. We agree that forcing functions to declare types while allowing for inference inside of function bodies is a wonderful sweet spot between full inference and no inference 우리는 함수 선언에선 타입을 명시하길 강제하면서도 함수의 내부에서는 추론을 허용합니다(명시하지 않아도 됩니다). 이것은 추론과 명시 사이에서 적절한 합의점이 됩니다.

      값의 반환에 대해 알아볼까요? 다음은 정수에 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은 기본적으로 표현식 기반 언어입니다. 구문은 오직 두 종류만 있고, 나머지는 모두 표현식입니다.

      근데 무엇이 다른가요? 표현식은 값을 반환하고, 구문은 반환값이 없습니다.
      That’s why we end up with ‘not all control paths return a value’ here: `x + 1;` 문장은 값을 반환하지 않습니다. Rust에는 두 종류의 문장이 있습니다. ‘선언문’ 과 ‘표현문’ 입니다. 나머지는 표현식입니다. 우선 선언문에 대해 이야기 해볼까요?

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

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

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

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

      The compiler is telling us here that it was expecting to see the beginning of
      an expression, and a `let` can only begin a statement, not an expression.

      Note that assigning to an already-bound variable (e.g. `y = 5`) is still an
      expression, although its value is not particularly useful. Unlike other
      languages where an assignment evaluates to the assigned value (e.g. `5` in the
      previous example), in Rust the value of an assignment is an empty tuple `()`
      because the assigned value can have [just one owner](ownership.html), and any
      other returned value would be too surprising:

      ```rust
      let mut y = 5;

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

      The second kind of statement in Rust is the *expression statement*. Its
      purpose is to turn any expression into a statement. In practical terms, Rust's
      grammar expects statements to follow other statements. This means that you use
      semicolons to separate expressions from each other. This means that Rust
      looks a lot like most other languages that require you to use semicolons
      at the end of every line, and you will see semicolons at the end of almost
      every line of Rust code you see.

      What is this exception that makes us say "almost"? You saw it already, in this
      code:

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

      Our function claims to return an `i32`, but with a semicolon, it would return
      `()` instead. Rust realizes this probably isn’t what we want, and suggests
      removing the semicolon in the error we saw before.

      ## 미리 반환 (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` 없는 정의는 좀 이상하게 보일 수 있습니다. but it becomes intuitive over time.

      ## Diverging functions

      Rust has some special syntax for ‘diverging functions’, which are functions that
      do not return:

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

      `panic!` is a macro, similar to `println!()` that we've already seen. Unlike
      `println!()`, `panic!()` causes the current thread of execution to crash with
      the given message.

      Because this function will cause a crash, it will never return, and so it has
      the type '`!`', which is read "diverges." A diverging function can be used
      as any type
      이것은 결국 '모든 제어 흐름이 값을 반환하지 않습니다(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.html)를 가지기 때문입니다. 따라서 반환된 값은 매우 놀라울 것입니다:

      ```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();
      ```