Generics 创建简洁和泛型错误类型时出现问题

Generics 创建简洁和泛型错误类型时出现问题,generics,types,error-handling,rust,Generics,Types,Error Handling,Rust,我很难尝试创建一种既通用又不会陷入混乱的impls和通用参数的错误类型 我有一个结构,其中包含几个子系统的通用参数,每个参数都有返回自定义错误类型的方法: struct App<A: ..., B: ..., ...> { a: A, b: B, ... } impl<A: ..., B: ..., ...> App<A, B, ...> { fn do_something_cross_cutting(&self) -

我很难尝试创建一种既通用又不会陷入混乱的
impl
s和通用参数的错误类型

我有一个结构,其中包含几个子系统的通用参数,每个参数都有返回自定义错误类型的方法:

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
现在是泛型的,而且似乎不可能在
impl
块中定义类型别名,因此每个可能失败的方法都会有非常广泛的返回类型(
Result

快速出错是可以避免的,但同样以可读性和代码大小为代价,并且它不能解决第二个问题

我提出了以下备选方案,但没有编译:

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))
    }
}

...
快速出错!{
#[导出(调试)]
枚举表{
错误(错误:框){
显示(…)
}
错误(错误:框){
显示(…)
}
...
}
}
从为AppError执行的impl{
fn from(err:A::Error)->Self{
AppError::AError(框::new(err))
}
}
...
rustc
抱怨类型参数'A'不受impl-trait、self-type或谓词的约束,我不知道如何修复它

最后一种可能是最不冗长的:只需传播一个
。这是我的B计划。最大的问题是有用的信息丢失了。通过定义
AppError
(递归地定义子系统特定的错误类型),我得到了一个穷人对错误的回溯。使用
将使错误难以跟踪


是否还有其他选择,或者我是否以错误的方式处理此问题?

错误
类型参数'A'不受impl-trait、self-type或谓词的约束是什么意思?让我们看一个简化的示例:

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
    }
}
trait Foo{
类型错误;
}
结构杆;
结构Baz;
为酒吧提供便利{
类型错误=std::io::Error;
}
为Baz提供impl-Foo{
类型错误=std::io::Error;
}
结构;
来自Quux的impl{
fn from(err:T::Error)->qux{
库克斯
}
}
问题是类型
U
可以匹配多个
T
T::Error
。例如,在这里,
std::io::Error
将同时匹配
Bar::Error
Baz::Error
。那么,编译器应该为
T
选择哪种类型呢


解决方案很简单:不要使用
A::Error
B::Error
;相反,将
A
B
直接定义为错误类型。在
App
中使用
AppError
时,将返回
AppError
,而不是
AppError
。是的,你必须到处重复
::Error
;我认为没有解决方法。

错误是什么
类型参数'a'不受impl-trait、self-type或谓词的约束
的意思是什么?让我们看一个简化的示例:

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
    }
}
trait Foo{
类型错误;
}
结构杆;
结构Baz;
为酒吧提供便利{
类型错误=std::io::Error;
}
为Baz提供impl-Foo{
类型错误=std::io::Error;
}
结构;
来自Quux的impl{
fn from(err:T::Error)->qux{
库克斯
}
}
问题是类型
U
可以匹配多个
T
T::Error
。例如,在这里,
std::io::Error
将同时匹配
Bar::Error
Baz::Error
。那么,编译器应该为
T
选择哪种类型呢


解决方案很简单:不要使用
A::Error
B::Error
;相反,将
A
B
直接定义为错误类型。在
App
中使用
AppError
时,将返回
AppError
,而不是
AppError
。是的,你必须到处重复
::Error
;我认为没有解决办法。

最后,我使用了一种自定义错误类型,它在构造时检索回溯(使用板条箱),并将实际错误存储为一个

然后对每个相关的错误类型
x
为x{}
执行apperrorg


因此,错误类型是简洁的(简单地说是
AppError
),泛型的(只是标记相关类型的问题),并且有回溯。缺点是无法解构和检查错误。需要一个标签可能有点痛苦,特别是由于一致性规则导致的外部板条箱上的错误。真正理想的解决方案是使用负特征边界,但我们还没有做到这一点。

最后,我使用了一种自定义错误类型,它在构造时检索回溯(使用板条箱),并将实际错误存储为

然后对每个相关的错误类型
x
为x{}
执行apperrorg


因此,错误类型是简洁的(简单地说是
AppError
),泛型的(只是标记相关类型的问题),并且有回溯。缺点是无法解构和检查错误。需要一个标签可能有点痛苦,特别是由于一致性规则导致的外部板条箱上的错误。真正理想的解决方案是使用负特征边界,但我们还没有做到。

关于回溯问题:您尝试过吗?它有一个回溯功能,集成了。我来看看你的回溯问题:你试过了吗?它有回溯功能,集成了。我来看看
#[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 {...}
impl<E: AppErrorTag> From<E> for AppError {...}

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