首页 > 解决方案 > 无法从函数返回命令行参数

问题描述

我正在尝试制作一个简单的函数来返回命令行参数。代码如下所示:

use std::env;

fn main() {
    let (query, filename) = parse();
}

fn parse() -> (&str, &str) {
    let args: Vec<String> = env::args().collect();
    let query = args[1];
    let filename = args[2];

    return (query, filename)
}

但是它不会编译,错误如下所示:

error[E0106]: missing lifetime specifier
  --> src/main.rs:15:22
   |
15 | fn parse() -> (&str, &str) {
   |                      ^ help: consider giving it a 'static lifetime: `&'static`
   |
   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from

它建议我需要添加&'static到函数声明中,如下所示:

fn parse() -> (&'static str, &'static str) {

但这也不起作用;

error[E0308]: mismatched types
  --> src/main.rs:20:13
   |
20 |     return (query, filename)
   |             ^^^^^
   |             |
   |             expected reference, found struct `std::string::String`
   |             help: consider borrowing here: `&query`
   |
   = note: expected type `&'static str`
              found type `std::string::String`

error[E0308]: mismatched types
  --> src/main.rs:20:20
   |
20 |     return (query, filename)
   |                    ^^^^^^^^
   |                    |
   |                    expected reference, found struct `std::string::String`
   |                    help: consider borrowing here: `&filename`
   |
   = note: expected type `&'static str`
              found type `std::string::String`

它说我需要添加借款,如下所示:

return (&query, &filename)

但这也不起作用;

warning: unused variable: `query`
 --> src/main.rs:5:10
  |
5 |     let (query, filename) = parse();
  |          ^^^^^ help: consider prefixing with an underscore: `_query`
  |
  = note: #[warn(unused_variables)] on by default

warning: unused variable: `filename`
 --> src/main.rs:5:17
  |
5 |     let (query, filename) = parse();
  |                 ^^^^^^^^ help: consider prefixing with an underscore: `_filename`

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:17:17
   |
17 |     let query = args[1];
   |                 ^^^^^^^
   |                 |
   |                 cannot move out of borrowed content
   |                 help: consider borrowing here: `&args[1]`

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:18:20
   |
18 |     let filename = args[2];
   |                    ^^^^^^^
   |                    |
   |                    cannot move out of borrowed content
   |                    help: consider borrowing here: `&args[2]`

error[E0515]: cannot return value referencing local variable `filename`
  --> src/main.rs:20:12
   |
20 |     return (&query, &filename)
   |            ^^^^^^^^^---------^
   |            |        |
   |            |        `filename` is borrowed here
   |            returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `query`
  --> src/main.rs:20:12
   |
20 |     return (&query, &filename)
   |            ^------^^^^^^^^^^^^
   |            ||
   |            |`query` is borrowed here
   |            returns a value referencing data owned by the current function

不知道发生了什么或为什么它不起作用,我也直接从教程中复制了示例。

标签: rust

解决方案


字符串很难生锈。首先,让我们回顾一下“什么是所有权?” , "The Slice Type""Lifetime Syntax"来自 Rust Book。

问题是 an&str是一个对象的借用(由 表示&)。rust 编译器需要知道它将被借用多长时间。现在,rust 实际上会尝试“生命周期省略”,假设返回值的生命周期将匹配输入值的生命周期,但在这种情况下没有输入值,因此无法推断。它需要你用生命周期来注释返回类型。您可以使用'static,但您也可以为您的函数提供一个通用的生命周期说明符。这是一个简化的示例:

锈游乐场

fn main() {
    println!("{}", gimme_str());
}

fn gimme_str() -> &str {
    return "foo";
}

那也不会编译。但是要编译它,您需要做的就是添加生命周期,如下所示:

使用生命周期说明符

fn main() {
    println!("{}", gimme_str());
}

fn gimme_str<'a>() -> &'a str {
    return "foo";
}

或者,您可以从输入中推断出生命周期,尽管在这种情况下这很奇怪,因为输入未使用:

使用 Lifetime Elision(从输入推断)

fn main() {
    println!("{}", gimme_str("bar"));
}

fn gimme_str(_input: &str) -> &str {
    return "foo";
}

这就是生命周期说明符。但是您还有另一个问题,即您的返回值是函数拥有的字符串切片。我们可以通过修改之前的 rust playground 代码(带有生命周期说明符的代码)以简化的形式演示错误:

不能借用局部变量

fn main() {
    println!("{}", gimme_str());
}

fn gimme_str<'a>() -> &'a str {
    let foo = String::from("foo");
    return &foo;
}

借用总是借用某物在前面的示例代码中,我使用了字符串文字,这&str是对不可变字符串文字的借用。这是编译器生成的 rust 二进制文件的一部分,并且&str可以只指向二进制文件的该部分。您可以将底层内存视为由二进制文件本身“拥有”,这意味着它永远不会超出范围(即字符串文字的生命周期是'static)。

但是,在修改后的示例中,&foo不是对字符串文字的借用,而是对String对象的借用。该String对象被分配给一个局部变量,因此它的所有者是该变量的所有者——函数。这意味着它将超出范围并在函数退出时被删除。但是一旦String对象不再存在,借用它是无效的。

为了使String超出函数范围,我们必须返回String自身,以便将所有权转移给调用者。我们不能只是归还借款。见示例代码:

从函数转移所有权

fn main() {
    println!("{}", gimme_str());
}

fn gimme_str() -> String {
    let foo = String::from("foo");
    return foo;
}

要将其应用于您的案例,您将不得不String从您的函数返回,而不是&str.


推荐阅读