「函数能不能返回引用」是写 Rust 时最常撞上的编译错误之一。这篇把五种典型情形逐个编译:什么时候自动成立、返回值究竟借自哪个参数、哪些写法一定不行,以及为什么字面量永远可以。
源码:
1 | fn first_word(s: &str) -> &str { s.split(' ').next().unwrap_or("") }
2 | fn main() {
3 | let text = String::from("hello world");
4 | let w = first_word(&text);
5 | println!("first_word = {w}");
6 | drop(text); // text 被释放之后还用 w 吗?
7 | }
编译:成功
运行(退出码 0):
first_word = hello
编译:失败
error[E0505]: cannot move out of `text` because it is borrowed
--> <实验目录>/src/u24b.rs:5:10
|
3 | let text = String::from("hello world");
| ---- binding `text` declared here
4 | let w = first_word(&text);
| ----- borrow of `text` occurs here
5 | drop(text);
| ^^^^ move out of `text` occurs here
6 | println!("{w}");
| - borrow later used here
|
help: consider cloning the value if the performance cost is acceptable
|
4 | let w = first_word(&text.clone());
| ++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0505`. 源码:
1 | struct Config { name: String }
2 | impl Config {
3 | fn name_or<'a>(&'a self, fallback: &str) -> &'a str { if self.name.is_empty() { "(空)" } else { &self.name } }
4 | fn pick(&self, _other: &str) -> &str { &self.name } // 省略规则:返回值借自 &self
5 | }
6 | fn main() {
7 | let c = Config { name: "prod".into() };
8 | let n;
9 | {
10 | let tmp = String::from("临时参数");
11 | n = c.pick(&tmp); // tmp 比 n 先死,不影响 n:n 借自 c
12 | }
13 | println!("n = {n}");
14 | }
编译:成功
运行(退出码 0):
n = prod 源码:
1 | fn make<'a>() -> &'a str {
2 | let s = String::from("本地");
3 | s.as_str()
4 | }
5 | fn main() { println!("{}", make()); }
编译:失败
error[E0515]: cannot return value referencing local variable `s`
--> <实验目录>/src/u24d.rs:3:5
|
3 | s.as_str()
| -^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `s` is borrowed here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0515`.
源码:
1 | fn consume_and_view(s: String) -> &'static str { &s }
2 | fn main() { println!("{}", consume_and_view("x".into())); }
编译:失败
error[E0515]: cannot return reference to function parameter `s`
--> <实验目录>/src/u24e.rs:1:50
|
1 | fn consume_and_view(s: String) -> &'static str { &s }
| ^^ returns a reference to data owned by the current function
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0515`. 源码:
1 | fn find<'a>(v: &'a [String], key: &str) -> Option<&'a String> { v.iter().find(|s| s.as_str() == key) }
2 | fn main() {
3 | let names = vec!["ann".to_string(), "bob".to_string()];
4 | let found;
5 | {
6 | let key = String::from("bob"); // key 在内层块里,先死
7 | found = find(&names, &key);
8 | }
9 | println!("{:?}", found); // found 借自 names,没问题
10 | }
编译:成功
运行(退出码 0):
Some("bob")
源码:
1 | fn find<'a>(v: &'a [String], key: &str) -> Option<&'a String> { v.iter().find(|s| s.as_str() == key) }
2 | fn main() {
3 | let found;
4 | {
5 | let names = vec!["ann".to_string(), "bob".to_string()];
6 | found = find(&names, "bob");
7 | }
8 | println!("{:?}", found);
9 | }
编译:失败
error[E0597]: `names` does not live long enough
--> <实验目录>/src/u24g.rs:6:22
|
5 | let names = vec!["ann".to_string(), "bob".to_string()];
| ----- binding `names` declared here
6 | found = find(&names, "bob");
| ^^^^^^ borrowed value does not live long enough
7 | }
| - `names` dropped here while still borrowed
8 | println!("{:?}", found);
| ----- borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0597`. 源码:
1 | static NAME: &str = "全局名字";
2 | fn greeting(formal: bool) -> &'static str { if formal { "您好" } else { "嗨" } }
3 | fn name() -> &'static str { NAME }
4 | fn main() {
5 | let g = { let flag = true; greeting(flag) }; // flag 已经出作用域,g 仍然有效
6 | println!("{g} {}", name());
7 | }
编译:成功
运行(退出码 0):
您好 全局名字生命周期标注的含义见生命周期标注到底描述什么,省略规则见生命周期省略规则。