반응형

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

 

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

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



2022. 09. 07  최초작성

2023. 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());
}


간단하게 정리해본 Rust 강좌 1 : 변수, 상수, 함수, 튜플, 배열, 데이터타입, println

https://webnautes.tistory.com/2194

 

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

https://webnautes.tistory.com/2195

 

간단하게 정리해본 Rust 강좌 3 : 열거형, 제네릭 데이터 타입, Option, Result, unwrap, vector

https://webnautes.tistory.com/2196

 

간단하게 정리해본 Rust 강좌 4 : 소유권, 참조, 역참조, 생명주기

https://webnautes.tistory.com/2197

 

간단하게 정리해본 Rust 강좌 5 : 문자열, utf-8

https://webnautes.tistory.com/2198

 

간단하게 정리해본 Rust 강좌 6 : 모듈

https://webnautes.tistory.com/2199

 

간단하게 정리해본 Rust 강좌 7 : &self, &mut self, trait, 동적 디스패치, 정적 디스패치, Generic 메서드, Box

https://webnautes.tistory.com/2200

 

간단하게 정리해본 Rust 강좌 8 : 참조자, 댕글링 참조, 원시 포인터

https://webnautes.tistory.com/2203

 

간단하게 정리해본 Rust 강좌 9 : Box, 재귀적 데이터타입, Deref 트레잇

https://webnautes.tistory.com/2202





반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts