간단하게 정리해본 Rust 강좌 2 : if-else, loop, while, for, match, struct, method

간단하게 정리해본 Rust 강좌입니다. 

다음 문서를 기반으로 작성했습니다.

Tour of Rust https://tourofrust.com/00_ko.html 

.

2022. 09. 07  최초작성

2023. 03. 23 

2026. 03. 23   최종작성

.

Rust 개발 환경 만드는 방법은 아래 포스트를 참고하세요.

https://webnautes.tistory.com/2110

.

if/else

if/else 문을 사용하면 if 문에 주어지는 조건에 따라 원하는 코드를 실행하도록 할 수 있습니다.

if문에 조건을 지정할때 다음과 같은 관계 연산자와 논리 연산자를 사용할 수 있습니다.

.

관계 연산자 

  •  ==  : 양변에 있는 값이 갔다는 의미입니다.
  •  !=   : 양변에 있는 값이 같지 않다는 의미입니다.
  •  <    : 오른쪽 값이 더 크다는 의미입니다.
  •  >    : 왼쪽 값이 더 크다는 의미입니다.
  •  <=  : 오른쪽 값이 더 크거나 양변의 값이 같다는 의미입니다.
  •  >=  : 왼쪽 값이 더 크거나 양변의 값이 같다는 의미입니다.

.

논리 연산자

  • !     : 뒤에 주어지는 조건이 True가 아닌 경우에 조건이 True가 됩니다.  
  • ||    :  좌우에 있는 조건 중 하나만 만족해도 조건이 True가 됩니다. 왼쪽 조건의 우선순위가 더 높습니다.
  • && : 좌우에 있는 조건이 모두 만족해야 조건이 True가 됩니다.

.

다음 코드는 변수 x에 저장된 값이 42보다 작은지, 42와 같은지, 아니면 42보다 큰지에 따라 다른 출력을 하는 예제입니다. 

fn main() {

    // 변수 x에 42를 저장합니다.
    let x = 42;

    // 변수 x의 값이 42보다 작다면 조건 x < 42가 true가 되어 바로 아래 블럭 안에 있는 코드가 실행됩니다.     // 변수 x의 값이 42보다 크거나 같으면 조건 x < 42가 false가 되어 else문으로 넘어갑니다.
    if x < 42 {
       
        println!(“42보다 작다”);

    } else if x == 42 { // 변수 x의 값이 42와 같다면 조건 x == 42가 true가 되어 바로 아래 블럭 안에 있는 코드가 실행됩니다.                        // 변수 x의 값이 42가 아니라면 조건 x == 42가 false가 되어 else문으로 넘어갑니다.
       
        println!(“42와 같다”);

    } else { // if 문이 없고 else만 있는 경우 아래 있는 블럭의 코드가 바로 실행됩니다.              // 이 경우는 앞에서 체크한 조건에 모두 해당하지 않은 경우입니다. 

        println!(“42보다 크다”);
       
    }
}

.

변수 x에는 42가 저장되어 있기 때문에 실행해보면 “42와 같다”가 출력됩니다. 

.

실행 결과

42와 같다

.

.

다음 코드처럼 if 문에서 값을 전달받아서 변수 v에 저장할 수 있습니다.  if /else문 다음에 나오는 블럭의 마지막줄에 적은 값이나 변수의 값이 리턴됩니다. 아래 코드에선  조건 x == 42를 만족했는지 여부에 따라 “42임” 또는 “42아님”이 변수 v에 저장됩니다. 

fn main() {

    let x = 42;

    let v = if x == 42 { “42임” } else { “42아님” };

    println!(“{}”, v);
}

.

변수 x에는 42가 저장되어 있기 때문에 if문에서 “42임”을 전달받아 변수 v에 저장됩니다.

.

실행 결과

42임

.

.

loop

loop 문을 사용하면 블록 내에 있는 코드를 무한 반복을 합니다.

if 문으로 조건을 체크하여 만족하면 break를 사용하여 무한 반복을 중단할 수 있습니다.

fn main() {
    //변수의 값을 변경해야 하므로 let대신에 let mut를 사용하여 변수를 선언해야 합니다.
    let mut x = 0;


    // 무한 루프입니다. 블럭 {} 안의 코드를 반복합니다.     loop {

        // 변수 x에 1을 더합니다.
        x += 1;

        // 변수 x가 42가 되면 break를 사용하여 무한 루프에서 빠져나옵니다.
        if x == 42 {
            break;
        }
    }
    println!(“{}”, x);
}

.

실행결과 42가 출력됩니다. 

42

.

.

loop에서 break을 사용하여 반복을 중단하면서 값을 리턴받아 변수에 저장할 수 있습니다.

다음 코드는 5개의 문자가 저장된 배열에서 원하는 문자가 있는 위치를 출력합니다.

