包含引用时如何 return 一个错误?

How to return an Error when it contains a reference?

我有一个 returns 一个 Result<_, MyError> 的 Rust 方法。此方法在 State 结构上 运行 并且 MyError 具有生命周期说明符 'a 因为它需要保留一些 &strs.

我正在尝试编写这样的特征:

trait MyTrait {
    type Error;

    fn work(&self) -> Result<(), Self::Error>;
}

impl<'a> MyTrait for MyImpl<'a> {
    type Error = MyError<'a>;

    fn work(&self) -> Result<(), MyError<'a>> {
        let state = State::new();

        state.work() // returns Result<(), MyError> but state doesn't live long enough
    }
}

如何解决这个错误?我应该更改 MyError 以保留 String 而不是 &'a str 吗?我应该把 state 放在 MyImpl 里面吗?这trait定义明确吗?

我想为 do() 的每个 运行 创建一个 State

这是一个 MCVE:

enum MyError<'a> {
    Some(&'a str),
}

trait MyTrait {
    type Error;

    fn work(&self) -> Result<(), Self::Error>;
}

struct MyImpl<'a> {
    pub some_string: &'a str,
}

impl<'a> MyTrait for MyImpl<'a> {
    type Error = MyError<'a>;

    fn work(&self) -> Result<(), MyError<'a>> {
        let state = State::new();

        state.work() // returns Result<(), MyError> but state doesn't live long enough
    }
}

struct State;

impl State {
    pub fn new() -> State {
        State
    }

    pub fn work(&self) -> Result<(), MyError> {
        Err(MyError::Some("hi"))
    }
}

fn main() {}

(Playground)

问题是,根据 State::work() 的签名,MyError 的生命周期参数与 &self 的生命周期参数相关联参考:

// without lifetime elision
pub fn work<'a>(&'a self) -> Result<(), MyError<'a>>

然后这个值被 returned in MyImpl::work():

fn work(&self) -> Result<(), MyError<'a>> {
    let state = State::new();

    state.work()
}

问题是,impl<'a> MyTrait for MyImpl<'a> 中的生命周期参数 'a 表示生命周期严格大于 MyError [=62] =]由 State::work() 编辑。为什么会这样?好吧,让我们再看看MyImpl::work()

fn work(&self) -> Result<(), MyError<'a>> {
    let state = State::new();
    state.work()
}

请记住 State::work(&self) return 是一个 MyError 的生命周期与 &self 的生命周期相关联,也就是说,在这种特殊情况下,它将是 [= 的生命周期28=]。后者是局部变量,在 work() returns.

之后立即销毁

然而,impl<'a> MyTrait for MyImpl<'a>中的'a表示存储在MyImpl(即self中)的字符串切片的生命周期。自然地,因为 MyImpl::work() 可以被调用,这意味着它被调用的值处于有效状态并且包含一个活跃的切片。因此,它的生命周期比 MyImpl::work() 中可以创建的任何东西都长。所以 return 任何不是从 MyImpl 中的这个字符串切片派生的东西都是不合理的;例如,这是有效的:

impl<'a> MyTrait for MyImpl<'a> {
    type Error = MyError<'a>;

    fn work(&self) -> Result<(), MyError<'a>> {
        Err(MyError::Some(self.some_string))
    }
}

现在 MyError 值的生命周期正好是 self.some_string 的生命周期,借用检查器变得很高兴。

现在,有哪些选择?首先,最简单的方法是将拥有的 String 存储在 MyError:

enum MyError {
    Some(String)
}

impl<'a> MyTrait for MyImpl<'a> {
    type Error = MyError;

    fn work(&self) -> Result<(), MyError> {
        let state = State::new();
        state.work()
    }
}

struct State;

impl State {
    pub fn new() -> State {
        State
    }

    pub fn work(&self) -> Result<(), MyError> {
        Err(MyError::Some("hi".into()))
    }
}

我认为这是最惯用且最灵活的方法。具有非自给自足的错误值实际上是非常罕见的;我想我以前从未见过。另一种选择是使用 &'static str:

enum MyError {
    Some(&'static str)
}

struct State;

impl State {
    pub fn new() -> State {
        State
    }

    pub fn work(&self) -> Result<(), MyError> {
        Err(MyError::Some("hi"))
    }
}

此方法不允许您动态创建错误消息(您只能为错误消息使用字符串文字)但它更有效,因为它不需要为程序中的不愉快路径分配内存,并且它可能足以满足您的用例。