std :: error :: FromError惯用法

时间:2015-03-19 22:32:33

标签: error-handling rust

我正试图在我的项目中尽可能广泛地使用std::error::FromError特征来利用try!宏。但是,我有点迷失在不同mod之间的这些错误转换。

例如,我有mod(或crate)a,它使用自己的Error类型进行错误处理,并实现io::Error的错误转换:

mod a {
    use std::io;
    use std::io::Write;
    use std::error::FromError;

    #[derive(Debug)]
    pub struct Error(pub String);

    impl FromError<io::Error> for Error {
        fn from_error(err: io::Error) -> Error {
            Error(format!("{}", err))
        }
    }

    pub fn func() -> Result<(), Error> {
        try!(writeln!(&mut io::stdout(), "Hello, world!"));
        Ok(())
    }
}

我在同一情况下也有mod b,但它实现了num::ParseIntError的错误转换:

mod b {
    use std::str::FromStr;
    use std::error::FromError;
    use std::num::ParseIntError;

    #[derive(Debug)]
    pub struct Error(pub String);

    impl FromError<ParseIntError> for Error {
        fn from_error(err: ParseIntError) -> Error {
            Error(format!("{}", err))
        }
    }

    pub fn func() -> Result<usize, Error> {
        Ok(try!(FromStr::from_str("14")))
    }
}

现在我在我当前的mod super中,它有自己的Error类型,我的目标是编写一个这样的过程:

#[derive(Debug)]
struct Error(String);

fn func() -> Result<(), Error> {
    println!("a::func() -> {:?}", try!(a::func()));
    println!("b::func() -> {:?}", try!(b::func()));
    Ok(())
}

所以我绝对需要为a::Error类型实现b::ErrorError的转化:

impl FromError<a::Error> for Error {
    fn from_error(a::Error(contents): a::Error) -> Error {
        Error(contents)
    }
}

impl FromError<b::Error> for Error {
    fn from_error(b::Error(contents): b::Error) -> Error {
        Error(contents)
    }
}

好的,直到那时为止。现在我需要写这样的东西:

fn another_func() -> Result<(), Error> {
    let _ = try!(<usize as std::str::FromStr>::from_str("14"));
    Ok(())
}

此处出现问题,因为没有从num::ParseIntError转换为Error。所以我似乎必须再次实施它。但我为什么要这样?已经实施了从num::ParseIntErrorb::Error的转换,还有从b::ErrorError的转换。因此,在没有我明确帮助的情况下,确实有一种干净的方法可以将一种类型转换为另一种类型。

所以,我删除了impl FromError<b::Error>块并尝试了这个毯子impl:

impl<E> FromError<E> for Error where b::Error: FromError<E> {
    fn from_error(err: E) -> Error {
        let b::Error(contents) = <b::Error as FromError<E>>::from_error(err);
        Error(contents)
    }
}

它甚至有效!但是,我没有成功用a::Error重复这个技巧,因为rustc开始抱怨冲突的实现:

experiment.rs:57:1: 62:2 error: conflicting implementations for trait `core::error::FromError` [E0119]
experiment.rs:57 impl<E> FromError<E> for Error where a::Error: FromError<E> {
experiment.rs:58     fn from_error(err: E) -> Error {
experiment.rs:59         let a::Error(contents) = <a::Error as FromError<E>>::from_error(err);
experiment.rs:60         Error(contents)
experiment.rs:61     }
experiment.rs:62 }
experiment.rs:64:1: 69:2 note: note conflicting implementation here
experiment.rs:64 impl<E> FromError<E> for Error where b::Error: FromError<E> {
experiment.rs:65     fn from_error(err: E) -> Error {
experiment.rs:66         let b::Error(contents) = <b::Error as FromError<E>>::from_error(err);
experiment.rs:67         Error(contents)
experiment.rs:68     }
experiment.rs:69 }

我甚至可以理解问题的根源(FromError<E>a::Error可以实现一种类型b::Error,但我无法解决它。

理论上,也许这是一种错误的方式,我的问题有另一种解决方案吗?或者我仍然需要在每个新模块中手动重复所有错误转换?

1 个答案:

答案 0 :(得分:2)

  

没有从num::ParseIntError转换为Error

从概念上看,你似乎做错了事。当库生成io::Error时,就像您的第一个示例一样,它应该直到该库来决定如何处理该错误。但是,从您的问题来看,这听起来好像是在其他地方生成io::Error s ,然后又希望将它们视为第一个库。

这似乎很奇怪。我不希望将库B生成的错误交给库A并说出&#34;包装此错误就好像你做了它一样#34;。也许您正在做的事情应该是相应库的部分?然后它可以像往常一样处理错误。也许您可以接受一个闭包并在适当时调用错误转换。

  

因此,在没有我的明确帮助的情况下,Rust有一种简洁的方法可以将一种类型转换为另一种

(强调我的)。这对我来说似乎非常可怕。隐式转换中应该允许多少个步骤?如果有多条路径,或者即使有循环,该怎么办?将这些作为明确的步骤对我来说似乎是合理的。

  

我甚至可以理解问题的根源[...],但我无法解决问题。

我认为不可能解决这个问题。如果你可以用多种不同的方式为同一类型实现一个特征,那么就没有办法在它们之间进行选择,因此编码模糊不清并被编译器拒绝。