fn main() {


    // 변수 array에 5개의 문자로 구성된 배열을 대입합니다.
    let array: [char; 5] = [‘A’, ‘A’, ‘A’, ‘C’, ‘A’];


    // index를 1씩 증가시키면서 루프에서 현재 인덱스가 가리키는 배열 array의 원소가 ‘C’인지 검사합니다.
    let mut index = 0;

    let ret = loop {
       
        // 현재 index가 가리키는 배열 원소의 값이 ‘C’인지 검사합니다.
        if array[index] == ‘C’ {

            // break 문에 의해서 반복을 중단하면서 index에 저장된 값을 변수 ret에 전달합니다.
            break index;
        }

        index += 1;
    };
   
    // 배열 array의 4번째 문자가 ‘C’이므로 3이 출력됩니다.
    println!(“loop의 리턴값 : {}”, ret);
}

.

.

5개의 문자열이 저장되어있는 배열로 바꾸어 봅니다.

fn main() {


    // 변수 array에 5개의 문자열로 구성된 배열을 대입합니다.
    let array: [&str; 5] = [“Apple”, “Apple”, “Apple”, “Cat”, “Apple”];


    // index를 1씩 증가시키면서 루프에서 현재 인덱스가 가리키는 배열 array의 원소가 “Cat”인지 검사합니다.
    let mut index = 0;

    let ret = loop {
       
        // 현재 index가 가리키는 배열 원소의 값이 “Cat”인지 검사합니다.
        if array[index] == “Cat” {

            // break 문에 의해서 반복을 중단하면서 index에 저장된 값을 변수 ret에 전달합니다.
            break index;
        }

        index += 1;
    };

    // 배열 array의 4번째 문자열이 ‘Cat’이므로 3이 출력됩니다.
    println!(“loop의 리턴값 : {}”, ret);
}

.

앞에서 사용한 코드가 위험한 코드가 될 수 있습니다. 배열에서 조건을 만족하는 것을 찾지 못하면 다음과 같은 에러가 발생합니다. 테스트 삼아 위 코드의 14번째 줄에 있는 if 문의 Cat을 Dog로 변경해보세요.

thread ‘main’ panicked at ‘index out of bounds: the len is 5 but the index is 5’, src/main.rs:14:12

.

.

while

while 문도 블럭 {} 안의 코드를 반복을 합니다. loop 문과 차이는 while 키워드 뒤에 오는 조건을 만족하는 동안에만(조건이 true인 동안에만) 블록 내에 있는 코드를 반복합니다. 

fn main() {

    // 값을 변경해야 하므로 let mut를 사용하여 변수를 선언해야 합니다.
    let mut x = 0;

    // 변수 x의 값이 10이 아닌 동안 반복합니다.
    while x != 10 {
   
        // 변수 x의 값을 1 증가시킵니다.
        x += 1;
    }
     // 실행결과 10이 출력됩니다.    println!(“{}”, x)
}

.

.

for

for 문을 사용하면 지정한 범위내에서 반복할 수 있습니다. 

in 다음에 .. 연산자를 사용하면 시작값으로 주어진 정수부터 끝값으로 주어진 정수 전까지 반복하게 됩니다. 

in 다음에 ..= 연산자를 사용하면 시작값으로 주어진 정수부터 끝값으로 주어진 정수까지 반복하게 됩니다.

fn main() {
 
    // 끝값 5를 제외하고 0 ~ 4까지 정수를 출력합니다.
    for x in 0..5 {
        println!(“{}”, x);
    }
    println!(“”);

    // 끝값 5를 포함하여 0 ~ 5까지 정수를 출력합니다.
    for x in 0..=5 {
        println!(“{}”, x);
    }
}

.

실행결과

0

1

2

3

4

0

1

2

3

4

5

.

.

match

C/C++의 switch 문과 유사합니다. 

match 키워드 다음에 주어진 변수 x의 값에 대해 조건들을 검사하여 일치하면 해당 코드를 실행합니다.

fn main() {

    let x = 1;

    match  x {        // 변수 x의 값이 1인 경우입니다.
        1 => println!(“1입니다.”),
        // 변수 x의 값이 2인 경우입니다.
        2 => println!(“2입니다.”),
        // 나머지 경우가 해당됩니다.
        _=>println!(“다른 숫자군요.”),
    }
   
}

실행결과 “1입니다”가 출력됩니다. 

.

.

다음처럼 match를 문자열에 대해서도 사용할 수 있습니다. 

https://stackoverflow.com/a/32790546
fn main() {

    let x = String::from(“abc”);

    match  x.as_str() {
        // 변수 x의 값이 ABC인 경우입니다.
        “ABC” => println!(“대문자 ABC입니다.”),
        // 변수 x의 값이 abc인 경우입니다.
        “abc” => println!(“소문자 abc입니다.”),
        // 나머지 경우가 해당됩니다.
        _=>println!(“다른 문자열 이군요.”),
    }
   
}

