제어 흐름문
if 표현식과 loop·while·for 반복문을 사용하고 루프 라벨과 break 반환값으로 중첩 흐름을 제어합니다.
조건이 참인지에 따라 코드를 실행하거나, 조건이 맞는 동안 코드를 되풀이하는 기능은 대부분의 프로그래밍 언어에 있는 기본 도구입니다. 러스트에서 실행 흐름을 정하는 가장 흔한 도구는 if 표현식과 반복문입니다. 이 글에서는 if로 실행할 코드를 고르는 방법과, loop, while, for 세 가지 반복문의 쓰임을 살펴봅니다.
if 표현식
if 표현식은 조건에 따라 코드를 나누어 실행하게 합니다. 조건을 주고, 그 조건이 맞으면 이 코드 블록을 실행하고 맞지 않으면 실행하지 말라고 적는 것입니다. cargo new branches로 새 프로젝트를 만들고 다음 코드를 넣습니다.
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}condition was trueif 표현식은 if 키워드로 시작하고 그 뒤에 조건이 옵니다. 여기서는 number가 5보다 작은지가 조건이며, 조건이 참일 때 실행할 코드는 조건 바로 뒤의 중괄호 블록에 둡니다. 이 블록을 추리 게임의 match에서처럼 갈래(arm)라고 부르기도 합니다. else를 붙이면 조건이 거짓일 때 실행할 블록을 줄 수 있고, else가 없는데 조건이 거짓이면 if 블록을 건너뛰고 다음 코드로 넘어갑니다. number를 7로 바꾸면 조건이 거짓이 되어 condition was false가 출력됩니다.
if의 조건은 반드시 bool이어야 합니다. 다음 코드는 컴파일되지 않습니다.
fn main() {
let number = 3;
if number {
println!("number was three");
}
}error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if number {
| ^^^^^^ expected `bool`, found integerbool을 기대했는데 정수가 왔다는 오류입니다. Ruby나 JavaScript와 달리 러스트는 불리언이 아닌 값을 불리언으로 저절로 바꾸지 않으므로, if의 조건에는 언제나 bool인 식을 써야 합니다. 숫자가 0이 아닐 때만 실행하고 싶다면 그 뜻을 그대로 조건으로 적습니다.
fn main() {
let number = 3;
if number != 0 {
println!("number was something other than zero");
}
}number was something other than zeroelse if로 여러 조건식 다루기
if와 else 사이에 else if를 넣어 여러 조건을 차례로 검사할 수 있습니다. 아래 코드는 6이 4, 3, 2로 나누어떨어지는지를 이 순서로 검사합니다. 6은 3으로도 2로도 나누어떨어진다는 점을 염두에 두고 출력을 보세요.
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}number is divisible by 3이 프로그램에는 실행될 수 있는 길이 넷 있습니다. 러스트는 조건을 위에서부터 차례로 검사해 처음으로 참인 조건의 블록만 실행하고, 나머지 조건은 검사하지 않습니다. 6은 2로도 나누어떨어지지만 3으로 나누어떨어지는 조건이 먼저 참이 되므로 number is divisible by 2는 출력되지 않습니다. else if가 많아져 조건 사이의 우선순위가 헷갈리거나 경우를 빠뜨리기 쉽다면, match 제어 흐름 구조에서 볼 match로 바꾸는 것을 생각해 봅니다.
let 구문에서 if 사용하기
if는 표현식이므로 let 구문의 오른쪽에 써서 그 결과를 변수에 넣을 수 있습니다. 아래 코드의 if condition { 5 } else { 6 }은 두 블록 가운데 하나의 값이 되는 하나의 식이고, 그 값이 number에 묶입니다. 블록 안의 5와 6에 세미콜론이 없다는 점도 함께 보세요.
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {number}");
}The value of number is: 5함수에서 보았듯이 블록의 값은 마지막 표현식의 값이고, 숫자 하나도 표현식입니다. 그래서 if 표현식 전체의 값은 실제로 실행된 블록의 값이 되며, 여기서는 조건이 참이므로 5입니다. 그런데 어느 블록이 실행될지는 실행해야 알 수 있으므로, 모든 갈래의 값은 같은 타입이어야 합니다. 갈래의 타입이 다르면 컴파일되지 않습니다.
fn main() {
let condition = true;
let number = if condition { 5 } else { "six" };
println!("The value of number is: {number}");
}error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:4:44
|
4 | let number = if condition { 5 } else { "six" };
| - ^^^^^ expected integer, found `&str`
| |
| expected because of thisif 갈래는 정수를, else 갈래는 문자열을 돌려주므로 두 타입이 맞지 않는다는 오류입니다. 변수의 타입은 하나여야 하고, 러스트는 number를 쓰는 모든 곳에서 그 타입이 맞는지 컴파일할 때 확인하기 위해 number의 타입을 미리 알아야 합니다. 실행할 때 타입이 정해지도록 허용하면 컴파일러가 변수마다 가능한 여러 타입을 추적해야 하므로 더 복잡해지고, 코드에 대해 보장할 수 있는 것도 줄어듭니다.
아래 표는 if를 값으로 쓸 때 확인할 점을 정리한 것입니다.
RUST · IF EXPRESSION
실행은 한 갈래를 고르고 타입 검사는 모든 갈래를 본다
조건은 실행할 값을 고르지만 바인딩의 타입을 바꾸지는 않습니다. 꼬리값, 공통 타입과 else 생략 시 () 계약을 함께 확인합니다.
| 검사 지점 | 언어 계약 | 코드에서 확인할 것 | 대표 결과 |
|---|---|---|---|
| 조건 | 이 절의 일반 조건식은 반드시 bool이다. 조건부 let 매치는 별도 조건 형식이다. |
if number != 0처럼 의도를 명시한다. 일반 정수식을 참·거짓으로 바꾸지 않는다. |
true인 첫 갈래만 실행한다. |
| 선택된 블록 | 전체 if의 값은 실제로 실행된 블록의 값이다. |
블록의 꼬리 표현식은 세미콜론 없이 값을 내보낸다. 세미콜론으로 끝나면 보통 ()가 된다. |
{ 5 } → 5; { 5; } → () |
| 모든 갈래 | 각 갈래의 결과는 강제 변환을 포함해 하나의 공통 타입으로 결정되어야 한다. | 소스 타입이 글자 그대로 같지 않아도 허용된 coercion으로 공통 타입을 만들 수 있다. | &String과 &str → 공통 &str 가능; i32와 &str → 호환 불가 |
else 생략 |
아무 블록도 실행되지 않는 경로의 값은 ()이므로 전체 결과도 ()여야 한다. |
효과만 수행하는 if에는 자연스럽지만, 임의의 값을 만들려면 호환되는 else가 필요하다. |
if ready { notify(); } → () |
let 우변 |
선택된 값 하나가 추론된 공통 타입의 바인딩을 만든다. | 런타임 조건은 어떤 값을 고를지만 결정하고 변수의 타입을 바꾸지 않는다. | let n = if ok { 5 } else { 6 }; → n: i32 |
- 1. 조건은
bool - 계약 이 절의 일반 조건식은 반드시
bool입니다. 조건부let매치는 별도 형식이며, 일반 정수식을 참·거짓으로 바꾸지 않습니다. - 확인
if number != 0처럼 의도를 명시하고, 첫 번째로 참인 갈래만 실행합니다. - 2. 선택된 블록이 값을 만든다
- 계약 전체
if는 실제로 실행된 블록의 값으로 평가됩니다. - 확인
{ 5 }의 꼬리 표현식은5이고,{ 5; }처럼 세미콜론으로 끝나면 보통()입니다. - 3. 모든 갈래는 공통 타입
- 계약 갈래 결과는 허용된 강제 변환까지 적용해 하나의 공통 타입으로 결정되어야 합니다.
- 예
&String과&str은 공통&str이 될 수 있지만,i32와&str은 호환되지 않습니다. - 4.
else가 없으면() - 계약 아무 블록도 실행되지 않는 경로가
()이므로 전체 결과도()여야 합니다. - 선택 효과만 수행할 때는 자연스럽지만 임의의 값을 만들려면 호환되는
else가 필요합니다. - 5.
let에는 타입 하나 - 계약 선택된 값이 추론된 공통 타입의 바인딩을 만듭니다.
- 예
let n = if ok { 5 } else { 6 };에서 런타임 조건은 값을 고를 뿐n: i32를 바꾸지 않습니다.
조건은 bool이어야 하고, 참인 첫 갈래 하나만 실행됩니다. { 5 }처럼 세미콜론 없이 끝나는 블록은 그 값을 내보내지만 { 5; }처럼 세미콜론으로 끝나면 ()이 됩니다. 모든 갈래의 값은 하나의 공통 타입이어야 하며, else가 없는 if의 값은 ()이므로 그런 if를 정수 변수에 넣을 수는 없습니다.
반복문을 이용한 반복
코드 블록을 여러 번 실행해야 할 때가 많습니다. 러스트의 반복문(loop)은 본문을 끝까지 실행한 뒤 다시 처음부터 실행하며, 반복문의 종류와 조건에 따라 본문이 한 번도 실행되지 않을 수도 있습니다. cargo new loops로 새 프로젝트를 만들어 세 가지 반복문 loop, while, for를 차례로 써 봅니다.
loop로 코드 반복하기
loop는 멈추라고 할 때까지 본문을 끝없이 되풀이합니다. 조건을 검사하는 자리가 따로 없으므로, 아래 코드처럼 본문에 반복을 끝내는 코드가 없으면 프로그램은 스스로 끝나지 않습니다.
fn main() {
loop {
println!("again!");
}
}이 프로그램은 직접 멈출 때까지 again!을 계속 출력합니다. 대부분의 터미널에서는 Ctrl+C를 눌러 끝없이 도는 프로그램을 멈출 수 있습니다.
again!
again!
again!
again!
^Cagain!^C는 Ctrl+C를 누른 곳을 나타내며, 프로그램이 멈추라는 신호를 받은 시점에 따라 그 뒤에 again!이 한 번 더 나올 수도 있습니다. 코드로 반복을 끝내려면 본문에서 break를 씁니다. 정답을 맞힌 후 종료하기에서 정답을 맞힌 플레이어의 게임을 끝낼 때 쓴 것이 이 break입니다. 추리 게임에서 쓴 continue는 이번 회차의 남은 코드를 건너뛰고 다음 회차로 넘어갑니다.
반복문에서 값 반환하기
loop는 스레드의 작업이 끝났는지 확인하는 것처럼 실패할 수 있는 일을 다시 시도할 때 쓰이기도 합니다. 그 결과를 반복문 밖으로 넘기고 싶다면, 반복을 끝내는 break 뒤에 돌려줄 값을 적습니다.
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {result}");
}The result is 20반복할 때마다 counter에 1을 더하고, 10이 되면 break counter * 2로 20을 돌려주며 반복을 끝냅니다. 그 값이 result에 묶이며, let 구문을 끝내려고 loop 블록 뒤에 세미콜론을 붙였습니다. break 대신 return을 쓰면 반복문이 아니라 함수 전체에서 빠져나갑니다.
루프 라벨로 여러 반복문 사이의 모호함 없애기
반복문 안에 반복문이 있으면, break와 continue는 가장 안쪽의 반복문에 적용됩니다. 반복문에 루프 라벨(loop label)을 붙이면 break나 continue가 어느 반복문에 적용될지 직접 정할 수 있으며, 루프 라벨은 작은따옴표로 시작합니다.
fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
}count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2바깥 반복문에는 'counting_up이라는 라벨이 붙어 있고 count를 0부터 세며, 라벨이 없는 안쪽 반복문은 remaining을 10에서 9로 셉니다. 라벨 없는 첫 break는 안쪽 반복문만 끝내고, break 'counting_up;은 바깥 반복문까지 끝냅니다. 그래서 count가 0과 1일 때는 remaining이 9가 되어 안쪽만 끝나고, count가 2가 되자 remaining이 10인 첫 회차에서 바깥 반복문이 끝납니다.
더 깊이: 왜 loop만 값을 돌려줄 수 있을까
while과 for는 조건이 거짓이 되거나 원소가 바닥나면 break 없이도 끝나므로, 그때 돌려줄 값이 없습니다. 그래서 Reference는 break로 값을 돌려주는 것을 loop와 라벨 붙은 블록에만 허용하며, while과 for의 값은 언제나 유닛 ()입니다. 반대로 break가 하나도 없는 loop는 끝나지 않는 식이어서 값이 없는 타입 !를 가집니다. !는 어떤 타입으로든 바뀔 수 있으므로, 끝나지 않는 loop는 반환 타입이 정해진 함수의 마지막 식으로도 쓸 수 있습니다. 이 타입은 고급 타입에서 다룹니다.
Rust 1.65부터는 반복하지 않는 블록에도 라벨을 붙여 break로 일찍 빠져나올 수 있습니다. 라벨 붙은 블록(labeled block)은 'block: { ... }처럼 쓰며, 안에서 break 'block 1;을 만나면 블록이 그 값으로 끝나고, 끝까지 실행되면 마지막 식이 블록의 값이 됩니다. 반복문과 달리 라벨 붙은 블록을 끝내는 break에는 라벨을 반드시 적어야 하며, 블록 자체도 라벨로 시작해야 합니다. 여러 조건을 차례로 검사하다가 중간에 결과를 정해 빠져나와야 할 때, 함수를 따로 만들거나 loop를 억지로 쓰지 않고 이 문법을 쓸 수 있습니다.
while을 이용한 조건 반복문
조건이 참인 동안 반복하다가 거짓이 되면 멈추는 반복도 흔합니다. 이것은 loop와 if, else, break를 조합해 만들 수도 있지만, 워낙 자주 쓰이므로 러스트에는 while 반복문이 따로 있습니다.
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}3!
2!
1!
LIFTOFF!!!조건 number != 0이 참인 동안 숫자를 출력하고 1씩 줄이며, 0이 되어 조건이 거짓이 되면 반복을 끝내고 LIFTOFF!!!를 출력합니다. loop와 if, break로 같은 일을 할 때 생기는 중첩이 없어 코드가 더 간단합니다. 조건 자리에 패턴을 쓰는 while let도 있는데, 식을 다시 계산할 때마다 패턴이 맞으면 본문을 실행하고 맞지 않으면 반복을 끝내며, 패턴이 사용될 수 있는 모든 곳에서 다룹니다.
for를 이용한 컬렉션에 대한 반복문
배열 같은 컬렉션의 원소를 하나씩 다룰 때도 while을 쓸 수 있습니다. for를 보기 전에, 먼저 같은 일을 while과 인덱스로 하는 코드를 보고 이 방식의 약점을 확인합니다. 아래 코드에서는 조건의 5가 배열의 길이와 맞아야 한다는 점을 눈여겨보세요.
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}
}the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50인덱스 0부터 시작해 index < 5가 거짓이 될 때까지 원소를 출력합니다. index가 5가 되면 여섯 번째 원소를 읽기 전에 반복이 끝나므로 다섯 값이 모두 출력됩니다. 하지만 이 방식은 실수하기 쉽습니다. 배열의 원소를 네 개로 줄이면서 조건을 index < 4로 고치는 것을 잊으면, 범위를 벗어난 인덱스로 패닉이 납니다. 또 반복할 때마다 인덱스가 배열의 범위 안에 있는지 검사하는 코드가 들어가므로 더 느려질 수도 있습니다.
for 반복문을 쓰면 컬렉션의 원소마다 코드를 실행하는 일을 더 간단하고 안전하게 할 수 있습니다.
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {element}");
}
}출력은 while을 쓴 코드와 같지만, 인덱스를 직접 다루지 않으므로 배열의 끝을 넘거나 원소를 빠뜨리는 오류가 생기지 않습니다. 배열의 원소 개수를 바꿔도 함께 고칠 코드가 없습니다. 이런 안전함과 간결함 때문에 for는 러스트에서 가장 많이 쓰이는 반복문입니다. for가 원소를 꺼내는 방식과 그때의 소유권은 반복자 기반 데이터 처리에서 다룹니다.
정해진 횟수만큼 반복할 때도 대부분 for를 씁니다. 표준 라이브러리의 범위(range)를 쓰면 되는데, start..end는 start를 포함하고 end는 포함하지 않으며, start..=end는 양 끝을 모두 포함합니다. 앞의 카운트다운은 범위와, 반복 순서를 뒤집는 rev 메서드로 다음과 같이 쓸 수 있습니다.
fn main() {
for number in (1..4).rev() {
println!("{number}!");
}
println!("LIFTOFF!!!");
}1..4는 1, 2, 3이고 rev가 순서를 뒤집으므로, while을 쓴 코드와 같이 3!, 2!, 1!, LIFTOFF!!!가 출력됩니다. 아래 표는 if와 세 반복문을 언제 쓰는지 정리한 것입니다.
RUST · CONTROL FLOW
반복 대상과 종료 조건으로 제어 흐름을 고른다
경로 선택은 if, 대상 집합은 for, 상태 조건은 while입니다. 패턴 성공은 while let, 본문이 종료 시점과 결과값을 정하면 loop를 검토합니다.
if· 경로 하나 선택- 진입 일반
bool식이나 조건부let매치를 순서대로 평가해 첫 번째로 맞는 갈래 하나를 실행합니다. - 종료·값 선택된 블록의 값을 내보내며 모든 경로는 강제 변환을 포함해 공통 타입이어야 합니다.
- 데이터·소유권 반복자를 만들지 않으며 각 갈래의 이동과 빌림 규칙이 그대로 적용됩니다.
- 선택 한 시점에 실행할 경로 하나를 고를 때 사용합니다.
loop· 본문이 종료 결정- 진입 사전 조건 없이 본문에 들어가 계속 반복합니다.
- 종료·값 연결된
break로 끝냅니다. 일반 반복문 중break expr로 유닛이 아닌 값을 내보낼 수 있는 형식입니다. - 데이터·소유권 대상 집합을 자동으로 소비하지 않으며 본문이 상태와 자원을 직접 다룹니다.
- 선택 재시도처럼 종료 시점이 본문 안에 있거나 결과를 반환할 때 사용합니다.
while condition· 상태 조건- 진입 매 회차 전에
bool조건을 검사하므로 0회 실행할 수 있습니다. - 종료·값 거짓이면 본문을 실행하지 않고 다음 문장으로 이어지며 결과는
()입니다. - 데이터·소유권 조건과 본문이 갱신하는 상태를 직접 관리합니다.
- 선택 남은 시간이나 큐 상태처럼 불리언 조건이 반복 여부를 정할 때 사용합니다.
while let PAT = expr· 패턴 조건- 진입 식을 다시 평가하고 패턴이 맞을 때만 새 바인딩으로 본문을 실행합니다.
- 종료·값 패턴이 맞지 않으면 다음 문장으로 이어지며 결과는
()입니다. - 데이터·소유권 식의 이동·빌림이 매 검사에 적용되고 새 바인딩은 조건 뒤 본문 범위에서 사용합니다.
- 선택
pop()의Some처럼 같은 패턴이 성공하는 동안 처리할 때 사용합니다. for PAT in expr· 대상 집합- 진입
IntoIterator::into_iter(expr)가 만든 반복자에서 요소가 나올 때마다 패턴에 바인딩합니다. - 종료·값 반복자가 비면 끝나며 결과는
()입니다. - 데이터·소유권
items는 소비할 수 있고&items는 공유 빌림,&mut items는 가변 빌림입니다. - 선택 컬렉션·반복자·범위처럼 대상 집합이 있을 때 사용합니다. 정수에서
a..b는b를 제외하고a..=b는 포함합니다.
break와 continue는 가장 안쪽 반복문을 대상으로 합니다. 'outer: 같은 라벨은 중첩된 loop·while·while let·for에서 목표를 명시합니다.실행할 길 하나를 고를 때는 if, 컬렉션이나 범위의 원소를 차례로 다룰 때는 for, 어떤 상태가 유지되는 동안 반복할 때는 while, 패턴이 맞는 동안 반복할 때는 while let을 씁니다. 언제 끝낼지와 무엇을 돌려줄지를 본문에서 정해야 한다면 loop와 break를 생각해 봅니다.
정리
if의 조건은 반드시bool이어야 하며,else if가 여럿이면 처음으로 참인 조건의 블록만 실행됩니다.if는 표현식이므로let의 오른쪽에 쓸 수 있고, 이때 모든 갈래의 값은 같은 타입이어야 합니다.loop는break로 끝낼 때까지 반복하며,break뒤에 적은 값을 반복문의 결과로 돌려줄 수 있습니다.- 중첩된 반복문에서
break와continue는 가장 안쪽 반복문에 적용되며, 루프 라벨로 적용할 반복문을 정할 수 있습니다. while은 조건이 참인 동안 반복합니다.for는 컬렉션이나 범위의 원소를 차례로 다루며, 인덱스를 직접 쓰는 것보다 안전해서 가장 많이 쓰입니다.
연습 문제
1. 화씨를 섭씨로
화씨 온도 F를 섭씨로 바꾸는 식은 C = (F - 32.0) * 5.0 / 9.0입니다. 이 식으로 f64를 받아 f64를 돌려주는 함수 to_celsius를 만들고, 32, 212, -40에 대해 출력해 보세요. 결과는 무엇인가요?
해설 보기
fn to_celsius(fahrenheit: f64) -> f64 {
(fahrenheit - 32.0) * 5.0 / 9.0
}
fn main() {
for fahrenheit in [32.0, 212.0, -40.0] {
println!("{}", to_celsius(fahrenheit));
}
}0
100
-4032°F는 (32 - 32) × 5 / 9 = 0°C, 212°F는 180 × 5 / 9 = 100°C, -40°F는 -72 × 5 / 9 = -40°C입니다. 매개변수와 반환 값이 모두 f64이므로 리터럴도 32.0처럼 소수점을 붙여 부동 소수점 연산으로 계산하며, {}로 출력한 f64는 소수 부분이 0이면 0, 100처럼 정수 모양으로 나옵니다.
2. n번째 피보나치 수
피보나치 수는 F0 = 0, F1 = 1이고, 그 뒤로는 앞의 두 수를 더한 값입니다. for와 변수 두 개로 n번째 피보나치 수를 구하는 프로그램을 쓰고, n이 5일 때 무엇이 출력되는지 확인해 보세요.
해설 보기
fn main() {
let n = 5_u32;
let mut previous = 0_u32;
let mut current = 1_u32;
for _ in 0..n {
let next = previous + current;
previous = current;
current = next;
}
println!("{previous}");
}5previous와 current는 연속한 두 피보나치 수를 담으며, (0, 1)에서 시작해 반복할 때마다 (1, 1), (1, 2), (2, 3), (3, 5), (5, 8)로 한 칸씩 나아갑니다. 0..n은 n번 반복하므로 다섯 번 뒤의 previous인 5가 F5입니다. n이 0이면 한 번도 반복하지 않아 0을, 1이면 한 번 반복해 1을 출력합니다. 반복 변수 i를 쓰지 않으므로 그 자리에 _를 썼습니다.
3. 어느 반복문이 알맞을까
(가) 배열에 든 점수를 모두 더하기, (나) 사용자가 quit을 입력할 때까지 명령을 계속 받기, (다) 1부터 시작해 제곱이 1000을 넘는 첫 정수를 찾아 그 값을 변수에 넣기에는 각각 어떤 반복문이 알맞을까요?
해설 보기
(가)는 컬렉션의 모든 원소를 차례로 다루므로 for가 알맞습니다. (나)는 언제 끝날지 미리 알 수 없고 입력을 받은 뒤에야 끝낼지 정하므로, loop 안에서 입력을 받고 quit이면 break하는 방식이 자연스럽습니다. (다)는 찾은 값을 반복문 밖으로 넘겨야 하므로 let n = loop { ... break i; };처럼 break로 값을 돌려주는 loop가 알맞으며, 답은 32입니다(31의 제곱은 961, 32의 제곱은 1024).
이것으로 2장을 마칩니다. 변수, 스칼라 타입과 복합 타입, 함수, 주석, if와 반복문을 익혔으며, 다음 장의 소유권이 뭔가요?에서는 다른 언어에는 흔하지 않은 러스트의 핵심 개념인 소유권을 다룹니다.