【问题标题】:Borrowed value does not live long enough (&str -> Option<&str>)借来的价值不够长(&str -> Option<&str>)
【发布时间】:2019-11-29 09:34:49
【问题描述】:

获取连续相等字符的最长子串是一项功课。

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"));
}

我希望输出是

in Some("cccc")
out Some("cccc")

但出现编译错误。

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 | | }
   | |_^

error: aborting due to previous error

如果返回 None 而不是 some,则代码会按预期编译和运行:

in Some("cccc")
out None

我很困惑,因为错误消息指向此更改之前的一行..

有人可以帮忙吗?谢谢。

P.S.:我把它改成记录开始和结束位置索引,这样我就可以返回Some(&amp;s[start..end])。我认为s 绝对应该有足够的寿命,是的,它有效!但我仍然想知道为什么上面的实现不起作用..

【问题讨论】:

  • 你创建了一个String就返回它为什么你要返回一个&amp;str
  • @Stargateur 好吧.. 这是作业,这是必需的类型。我尝试将其更改为 String 并且它有同样的问题,所以没关系..
  • 如果是作业,把它放在你的问题中会有所帮助。
  • 好吧,你必须避免创建一个字符串,你必须在参数中使用str。

标签: rust


【解决方案1】:

根本问题是您在函数中间创建了一个堆分配的字符串,并且您正在返回对该字符串的引用。当函数退出时,函数拥有的任何值(例如字符串)都将被删除(并且引用将失效)。 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: &amp;str) -&gt; Option&lt;String&gt;

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

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

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

最后,如果你在函数的任何地方都有一个String 类型的变量,那么在返回&amp;str 时,你通常会遇到生命周期问题(无论如何都要处理字符串)。

【讨论】:

    【解决方案2】:

    我认为,如果您跟踪传入字符串的索引,然后返回该字符串的一个片段,那么这会更有意义:

    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("????")
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-14
      相关资源
      最近更新 更多