실행결과

소문자 abc입니다.

.

.

다양한 방식으로 match 문에 조건을 추가할 수 있습니다. 

fn main() {

    let x = 42;

    match x {


        // 하나의 값과 일치하는 경우입니다.
        0 => {
            println!(“0과 일치”);
        }


        // 두개의 값중 하나와 일치하는 경우입니다.
        1 | 2 => {
            println!(“1 또는 2와 일치!”);
        }

       
        // 범위 내의 숫자와 일치하는 경우입니다.
        3..=9 => {
            println!(“3에서 9사이의 숫자와 일치”);
        }


        // 찾은 숫자를 변수에 대입할 수 있습니다.
        matched_num @ 10..=100 => {
            println!(“10에서 100사이의 숫자 {}와 일치!”, matched_num);
        }


        // 앞에서 지정한 조건에 매칭이 안된경우 실행되는 코드입니다. 
        _ => {
            println!(“일치하는 조건이 없었음”);
        }
    }
   
}

실행결과

10에서 100사이의 숫자 42와 일치 !

.

.

다음처럼 match 문에서 값을 전달받아 변수 result에 저장할 수 있습니다.

fn main() {

    let food = “햄버거”;
    let result = match food {

        // 변수 food의 값이 핫도그인 경우입니다.
        “핫도그” => “핫도그다”,

        // 전달되는 값이 하나라면 중괄호 {}는 필수가 아닙니다.
        _ => “핫도그가 아니다”,
    };

    // 실행결과 핫도그가 아니다가 출력됩니다.
    println!(“{}”, result);
}

.

.

블록에서 값 리턴하기

블록 끝에 값이나 변수가 주어지면 블록 밖에 있는 변수에 대입됩니다. 

fn main() {

    // 블럭 마지막에 있는 a + b의 결과값이 리턴되어 변수 v에 저장됩니다.
    let v = {

        let a = 1;
        let b = 2;

        a + b
    };

    // 실행결과 3이 출력됩니다.
    println!(“{}”, v);

}

.

.

구조체(struct)

구조체는 struct 키워드로 선언됩니다.

// println! 함수를 사용하여 구조체를 출력해보려면 디버깅 모드를 사용해야 합니다. #[derive(Debug)]

// 두개의 변수로 구성된 구조체 Vec2를 선언합니다.
struct Vec2 {
    _x: f64,
    _y: f64,
}


