在 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 "";
}
}

我得到了错误 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脱离我的功能。谁能给我解释一下为什么这样不好,为什么我不该这么做?或者在某些情况下也不是坏事?

50852 次浏览

You cannot return a ABC0 if you've allocated the String in the function. There's further discussion about why, as well as the fact that it's not limited to strings. That makes your choice much easier: return the String.

Strings are heap-allocated and built to be mutable.

Strings are heap-allocated because they have an unknown length. Since that allocation is solely owned by the String, that's what grants the ability to mutate the string.

My function just returns a filepath for reference purposes, and I'd rather leave it up to the caller to decide if they need a heap-stored mutable string.

This isn't possible. Your function has performed an allocation. If you don't return the allocation to the caller, then the value must be deallocated to prevent memory leaks. If it was returned after deallocation, that would be an invalid reference, leading to memory safety violations.

But I can also seemingly do this:

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

to get a &str out of my function. Can someone explain to me why this is bad and why I should never do it? Or maybe it's not bad and okay in certain situations?

It's not bad, it just doesn't really allow you to do what you want in your original case. That "hello world" is a &'static str, a string slice that's been stored inside the code of the program itself. It has a fixed length and is known to live longer than main.

The signature fn hello_string(x: &str) -> &str can be expanded to fn hello_string<'a>(x: &'a str) -> &'a str. This indicates that the resulting string slice must have the same lifetime as the input string. A static string will outlive any lifetime, so that's valid to substitute.

This would be useful for a function where the result is based on the input string only:

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

However, in your case, the function owns the String. If you attempted to return a reference to a String, completely unrelated to the input string:

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

You'll run into the common error message "borrowed value does not live long enough". That's because the borrowed value only lives until the end of method, not as long as the input string slice. You can't "trick" the compiler (or if you can, that's a major bug).

If you want to return a &str in Rust you have to add generic lifetime. Example:

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

or,

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

Here have a 3 rules.

  1. Each parameter that is a reference gets it's own lifetime parameter.
  2. If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters.
  3. If there are multiple input lifetime parameters, but one of them is &self or &mut self the lifetime of self is assigned to all output lifetime parameters.