rust - 如何使用具有生命周期的类型作为 io::Error::new 的 `error` 参数?

标签 rust

我正在尝试通过实现 std::error::Error 创建自定义错误类型以在我的 Rust 项目中使用。我还创建了一个小的快捷函数来创建 std::io::Error。不幸的是,我受困于生命周期,所以我寻求帮助:

use std::error::Error;
use std::fmt;
use std::io;

#[derive(Debug)]
pub struct BadString<'a> {
    desc: &'a str,
}

impl<'a> BadString<'a> {
    pub fn new(desc: &str) -> BadString {
        BadString{ desc: desc }
    }
}

impl<'a> Error for BadString<'a> {
    fn description(&self) -> &str { &self.desc }
}

impl<'a> fmt::Display for BadString<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

fn bad_str_err(desc: &str) -> io::Error {
    let err = BadString::new(desc);
    io::Error::new(io::ErrorKind::Other, err)
}

fn main() {

}

playground

这会报告错误:

<anon>:27:30: 27:34 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
<anon>:27     let err = BadString::new(desc);
                                       ^~~~
<anon>:28:5: 28:46 note: first, the lifetime cannot outlive the call at 28:4...
<anon>:28     io::Error::new(io::ErrorKind::Other, err)
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:28:42: 28:45 note: ...so that argument is valid for the call
<anon>:28     io::Error::new(io::ErrorKind::Other, err)
                                                   ^~~
<anon>:27:30: 27:34 note: but, the lifetime must be valid for the expression at 27:29...
<anon>:27     let err = BadString::new(desc);
                                       ^~~~
<anon>:27:30: 27:34 note: ...so that auto-reference is valid at the time of borrow
<anon>:27     let err = BadString::new(desc);
                                       ^~~~
error: aborting due to previous error
playpen: application terminated with error code 101

我不确定如何修复它以便编译。

最佳答案

我们来看 io::Error::new 的签名:

fn new<E>(kind: ErrorKind, error: E) -> Error 
    where E: Into<Box<Error + Send + Sync>>

这表明 error可以是任何类型,只要该类型实现了特征 Into<Box<Error + Send + Sync>> .该特征意味着该类型可以转换为装箱特征对象。特征对象本身必须实现特征 Error , SendSync .不明显的是,默认情况下,特征对象也有一个 'static。 lifetime bound(这是有道理的,但它似乎确实让人绊倒)。

让我们尝试自己进行转换:

fn bad_str_err(desc: &str) -> io::Error {
    let err = BadString::new(desc);
    let foo: Box<Error + Send + Sync + 'static> = err.into();
}

我们得到了同样的错误——“由于需求冲突,无法推断出自动强制转换的适当生命周期”。所以我们的问题就在于能否转换为这个trait对象。

Send Sync 是两个关键特征,有助于指导编译器了解哪些类型可以安全地在线程之间发送/共享。为了在线程之间安全地共享某些东西,它不能在另一个线程拥有它时“消失”。这是 Rust 在编译时帮助防止的一种错误。

在这种情况下,您打算使用一个字符串切片 ( &str ),但该切片不拥有底层内存,它只是引用它。一旦该内存超出范围,任何引用都需要变为无效。这是 Rust 在编译时阻止的另一件事。

在这种情况下,最简单的做法就是不使用引用:

use std::error::Error;
use std::{fmt, io};

#[derive(Debug)]
pub struct BadString {
    desc: String,
}

impl BadString {
    pub fn new(desc: &str) -> BadString {
        BadString { desc: desc.into() }
    }
}

impl Error for BadString {
    fn description(&self) -> &str { &self.desc }
}

impl fmt::Display for BadString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

fn bad_str_err(desc: &str) -> io::Error {
    let err = BadString::new(desc);
    io::Error::new(io::ErrorKind::Other, err)
}

fn main() {}

A String拥有底层内存,因此它可以安全地跨线程边界传输,无需担心任何其他对象被意外释放。

关于rust - 如何使用具有生命周期的类型作为 io::Error::new 的 `error` 参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34030455/

相关文章:

reference - 在结构中保留对 timer::guard 的引用

rust - 如何从Reverse()中获取值?

rust - 如何将*可选*引用返回到 RefCell 内容

gtk - 使用 rust-gnome 时如何将自己的数据获取到 GTK 回调?

javascript - 如何使用 JavaScript 将文件读取到 WebAssembly?

rust - 如何延长 Rust 中迭代器适配器内临时变量的生命周期?

optimization - 为什么在启用优化的情况下编译此代码,运行速度如此之快?

rust - 你能在生命周期系统中表达 'valid for lifetime of returned value' 吗?

windows - 为什么我的规范化路径的前缀是\\?\

struct - 从具有生命周期的结构切换到特征