我在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();
}
我收到以下错误:
error[E0106]: missing lifetime specifier
--> src/main.rs:7:37
|
7 | fn str_split(s: &str, pat: &str) -> &str {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s` or `pat`
非常感谢任何解释。
答案 0 :(得分:3)
&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
}
如果您不熟悉这种语法,请务必阅读the chapter on lifetimes。
为什么编译器不能单独解决它?因为Rust的原则是函数的签名不应该因为其实现的变化而改变。它简化了编译器(它不必处理其签名尚未完全确定的相互依赖的函数),并且还简化了对自己代码的维护。例如,如果您要更改函数的实现,请执行以下操作:
fn str_split(s: &str, pat: &str) -> &str {
pat
}
然后输出的生命周期参数必须链接到pat
的生命周期参数。在图书馆,这是一个突破性的变化;如果没有你注意到,你不希望破坏变化!
答案 1 :(得分:2)
错误消息告诉您错误,但不是如何修复它:
= help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `s` or `pat`
编译器使用生命周期来确定代码是否安全。部分原因是知道每个参考的来源。签名:
fn str_split(s: &str, pat: &str) -> &str
不表示是否将引用返回s
或引用pat
,因此Rust无法告知如何检查引用的有效性。 (另请参阅this question了解该函数根本没有引用参数的版本。)
要解决此问题,您需要引入通用生存期参数:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str
粗略地说,&#34;对于任何生命周期'a
的字符串,您可以在其上调用str_split
和另一个字符串并获取生命周期'a
的引用&#34; &pat
未使用'a
进行注释,因为其生命周期与结果无关。
Rust编程语言has a chapter on lifetimes解决了这个问题,我强烈建议你阅读它; Rust的生命不仅仅是防止悬空指针。
最后,虽然不是问题的一部分,但这个功能的主体是一个单行。除非这纯粹是一种学习练习,否则不要做比以往更多的工作:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
s.split(pat).next().unwrap_or(s)
}
答案 2 :(得分:0)
感谢大家解释错误及其背后的原因。我修改了代码并进行了一些修改,我想解释一下。首先感谢@trentcl注意到模式匹配在语义上是错误的。原因是搜索模式是通过匹配数组中的每个字节而不是整个数组本身来完成的。这促使我将函数更改为仅通过拆分第一次出现的空格字符' '
来返回单词。
此外,函数签名需要包含一个生命周期特征,以使其正确编译。工作代码如下:
// 4 Demo with string spliting
fn demo4() {
let s = String::from("Elijah Wood");
let firstname = str_split(&s);
println!("First name of actor: {}", firstname);
}
// splits a string at first space
fn str_split<'a>(s : &'a str) -> &'a str {
let bytes = s.as_bytes();
for(i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}