我在 Rust 中有以下代码。我知道我不应该返回对局部变量的引用,在这种情况下我不是。的字符串分割为被传递&str的参考和,求出分割边界之后,我返回&s[0..idx]其中idx是边界的末尾。我相信这不会导致“悬空”参考相关错误。然而,事实证明我错了!
fn demo4() {
let mut s = String::from("Elijah Wood");
let firstname = str_split(&s, &String::from(" "));
println!("First name of actor: {}", firstname);
}
// can handle both &str and &String
fn str_split(s: &str, pat: &str) -> &str {
let bytes = s.as_bytes();
let b_pat = pat.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b_pat {
return &s[0..i];
}
}
&s[..]
}
fn main() {
demo4();
}
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
fn demo4() {
let mut s = String::from("Elijah Wood");
let firstname = str_split(&s, &String::from(" "));
println!("First name of actor: {}", firstname);
}
// can handle both &str and &String
fn str_split(s: &str, pat: &str) -> &str {
let bytes = s.as_bytes();
let b_pat = pat.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b_pat {
return &s[0..i];
}
}
&s[..]
}
fn main() {
demo4();
}
Run Code Online (Sandbox Code Playgroud)
任何解释都非常感谢。
错误消息会告诉您出了什么问题,但不会告诉您如何修复它:
= help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `s` or `pat`
Run Code Online (Sandbox Code Playgroud)
编译器使用生命周期来确定代码是否安全。其中一部分是知道每个参考文献可以借用什么。签名:
fn str_split(s: &str, pat: &str) -> &str
Run Code Online (Sandbox Code Playgroud)
不指示是否str_split返回引用 intos或引用 into pat,因此 Rust 无法告诉如何检查引用的有效性。(另请参阅此问题以了解该函数根本没有引用参数的版本。)
要解决此问题,您需要引入生命周期参数:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str
Run Code Online (Sandbox Code Playgroud)
这大概是说,“如果你借用一个字符串一段时间'a,你可以调用str_split它(和另一个字符串)并取回一个对生命周期有效的引用'a。” &pat没有用 注释'a,因为结果不借自pat,只借自s。
Rust 编程语言有一章关于生命周期的内容解决了这个问题,我强烈建议你阅读它;Rust 的生命周期不仅仅是防止悬空指针。
尽管不是问题的一部分,但此函数的主体是单行的。除非这纯粹是一种学习练习,否则不要做多余的工作:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
s.split(pat).next().unwrap_or(s)
}
Run Code Online (Sandbox Code Playgroud)
&str是 的简写&'a str,其中'a是一些需要事先声明的生命周期参数。在一些简单的情况下。可以省略这些生命周期参数,编译器会为您扩展它。但是,在某些情况下,您需要显式声明生命周期。
来自The Rust Programming Language, Second Edition(重点是我的),这里是关于省略生命周期参数的规则:
作为引用的每个参数都有自己的生命周期参数。换句话说,一个带一个参数的函数得到一个生命周期参数:
fn foo<'a>(x: &'a i32),一个带有两个参数的函数得到两个独立的生命周期参数:fn foo<'a, 'b>(x: &'a i32, y: &'b i32),依此类推。如果只有一个输入生命周期参数,则该生命周期将分配给所有输出生命周期参数:
fn foo<'a>(x: &'a i32) -> &'a i32。如果有多个输入生命周期参数,但其中一个是
&self或&mut self因为这是一种方法,则生命周期self被分配给所有输出生命周期参数。这使得编写方法更好。
您的函数的问题在于它有两个输入生命周期参数,因此编译器不会为您选择一个。你必须像这样编写你的函数:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
s
}
Run Code Online (Sandbox Code Playgroud)
如果您对这种语法不熟悉,请务必阅读生命周期一章。
为什么编译器不能自己解决?因为 Rust 有一个原则,即函数的签名不应该因为其实现的改变而改变。它简化了编译器(它不必处理签名尚未完全确定的相互依赖的函数),并且还简化了您自己的代码的维护。例如,如果您要像这样更改函数的实现:
fn str_split(s: &str, pat: &str) -> &str {
pat
}
Run Code Online (Sandbox Code Playgroud)
那么输出的生命周期参数必须链接到pat的生命周期参数。在图书馆中,这是一个突破性的变化;您不希望重大更改在您不注意的情况下溜走!
| 归档时间: |
|
| 查看次数: |
1172 次 |
| 最近记录: |