I have the following code in Rust. I know that I am not supposed to return references to local variables, and in this case I am not. The string to split is passed as a &str
reference and, after determining the split boundary, I am returning &s[0..idx]
where idx
is the end of the boundary. I was confident that this would not result in a "dangling" reference related error. However, it turns out I was wrong!
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();
}
I am getting the following error:
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`
Any explanation is greatly appreciated.
The error message tells you what's wrong, although not how to fix it:
= help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `s` or `pat`
The compiler uses lifetimes to determine whether code is safe or not. Part of that is knowing what each reference could be borrowing from. The signature:
fn str_split(s: &str, pat: &str) -> &str
does not indicate whether str_split
returns a reference into s
or a reference into pat
, so Rust can't tell how to check the validity of the reference. (See also this question for a version of this where the function has no reference arguments at all.)
To fix this, you need to introduce a lifetime parameter:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str
This says, roughly, "If you borrow a string for some lifetime 'a
, you can call str_split
on it (and another string) and get back a reference also valid for lifetime 'a
." &pat
is not annotated with 'a
, because the result does not borrow from pat
, only from s
.
The Rust Programming Language has a chapter on lifetimes that addresses this very issue and I would strongly recommend you read it; Rust's lifetimes go beyond merely preventing dangling pointers.
Although not part of the question, the the body of this function is a one-liner. Unless this is purely a learning exercise, don't do more work than you have to:
fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
s.split(pat).next().unwrap_or(s)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With