Можно ли реализовать метод, который принимает строку формата?

я обнаружил, что использую .expect("...") паника с понятным сообщением об ошибке во многих местах, где я не забочусь о восстановлении после ошибки. Пример:

let p = "foo.txt";
let f = File::open(p).expect("File not found");

однако я хотел бы напечатать больше информации, используя форматированную строку. Вот что я мог бы сделать:--7-->

let f = File::open(p).expect(&format("{} not found", p));

здесь есть две проблемы:

  1. на format звонок будет оцениваться с нетерпением.

  2. это излишне многословный.

в идеале, я хотел бы написать:

// pseudocode
let f = File::open(p).expect_fmt("{} not found", p);

но я предполагаю, что это не возможно без вариативную универсальные функции и компиляции синтаксический анализ строк.

единственная альтернатива, которую я нашел, это следующее:

let f = File::open(p).unwrap_or_else(|_| panic!("{} not found", p));

который все еще слишком многословен на мой вкус.

это приемлемо, если ответ использует ночную ржавчину.

2 ответов


TL; DR: нет.


В то время как не так много нужно сделать для уменьшения проблем многословия, вы можете избежать выделения памяти с помощью std::fmt::Arguments

trait ResultExt<T, E> {
    fn expect_fmt<D>(self, msg: D) -> T
    where
        D: std::fmt::Display;
}

impl<T, E> ResultExt<T, E> for Result<T, E>
where
    E: std::error::Error,
{
    fn expect_fmt<D>(self, msg: D) -> T
    where
        D: std::fmt::Display,
    {
        match self {
            Ok(t) => t,
            Err(e) => panic!("{}: {}", msg, e),
        }
    }
}

use std::fs::File;

fn main() {
    let p = "foo.txt";
    let f = File::open(p).expect_fmt(format_args!("{} not found", p));
}

не стесняйтесь регулировать границы признака, как вы считаете нужным.

не возможно без вариативную универсальные функции и синтаксический анализ строк во время компиляции

именно это format_args макрос, но вы все равно должны позвонить он.


для избежания нетерпеливого форматирования сообщения об ошибке может использоваться закрытие, вместе с простым макросом для уменьшения многословия немного:

macro_rules! crash {
    ( $( $p:tt ),* ) => {
        |_| panic!($($p),*);
    };
}

затем утверждение сахаризируется как:

    let f = File::open(p).unwrap_or_else(crash!("{} not found", p));

нет большой добавленной стоимости с этим подходом.

вы рассматривали ошибка-цепь или провал как альтернативы для вашего дизайна управления ошибками?

они имеют много, котор нужно предложить для эффективной ошибки обращение.

используя простой макрос, вы все равно должны настроить творчески синтаксис, чтобы приблизиться к вашей цели, ниже error-chain пример:

macro_rules! or {
    ( $( $p:tt ),* ) => {{
        || format!($($p),*)
    }};
}

fn run() -> Result<()> {
    use std::fs::File;
    // This operation will fail
    let p = "foo";

    File::open(p).chain_err(or!("unable to open {} file", p))?;

    Ok(())
}

читать как: invoke chain_error способ с список вариативная арг.