结果<T, E> 输入 Rust。为什么引用不能作为 T 或 E 传递?

Result<T, E> Type in Rust. why reference can't be passed as T or E?

我是 Rust 的新手,最近我读到了有关结果类型的内容。 喜欢这个概念并开始在我的应用程序中使用它。

我的第一次尝试是使用 &str。所以我的 return 类型看起来像这样:

pub fn calculate_risk_score(a: i32, b: i32) -> Result<i32, &str> {
    return Ok(a + b);
}

这就是我遇到的错误:

以下代码有效:

pub fn calculate_risk_score(a: i32, b: i32) -> Result<i32, &'static str> {
    return Ok(a + b);
}

现在我不认为我完全理解为什么将引用作为泛型传递是错误的。此外,我不明白为什么 static 解决了这个问题,我也不完全理解关于命名生命周期参数的解释。 我读了那些文章,但我想我仍然缺少一些东西。

https://doc.rust-lang.org/std/result/

https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html

所以我的问题:

  1. 为什么 &'static str 可以作为泛型类型传递而 &str 不能?
  2. 它和寿命有什么关系?为什么当我将它设置为 &str 时编译器无法确定引用的有效期?

1.为什么 'static 终生有效?

'static 生命周期有效,因为它意味着引用值在程序的整个生命周期内都存在。 Rust 中直接在源代码中编写的每个字符串的生命周期为 'static.,例如let s = "hello world";,因为它包含在已编译的二进制文件中,因此永远不会被删除。

2。为什么要明确生命周期以及如何解决?

编译器无法确定 returned 值存在多长时间,因为它期望明确的生命周期。一个简单的解决方法是 return 自有字符串(String 类型)而不是借用的字符串,或者只使用 &'static str.

我建议您阅读 References and Borrowing and Validating References with Lifetimes 以更好地理解 Rust 中的生命周期是如何工作的。

要了解此函数签名的问题:

pub fn calculate_risk_score(a: i32, b: i32) -> Result<i32, &str> {
    todo!()
}

...考虑一下 &str 是什么意思。这意味着:借用 某人字符串的一部分。如果允许这样的函数,可以这样调用它:

if let Err(e) = calculate_risk_score(foo, bar) {
    // `e` is &str - who is it borrowed from, and how long is it valid?
    // Can I put it in a container? Can I return it from this function?
    // Can I put it in a global variable?
    ...
}

Lifetimes 通过将 returned 借用的生命周期与调用者提供的函数参数的生命周期联系起来,为这些问题提供了答案,因此必须知道其范围。这通常与 self:

的范围一样简单
struct Parser {
    last_error: String,
    // ...
}

impl Parser {
    // Equivalent to fn calculate_risk_scope<'a>(&'a self, a: i32, b: i32) -> Result<i32, &'a str>
    fn calculate_risk_scope(&self, a: i32, b: i32) -> Result<i32, &str> {
        if error {
            return Err(self.last_error.as_str());
        }
        ...
    }
}

现在 &str 借用的生命周期与 &self 借用的生命周期相关联,即 returned 错误可以使用,只要对象被调用的方法有效且未修改。

对于原始函数,&str 是整个签名中唯一的引用,因此没有其他借用连接到它,编译器要求您提供明确的生命周期。它建议 'static 作为给定约束的合理选择,如果您使用的是被烘焙到可执行文件中且从未被释放的文字字符串,它确实有效。

请注意,编译器永远不会使用函数的 主体 来推断参数或 return 值的生命周期。这是有意的,并允许函数的签名构成不依赖于实现的 API 合约。当函数足够简单以至于它接收一个引用和 return 一个引用时,编译器将认为它们的生命周期是相连的,就好像它们被拼写为 fn calculate_risk_scope<'a>(&'a self, a: i32, b: i32) -> Result<i32, &'a str> 一样。这称为 lifetime elision 并且仅通过检查函数签名来工作,完全忽略其主体。 (主体当然必须符合签名中的生命周期。)