如何使用具有生命周期的类型作为 io::Error::new 的“错误”参数



我正在尝试通过实现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() {
}

操场

这将报告错误:

<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>>。该特征意味着可以将类型转换为盒装特征对象。特质对象本身必须实现特质ErrorSendSync。不明显的是,默认情况下,特征对象也具有'static生命周期的绑定(这是有道理的,但它似乎确实会让人们感到困惑(。

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

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

我们得到同样的错误——"由于需求冲突,无法推断出自动胁迫的适当生命周期"。所以我们的问题在于转换为这个特质对象的能力。

SendSync是两个关键特征,有助于指导编译器了解哪些类型可以安全地在线程之间发送/共享。为了在线程之间安全地共享某些内容,当另一个线程拥有它时,它不能"消失"。这是 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() {}

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

最新更新