5.6. 반복 (Loops) - 100%
러스트는 몇 가지 반복적인 활동을 수행하는데 있어 세 가지 접근을 제공합니다: loop
, while
, 그리고 for
. 각각의 접근은 본인만의 사용법이 있습니다.
loop
infinite loop
은 러스트에서 사용할 수 있는 가장 단순한 형태의 루프입니다. loop
키워드를 써서 몇 가지 종료 구문에 다다를 때까지 무한하게 루프하는 방식을 제공합니다. 러스트의 loop
는 다음과 같습니다.
loop {
println!("Loop forever!");
}
while
러스트에는 while
반복문 역시 존재합니다. 다음과 같은 모습이죠.
let mut x = 5; // mut x: i32
let mut done = false; // mut done: bool
while !done {
x += x - 3;
println!("{}", x);
if x % 5 == 0 {
done = true;
}
}
while
반복문은 당신이 얼마나 많은 횟수를 반복해야할지 확신이 없을 때 좋은 선택지입니다.
무한 루프가 필요할 때, 다음과 같이 쓰고 싶은 마음이 들겠죠.
while true {
하지만, 이 경우를 처리하는데에는 loop
가 더 어울립니다.
loop {
이렇게 생성된 반복문은 항상 반복할 것이기 때문에, 러스트의 제어흐름 분석은 이러한 반복문을 while true
와 다르게 취급합니다. 일반적으로, 우리가 컴파일러에게 더 많은 정보를 줄수록 컴파일러는 더 나은 보안과 최적화를 할 수 있기 때문에, 만약 무한 루프를 만들 생각이라면 loop
를 사용하는게 좋습니다.
for
for
반복문은 어떤 내용을 특정 회수만큼 반복하고 싶을 때 사용합니다. 하지만, 러스트에서의 for
반복문은 다른 시스템 언어에서와는 약간 다르게 작동합니다. 러스트의 for
반복문은 "C-스타일"의 for
반복문처럼 생기지 않았습니다.
for (x = 0; x < 10; x++) {
printf( "%d\n", x );
}
그보다는, 이런 식이죠.
for x in 0..10 {
println!("{}", x); // x: i32
}
약간 더 추상적으로 나타내자면,
for var in expression {
code
}
이 때의 expression은 반복자입니다. 반복자는 일련의 원소들을 반환하는데, 이 때 각각의 원소가 반복문의 한 회차 반복에 해당합니다. 해당 원소의 값은 var
라는 이름에 바인딩되고, 이 바인딩은 반복문의 본문 (위의 예제에서 code 부분) 동안 유효합니다. 본문을 한 번 지나고 나면, 반복자로부터 다음 값을 받아오고, 한 번 더 반복하는 식이죠. 반복자에 더이상 남은 값이 없을 때, for 반복문은 끝이 납니다.
우리의 예제로 돌아가서, 0..10
은 시작점과 끝점을 받은 뒤, 그 사이의 값들을 원소로 갖는 반복자를 반환합니다. 이 때 상한값은 제외되므로, 위의 반복문은 0
부터 9
까지 출력하게 됩니다. 10
은 출력되지 않죠.
러스트가 "C-Style" for
반복문을 지원하지 않는 것은 의도적입니다. 반복문의 각 인자들을 수동적으로 조작해주는 일은 숙련된 C 개발자들에게조차 복잡하고 에러를 일으키기 쉬운 작업이거든요.
열거하기 (Enumerate)
몇번 반복했는지 추적해야하고 싶을 경우엔, .enumerate()
함수를 사용하면 됩니다.
범위를 열거하기 (On ranges)
for (i,j) in (5..10).enumerate() {
println!("i = {} and j = {}", i, j);
}
출력
i = 0 and j = 5
i = 1 and j = 6
i = 2 and j = 7
i = 3 and j = 8
i = 4 and j = 9
범위 앞뒤로 괄호를 붙혀야 한다는 것을 잊지마세요.
반복자를 열거하기 (On iterators)
# let lines = "hello\nworld".lines();
for (linenumber, line) in lines.enumerate() {
println!("{}: {}", linenumber, line);
}
출력
0: Content of line one
1: Content of line two
2: Content of line three
3: Content of line four
이른 반복 종료 (Ending iteration early)
우리가 앞서 보았던 while
루프를 다시 봐봅시다:
let mut x = 5;
let mut done = false;
while !done {
x += x - 3;
println!("{}", x);
if x % 5 == 0 {
done = true;
}
}
언제 루프가 끝나는지 알기 위해서 특정한 mut
boolean 변수 바인딩 done
을 보관해야 했습니다. 러스트는 우리에게 반복을 조작하는데 도움을 주고자 두 가지 키워드를 제공합니다: break
과 continue
.
이 예제에서, break
을 활용하여 더 나은 방식으로 루프를 작성할 수 있습니다.
let mut x = 5;
loop {
x += x - 3;
println!("{}", x);
if x % 5 == 0 { break; }
}
이제 loop
로 인하여 영원히 반복하며, 일찍(early) 루프를 탈출하기 위해 break
을 사용했습니다. 명시적인 return
구문을 행하는 것 또한 일찍 반복 중단을 제공합니다.
continue
역시 유사하나, 반복을 중단하는 것 대신 다음 반복으로 진행합니다. 이것은 오로지 홀수만 출력할 것입니다:
for x in 0..10 {
if x % 2 == 0 { continue; }
println!("{}", x);
}
루프 레이블 (Loop labels)
중첩 루프가 사용되며, break
또는 continue
구문이 어느 루프를 대상으로 하는지 명시해야 할 상황을 맞이할 수 있습니다. 다른 대부분의 언어들과 같이, 기본적으로 break
과 continue
는 가장 안쪽의 루프에 적용될 것입니다. 바깥 루프를 대상으로 break
또는 continue
를 사용해야 할 상황에서 레이블을 사용해 break
또는 continue
가 어느 루프에 적용되는지 명시할 수 있습니다. 이것은 오로지 x
와 y
가 홀수일 때 한하여 출력할 것입니다:
'outer: for x in 0..10 {
'inner: for y in 0..10 {
if x % 2 == 0 { continue 'outer; } // continues the loop over x
if y % 2 == 0 { continue 'inner; } // continues the loop over y
println!("x: {}, y: {}", x, y);
}
}