Возврат сообщения об ошибке в функцию, ожидающую 'Box' - PullRequest
1 голос
/ 15 октября 2019

Я новичок в Rust и пытаюсь распространить ошибки, которые будут обработаны в вызывающей функции. Из официальной книги о ржавчине я прочитал, что Result «Box » используется, чтобы сказать «захватить любой тип ошибки», но я не прочитал достаточно далеко, чтобы понять, как это на самом деле работает.

У меня есть функция с именем:

fn foo() -> Result<String, Box<dyn Error>> {
    Command::new("an_executable")
        .args(&["-p", path])
        .output()?;
    if condition {
        return Err("Error...");
    }
    // Do stuff, return String 
}

Так что кто-то может объяснить, как я должен возвращать ошибку, если условие с этим типом возврата выполняется. Должен ли я изменить тип возвращаемого значения или просто вернуть что-то другое? каким будет стандарт RUST в этом сценарии?

Текущая ошибка компиляции заключается в том, что Err («Ошибка ...») не соответствует типу возвращаемого значения

1 Ответ

3 голосов
/ 15 октября 2019

Давайте начнем с абсолютного минимального воспроизведения вашей проблемы:

use std::error::Error;

fn foo() -> Result<String, Box<dyn Error>> {
    Err("Error...")
}

Возвращенная ошибка:

error[E0308]: mismatched types
 --> src/lib.rs:4:9
  |
4 |     Err("Error...")
  |         ^^^^^^^^^^ expected struct `std::boxed::Box`, found reference
  |
  = note: expected type `std::boxed::Box<dyn std::error::Error>`
             found type `&'static str`

Это говорит о том, что подпись функции ожидала, что вы вернете Err содержащий Box, но вы фактически вернули &str. Итак, давайте разберем эту строку:

use std::error::Error;

fn foo() -> Result<String, Box<dyn Error>> {
    Err(Box::new("Error..."))
}

Теперь вы получаете новую ошибку:

error[E0277]: the trait bound `&str: std::error::Error` is not satisfied
 --> src/lib.rs:4:9
  |
4 |     Err(Box::new("Error..."))
  |         ^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `&str`
  |
  = note: required for the cast to the object type `dyn std::error::Error`

Опять же, проблема в том, что вы возвращаете тип, который не соответствует вашей функцииподпись - это ожидание того, что поле будет содержать что-то, что реализует черту Error, но если вы посмотрите на документы , вы заметите, что &str не относится к тем типам, которые его реализуют. Вам нужно обернуть вашу строку в тип, который реализует Error:

use std::error::Error;
use std::fmt;

#[derive(Debug)]
struct StrError<'a>(&'a str);

// Error doesn't require you to implement any methods, but
// your type must also implement Debug and Display.
impl<'a> Error for StrError<'a> {}

impl<'a> fmt::Display for StrError<'a>{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Delegate to the Display impl for `&str`:
        self.0.fmt(f)
    }
}

fn foo() -> Result<String, Box<dyn Error>> {
    Err(Box::new(StrError("Error...")))
}

Этот код компилируется!

РЕДАКТИРОВАТЬ: Я только что узнал, что естьболее простой способ сделать это - есть реализация From<&str> для Box<dyn Error>, поэтому, если вы вызовете это, вы можете избежать описанного выше шаблона. То же самое происходит под капотом, более или менее, и я надеюсь, что более подробное объяснение было полезно!.

use std::error::Error;

fn foo() -> Result<String, Box<dyn Error>> {
    Err("Error...".into())
    // `Err(Box::<dyn Error>::from("Error..."))` would also work, but is more ugly!
}
...