【问题标题】:Proper way to return a new string in Rust在 Rust 中返回新字符串的正确方法
【发布时间】:2017-08-22 02:08:21
【问题描述】:

我刚刚花了一周时间阅读 Rust Book,现在我正在编写我的第一个程序,它将文件路径返回到系统壁纸:

pub fn get_wallpaper() -> &str {
    let output = Command::new("gsettings");
    // irrelevant code
    if let Ok(message) = String::from_utf8(output.stdout) {
        return message;
    } else {
        return "";
    }
}

我收到了错误 expected lifetime parameter on &str 并且我知道 Rust 想要一个输入 &str 将作为输出返回,因为我在函数内创建的任何 &str 都会在函数结束后立即清理。

我知道我可以通过返回 String 而不是 &str 来回避这个问题,类似问题的许多答案都说了这么多。但我似乎也可以这样做:

fn main() {
    println!("message: {}", hello_string(""));
}

fn hello_string(x: &str) -> &str {
    return "hello world";
}

从我的功能中获取&str。有人可以向我解释为什么这很糟糕以及为什么我永远不应该这样做吗?或者在某些情况下可能还不错?

【问题讨论】:

  • 我真的不知道如何避免这种情况。您可以尝试通过预先分配缓冲区来降低成本,但必须有人拥有此内存。在从命令的输出中读取字符串之前,您无法知道字符串的长度。在堆上分配对我来说很有意义。
  • @bluejekyll 我已经澄清了我的问题,以展示一种方法,您似乎可以通过提供一个虚拟参数来获得 &str 函数的生命周期。

标签: rust


【解决方案1】:

如果你想在 Rust 中返回 &str,你必须添加泛型生命周期。示例:

fn hello_string<'life>() -> &'life str {
    return "hello world";
}

或者,

fn hello_string<'life>(a: &'life str, b: &'life str) -> &'life str {
    return "hello world";
}

这里有 3 条规则。

  1. 作为引用的每个参数都有自己的生命周期参数。
  2. 如果只有一个输入生命周期参数,则将该生命周期分配给所有输出生命周期参数。
  3. 如果有多个输入生命周期参数,但其中之一是 &amp;self&amp;mut self,则将 self 的生命周期分配给所有输出生命周期参数。

【讨论】:

    【解决方案2】:

    cannot return a &amp;str if you've allocated the String in the function。还有关于why 的进一步讨论,以及它是not limited to strings 的事实。这让您的选择更加更容易:返回String

    Strings 是堆分配的并被构建为可变的。

    Strings 是堆分配的,因为它们的长度未知。由于该分配由String 单独拥有,这就是赋予字符串变异能力的原因。

    我的函数只是返回一个文件路径以供参考,我宁愿把它留给调用者来决定他们是否需要堆存储的可变字符串。

    这是不可能的。您的函数已执行分配。如果您不将分配返回给调用者,则必须释放该值以防止内存泄漏。如果在释放后返回,那将是一个无效的引用,导致内存安全违规。

    但我似乎也可以这样做:

    fn hello_string(x: &str) -> &str {
        return "hello world";
    }
    

    从我的功能中获取&amp;str。有人可以向我解释为什么 很糟糕,为什么我永远不应该这样做?或者也许它还不错,还可以 某些情况?

    这不是,它只是不能让你在原来的情况下做你想做的事。 "hello world" 是一个&amp;'static str,它是一个存储在程序本身代码中的字符串切片。它有一个固定的长度,并且已知比main 寿命更长。

    签名fn hello_string(x: &amp;str) -&gt; &amp;str 可以扩展为fn hello_string&lt;'a&gt;(x: &amp;'a str) -&gt; &amp;'a str。这表明生成的字符串切片必须具有与输入字符串相同的 生命周期。静态字符串的寿命将超过 任何 个生命周期,因此可以替换。

    这对于结果仅基于输入字符串的函数很有用:

    fn long_string(x: &str) -> &str {
        if x.len() > 10 {
            "too long"
        } else {
            x
        }
    }
    

    但是,在您的情况下,该函数拥有String。如果您尝试返回对 String 的引用,与输入字符串完全无关:

    fn hello_string(x: &str) -> &str {
        &String::from("hello world")
    }
    

    您会遇到常见的错误消息“借用值的寿命不够长”。那是因为借来的值只存在到方法结束,而不是与输入字符串切片一样长。您不能“欺骗”编译器(或者如果可以,那是一个主要错误)。

    【讨论】:

    • 引用不就像指向内存位置的 (C/C++) 指针吗? String::from("hello world")不是在堆中分配的吗?那么,为什么 Rust 不能只返回指向该堆位置的引用(指针)呢?在这一点上我真的很困惑。
    • @JohnTortugo 是的,引用 在许多方面都像 指针(而在其他方面则不然)。如果你看到像char *foo() 这样的C 函数,你是否释放返回的数据? String 实际上是一个带有(allocated_capacity, used_size, pointer_to_heap_allocation) 的结构。
    • 谢谢,@Shepmaster。使用char *foo(),如果我需要延长参考时间,至少我可以选择 [not] free。
    • @JohnTortugo 不是真的,因为你不知道 if 你可以根据签名在 C 代码中释放它。 Rust 通过将两个概念分成两种类型来消除歧义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 2017-03-02
    相关资源
    最近更新 更多