查找连续相等字符的最长子字符串时如何处理"borrowed value does not live long enough"错误?



我有一篇作业来获取带有签名fn(s: &str) -> Option<&str>的连续相等字符的最长子字符串。但是,我的尝试会产生编译器错误:

pub fn longest_sequence(s: &str) -> Option<&str> {
let s_len = s.len();
if s_len == 0 {
return None
}
let mut cur_char = s.chars().nth(0);
let mut cur_occ = 0;
let mut max_char = s.chars().nth(0);
let mut max_occ = 0;
for i in 0..s_len {
let c = s.chars().nth(i);
if c == cur_char {
cur_occ = cur_occ + 1;
} else {
if cur_occ > max_occ {
max_occ = cur_occ;
max_char = cur_char;
}
cur_char = c.clone();
cur_occ = 1;
}
}
if cur_occ > max_occ {
max_occ = cur_occ;
max_char = cur_char;
}
let cr = max_char.unwrap();
let charstr = cr.to_string();
let string = charstr.repeat(max_occ);
let strr = string.as_str();
let some = Some(strr.clone());
println!("in {:?}",some);
some // Compilation error
//  None  // if returning None, then it compiles and runs as expected
}
fn main () {
println!("out {:?}",longest_sequence(&"aaabbbcccc"));
}
error[E0597]: `string` does not live long enough
--> r.rs:31:16
|
31 |     let strr = string.as_str();
|                ^^^^^^ borrowed value does not live long enough
...
35 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 1:1...
--> r.rs:1:1
|
1  | / pub fn longest_sequence(s: &str) -> Option<&str> {
2  | |     let s_len = s.len();
3  | |     if s_len == 0 {
4  | |         return None
...  |
34 | |     some
35 | | }
| |_^

如果我返回None而不是some,代码将编译并输出如您所期望的那样:

in Some("cccc")
out None

我怎样才能做到这一点?有人可以帮助我了解这个问题吗?

我认为如果您跟踪传入字符串中的索引,然后返回该字符串的一部分,这将更有意义:

s.get(start_index..stop_index)

Rust 使这有点棘手,因为您需要正确索引 utf-8 字符的字节,但您不仅想要最长的字节字符串,还想要最长的字符。

不过,我认为做这样的事情更容易理解和更清晰:跟踪当前最长序列的长度以及开始和停止字节。保留当前字符序列的字符计数,然后使用get()返回切片。这避免了在函数中创建新字符串的问题。

pub fn longest_sequence(s: &str) -> Option<&str> {
if s.is_empty() {
return None
}
let mut chars = s.char_indices();
let (mut char_start, mut curr) = chars.next().unwrap(); // get initial character
let mut longest = (char_start,curr.len_utf8(), 1);      // (start(byte), stop(byte), len(characters)
let mut curr_char_count = 1;                            // number of chars in current run
for (idx, c) in chars {
match c {
c if c == curr => {
curr_char_count += 1;
if curr_char_count > longest.2 {
longest = (char_start, idx + c.len_utf8(), curr_char_count);
}
}
n => {
curr = n;
curr_char_count = 1;
char_start = idx;
}
}
}
s.get(longest.0..longest.1)
}

这将允许非 ascii 字符串工作,并且仍然返回原始字符串的一部分:

fn main() {     
let s= longest_sequence("BB💣💣💣💣mmm!");
println!("{:?}", s);
// Some("💣💣💣💣")
}

根本问题是你在函数的中间创建一个堆分配的字符串,并且你返回了对该字符串的引用。当函数退出时,将删除函数拥有的任何值(例如字符串)(并且引用将失效)。Rust 刚刚将您从内存错误中拯救出来!

例如,这里的这一行:

let string = charstr.repeat(max_occ);

重复一个字符并创建一个字符串,但它需要放在某个地方来放置字符串。因此,它会在堆上创建一个字符串。但是,字符串必须在某个时候被释放(否则你会有内存泄漏!),所以 Rust 有所有权的概念。当前函数拥有的任何值也必须由函数删除或取消分配。

在这里,您借用了字符串:

let strr = string.as_str();

借款不会转让所有权。因此,该字符串仍归当前函数所有。这将解释错误消息:error[E0597]: `string` does not live long enough。您无法返回借用,因为拥有的值会在函数结束时被删除(或者更具体地说,借用的生存期仅适用于当前函数)。

有两种方法可以解决此问题:

  1. 直接返回字符串:

pub fn longest_sequence(s: &str) -> Option<String>

如果返回String而不是&str,则将字符串的所有权转移给调用方。

  1. 返回函数参数中字符串的借用

您可以修改算法以获取所需子字符串的索引,然后返回s的切片。这很好,因为返回值不会超过任何拥有的值。具体来说,它将具有与s相同的生命周期。

最后,如果函数中的任何位置都有String类型的变量,则在返回&str时,通常会遇到生存期问题(无论如何都是字符串处理)。

相关内容

最新更新