这是一个错误结构:
#[derive(Debug)]
pub struct Error {
msg: &'static str,
//source: Option<Box<dyn std::error::Error>>, // old
source: Option<Box<dyn std::error::Error + Send>>, // new
}
impl Error {
fn new_caused<E>(msg: &'static str, err: E) -> Self
where
E: 'static + std::error::Error + Send,
{
Self {
msg: msg,
source: Some(Box::from(err)),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{}", self.msg) // HACK
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|err| err.as_ref())
}
}
fn main() {
let err = "this will fail".parse::<i32>().unwrap_err();
let err = Error::new_caused("some msg", err);
}
我决定使其变为Send
,因此我将source: Option<Box<dyn std::error::Error>>
更改为source: Option<Box<dyn std::error::Error + Send>>
,然后发生了奇怪的事情。
new_caused
拒绝再编译:
error[E0277]: the trait bound `std::boxed::Box<dyn std::error::Error + std::marker::Send>: std::convert::From<E>` is not satisfied
--> src/main.rs:14:26
|
14 | source: Some(Box::from(err)),
| ^^^^^^^^^^^^^^ the trait `std::convert::From<E>` is not implemented for `std::boxed::Box<dyn std::error::Error + std::marker::Send>`
|
= note: required by `std::convert::From::from`
将Box::from
更改为Box::new
很有帮助,即使它们的签名看起来相同,并且Box::from
的实现只是调用Box::new
。
source
也变得不正确:
error[E0308]: mismatched types
--> src/main.rs:27:9
|
27 | self.source.as_ref().map(|err| err.as_ref())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::error::Error`, found trait `std::error::Error + std::marker::Send`
|
= note: expected enum `std::option::Option<&(dyn std::error::Error + 'static)>`
found enum `std::option::Option<&dyn std::error::Error + std::marker::Send>`
为什么未使用的Send
特性不会像其他特性一样被忽略?
将组合器逻辑替换为其手动版本可以正常工作:
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.source {
Some(source) => Some(source.as_ref()),
None => None
}
}
对此“魔术”有什么解释?有什么更好的处理方法?
答案 0 :(得分:4)
对于魔术#1,这是因为标准库具有以下实现:
impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a>
impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a>
没有E: Error + Send
的{{1}}无法实现。
简单的解决方案#1:在有Sync
的地方添加Sync
或使用Send
。
魔术#2更为复杂:您有一个Box::new
,需要一个std::option::Option<&dyn std::error::Error + Sync>
。您知道Option<&dyn std::error::Error>
可以转换为&(dyn std::error::Error + Send)
,因此您希望&dyn std::error::Error
也可以转换,但是这些转换不是可传递的 1 ,因此失败
Option<_>
和map
之间的区别在于类型推导的顺序:
在match
情况下,闭包的类型推导为map
。由于它返回类型为Box<dyn std::error::Error + Sync>
的{{1}},因此闭包将返回该类型。然后err.as_ref()
返回一个&dyn std::error::Error + Sync
,其类型与闭包返回类型相同,因此您将得到最终的Option::map
和一个错误。
在Option<_>
代码中,当您编写Option<&dyn std::error::Error + Sync>
时,match
被推导为类型Some(source) => Some(source.as_ref())
,但是从返回的类型{{1}推导出右侧。 },因此将source
的参数强制转换为该类型:Box<dyn std::error::Error + Sync>
被转换为正确的类型并进行编译。
我认为编写此示例的最简单方法是在映射内部添加强制转换Option<&dyn std::error::Error>
,以指示编译器从用法而不是从内部代码推导闭包的类型:
Some
如果代码更复杂,则source.as_ref()
可能不可行。那么as _
就足够了。
Playground和固定代码。
1:我想我读过有关自动进行这些转换的信息(关于自动特征,它们是 covariant ?),但我在任何地方都找不到它...