프로그래밍의 대표적인 컨트롤 플로우라 하면
조건문과 반복문일 것이고
구문으로 말하면 if문과 for문일 것이다
러스트에서도 if와 for문이 존재한다
for n in
1..=10 { // 1부터 10까지 도는 for문
if n
% 2 == 1 {
println
!("{} is odd", n
);
} else {
println
!("{} is even", n
);
}
}
위는 if문과 for문을 한번에 보여주는 예시다
특이한 부분이라고 하면 1..=10 이 부분일 것이다
이는 러스트에서 range라고 하는 개념이다
0..10: 0부터 9까지
마지막 숫자는 빠진다
for문에서 i = 0에 i < 10을 쓰는 것과 비슷한 느낌
1..=10: 1부터 10까지
마지막 숫자를 포함하려면 이렇게 써야 한다
다른 언어에서 또 다른 조건문과 반복문이라고 하면
switch와 while이 있을 것이다
러스트에도 while은 있다
그리고 switch는 없지만 match라는 것이 존재한다
그리고 조건식 없는 반복문도 있는데, 바로 loop라고 한다
let mut n
= 0;
loop
{
n
+= 1; // 러스트에서는 ++n, n++이 없다.
match n
{
10.. => break, // 10 이상이면 탈출
_
=> continue,
}
}
보다시피 아예 무한반복을 가정해서 조건식이 없는 모습이다
탈출하려면 break로 탈출해야 한다
match는 기본적으로 switch와 비슷한 역할을 한다
다른 언어에서 default인 부분은 언더바로 표시한다
c나 c++에서 중첩 반복문을 loop로 탈출하는 경우가 있다
러스트에서는 반복문에 이름을 지정하고
그 반복문을 탈출하는 것이 가능하다
'outer
: loop
{
'inner
: loop
{
break 'outer
;
}
}
원래 break만 치면 가장 안쪽에 있는 반복문을 탈출하지만
이렇게 레이블링을 하면 더 바깥쪽의 반복문을 탈출하는 것도 가능하다
match에는 또 한 가지 특이한 사용법이 있는데,
그건 바로 배열이나 투플 같은 요소를 부분적으로 사용하는 것이다.
let triple
= (0, -2, 3);
// 투플 출력은 {:?}를 써야 한다
println
!("Tell me about {:?}", triple
);
match triple
{
// 첫 요소가 0일 때
// 나머지 두 요소에는 y, z라는 변수명을 사용
(0, y
, z
) => println
!("First is `0`, `y` is {:?}, and `z` is {:?}", y
, z
),
// 첫 요소가 1일 때
(1, ..) => println
!("First is `1` and the rest doesn't matter"),
// 마지막 요소가 2일 때
(.., 2) => println
!("last is `2` and the rest doesn't matter"),
// 첫 요소가 3이고, 마지막 요소가 4일 때
(3, .., 4) => println
!("First is `3`, last is `4`, and the rest doesn't matter")
// 나머지 경우
_
=> println
!("It doesn't matter what they are"),
}
러스트는 이런 것들에 대한 검사가 잘돼 있어서,
만약 조건이 겹치는 게 있으면 에러까진 아니지만 경고가 뜬다
경고 내용은 첫 번째 경우 이외엔 나머지 것들에 도달하지 않는다는 것이다
마지막으로 if let, while let에 대해 알아보도록 하겠다
러스트에는 null 대신에 std::Option이라는 enum을 사용한다
Option은 Some<T>거나, None이다
그런데 이때 match를 쓰면 언더바 부분이 낭비된다
// Make `optional` of type `Option<i32>`
let optional
= Some(7);
match optional
{
Some(i
) => {
println
!("This is a really long string and `{:?}`", i
);
// ^ Needed 2 indentations just so we could destructure
// `i` from the option.
},
_
=> {},
// ^ Required because `match` is exhaustive. Doesn't it seem
// like wasted space?
};
이때, if let을 사용하면 코드를 좀 줄일 수 있다
fn
main() {
// number에는 Some(7)이 들어가 있고, letter는 None 상태다
let number
= Some(7);
let letter
: Option
<i32
> = None
;
// 이 경우엔 number에 값이 있어서 7이 i가 된다
if let
Some(i
) = number
{
println
!("Matched {:?}!", i
);
}
// letter는 None 상태라 else에 들어가게 된다
if let
Some(i
) = letter
{
println
!("Matched {:?}!", i
);
} else {
// Destructure failed. Change to the failure case.
println
!("Didn't match a number. Let's go with a letter!");
}
}
while let은 다음과 같이 활용할 수 있다
fn
main() {
// optional을 Some(0) 상태로 초기화
let mut optional
= Some(0);
// optional이 Some 상태인 경우에만 루프
while let
Some(i
) = optional
{
if i
> 9 { // 9보다 커지면 None을 대입하고 탈출
println
!("Greater than 9, quit!");
optional
= None
;
} else { // Some 내부의 크기를 1씩 증가시킨다
println
!("`i` is `{:?}`. Try again.", i
);
optional
= Some(i
+ 1);
}
}
나도 러스트엔 익숙치가 않아서
이러한 것들의 존재가 다 귀찮게만 느껴질 수 있을 거라는 점 백번 이해한다
하지만 익숙해진다고 가정하고 보면
이러한 것들의 존재는 흔하게 치던 코드들을
훨씬 더 짧게 줄여줘 생산성을 높일 수 있다고 생각한다
게다가 러스트는 무비용, 저비용 추상화를 지향하기 때문에
이런 기능을 자주 쓴다고 해서 특별히 성능이 떨어지지도 않는다
이런 것도 현대 프로그래밍 언어의 장점이라고 할 수 있을 것이다
이제 마무리를 하겠다
1편에서는 데이터 타입, 2편에서는 컨트롤 플로우에 대해 알아보았다
둘은 프로그래밍에서 가장 핵심적인 개념이라고도 할 수 있는 것들이기 때문에,
사실 이것만 어느 정도 익혀도 입문할 준비 자체는 됐다고도 볼 수 있다
다만 이것으로 프로그램을 만들려면 몇 가지 더 필요한 게 있는데,
바로 콘솔에서의 입출력이다
3편에서는 어렴풋이 넘겼던 println!의 좀 더 구체적인 사용법,
그리고 사용자의 입력을 받는 방법에 대해서 알아도보록 하겠다.
댓글 0