在Rust中返回新字符串的正确方法

eik*_*iko 18 rust

我只花了一个星期阅读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 "";
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到错误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";
}
Run Code Online (Sandbox Code Playgroud)

得到&str我的功能.有人可以向我解释为什么这很糟糕,为什么我不应该这样做?或者也许它在某些情况下并不坏并且没问题?

She*_*ter 26

不能返回&str,如果你已经分配String的功能.还有进一步的讨论为什么,以及它不仅限于字符串的事实.这使您的选择容易:返回String.

Strings是堆分配的,并且构建为可变的.

Strings是堆分配的,因为它们具有未知长度.由于该分配完全由该分配String,因此授予变异字符串的能力.

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

这是不可能的.您的功能已执行分配.如果不将分配返回给调用者,则必须取消分配该值以防止内存泄漏.如果在重新分配后返回,那将是无效的引用,导致内存安全违规.

但我似乎也可以这样做:

fn hello_string(x: &str) -> &str {
    return "hello world";
}
Run Code Online (Sandbox Code Playgroud)

得到&str我的功能.有人可以向我解释为什么这很糟糕,为什么我不应该这样做?或者也许它在某些情况下并不坏并且没问题?

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

签名fn hello_string(x: &str) -> &str可以扩展为fn hello_string<'a>(x: &'a str) -> &'a str.这表示生成的字符串切片必须与输入字符串具有相同的生命周期.静态字符串将比任何生命周期都长,因此替换有效.

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

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

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

fn hello_string(x: &str) -> &str {
    &String::from("hello world")
}
Run Code Online (Sandbox Code Playgroud)

你会遇到常见的错误信息"借来的价值不够长".这是因为借用的值只存在于方法结束之前,而不是输入字符串切片.你不能"欺骗"编译器(或者你可以,这是一个主要的错误).

  • 引用不就像指向内存位置的 (C/C++) 指针吗?`String::from("hello world")` 不是在堆中分配的吗?为什么 Rust 不能只返回一个指向该堆位置的引用(指针)呢?我对这一点真的很困惑。 (2认同)
  • @JohnTortugo是的,引用*在很多方面*都像指针(而在其他方面则不然)。如果您看到像“char *foo()”这样的 C 函数,您是否释放返回的数据?“String”实际上是一个带有“(allocated_capacity,used_size,pointer_to_heap_allocation)”的结构。 (2认同)
  • @JohnTortugo 不是真的,因为你不知道*是否*可以仅根据签名在 C 代码中释放它。Rust 通过将这两个概念分为两​​种类型来消除歧义。 (2认同)