Rust는 왜 '?'를 사용할 때 오류 유형을 암시 적으로 변환합니까? 하지만 반환 값이 아닙니까?

레나토

사용법을 설명하기 위해 다음 두 가지 오류 유형 및 함수가 제공됩니다 ( Rust Playground 링크 ).

#[derive(std::fmt::Debug)]
struct MyError;

#[derive(std::fmt::Debug)]
struct OtherError;

impl std::error::Error for MyError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "MyError")
    }
}

impl std::fmt::Display for OtherError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "OtherError")
    }
}

impl std::error::Error for OtherError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<OtherError> for MyError {
    fn from(_: OtherError) -> Self {
        MyError {}
    }
}

fn my_error() -> Result<(), MyError> { Ok(()) }

fn other_error() -> Result<(), OtherError> { Ok(()) }

내가 함수에 나는 경우 반환 ResultMyError그와 같은 Error유형, 나는 두 함수 모두 반환 호출 할 수 있습니다 MyError그리고 OtherError거기에 있기 때문에 From그들 사이에 변환.

그러나 단순히 Result"기타"유형에 대해 를 반환 할 수는 없습니다 . 대신 ?뒤에 와야 Ok(())합니다. 이것은 나에게 일치하지 않는 것 같습니다.

예를 들어 다음은 잘 작동합니다.

fn main() -> Result<(), MyError> {
    my_error()
}

이것은 또한 다음을 수행합니다.

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()?;
    my_error()
}

그러나 이것은 실패합니다.

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()
}

오류:

error[E0308]: mismatched types
  --> src/main.rs:43:5
   |
41 | fn main() -> Result<(), MyError> {
   |              ------------------- expected `std::result::Result<(), MyError>` because of return type
42 |     my_error()?;
43 |     other_error()
   |     ^^^^^^^^^^^^^ expected struct `MyError`, found struct `OtherError`
   |
   = note: expected enum `std::result::Result<_, MyError>`
              found enum `std::result::Result<_, OtherError>`

왜 그런 겁니까?

이 작업을 수행하려면이 작업을 수행해야한다는 것을 알게 되었기 때문에 코드의 일부가 더 장황 해집니다.

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()?;
    Ok(())
}

이것이 유일한 해결책입니까? 나는 그것이 이런 식으로 작동하는 이유를 이해하는 데 더 관심이 있지만 어리석은 일을하고 있다면 더 잘 할 수있는 것을 지적하십시오.

발렌틴

?오퍼레이터는 동일하다 try!다음과 같이 단순화 된 버전은 매크로 :

macro_rules! r#try {
    ($expr:expr $(,)?) => {
        match $expr {
            Ok(val) => val,
            Err(err) => {
                return Err(From::from(err));
            }
        }
    };
}

결과 가 포함 된 복구 가능한 오류 페이지의 A Shortcut for Propagating Errors : the? 운영자 섹션.

matchListing 9-6 표현식 이하는 것과 ?연산자 가하는 것 사이에는 차이 가 있습니다. ?연산자가 호출 한 오류 값fromFrom 은 표준 라이브러리 특성정의 된 함수를 통과하여 오류를 변환하는 데 사용됩니다. 한 유형에서 다른 유형으로. ?조작자가 전화 from기능을, 수신 오류 유형은 현재 함수의 리턴 타입에서 정의 된 에러의 종류로 변환된다. 이는 함수가 여러 가지 이유로 인해 일부가 실패하더라도 함수가 실패 할 수있는 모든 방법을 나타내는 하나의 오류 유형을 반환 할 때 유용합니다. 각 오류 유형이 from자신을 반환 된 오류 유형으로 변환하는 방법을 정의하는 함수를 구현하는 ? 연산자는 변환을 자동으로 처리합니다.

오류 전파를위한 단축키 :? 연산자-Rust 프로그래밍 언어

(강조 내)

예외 유형 업 캐스팅 섹션 에서 정보를 찾을 수 있는 RFC 243 에서 이에 대한 참조를 찾을 수도 있습니다 .

따라서 ?연산자는 하위 유형에서 상위 유형으로 강제 변환의 특성에서 이러한 암시 적 변환을 수행해야합니다. 현재 RFC는 std::convert::Into이 목적을 위해 특성을 사용합니다 (에서 일괄 impl전달이 있음 From). [...]

- 예외 유형 upcasting - RFC 243

또한보십시오:

Rust By Example 에서 페이지 다른 용도? 도 암시 적 변환 동작을 언급합니다. 또한 사용자 지정 오류 유형을 만드는 예도 제공합니다.


결론적으로 다음과 같습니다.

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()?;
    Ok(())
}

본질적으로 다음과 같습니다.

fn main() -> Result<(), MyError> {
    match my_error() {
        Ok(_) => {}
        Err(err) => return Err(From::from(err));
    }
    match other_error() {
        Ok(_) => {}
        Err(err) => return Err(From::from(err));
    }
    Ok(())
}

반환 other_error()하고 암시 적으로 오류를 변환하는 경우 약간 무섭습니다. 예를 들어 반환 유형이 수정되고 return ...암시 적으로 단지 Into이면 즉시 명확하지 않은 문제가 발생할 수 있습니다.


추가를 피 Ok(())하려면 map_err(), 즉를 사용할 수 있습니다 .map_err(From::from). 그러나 즉시 반환되지 않으면 컴파일러가 올바른 유형을 추론 할 수없는 경우 쉽게 실행할 수 있습니다.

이러한 경우에 당신은 더 명시 적 형태, 즉 사용할 수 있습니다 .map_err::<MyError, _>(From::from), 또는 단지를 .map_err(MyError::from).

fn main() -> Result<(), MyError> {
    my_error()?;
    // other_error().map_err(From::from)
    // other_error().map_err::<MyError, _>(From::from)?;
    other_error().map_err(MyError::from)
}

당신이 경우 MyError했다 enum있던, MyError::OtherError변형, 당신도 바로 할 수 .map_err(MyError::OtherError). 이것은 enum함수 호출처럼 보이는 것을 구성 할뿐만 아니라 실제로 functions로 구현되기 때문입니다 .

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

Related 관련 기사

뜨겁다태그

보관