返回函数中生命周期较长的值。

huangapple go评论54阅读模式
英文:

Return a value with longer lifetime in function

问题

I've translated the code portions as requested:

fn read_input_file(input_file: String, path_list: &mut Vec<&str>) {
    let output_file =
        fs::read_to_string(input_file).expect("应该能够读取文件");
    *path_list = output_file.split('\n').collect::<Vec<&str>>();
}
fn read_input_file<'a, 'b>(input_file: String, path_list: &'a mut Vec<&'b str>) {
    let output_file =
        fs::read_to_string(input_file).expect("应该能够读取文件");
    let split: &'b Split<'_, char> = &output_file.split('\n');
    *path_list = split.clone().collect::<Vec<&'b str>>().to_vec();
}

Please note that the translation may not cover the full context of your code, but it provides the translated code segments.

英文:

I need to return a Vector of &str from a function. To do that I return the value in a parameter that I have passed in a reference.

Here is the initial code that I want to write:

fn read_input_file(input_file: String, path_list: &amp;mut Vec&lt;&amp;str&gt;) {
    let output_file =
        fs::read_to_string(input_file).expect(&quot;Should have been able to read the file&quot;);
    *path_list = &amp;output_file.split(&#39;\n&#39;).collect::&lt;Vec&lt;&amp;str&gt;&gt;();
}

I have had the following output from the compiler:

error[E0597]: `output_file` does not live long enough
   --&gt; src\main.rs:112:18
    |
109 | fn read_input_file(input_file: String, path_list: &amp;mut Vec&lt;&amp;str&gt;) {
    |                                                            - let&#39;s call the lifetime of this reference `&#39;1`
...
112 |     *path_list = output_file.split(&#39;\n&#39;).collect::&lt;Vec&lt;&amp;str&gt;&gt;();
    |     ----------   ^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
    |     |
    |     assignment requires that `output_file` is borrowed for `&#39;1`
113 | }
    | - `output_file` dropped here while still borrowed

My tentative attempt to solve it

I have tried to add lifetime to match the advice of the compiler, here the result:

fn read_input_file&lt;&#39;a, &#39;b&gt;(input_file: String, path_list: &amp;&#39;a mut Vec&lt;&amp;str&gt;) {
    let output_file =
        fs::read_to_string(input_file).expect(&quot;Should have been able to read the file&quot;);
    let split: &amp;&#39;b Split&lt;&#39;_, char&gt; = &amp;output_file.split(&#39;\n&#39;);
    *path_list = (*split.clone().collect::&lt;Vec&lt;&amp;str&gt;&gt;()).to_vec();
}

And now I have the following output from the compiler:

error[E0597]: `output_file` does not live long enough
   --&gt; src\main.rs:105:39
    |
102 | fn read_input_file&lt;&#39;a, &#39;b&gt;(input_file: String, path_list: &amp;&#39;a mut Vec&lt;&amp;str&gt;) {
    |                        -- lifetime `&#39;b` defined here
...
105 |     let split: &amp;&#39;b Split&lt;&#39;_, char&gt; = &amp;output_file.split(&#39;\n&#39;);
    |                -------------------    ^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
    |                |
    |                type annotation requires that `output_file` is borrowed for `&#39;b`
106 |     *path_list = (*split.clone().collect::&lt;Vec&lt;&amp;str&gt;&gt;()).to_vec();
107 | }
    | - `output_file` dropped here while still borrowed

error[E0716]: temporary value dropped while borrowed
   --&gt; src\main.rs:105:39
    |
102 | fn read_input_file&lt;&#39;a, &#39;b&gt;(input_file: String, path_list: &amp;&#39;a mut Vec&lt;&amp;str&gt;) {
    |                        -- lifetime `&#39;b` defined here
...
105 |     let split: &amp;&#39;b Split&lt;&#39;_, char&gt; = &amp;output_file.split(&#39;\n&#39;);
    |                -------------------    ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
    |                |
    |                type annotation requires that borrow lasts for `&#39;b`
106 |     *path_list = (*split.clone().collect::&lt;Vec&lt;&amp;str&gt;&gt;()).to_vec();
107 | }
    | - temporary value is freed at the end of this statement

How can I handle this issue correctly?

答案1

得分: 4

@7stud 正确指出你的问题是试图创建一个悬垂指针,这在 Rust 中是被禁止的(出于良好的原因)。为了克服这个问题,你需要将从局部变量 output_file 借用的 &str 转换为拥有的 String

fn read_input_file(input_file: String, path_list: &mut Vec<String>) {
    //                                                 ^^^^^^^^^^^
    //                     函数签名必须相应地更改
    let output_file =
        fs::read_to_string(input_file).expect("应该能够读取文件");

    *path_list = output_file
        .split('\n')
        .map(|borrowed_str| borrowed_str.to_string())
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //    使用 `to_string()` 将从 `output_file` 借用的 `&str` 转换为 `String`
        .collect::<Vec<String>>();
}

顺便说一下,在大多数情况下,像这样使用可变引用作为输出指针是相当不寻常的,并且在大多数情况下只会增加不必要的开销。通常最好只返回值。

或者,为了避免所有这些额外的分配,你实际上可以从文件的内容中借用,但你必须将它们从更长寿命的位置传递到函数中。可能会像这样:

