pas*_*ugh 3 multithreading compiler-errors traits thread-safety rust
这是一个错误结构:
#[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);
}
Run Code Online (Sandbox Code Playgroud)
我决定让它成为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`
Run Code Online (Sandbox Code Playgroud)
更改Box::from为Box::newhelped,即使它们的签名看起来相同并且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>`
Run Code Online (Sandbox Code Playgroud)
为什么未使用的Send特征不像其他特征一样被忽略?
用手动版本替换组合器逻辑效果很好:
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.source {
Some(source) => Some(source.as_ref()),
None => None
}
}
Run Code Online (Sandbox Code Playgroud)
对于这种“魔力”有什么解释以及更好的处理方法是什么?
对于魔术#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>
Run Code Online (Sandbox Code Playgroud)
如果E: Error + Send没有Sync.
简单的解决方案#1:Sync在有Send或 使用 的地方添加Box::new。
Magic #2 更复杂:您有一个std::option::Option<&dyn std::error::Error + Sync>需要Option<&dyn std::error::Error>. 您知道 a&(dyn std::error::Error + Send)可转换为 a &dyn std::error::Error,因此您期望 aOption<_>也可转换,但这些转换不具有传递性1,因此失败。
mapthe和 the的区别match在于类型推导的顺序:
在这种map情况下,闭包的类型被推导出为采用 a Box<dyn std::error::Error + Sync>。由于它返回err.as_ref(), of type &dyn std::error::Error + Sync,这就是闭包返回的类型。然后Option::map返回一个Option<_>与闭包返回类型相同的类型,这样你就会得到一个finalOption<&dyn std::error::Error + Sync>和一个错误。
在match代码中,当您编写 时Some(source) => Some(source.as_ref()),source被推导为 type Box<dyn std::error::Error + Sync>,但右侧是从返回的 type 推导出来的Option<&dyn std::error::Error>,因此参数 toSome被强制转换为该类型:source.as_ref()转换为正确的类型并进行编译。
我认为编写此示例的最简单方法是as _在映射内添加强制转换,以指示编译器从用法而不是从内部代码推断闭包的类型:
self.source.as_ref().map(|err| err.as_ref() as _)
Run Code Online (Sandbox Code Playgroud)
如果代码比较复杂,as _可能不太可行。那么 amatch就完全足够了。
具有固定代码的游乐场。
1:我想我读过有关使这些转换自动进行的内容(自动特征的协变?),但我在任何地方都找不到它......
| 归档时间: |
|
| 查看次数: |
1309 次 |
| 最近记录: |