rust - 导致类型不匹配的特征对象
问题描述
从外部库实现特征时,我发现我的初始实现无法编译。
// From library
trait SomeTrait {
fn do_thing(&self) -> Result<i32, Box<dyn Error + Sync + Send + 'static>>;
}
// My code
struct Foo;
impl SomeTrait for Foo {
fn do_thing(&self) -> Result<i32, Box<dyn Error + Sync + Send + 'static>> {
i32::from_str("42").map_err(|e| Box::new(MyError))
}
}
#[derive(Debug)]
struct MyError;
impl Display for MyError {...}
impl Error for MyError {}
来自 rustc v1.55.0 的错误:
error[E0308]: mismatched types
--> src/main.rs:17:9
|
16 | fn do_thing(&self) -> Result<i32, Box<dyn Error + Sync + Send + 'static>> {
| --------------------------------------------------- expected `Result<i32, Box<(dyn std::error::Error + Send + Sync + 'static)>>` because of return type
17 | i32::from_str("42").map_err(|e| Box::new(MyError))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::error::Error`, found struct `MyError`
|
= note: expected enum `Result<_, Box<(dyn std::error::Error + Send + Sync + 'static)>>`
found enum `Result<_, Box<MyError>>`
如果我将主体更改do_thing
为这个,编译器会很高兴。
match i32::from_str("42") {
Ok(v) => Ok(v),
Err(_) => Err(Box::new(MyError)),
}
我也可以这样做,编译器也接受但非常难看。
i32::from_str("42").map_err(|e| Box::new(MyError) as Box<dyn Error + Sync + Send + 'static>)
我显然有一个可行的解决方案,但我想知道实现之间的区别是什么,以避免将来拉我的头发。
解决方案
从Box<MyError>
to的转换Box<dyn Error + ...>
称为unsizing coercion(因为它从具有静态已知大小的类型转换为没有大小的类型)。可以如此转换类型的确切规则由特殊特征CoerceUnsized
和定义Unsize
。(这些特性目前是不稳定的,但这只是意味着你不能在稳定的 Rust 中实现或直接调用它们;你仍然可以利用它们的效果。)
文档告诉我们存在以下实现:
impl<T, U, A> CoerceUnsized<Box<U, A>> for Box<T, A>
where
T: Unsize<U> + ?Sized,
A: Allocator,
U: ?Sized,
这个通用实现涵盖了以下情况
impl CoerceUnsized<Box<dyn Error + ...>> for Box<MyError>
这样您就可以在内部强制map_err
执行,但是对于 没有这样的实现Result
,因此您不能对整个执行强制执行Result
,并且必须通过将错误值带出到它自己的类型变量Box<MyError>
分配或返回到需要Box<dyn Error + ...>
.
这就是您要的“原因”。现在是编写代码的提示:您可以as _
说“我想要一个强制,但请推断类型,因为它很明显”:
i32::from_str("42").map_err(|e| Box::new(MyError) as _)
这样您就不必编写冗长的dyn
类型。您还可以定义一个类型别名来减少重复,同时仍然是明确的:
type DynError = Box<dyn Error + Sync + Send + 'static>;
...
i32::from_str("42").map_err(|e| Box::new(MyError) as DynError)
但是,对于 的特定情况Box<dyn Error>
,更好的选择是仅使用?
运算符及其自动Into
转换:
fn do_thing(&self) -> Result<i32, Box<dyn Error + Sync + Send + 'static>> {
Ok(i32::from_str("42")?)
}
这是有效?
的,因为通过转换返回错误Into::into(e)
,并且标准库包含从any Error
到的通用转换Box<dyn Error + Send + Sync>
:
impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a>
这可能是图书馆希望您使用的。
在这个简单的例子中, the?
也等价于:
fn do_thing(&self) -> Result<i32, Box<dyn Error + Sync + Send + 'static>> {
i32::from_str("42").map_err(Into::into)
}