Fluent Rust - retry logic

Migo·2023년 8월 22일
0

Fluent Rust

목록 보기
5/23
post-thumbnail

Brief introduction to ownership

러스트의 Onwership 시스템은 Value의 이동을 추적하는데 있어 강력한 제어장치로 작동한다.
아래 코드를 보자.

fn main() {
    let value = "migo".to_string();
    string_accepting_function(value);
    println!("{}", value); // error!
}

fn string_accepting_function(value: String) {}

"migo"라는 String value에 대한 ownership을 가진 value는, string_accepting_function에, 자신이 가진 값을 던진 순간, 더 이상 해당 값에 대한 ownership을 유지할 수 없고, 따라서 위의 코드는 컴파일 되지 않는다.

Downside of linear data flow enforced by ownership?

만약 모든 함수 호출이 외부 시스템에 대한 의존성이 없이, 어플리케이션 로직만으로 무결성 검증이 가능하다면, 위와 같은 시스템은 전혀 문제가 되지 않는다. 그러나, 이러한 단방향적 데이터 흐름실패 케이스에 대한 동일 데이터 재사용에 있어 문제를 야기할 수 있다... 라고 생각할 수 있다. 아래의 예시를 보자.

use rand::seq::SliceRandom;

struct Error;

fn main() {
    let value = "migo".to_string();
    let _ = call_to_external_system(name);
}

fn call_to_external_system(_value: String) -> Result<(), Error> {
    let is_success = [true, false].choose(&mut rand::thread_rng()).unwrap();
    if *is_success {
        Ok(())
    } else {
        Err(Error)
    }
}

두 가지 가정을 해보자.
1. call_to_external_system을 외부 네트워크 등에 대한 의존성을 갖는 함수이다.
2. 해당 함수에 인자가 큰 데이터사이즈를 갖는다.

사실, 현업코드를 작성하면, 위와 같은 상황은 매우 빈번하게 발생한다. 그리고 그런 경우 우리는 Retry Logic을 생각하게 된다. 아주 간단하게 해당 상황을 Python코드로 작성하면 아래와 같을 것이다.

var = "some big big big size value"
retry_cnt = 0
while retry_cnt < 3:
    try:
        call_to_external_system(var)
    except SomeError as e:
        print(e)
        retry_cnt += 1
    else:
		break   

하지만 다시 한번, Rust의 데이터 흐름은 linear하다. 따라서, 위와 같은 코드를 Rust에서 직역하여 구현하려고하면, 곤란함이 있을것이다.

Solution - be fluent with Rust

Rust에선 위의 retry 로직 문제를, 값의 재생성이나 레퍼런스의 충돌과 같은 문제 없이 깔끔하게 해결할 수 있다.

use rand::seq::SliceRandom;

// On failure, you wrap the value passed into function into SendError variant.
enum Error {
    SendError(String),
}

fn main() {
    let mut value = "big big big value".to_string();
    let success = 's: {
        for _ in 0..2 {
            value = match call_to_external_system(value) {
                Ok(()) => break 's true,
                Err(Error::SendError(value)) => value,
            }
        }
        false
    };
    println!("Success:{}", success);
}

fn call_to_external_system(value: String) -> Result<(), Error> {
    let is_success = [true, false].choose(&mut rand::thread_rng()).unwrap();
    if *is_success {
        Ok(())
    } else {
        Err(Error::SendError(value))
    }
}
profile
Dude with existential crisis

0개의 댓글