
Rust 프로그래밍의 첫 단추, 함수와 제어 흐름
Rust 코드를 작성하다 보면 갑자기 화면 가득 붉은색 컴파일 에러 메시지가 쏟아지는 경험을 하게 돼요. 분명 논리적으로는 맞다고 생각했는데, 왜 컴파일러는 이 코드를 거부하는 걸까요? 많은 입문자가 함수를 정의할 때 반환 타입을 잘못 지정하거나, 조건문에서 예상치 못한 흐름을 만들어내며 좌절하곤 해요.
이런 문제는 단순한 오타 때문이 아니에요. Rust가 가진 독특한 표현식(Expression) 중심의 철학을 제대로 이해하지 못했기 때문에 발생하는 현상이에요. 함수와 제어 흐름은 단순히 코드를 실행하는 순서를 정하는 것을 넘어, Rust의 안전한 메모리 관리 시스템과 긴밀하게 연결되어 있어요. 이 기초가 흔들리면 나중에 배울 소유권 개념에서 더 큰 벽에 부딪힐 수밖에 없어요.
오늘 우리는 복잡한 이론 대신, 당장 코드를 짜고 실행할 수 있는 실전적인 방법을 살펴볼 거예요. 이 글을 다 읽고 나면 더 이상 함수 정의 앞에서 망설이지 않고, 상황에 맞는 적절한 제어문을 골라 쓸 수 있게 될 거예요.
- 재사용 가능한 코드를 만드는 함수 정의법
- 상황에 따라 논리를 결정하는 조건문과 매치문
- 효율적인 반복을 가능하게 하는 루프 구조
- 초보자가 가장 많이 하는 실수와 해결책
준비 단계: Rust의 핵심 문법 체계 이해하기
본격적으로 코드를 작성하기 전에, Rust가 코드를 바라보는 시각을 먼저 이해해야 해요. 다른 언어와 달리 Rust는 거의 모든 것이 표현식으로 동작해요. 표현식은 값을 계산하여 돌려주는 코드 조각을 의미하고, 문장은 단순히 명령을 수행하는 단위를 말해요. 이 차이를 아는 것이 Rust 함수와 제어 흐름을 마스터하는 첫걸음이에요.
학습 전 필수 체크리스트
먼저 개발 환경이 갖춰져 있는지 확인해봐요. Rust 컴파일러인 rustc와 패키지 관리자인 cargo가 설치되어 있어야 해요. 터미널에서 rustc --version을 입력했을 때 버전 정보가 나온다면 모든 준비가 끝난 셈이에요. 코드 에디터는 VS Code를 추천하며, Rust 전용 확장 기능을 반드시 설치해두는 것이 좋아요.
구조적 차이 비교
제어 흐름을 선택할 때 어떤 도구를 써야 할지 고민된다면 아래 표를 참고해 보세요. 상황에 맞는 도구를 선택하는 것만으로도 코드의 가독성이 크게 달라져요.
| 제어 도구 | 주요 용도 | 추천 상황 |
|---|---|---|
| if-else | 단순 조건 분기 | 참/거짓이 명확할 때 |
| match | 패턴 매칭 분기 | 다양한 값의 경우를 다룰 때 |
| loop | 무한 반복 | 조건이 충족될 때까지 반복할 때 |
| for | 범위 기반 반복 | 컬렉션이나 범위를 순회할 때 |
이 표를 통해 알 수 있듯이, 무조건적인 반복보다는 목적에 맞는 구조를 선택하는 것이 중요해요. 예를 들어, 단순히 숫자를 1부터 10까지 세고 싶다면 for 루프를 쓰는 것이 훨씬 효율적이고 안전해요.
실전 실행: 함수와 제어 흐름 마스터하기
이제 본격적으로 코드를 작성하며 원리를 익혀볼게요. 이론만 보는 것보다 직접 타이핑하며 에러를 마주하는 것이 가장 빠른 학습 방법이에요.
STEP 1. 함수 정의와 반환의 원리
Rust에서 함수는 fn 키워드로 시작해요. 함수의 이름, 매개변수, 그리고 반환 타입을 명확히 적어주는 것이 특징이에요. 여기서 가장 주의해야 할 점은 반환 타입의 명시예요. Rust는 타입 추론 기능이 뛰어나지만, 함수의 반환값은 반드시 명시해줘야 컴파일러가 안심하고 코드를 검사할 수 있어요.
함수 내부에서 값을 돌려줄 때 두 가지 방법이 있어요. 첫째는 return 키워드를 사용하는 것이고, 둘째는 마지막 줄에 세미콜론을 생략하여 표현식으로서 값을 반환하는 것이에요. Rust 개발자들은 보통 마지막 줄에 세미콜론을 붙이지 않는 방식을 선호해요. 이는 코드를 더 간결하게 만들어주기 때문이에요.
함수의 마지막 줄에 세미콜론(;)을 찍으면 그것은 ‘표현식’이 아니라 ‘문장’이 돼요. 즉, 값을 반환하지 않고
()(unit 타입)을 반환하게 되므로 주의가 필요해요.STEP 2. 조건문을 활용한 논리 제어
조건문은 프로그램의 방향을 결정하는 갈림길이에요. Rust의 if는 단순한 명령문이 아니라 값을 가질 수 있는 표현식이에요. 예를 들어, 조건의 결과에 따라 변수에 서로 다른 값을 할당할 수 있어요.
하지만 조건문에서 반드시 지켜야 할 규칙이 있어요. if 절과 else 절의 결과값 타입은 반드시 동일해야 해요. 만약 한쪽은 숫자를 반환하고 다른 쪽은 문자열을 반환한다면, 컴파일러는 어떤 타입을 변수에 담아야 할지 몰라 에러를 발생시켜요. 이는 Rust가 지향하는 강력한 타입 안전성을 보여주는 아주 좋은 예시예요.
STEP 3. 매치(match)를 통한 강력한 패턴 매칭
만약 조건이 3개 이상이라면 어떻게 해야 할까요? if-else if-else를 길게 늘어뜨리는 것보다 match를 사용하는 것이 훨씬 깔끔해요. 매치문은 특정 값의 패턴을 찾아내고, 그에 맞는 코드를 실행하는 아주 강력한 도구예요.
매치문의 가장 큰 장점은 철저함(Exhaustiveness)이에요. Rust 컴파일러는 개발자가 모든 가능한 경우의 수를 다 다루었는지 체크해요. 만약 숫자가 들어올 수 있는데 1, 2, 3만 처리하고 나머지에 대한 처리를 빠뜨렸다면, 컴파일러는 즉시 경고를 보내요. 덕분에 런타임에 예상치 못한 값이 들어와 프로그램이 죽는 사고를 미연에 방지할 수 있어요.
STEP 4. 루프 구조의 종류와 활용
프로그램을 반복시키는 방법은 세 가지가 있어요. 첫째, loop는 아무런 조건 없이 무한히 반복할 때 사용해요. 특정 조건에서 break를 호출하여 빠져나와야 해요. 둘째, while은 조건이 참인 동안만 반복해요. 셋째, for는 정해진 범위나 컬렉션을 순회할 때 가장 많이 쓰여요.
효율적인 루프를 짜기 위해서는 continue와 break의 사용법을 익히는 것이 중요해요. continue는 현재 반복의 나머지 부분을 건너뛰고 다음 반복으로 넘어가게 해주며, break는 반복 자체를 완전히 종료시켜요.
STEP 5. 종합 실전 예제: 점수 기반 등급 판독기
앞서 배운 내용을 모두 합쳐서 작은 프로그램을 만들어볼게요. 입력받은 점수에 따라 등급을 매기는 논리 구조예요.
먼저 함수를 정의해요. fn get_grade(score: i32) -> char와 같이 작성할 수 있어요. 함수 내부에서는 match를 사용하여 점수 범위를 나누는 것이 효율적이에요. 예를 들어, 90점 이상은 ‘A’, 80점 이상은 ‘B’ 식으로 패턴을 정의하는 거죠. 이때 0점 미만이나 100점 초과의 범위를 처리하기 위해 _(와일드카드) 패턴을 사용하는 것이 매치문의 정석이에요.
이 시나리오를 통해 우리는 함수 정의, 조건에 따른 값 반환, 그리고 패턴 매칭을 통한 복합적인 제어 흐름을 한 번에 학습할 수 있어요. 직접 이 로직을 구현해보면서 각 문법이 어떻게 맞물려 돌아가는지 체감해보시길 바라요.
자주 하는 실수와 해결법 및 FAQ
실습 과정에서 마주칠 수 있는 당황스러운 상황들을 정리했어요. 에러 메시지를 읽기 전에 이 리스트를 먼저 확인하면 해결 시간을 크게 줄일 수 있어요.
자주 하는 실수와 해결법
❌ 함수 마지막에 세미콜론을 붙여 반환값이 없는 경우
왜 발생하나요? Rust에서 표현식 뒤에 세미콜론을 붙이면 그것은 값을 반환하지 않는 문장이 되어버려요. 그래서 컴파일러는 반환 타입이 맞지 않는다고 에러를 내뱉어요.
✅ 마지막 줄의 세미콜론을 제거하거나 return 키워드를 사용하세요.
❌ if-else의 반환 타입이 서로 다른 경우
왜 발생하나요? Rust는 변수의 타입을 컴파일 시점에 확정해야 하는데, 조건에 따라 타입이 바뀌면 이를 결정할 수 없기 때문이에요.
✅ 모든 분기에서 동일한 타입을 반환하도록 코드를 수정하세요.
❌ match 문에서 모든 경우의 수를 다루지 않은 경우
왜 발생하나요? Rust의 안전성 원칙 때문이에요. 누락된 패턴이 있으면 런타임에 프로그램이 멈출 수 있어요.
✅ _ 패턴을 사용하여 나머지 모든 경우를 처리하세요.
❌ for 루프에서 범위를 잘못 지정한 경우
왜 발생하나요? 예를 들어 1부터 5까지 돌리고 싶은데 1..5라고 쓰면 5가 제외된 1, 2, 3, 4만 실행돼요.
✅ 5를 포함하고 싶다면 1..=5와 같이 사용하세요.
❌ 함수 매개변수에 mut를 붙이지 않아 값을 변경하지 못하는 경우
왜 발생하나요? Rust의 모든 변수는 기본적으로 불변(immutable)이에요. 함수 내부에서 매개변수 값을 바꿔야 한다면 선언 시 명시가 필요해요.
✅ 매개변수 이름 앞에 mut 키워드를 추가하세요.
자주 묻는 질문
Q. 함수에서 여러 개의 값을 한 번에 반환할 수 있나요?
네, 가능해요! 튜플(Tuple)을 사용하면 여러 데이터를 하나의 덩어리로 묶어서 반환할 수 있어요. 예를 들어 fn get_user() -> (String, i32)와 같이 작성하면 이름과 나이를 동시에 돌려줄 수 있어요.
Q. if 문과 match 문 중 무엇을 더 많이 쓰나요?
상황에 따라 다르지만, 조건이 단순한 참/거짓일 때는 if가 훨씬 직관적이에요. 반면, 비교해야 할 대상이 많거나 패턴을 분석해야 한다면 match를 쓰는 것이 코드의 품질과 안전성 측면에서 압도적으로 좋아요.
Q. loop 문을 사용하는 특별한 이유가 있나요?
단순히 조건이 참일 때까지 반복하는 것은 while로 충분해요. 하지만 특정 조건이 만족될 때까지 무조건 반복해야 하거나, 반복 내부에서 복잡한 로직을 거친 뒤 break로 탈출해야 하는 명확한 의도가 있다면 loop를 쓰는 것이 코드를 읽는 사람에게 의도를 더 잘 전달해요.
Q. match 문에서 숫자의 범위를 지정할 수도 있나요?
네, 아주 쉬워요! match score { 90..=100 => "A", 80..=89 => "B", _ => "F" }와 같이 범위 패턴을 사용하면 아주 간결하게 점수별 처리가 가능해요.
핵심 요약과 다음 여정
오늘 우리는 Rust 프로그래밍의 뼈대가 되는 함수와 제어 흐름에 대해 깊이 있게 살펴봤어요. 처음에는 컴파일러의 엄격한 규칙이 까다롭게 느껴질 수 있지만, 이는 결국 여러분의 코드를 더 안전하게 만들어주는 든든한 보호막이 되어줄 거예요.
- 함수는
fn으로 정의하며, 반환 타입은 반드시 명시해야 해요. - 함수 마지막 줄에 세미콜론을 빼면 값이 반환되는 ‘표현식’이 돼요.
if문은 값을 가질 수 있는 표현식으로 활용이 가능해요.match는 모든 경우의 수를 다루어야 하는 강력하고 안전한 도구예요.- 반복문은 목적에 따라
loop,while,for를 적절히 선택해야 해요.
이제 기초를 뗐으니 다음 단계로 나아갈 차례예요. 오늘 배운 내용을 바탕으로 직접 작은 계산기나 숫자 맞추기 게임을 만들어보며 손에 익히는 시간을 가져보세요. 작은 성공이 쌓여야 진짜 실력이 된답니다.
앞으로의 학습 계획
- 오늘 할 일: 오늘 배운 예제 코드를 직접 타이핑하고, 일부러 에러를 내보며 컴파일러 메시지 확인하기
- 이번 주 할 일: 함수와 제어 흐름만 사용하여 간단한 CLI 프로그램 하나 완성해보기
- 실행 직전 할 일: Rust의 다음 핵심 개념인 소유권(Ownership) 개념 미리 읽어보기
지금 바로 코드를 작성하며 Rust의 매력을 직접 느껴보세요! 여러분의 개발 여정을 응원할게요.
함께 읽으면 좋은 글: 입문자를 위한 Rust 학습 가이드, Rust의 소유권과 빌림 개념 완벽 정리