fn main() {

    // 2개의 구조체를 선언합니다.
    // 순서는 중요하지 않으며 변수이름만 중요합니다.
    let v1 = Vec2 { _x: 1.0, _y: 3.0 };
    let v2 = Vec2 { _y: 2.0, _x: 4.0 };
        // 디버깅 모드를 사용하여 구조체 출력시 다음 두가지 차이를 아래 결과에서 비교해보세요.
    println!(“{:#?}”, v1);
    println!(“{:?}”, v2);   
}

.

실행결과

.

.

다른 구조체의 값을 가져와 구조체의 값을 채울 수 있습니다. 

아래 예제에서는 구조체 v2의 값을 사용하여 구조체 v3의 일부 필드와 구조체 v4의 전체 필드를 채우고 있습니다. 

#[derive(Debug)]

struct Vec2 {
    _x: f64,
    _y: f64,
}


fn main() {

    // 2개의 구조체를 선언합니다.
    // 순서는 중요하지 않으며 필드 이름만 중요합니다.
    let v1 = Vec2 { _x: 1.0, _y: 3.0 };
    let v2 = Vec2 { _y: 2.0, _x: 4.0 };

    // 구조체 v3의 _y값이 구조체 v2의 _y값으로 채워집니다.
    let v3 = Vec2 { _x: 14.0, ..v2 }; 
    // 구조체 v4의 _x,_y의 값이 구조체 v2의 값으로 채워집니다.
    let v4 = Vec2 { ..v2 };
   
   
    println!(“{:#?}”, v1);
    println!(“{:?}”, v2);   
    println!(“{:#?}”, v3);
    println!(“{:#?}”, v4);
}

.

실행결과

.

.

구조체도 튜플처럼 값을 나누어 저장할 수 있습니다.

struct Vec2 {
    _x: f64,
    _y: f64,
}


fn main() {

    // 2개의 필드를 가지는 구조체를 선언합니다.
    let v = Vec2 { _x: 3.0, _y: 6.0 };
   
   
    // 구조체 v에 저장된 값이 변수 _x와 _y에 나누어 저장됩니다.
    let Vec2 { _x, _y } = v;
    println!(“{} {}”, _x, _y);

    // 필드 _y의 값은 버려집니다.
    let Vec2 { _x, .. } = v;
    println!(“{}”, _x);   
}

.

실행결과

3 6

3

.

.

let 구조체 구문을 if문의 조건으로 사용할 수 있습니다. 

struct Number {
    odd: bool,
    value: i32,
}

fn print_number(n: Number) {    // value 필드의 값이 0이라면, odd 필드는 조건으로 사용하지 않지만 적어줘야 합니다.
    if let Number { odd, value:0 } = n {
        println!(“Number is Zero: {}”, n.value);
    }     // odd 필드의 값이 true라면, value 필드는 조건으로 사용하지 않지만 적어줘야 합니다.     else if let Number { odd: true, value } = n {
        println!(“Odd number: {}”, value);
    }    // odd 필드의 값이 false라면, value 필드는 조건으로 사용하지 않지만 적어줘야 합니다.      else if let Number { odd: false, value } = n {
        println!(“Even number: {}”, value);
    }
}

fn main() {
    let one = Number { odd: true, value: 1 };
    let two = Number { odd: false, value: 2 };
    let zero = Number { odd: false, value: 0 };


    print_number(one);
    print_number(two);
    print_number(zero);
}

.

실행결과

Odd number: 1

Even number: 2

Number is Zero: 0

.

.

구조체를 match문의 패턴으로 사용할 수 있습니다. 

struct Number {
    odd: bool,
    value: i32,
}

fn print_number(n: Number) {

    // 구조체 n을 검사합니다.
    match n {
       
        // value 필드 값이 0이고 odd 필드 값이 false라면, 필드 순서가 구조체 정의와 다르게 바뀌어도 상관없습니다.
        Number { value:0, odd: false} => println!(“Number is Zero: {}”, n.value),

        // odd 필드 값이 true라면, ..을 사용하여 value 필드를 생략할 수 있습니다.
        Number { odd: true, .. } => println!(“Odd number”),

        // odd 필드 값이 false라면, value 필드는 조건으로 사용하지 않지만 적어줘야 합니다.
        Number { odd: false, value } => println!(“Even number: {}”, value),
    }
}

fn main() {
    let one = Number { odd: true, value: 1 };
    let two = Number { odd: false, value: 2 };
    let zero = Number { odd: false, value: 0 };


    print_number(one);
    print_number(two);
    print_number(zero);
}

.

실행결과

Odd number

Even number: 2

Number is Zero: 0

.

.

match 문을 사용하여 구조체의 일부 필드의 값을 검사할 수 있습니다.  

struct Number {
    odd: bool,
    value: i32,
}

fn print_number(n: Number) {
    match n {        // value 필드 값이 1이라면        Number { value: 1, .. } => println!(“One”),
        // value 필드 값이 2라면
        Number { value: 2, .. } => println!(“Two”),
        Number { value, .. } => println!(“{}”, value), // 이 줄이 없으면 컴파일시 에러납니다.
    }
}


fn main() {
    let one = Number { odd: true, value: 1 };
    let two = Number { odd: false, value: 2 };
   
    print_number(one);
    print_number(two);
}

.

실행결과

One

Two

.

.

match에서 언더바(_)를 사용하여 앞에서 지정하지 않은 나머지 조건에 대해 처리하도록 할 수 있습니다.

struct Number {
    odd: bool,
    value: i32,
}

fn print_number(n: Number) {

    // 구조체의 value 필드의 값을 검사합니다.
    match n.value {

        // 1인 경우
        1 => println!(“One”),

        // 2인 경우
        2 => println!(“Two”),
       
        // n.value값이 1과 2가 아닌 경우에 대한 처리를 하게 됩니다.
        _ => println!(“{}”, n.value),
    }
}

fn main() {
    // 구조체 3개를 선언합니다.
    let one = Number { odd: true, value: 1 };
    let two = Number { odd: false, value: 2 };
    let three = Number { odd: false, value: 3 };

    print_number(one);
    print_number(two);
    print_number(three);
}

.

실행 결과

One

Two

3

.

.

메소드 호출하기

함수와 달리, 메소드는 특정 데이터 타입과 연관된 함수입니다.

  • 정적 메소드(static methods) – 데이터 타입 그 자체에 속하는 메소드로서, :: 연산자를 이용하여 호출함.
  • 인스턴스 메소드(instance methods) – 데이터 타입의 인스턴스에 속하는 메소드로서, . 연산자를 이용하여 호출함.
fn main() {

    // 정적 메소드를 사용하여 String의 인스턴스를 생성
    let s = String::from(“Hello world!”);

    // 인스턴스의 메소드를 사용
    println!(“{}의 글자 수는 {}입니다.”, s, s.len());
}

.

.

관련 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다