트랙
/
Rust
Rust
/
연습 문제
/
루시안의 맛있는 라자냐
루시안의 맛있는 라자냐

루시안의 맛있는 라자냐

학습 연습 문제

소개

Rust에서는 값에 이름을 붙이는 것을 _바인딩_이라고 해요. 바인딩은 mut 키워드로 선언하지 않는 한 불변이에요. Rust는 정적 타입 언어라서 각 바인딩은 컴파일 시점에 알 수 있는 타입을 가져요.

바인딩은 보통 let 키워드로 정의해요. 대부분의 바인딩에서는 타입을 명시하는 것이 선택 사항이에요. Rust의 _타입 추론_이 보통 값에 따라 타입을 알아낼 수 있기 때문이죠. 바인딩은 이렇게 생겼어요:

// Automatically inferred type
let fingers = 10;

함수는 _아이템_이에요. 바인딩이 보통 특정 값을 가리키는 반면, 아이템은 코드를 구성하는 단위를 가리켜요. 보통 함수나 모듈이고, 프로그램이 살아 있는 동안 내내 사용할 수 있어요. 함수는 마지막 표현식의 결과를 자동으로 반환해요. 함수는 매개변수를 0개 이상 가질 수 있는데, 매개변수는 함수 호출이 지속되는 동안만 살아 있는 바인딩이에요.

함수에 대해서도 이론적으로는 타입 추론이 가능하지만, 언어 설계상 의도적으로 막아 두었어요. 그래서 코드를 작성할 때는 함수의 입력과 출력 타입이 무엇인지 정확히 명시하느라 시간이 조금 더 들지만, 코드를 읽을 때는 모든 입력과 출력 타입이 명시적으로 드러나 있어서 시간을 아낄 수 있어요.

fn add(x: i32, y: i32) -> i32 {
  x + y
}

함수를 호출할 때는 함수 이름을 쓰고 뒤에 괄호를 붙여요. 함수에 매개변수가 필요하면 괄호 안에 각 매개변수에 대응하는 인자를 지정해야 해요.

let five = add(2, 3);

바인딩의 타입을 추론할 수 없으면 컴파일러가 오류를 알려줘요. 이럴 때는 바인딩에 타입을 명시적으로 적어 주면 돼요.

// Explicit type annotation
let fingers: i32 = 10;

Rust의 아이템은 정의되기 전에도, 후에도 사용할 수 있어요. 정적인 수명을 가지기 때문이에요. 반면 바인딩은 정의된 후에만 사용할 수 있어요. 바인딩을 정의하기 전에 사용하면 컴파일 오류가 발생해요.

fn main() {
    // `fn add` hasn't yet been defined, but that's perfectly ok
    dbg!(add(3, 4));
}

fn add(x: i32, y: i32) -> i32 {
  x + y
}
// this won't compile; `a` is used before its binding is defined
let b = a;
let a = x + y;

Rust는 중괄호({})로 스코프를 정의해요. 스코프 안에서 정의한 바인딩은 그 스코프를 벗어날 수 없어요.

let a = 1;
dbg!(a); // 1
{
    // Here, we re-bind `a` to a new value, which is still immutable.
    // This technique is called _shadowing_. The new binding is constrained to
    // this anonymous scope. Outside this scope, the previous binding still
    // applies.
    let a = 2;
    let b = 3;
    dbg!(a, b); // 2, 3
}
// can't use `b` anymore because it is out of scope
// dbg!(b);

// The shadowed `a` in the inner scope above has fallen out of scope,
// leaving us with our original binding.
dbg!(a); // 1

Rust의 아이템은 흔히 모듈로 정리해요. 각 크레이트는 암묵적으로 하나의 모듈이지만, 그 안에 원하는 깊이만큼 하위 모듈을 정의할 수 있어요. 모듈은 관련 있는 기능을 묶어 주고, mod 키워드로 정의해요.

mod calc_i32 {
    fn add(a: i32, b: i32) -> i32 { a + b }
    fn sub(a: i32, b: i32) -> i32 { a - b }
    fn mul(a: i32, b: i32) -> i32 { a * b }
    fn div(a: i32, b: i32) -> i32 { a / b }
}

Rust는 두 가지 종류의 주석을 지원해요. // 키워드는 한 줄 주석을 나타내는데, 이 키워드 뒤부터 줄 끝까지는 모두 무시돼요. /*와 */ 키워드는 여러 줄 주석을 나타내는데, 두 키워드 사이에 있는 내용은 모두 무시돼요. 한 줄 주석을 쓰는 것이 관례이자 좋은 습관이에요.

Rust는 문서 주석도 지원하는데, cargo doc으로 생성한 문서에 나타나요. 바깥 문서 주석은 /// 키워드로 만들고, // 키워드와 똑같이 동작해요. 뒤따르는 아이템, 예를 들어 함수에 적용돼요:

/// The `add` function produces the sum of its arguments.
fn add(x: i32, y: i32) -> i32 { x + y }

안쪽 문서 주석은 //! 키워드로 만들고, // 키워드와 똑같이 동작해요. 자신을 감싸는 아이템, 예를 들어 모듈에 적용돼요:

mod my_cool_module {
    //! This module is the bee's knees.
}

문서 주석은 길이에 제한이 없고 마크다운을 담을 수 있으며, 생성된 문서에 렌더링돼요.

지침

이 연습 문제에서는 좋아하는 요리책에 나오는 근사한 라자냐를 만드는 데 도움이 될 코드를 작성해요.

라자냐를 조리하는 데 걸리는 시간과 관련된 네 가지 과제가 있어요.

1. 오븐 예상 시간을 분 단위로 정의하기

expected_minutes_in_oven 바인딩을 정의해서 라자냐를 오븐에 몇 분 동안 넣어 둬야 하는지 확인해요. 요리책에 따르면 오븐 예상 시간은 40분이에요:

expected_minutes_in_oven()
// Returns: 40

2. 오븐에 남은 시간을 분 단위로 계산하기

remaining_minutes_in_oven 함수를 정의해요. 이 함수는 라자냐가 오븐에 있었던 실제 시간(분)을 매개변수로 받아, 앞 과제에서 구한 오븐 예상 시간을 기준으로 라자냐가 오븐에 얼마나 더 있어야 하는지를 분 단위로 반환해요.

remaining_minutes_in_oven(30)
// Returns: 10

3. 준비 시간을 분 단위로 계산하기

preparation_time_in_minutes 함수를 정의해요. 이 함수는 라자냐에 추가한 층의 개수를 매개변수로 받아, 각 층을 준비하는 데 2분이 걸린다고 가정했을 때 라자냐를 준비하는 데 몇 분이 걸렸는지 반환해요.

preparation_time_in_minutes(2)
// Returns: 4

4. 경과 시간을 분 단위로 계산하기

elapsed_time_in_minutes 함수를 정의해요. 이 함수는 두 개의 매개변수를 받아요. 첫 번째 매개변수는 라자냐에 추가한 층의 개수이고, 두 번째 매개변수는 라자냐가 오븐에 있었던 시간(분)이에요. 이 함수는 라자냐를 요리하는 데 들인 시간, 즉 준비 시간(분)과 현재까지 라자냐가 오븐에 있던 시간(분)을 더한 값을 반환해야 해요.

elapsed_time_in_minutes(3, 20)
// Returns: 26
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Rust Exercism

루시안의 맛있는 라자냐 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Rust 트랙을 연습 문제 99개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.