2017-05-07 47 views
0

我很難嘗試創建一個通用的錯誤類型,但它不會轉化爲impl和泛型參數的混亂。創建簡潔和通用錯誤類型時遇到問題

我有分系統的幾個通用參數的結構,每個已返回自定義錯誤類型的方法:

struct App<A: ..., B: ..., ...> { 
    a: A, 
    b: B, 
    ... 
} 

impl<A: ..., B: ..., ...> App<A, B, ...> { 
    fn do_something_cross_cutting(&self) -> Result<(), AppError> { 
     a.run()?; 
     b.run()?; 

     Ok(()) 
    } 
} 

應該如何AppError被幹淨定義?起初,我嘗試使用quick_error!像這樣:

quick_error! { 
    #[derive(Debug)] 
    enum AppError<A: ..., B: ..., ...> { 
     AError(err: A::Error) { 
      from() 
      display(...) 
     } 

     BError(err: B::Error) { 
      from() 
      display(...) 
     } 

     ... 
    } 
} 

quick_error!似乎不支持通用枚舉。此外,由於AppError現在是通用的看起來好像不可能在impl塊中定義類型別名,每種可能失敗的方法都會有非常廣泛的返回類型(Result<_, AppError<A, B, ...>>)。

quick_error!可以避免,但同樣以犧牲可讀性和代碼大小爲代價,並且它不能解決第二個問題。

我想出了以下替換,但不編譯:

quick_error! { 
    #[derive(Debug)] 
    enum AppError { 
     AError(err: Box<Error>) { 
      display(...) 
     } 

     BError(err: Box<Error>) { 
      display(...) 
     } 

     ... 
    } 
} 

impl<A: ...> From<A::Error> for AppError { 
    fn from(err: A::Error) -> Self { 
     AppError::AError(Box::new(err)) 
    } 
} 

... 

rustc抱怨與the type parameter `A` is not constrained by the impl trait, self type, or predicates,我不知道如何解決它。

最後的可能性是最簡單的:簡單地傳播一個Box<Error>。這是我的計劃B.最大的問題是有用的信息丟失了。通過定義AppError(以及遞歸的子系統特定的錯誤類型),我得到了一個窮人的錯誤回溯。使用Box<Error>會使錯誤難以追蹤。

有沒有其他的選擇,或者我接近這個錯誤的方式嗎?

+0

關於你的追溯問題:你有沒有試過['error_chain'](https://crates.io/crates/error-chain)?它具有集成的回溯功能。 –

+0

我來看看 – moatPylon

回答

0

在我與檢索上構成的回溯(使用backtrace板條箱)的自定義錯誤類型去端部和存儲實際誤差爲Box<Error>

#[derive(Debug)] 
pub struct AppError { 
    err: Box<Error + Send + Sync>, 
    trace: Vec<String>, 
} 

impl AppError { 
    pub fn new<E: Into<Box<Error + Send + Sync>>>(err: E) -> Self { 
     let mut trace = Vec::new(); 
     // Get backtrace 

     AppError { 
      err: err.into(), 
      trace, 
     } 
    } 
} 

impl Display for AppError {...} 

impl Error for AppError {...} 

唯一的皺紋是由於AppError實現Error ,我不能有一個毯子impl<E: Error> From<E> for AppError。相反:

impl<E: AppErrorTag> From<E> for AppError {...} 

trait AppErrorTag: Into<Box<Error + Send + Sync>> {} 

然後對每一個相關的錯誤類型ximpl AppErrorTag for x {}。因此,錯誤類型是簡潔的(簡單地說是AppError),通用的(只是標記相關類型的問題),並且具有回溯。缺點是錯誤不能被解構和檢查。由於一致性規則,對外部包裝箱上的錯誤特別需要標籤可能會有點痛苦。真正理想的解決方案會使用負面的特質邊界,但我們還沒有。

2

錯誤the type parameter `A` is not constrained by the impl trait, self type, or predicates是什麼意思?讓我們來看看在降低的例子:

trait Foo { 
    type Error; 
} 

struct Bar; 

struct Baz; 

impl Foo for Bar { 
    type Error = std::io::Error; 
} 

impl Foo for Baz { 
    type Error = std::io::Error; 
} 

struct Quux; 

impl<T: Foo> From<T::Error> for Quux { 
    fn from(err: T::Error) -> Quux { 
     Quux 
    } 
} 

的問題是,一個類型U可以爲多個T匹配T::Error。這裏,例如,std::io::Error將匹配Bar::ErrorBaz::Error。那麼,編譯器爲T選擇哪種類型?

解決方法很簡單:不要使用A::ErrorB::Error;相反,直接定義AB的錯誤類型。當您在App內使用AppError時,將返回AppError<A::Error, B::Error>而不是AppError<A, B>。是的,你必須在任何地方重複::Error;我不認爲有這樣的解決方案。