티스토리 뷰

반응형

Control flow

  • if
    • If 다음에는 bool 타입이 와야 한다.
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");
        
    }
}
// if 문을 식(expression)으로 사용할 수 있다.

let condition = true;
let y = if condition { 5 } else { 6 };
println!("y는 {y}입니다"); // y는 입니다 
  • loop
    • loop는 값을 리턴할 수 있다.
// 반복이라는 글자를 계속 출력한다. 
loop {
	println!("반복!");
}

// counter 가 3이되면 loop를 종료시킨다. 
let mut counter = 0;
loop {
	println!("반복");
	counter += 1;
	if counter == 3{
		break;
	}
}

// loop 의 마지막을 반환값으로 줄 수 있다.
let mut counter = 0;
let x = loop {
	println!("반복");
	counter += 1;
	if counter == 3{
		break counter;
	}
}

println!("x는 {x}입니다."); // x는 3입니다.
  • while
let mut counter = 0;
while (counter < 3) {
	println!("반복!");
	counter += 1;
}

// 배열의 모든 원소 순회
let xs [1,2,3,4,5];
let mut idx = 0;
while (idx < xs.len()) {
	println!("xs[{}] {}", idx, xs[idx]);
	idx+=1;
}
  • for
// for each 방식 전체 순회
let xs [1,2,3,4,5];
for x in xs {
	println!("x = {}", x);
}

// 배열 요소를 접근해서 전체 순회
for i in (0..5) {
	println!("xs = {}", xs[i]);
}

// 거꾸로 순회한다.
for i in (0..5).rev() {
	println!("xs = {}", xs[i]);
}

if let 문법

if let 구문을 사용해서 나머지 패턴을 무시하고 하나의 패턴과 일치하는 값을 처리할 수 있다. 아래는 config_max 변수가 Some(..) 이면 .. 을 출력하고, 그렇지 않으면 무시하는 코드다.

fn main() {
    let config_max = Some(3u8);
    match config_max {
        Some(max) => println!("the maximum is configured to be {max}"),
        _ => (),
    }
}

위 코드는 아래와 같이 if let 문법을 사용하여 간단하게 나타낼 수 있다.

fn main() {
    let config_max = Some(3);
    if let Some(max) = config_max {
        println!("the maximum is configured to be {max}");
    }
     
    // else {
    } // 를 작성해서 _ 에 해당하는 코드가 있으면 그것도 작성할 수 있다. 
}

 

에러 처리

  • 복구 가능한 에러
    • ex ) File not found → 다른 경로를 입력받아서 재시도
    • 프로그래머가 예외처리를 해놓을 수 있다.
      • Result <T, E>
  • 복구 불가능한 에러
    • ex ) out of index
    • 프로그래머가 예외처리를 할 수 없거나 하면 안된다. 에러가 발생하면 바로 시스템이 다운되야 한다.
      • panic!

복구 불가능한 오류에는 panic!

  • panic! 매크로를 사용하여 강제 종료를 시킬 수 있다.
fn main() {
	panic!("강제종료");
}

fn main() {
	let arr = [1, 2, 3];
	arr[99]; // error: this operation will panic at runtime
}

에러 처리에는 Result<T, E>

아래 코드는 hello.txt 파일이 있으면 파일을 반환하고 없으면 panic! 을 일으켜서 프로그램을 종료한다.

// enum Result<T, E> {
//     Ok(T),
//     Err(E),
// }

use std::fs::File;

fn main() {
    let greeting_file_result = File::open("hello.txt");

    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("Problem opening the file: {error:?}"), // ("{:?}", error)의 다른 표현
    };
}

아래 코드는 파일이 있으면 Ok(f) 를 반환 하고, 없으면 에러를 반환한다. match 를 이용해서 에러의 종류를 확인하고 에러 종류가 NotFound 라면 파일을 생성한다. 그 외 문제라면 패닉을 발생시켜 프로그램을 종료한다. (13장에서 closure 를 배우고 나면 아래 코드에 나오는 파일 생성에 대한 실패 분기를 축약할 수 있다)

use std::{fs::File, io::ErrorKind};

fn main() {
    let file_result = File::open("hello.txt");

    let file = match file_result {
        Ok(f) => f,
        Err(e) => match e.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("파일 생성 실패!: {e:?}"),
            },
            _ => panic!("파일 접근 실패: {:?}", e),
        },
    };
}

