[英]Strange behavior when adding the Send trait to a boxed trait object
Here's an error struct:这是一个错误结构:
#[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);
}
I decided to make it Send
able so I changed source: Option<Box<dyn std::error::Error>>
to source: Option<Box<dyn std::error::Error + Send>>
and strange things happened.我决定让它能够Send
所以我将source: Option<Box<dyn std::error::Error>>
更改为source: Option<Box<dyn std::error::Error + Send>>
并且奇怪的事情发生了。
new_caused
refused to compile any more: 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`
Changing Box::from
to Box::new
helped, even though their signatures seem identical and Box::from
's implementation just calls Box::new
.将Box::from
更改为Box::new
帮助,即使它们的签名看起来相同并且Box::from
的实现只是调用Box::new
。
source
also became incorrect: 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>`
Why isn't the unused Send
trait ignored like other ones?为什么未使用的Send
特性不像其他特性那样被忽略?
Replacing that combinator logic with its manual version worked fine:用手动版本替换该组合器逻辑工作正常:
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.source {
Some(source) => Some(source.as_ref()),
None => None
}
}
What are the explanations for this "magic" and what are better methods of dealing with it?这种“魔法”的解释是什么,有什么更好的处理方法?
For magic #1, it is because the standard library has these implementations:对于魔法 #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>
There is no implementation for E: Error + Send
without the Sync
. E: Error + Send
without the Sync
没有实现。
Easy solution #1: add Sync
whereever there is a Send
or use Box::new
.简单的解决方案#1:在有Send
地方添加Sync
或使用Box::new
。
Magic #2 is more complicated: you have an std::option::Option<&dyn std::error::Error + Sync>
where you need an Option<&dyn std::error::Error>
. Magic #2 更复杂:你有一个std::option::Option<&dyn std::error::Error + Sync>
,你需要一个Option<&dyn std::error::Error>
。 You know that a &(dyn std::error::Error + Send)
is convertible to a &dyn std::error::Error
so you expect that the Option<_>
will be too, but those conversions are not transitive 1 , so it fails.您知道&(dyn std::error::Error + Send)
可转换为&dyn std::error::Error
因此您希望Option<_>
也是如此,但这些转换不是可传递的1 ,所以它失败了。
The difference between the map
and the match
is in the order of type deduction: map
和match
的区别在于类型推导的顺序:
In the map
case, the type of the closure is deduced as taking a Box<dyn std::error::Error + Sync>
.在map
情况下,闭包的类型被推导出为Box<dyn std::error::Error + Sync>
。 Since it returns err.as_ref()
, of type &dyn std::error::Error + Sync
, that is the type that the closure returns.由于它返回类型为&dyn std::error::Error + Sync
err.as_ref()
,这就是闭包返回的类型。 Then Option::map
returns an Option<_>
with the same type of the closure return type so you get a final Option<&dyn std::error::Error + Sync>
and an error.然后Option::map
返回一个Option<_>
与闭包返回类型相同的类型,所以你得到一个最终的Option<&dyn std::error::Error + Sync>
和一个错误。
In the match
code, when you write Some(source) => Some(source.as_ref())
, source
is deduced as type Box<dyn std::error::Error + Sync>
, but the right side is deduced from the returned type Option<&dyn std::error::Error>
, so the argument to Some
is cast to that type: source.as_ref()
is converted to the right type and it compiles.在match
代码中,当你写Some(source) => Some(source.as_ref())
, source
被推导为类型Box<dyn std::error::Error + Sync>
,但右侧是从返回类型Option<&dyn std::error::Error>
,因此Some
的参数被source.as_ref()
转换为该类型: source.as_ref()
被转换为正确的类型并进行编译。
I think the easiest way to write this example is to add a cast as _
inside the map, to instruct the compiler to deduce the type of the closure from the usage instead of from the inner code:我认为编写此示例的最简单方法是在映射中添加一个 cast as _
,以指示编译器从用法而不是从内部代码推断闭包的类型:
self.source.as_ref().map(|err| err.as_ref() as _)
If the code is more complex, as _
may not be feasible.如果代码比较复杂, as _
可能不可行。 Then a match
is perfectly adequate.那么一场match
就足够了。
Playground with the fixed code.带有固定代码的游乐场。
1: I think I read about making these conversions automatic ( covariant on auto traits?) but I cannot find it anywhere... 1:我想我读过关于使这些转换自动(自动特征的协变?),但我在任何地方都找不到它......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.