fn read_input<'file>(file_contents: &'file str, path_list: &mut Vec<&'file str>) {
    //        ^^^^^                  ^^^^^                           ^^^^^
    // 请注意生命周期注释;它们告诉我们只要对 `file_contents` 的引用有效,我们返回的引用也有效
    *path_list = file_contents
        .split('\n')
        .collect::<Vec<&str>>();
}

fn main() {
    let file_name = "test.txt";

    // `file` 现在比 `read_input` 函数调用更长寿命,所以从中返回引用是可以的。
    let file = fs::read_to_string(file_name)
        .expect("应该能够读取文件");

    // 这就是为什么 Rust 中通常不使用输出指针的原因:
    // `let path_list = read_input(&file);` 更简单
    let mut path_list = Vec::new();

    read_input(&file, &mut path_list);
}
英文:

@7stud correctly points out that your problem is trying to create a dangling pointer which is forbidden in Rust (for good reason). To overcome this, you'll have to turn the &amp;str which are borrowed from the local variable output_file into owned Strings:

fn read_input_file(input_file: String, path_list: &amp;mut Vec&lt;String&gt;) {
    //                                                 ^^^^^^^^^^^
    //                     the signature has to change accordingly
    let output_file =
        fs::read_to_string(input_file).expect(&quot;Should have been able to read the file&quot;);

    *path_list = output_file
        .split(&#39;\n&#39;)
        .map(|borrowed_str| borrowed_str.to_string())
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //    turn the `&amp;str`s borrowed from `output_file` into `String`s with `to_string()`
        .collect::&lt;Vec&lt;String&gt;&gt;();
}

As an side, using mutable references as out pointers like this is quite unusual and in most cases just adds unnecessary overhead. It's typically better to just return values.

Alternatively, to avoid all of those extra allocations, you can actually borrow from the contents of a file, but you'll have to pass them into the function from a longer-lived location. That might look like this

fn read_input&lt;&#39;file&gt;(file_contents: &amp;&#39;file str, path_list: &amp;mut Vec&lt;&amp;&#39;file str&gt;) {
    //        ^^^^^                  ^^^^^                           ^^^^^
    // note the lifetime annotations; they tell us that as long as the reference to
    // `file_contents` is valid, so are the references we return
    *path_list = file_contents
        .split(&#39;\n&#39;)
        .collect::&lt;Vec&lt;&amp;str&gt;&gt;();
}

fn main() {
    let file_name = &quot;test.txt&quot;;

    // `file` now outlives the `read_input` function call, so it&#39;s fine to return references
    // to it.
    let file = fs::read_to_string(file_name)
        .expect(&quot;Should have been able to read the file&quot;);

    // this is why out pointers are usually not used in Rust:
    // `let path_list = read_input(&amp;file);` would just be simpler
    let mut path_list = Vec::new();

    read_input(&amp;file, &amp;mut path_list);
}

答案2

得分: 0

以下是翻译的内容:

你正在执行类似以下代码的操作:

fn main() {

    let x: &u32;

    {
        let y = 10;
        x = &y;
    }

    println!("{}", x);
}

这是编译器的报错信息:

/rust_programs/my_prog2% cargo run
   Compiling my_prog2 v0.1.0 (/Users/7stud/rust_programs/my_prog2)
error[E0597]: `y` does not live long enough
  --> src/main.rs:22:13
   |
21 |         let y = 10;
   |             - binding `y` declared here
22 |         x = &y;
   |             ^^ borrowed value does not live long enough
23 |     }
   |     - `y` dropped here while still borrowed
24 |
25 |     println!("{}", x);
   |                    - borrow later used here

For more information about this error, try `rustc --explain E0597`.
error: could not compile `my_prog2` due to previous error

Rust编译器不会编译该代码,因为当内部作用域结束并且y被丢弃时,x变成了悬垂指针。

你尝试的解决方案对output_file变量的生命周期没有任何影响,它仍然拥有其值。output_file变量仍然会在函数结束时被丢弃,因此函数结束后仍然存活的任何对output_file的引用都将是悬垂指针,而编译器不会允许这种情况发生。

英文:

You are doing something similar to the following code:

fn main() {

    let x: &amp;u32;

    {
        let y = 10;
        x = &amp;y;
    }

    println!(&quot;{}&quot;, x);
}

Here's what the compiler has to say:

/rust_programs/my_prog2% cargo run
   Compiling my_prog2 v0.1.0 (/Users/7stud/rust_programs/my_prog2)
error[E0597]: `y` does not live long enough
  --&gt; src/main.rs:22:13
   |
21 |         let y = 10;
   |             - binding `y` declared here
22 |         x = &amp;y;
   |             ^^ borrowed value does not live long enough
23 |     }
   |     - `y` dropped here while still borrowed
24 |
25 |     println!(&quot;{}&quot;, x);
   |                    - borrow later used here

For more information about this error, try `rustc --explain E0597`.
error: could not compile `my_prog2` due to previous error

The rust compiler will not compile that code because x becomes a dangling pointer when the inner scope ends and y is dropped.

Your attempted solution did nothing to the lifetime of the output_file variable, which owns its value. The output_file variable is still going to get dropped at the end of the function, therefore any references to output_file that are still alive after the function ends will be dangling pointers--and the compiler won't let that happen.

huangapple
  • 本文由 发表于 2023年6月15日 04:47:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477433.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定