항상 위와 같이 쓰는 건 귀찮으니까 러스트에서 제공하는 unwrap() 함수로 대체할 수 있다. 정상적으로 열었을 때는 file을 돌려주지만 실패 했을 때는 panic 을 일으킨다.

fn main() {
    let file = File::open("hello.txt").unwrap();
}

조금 더 디테일한 커스텀을 원한다면 expect() 를 사용할 수 있다. expect 를 이용하면 panic 이 발생했을때 어떤 메시지를 출력할 것인지 정의할 수 있다.

fn main() {
		let file = File::open("hello.txt").expect("파일을 열 수 없음");
}

에러 전파하기

  • 내가 직접 에러를 처리하지 않고 호출한 쪽에 에러 처리를 위임한다. ( Java 의 Throw 개념 )

read_username_from_file() 을 호출하는 곳에서 에러처리를 하게 작성한 코드.

use std::fs::File;
use std::io::{self, Error, Read};

fn read_username_from_file() -> Result<String, Error> {
    let file_result = File::open("hello.txt");

    let mut file = match file_result {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut username = String::new();

    match file.read_to_string(&mut username) {
        Ok(_) => Ok(username),
        Err(e) => Err(e),
    }
}

fn main() {
    let username = read_username_from_file();
    println!("{:?}", username);
    let username = username.unwrap();
    println!("{username}");
}

/*
Ok("pott\\n")
pott

*/

hello.txt 파일 이 있으면 파일에서 내용을 읽어 username(=pott) 을 반환하고 , 없으면 Error 를 던진다.

위와 같은 형식은 너무나 빈번하게 사용하기 때문에 아래와 같이 ? 로 축약해서 작성할 수 있다.

fn read_username_short() -> Result<String, Error> {
    let mut file = File::open("hello.txt")?;
    let mut username = String::new();
    file.read_to_string(&mut username)?;
    Ok(username)
}

위의 표현은 아래로 더 축약할 수 있다.

/* ?축약표현을 연이어 쓸 수도 있습니다. */
fn read_username_shorter() -> Result<String, Error> {
    let mut username = String::new();

    File::open("hello.txt")?.read_to_string(&mut username)?;

    Ok(username)
}

최종적으로 다음과 같이 한줄로 축약해서 사용할 수 있다.

use std::fs;

/* fs::read_to_string은 내부적으로 ?축약표현이 이미 들어있습니다. */
fn read_username_even_shorter() -> Result<String, Error> {
    fs::read_to_string("hello.txt")
}

Result가 반환되는 함수에서 ?연산자를 사용할 수 있으며, Option이 반환되는 함수에서 ? 연산자를 사용할 수 있지만, 두 가지를 혼합할 수는 없다. ? 연산자는 Result를 Option으로 자동 변환하거나 그 반대로 작동하지 않습니다. 그런 경우 Result에서 ok 메서드나 Option에서 ok_or 메서드와 같은 메서드를 사용하여 명시적으로 변환할 수 있다.

아래 코드는 에러가 난다. 즉, ? 는 반환 타입이 Result<T,E> 거나 Option<T> 인 함수에서만 사용가능 하다.

use std::fs::File;

fn main() {
    let greeting_file = File::open("hello.txt")?;
}

에러가 안나게 수정해 보았다.

use std::fs::File;
use std::io::Error;

fn main() {
    let file = open(); // file 의 타입은 Result<File, Error> 
    println!("{:?}", file);
}

fn open() -> Result<File, Error> {
    let greeting_file = File::open("hello.txt")?;
    Ok(greeting_file)
}

공식문서에서는 다음과 같이 더 직관적인 방법을 제공한다.

use std::error::Error;
use std::fs::File;

fn main() -> Result<(), Box<dyn Error>> {
    let greeting_file = File::open("hello.txt")?;

    Ok(())
}

일단 main() 은 조금 특별한 함수라서 Result<File, Error> 로 할 수는 없고, Result<(), Box<dyn Error>> 로 반환해야 한다.

Box<dyn Error> 유형은 트레잇 객체로, 일단은 어떤 종류의 오류 로 읽으면 된다. Box<dyn Error> 은 어떤 Err 값도 조기에 반환될 수 있게 한다. 이 main 함수의 본체가 std::io::Error 유형의 오류만 반환하더라도, Box<dyn Error>를 지정함으로써, 다른 오류를 반환하는 코드가 greeting_file 변수 선언 앞뒤로 추가 되더라도 이 시그니처는 계속해서 사용할 수 있다.

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
글 보관함