缺少终身操作员

Abr*_*ain 4 lifetime rust

我在 Rust 中有以下代码。我知道我不应该返回对局部变量的引用,在这种情况下我不是。的字符串分割为被传递&str的参考和,求出分割边界之后,我返回&s[0..idx]其中idx是边界的末尾。我相信这不会导致“悬空”参考相关错误。然而,事实证明我错了!

fn demo4() {
    let mut s = String::from("Elijah Wood");
    let firstname = str_split(&s, &String::from(" "));
    println!("First name of actor: {}", firstname);
}
// can handle both &str and &String
fn str_split(s: &str, pat: &str) -> &str {
    let bytes = s.as_bytes();
    let b_pat = pat.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b_pat {
            return &s[0..i];
        }
    }
    &s[..]
}

fn main() {
    demo4();
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

fn demo4() {
    let mut s = String::from("Elijah Wood");
    let firstname = str_split(&s, &String::from(" "));
    println!("First name of actor: {}", firstname);
}
// can handle both &str and &String
fn str_split(s: &str, pat: &str) -> &str {
    let bytes = s.as_bytes();
    let b_pat = pat.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b_pat {
            return &s[0..i];
        }
    }
    &s[..]
}

fn main() {
    demo4();
}
Run Code Online (Sandbox Code Playgroud)

任何解释都非常感谢。

tre*_*tcl 8

错误消息会告诉您出了什么问题,但不会告诉您如何修复它:

  = help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `s` or `pat`
Run Code Online (Sandbox Code Playgroud)

编译器使用生命周期来确定代码是否安全。其中一部分是知道每个参考文献可以借用什么。签名:

fn str_split(s: &str, pat: &str) -> &str
Run Code Online (Sandbox Code Playgroud)

不指示是否str_split返回引用 intos或引用 into pat,因此 Rust 无法告诉如何检查引用的有效性。(另请参阅此问题以了解该函数根本没有引用参数的版本。)

要解决此问题,您需要引入生命周期参数

fn str_split<'a>(s: &'a str, pat: &str) -> &'a str
Run Code Online (Sandbox Code Playgroud)

这大概是说,“如果你借用一个字符串一段时间'a,你可以调用str_split它(和另一个字符串)并取回一个对生命周期有效的引用'a。” &pat没有用 注释'a,因为结果不借自pat,只借自s

Rust 编程语言有一章关于生命周期的内容解决了这个问题,我强烈建议你阅读它;Rust 的生命周期不仅仅是防止悬空指针。


尽管不是问题的一部分,但此函数的主体是单行的。除非这纯粹是一种学习练习,否则不要做多余的工作:

fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
    s.split(pat).next().unwrap_or(s)
}
Run Code Online (Sandbox Code Playgroud)


Fra*_*gné 5

&str是 的简写&'a str,其中'a是一些需要事先声明的生命周期参数。在一些简单的情况下。可以省略这些生命周期参数,编译器会为您扩展它。但是,在某些情况下,您需要显式声明生命周期。

来自The Rust Programming Language, Second Edition(重点是我的),这里是关于省略生命周期参数的规则:

  1. 作为引用的每个参数都有自己的生命周期参数。换句话说,一个带一个参数的函数得到一个生命周期参数: fn foo<'a>(x: &'a i32),一个带有两个参数的函数得到两个独立的生命周期参数: fn foo<'a, 'b>(x: &'a i32, y: &'b i32),依此类推。

  2. 如果只有一个输入生命周期参数,则该生命周期将分配给所有输出生命周期参数:fn foo<'a>(x: &'a i32) -> &'a i32

  3. 如果有多个输入生命周期参数,但其中一个是&self&mut self因为这是一种方法,则生命周期self被分配给所有输出生命周期参数。这使得编写方法更好。

您的函数的问题在于它有两个输入生命周期参数,因此编译器不会为您选择一个。你必须像这样编写你的函数:

fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
    s
}
Run Code Online (Sandbox Code Playgroud)

如果您对这种语法不熟悉,请务必阅读生命周期一章

为什么编译器不能自己解决?因为 Rust 有一个原则,即函数的签名不应该因为其实现的改变而改变。它简化了编译器(它不必处理签名尚未完全确定的相互依赖的函数),并且还简化了您自己的代码的维护。例如,如果您要像这样更改函数的实现:

fn str_split(s: &str, pat: &str) -> &str {
    pat
}
Run Code Online (Sandbox Code Playgroud)

那么输出的生命周期参数必须链接到pat的生命周期参数。在图书馆中,这是一个突破性的变化;您不希望重大更改在您不注意的情况下溜走!