Penflip

Penflip - Write better with others

  • Loading...
  • Discover Projects
  • Help
  • Signup
  • Login
  • Welcome back!
    No account? Signup Close
    Ready to write better?
    Have an account? Login Close

sarojaba · Rust Doc Korean

Make Changes
46

5.2. 함수 (Functions) - 100%

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

fn main() {
}

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

fn foo() {
}

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

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

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

fn main() {
    print_number(5);
}

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

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

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

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

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

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

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

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

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

`)`이 아닌 `!`, `:`, 또는 `@`이 와야 합니다.
expected one of `!`, `:`, or `@`, found `)`
fn print_number(x, y) {

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

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

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

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

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

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

에러: 모든 제어 경로가 값을 반환하지 않습니다.
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와 같은 언어에서는 변수 바인딩은 구문이 아닌, 표현식으로 작성될 수 있습니다.

x = y = 5

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

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

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

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

let mut y = 5;

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

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

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

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

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

미리 반환 (Early returns)

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

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

    // 이 코드는 절대 실행되지 않습니다!
    // we never run this code!
    x + 1
}

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

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

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

발산하는 함수 (Diverging functions)

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

fn diverges() -> ! {
    // panic!("이 함수는 절대 리턴하지 않습니다!");
    panic!("This function never returns!");
}

panic!()은 println!()과 비슷한 매크로입니다. println!()과 다른점은 panic!()은 현재 실행하고 있는 스레드를 충돌시키고 메세지를 전달합니다. 이 함수는 크래쉬를 발생시키기 때문에 절대로 리턴하지 않습니다. 그래서 'diverges (다이버지)' 라 부르는 '!' 타입을 가집니다.

diverges()를 호출하는 main 함수를 추가하고 실행하였을 때, 아래와 같은 결과를 보게 되실겁니다:

thread ‘<main>’ panicked at ‘This function never returns!’, hello.rs:2

더 많은 정보를 얻어야 한다면 RUST_BACKTRACE 환경 변수를 설정하여 백스레이스를 볼 수 있습니다.

$ RUST_BACKTRACE=1 ./diverges
thread '<main>' 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 - <unknown>
  13:                0x0 - <unknown>

또한 RUST_BACKTRACE는 Cargo의 run 커맨드와도 작동합니다.

$ RUST_BACKTRACE=1 cargo run
     Running `target/debug/diverges`
thread '<main>' 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 - <unknown>
  13:                0x0 - <unknown>

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

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

함수 포인터 (Function pointers)

함수를 가리키는 변수 바인딩을 만들 수 있습니다.

let f: fn(i32) -> i32;

f는 i32를 인자로 받아서 i32를 반환하는 함수를 가리키는 변수 바인딩입니다. 예:

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;

이제부터 함수를 호출하는데 f를 사용할 수 있습니다.

# fn plus_one(i: i32) -> i32 { i + 1 }
# let f = plus_one;
let six = f(5);
Updated by djKooks almost 3 years (view history)
5.1. 변수 바인딩 (Variable Bindings) - 100% 5.3. 기본형 (Primitive Types) - 100%

Contents

    Rust 문서 한글화 프로젝트 1. 소개(Introduction) - 95% 2. 시작하기(Getting Started) - 100% 2.1. Rust 설치하기 (Installing Rust) - 95% 2.2. 안녕, 세상아! (Hello, world!) - 100% 2.3. 안녕, 카고! (Hello, Cargo!) - 100% 3. Rust 배우기 (Learn Rust) - 100% 3.1. 추리 게임 (Guessing Game) - 100% 3.2. 식사하는 철학자들 (Dining Philosophers) - 100% 3.3. 다른 언어에 Rust 포함하기 (Rust Inside Other Languages) - 100% 4. 효과적인 Rust (Effective Rust) - 100% 4.1. 스택과 힙 (The Stack and the Heap) - 100% 4.2. 테스팅 (Testing) - 95% 4.3. 조건부 컴파일 (Conditional Compilation) - 70% 4.4. 문서화 (Documentation) - 20% 4.5. 반복자 (Iterators) - 100% 4.6. 동시성 (Concurrency) - 90% 4.7. 오류 처리 (Error Handling) - 0% 4.8. Choosing your Guarantees - 0% 4.9. 외부 함수 인터페이스 (Foreign Function Interface) - 50% 4.10. Borrow 와 AsRef - 5% 4.11. 배포 채널 (Release Channels) - 80% 5. 문법과 의미 (Syntax and Semantics) - 100% 5.1. 변수 바인딩 (Variable Bindings) - 100% 5.2. 함수 (Functions) - 100% 5.3. 기본형 (Primitive Types) - 100% 5.4. 주석 (Comments) - 100% 5.5. 조건식 (if) - 100% 5.6. 반복 (Loops) - 100% 5.7. 소유권 (Ownership) - 100% 5.8. 참조와 빌림 (References and Borrowing) - 100% 5.9. 수명 (Lifetimes) - 90% 5.10. 가변성 (Mutability) - 99% 5.11. 구조체 (Structs) - 100% 5.12. 열거형 (Enums) - 100% 5.13. 정합 (Match) - 100% 5.14. 패턴 (Patterns) - 80% 5.15. 메소드 문법 (Method Syntax) - 100% 5.16. 벡터 (Vectors) - 100% 5.18. 문자열(Strings) - 100% 5.19. 제너릭 (Generics) - 100% 5.20. 트레잇 (Traits) - 100% 5.21. 드랍 (Drop) - 100% 5.22. if let - 100% 5.23. 트레잇 객체 (Trait Objects) 5.24. 클로저 (Closures) - 10% 5.25. Universal Function Call Syntax 5.26. Crates and Modules 9. 용어 색인 (Concordance) Show all
    Discussions 5 Pending changes 5 Contributors
    Download Share

    Download

    Working...

    Downloading...

    Downloaded!

    Download more

    Error!

    Your download couldn't be processed. Check for abnormalities and incorrect syntax. We've been notified of the issue.

    Back

    Download PDF Download ePub Download HTML Download Word doc Download text Download source (archive)

    Close
962 Words
6,448 Characters

Share

Collaborators make changes on their own versions, and all changes will be approved before merged into the main version.

Close

Penflip is made by Loren Burton
in Los Angeles, California

Tweet

    About

  • Team
  • Pricing
  • Our Story

    Quick Start

  • Markdown
  • Penflip Basics
  • Working Offline

    Support

  • Help
  • Feedback
  • Terms & Privacy

    Connect

  • Email
  • Twitter