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