我正在学习《铁 rust 》中的弦乐.我想实现一个函数来计算字符串列表中的长公共前缀.

我的代码

impl Solution {
    pub fn get_common_prefix(s1: &String, s2: &String) -> String {
        let mut idx: usize = 0;
        if s1.len() > s2.len() {
            std::mem::swap(&mut s1, &mut s2);
        }
        
        while idx < s1.len() && s1.chars().nth(idx) == s2.chars().nth(idx) {
            idx += 1;    
        }
        return s1[0..idx].to_string();
        
        
    }
    pub fn longest_common_prefix(mut strs: Vec<String>) -> String {
        strs.sort();
        let mut longest_pref = strs[0];
        for i in 0..strs.len() {
            longest_pref = Self::get_common_prefix(&longest_pref, &strs[i]);
        }
        return longest_pref;
    }
}

我有一个错误.你能帮我修一下吗?

Line 5, Char 37: lifetime mismatch (solution.rs)
  |
2 |     pub fn get_common_prefix(s1: &String, s2: &String) -> String {
  |                                  -------      ------- these two types are declared with different lifetimes...
...
5 |             std::mem::swap(&mut s1, &mut s2);
  |                                     ^^^^^^^ ...but data from `s2` flows into `s1` here

我在这里读了https://doc.rust-lang.org/rust-by-example/scope/lifetime.html年的人生故事,但没有成功

推荐答案

如果您try 显式的隐式生存期,您将得到get_common_prefix的以下签名:

fn get_common_prefix<'a, 'b>(s1: &'a String, s2: &'b String) -> String { 
    ...
}

特别是,您不能交换这两个值,因为这两个borrow 彼此的持续时间都不长.相反,你可以这样做

fn get_common_prefix<'a>(s1: &'a String, s2: &'a String) -> String { 
    ...
}

这样做可以解决问题,但也会引发许多其他错误,因为您的代码还有其他问题.让我们一个接一个地看一遍.

首先,它现在会抱怨std::mem::swap(&mut s1, &mut s2);是非法的,因为

error[E0596]: cannot borrow `s1` as mutable, as it is not declared as mutable
 --> src/main.rs:4:24
  |
4 |         std::mem::swap(&mut s1, &mut s2);
  |                        ^^^^^^^ cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
1 | pub fn get_common_prefix<'a>(mut s1: &'a String, s2: &'a String) -> String {
  |                              +++

error[E0596]: cannot borrow `s2` as mutable, as it is not declared as mutable
 --> src/main.rs:4:33
  |
4 |         std::mem::swap(&mut s1, &mut s2);
  |                                 ^^^^^^^ cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
1 | pub fn get_common_prefix<'a>(s1: &'a String, mut s2: &'a String) -> String {
  |                                              +++

但是,Rust相当不错,它告诉您在本例中要做什么,将s1s2都声明为可变的:

fn get_common_prefix<'a>(mut s1: &'a String, mut s2: &'a String) -> String { 
    ...
}

功能get_common_prefix现在是正确的,但longest_common_prefix中仍然有错误:

error[E0507]: cannot move out of index of `Vec<String>`
  --> src/main.rs:16:28
   |
16 |     let mut longest_pref = strs[0];
   |                            ^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait
   |
help: consider borrowing here
   |
16 |     let mut longest_pref = &strs[0];
   |                            +

问题是您将从strs中提取strs[0],但不删除它,这是非法的,因为第一个字符串现在将被拥有两次(一次由longest_pref拥有,一次由strs拥有).

一种解决方案是实际取strs[0]swap_remove(假设我们不关心元素的顺序,这在从向量中删除元素时非常有效):

pub fn longest_common_prefix(mut strs: Vec<String>) -> String {
    strs.sort();
    let mut longest_pref = strs.swap_remove(0);
    for i in 1..strs.len() {
        longest_pref = get_common_prefix(&longest_pref, &strs[i]);
    }
    return longest_pref;
}

这行得通但是...由于几个原因,这是相当低效的.首先,即使它不是一个糟糕的性能冲击,在函数签名中使用&String几乎总是错误的,因为您可以用它做的所有事情(实际上,Rust会为您做这件事,所以您可能没有意识到这一点)是从Deref&str,这基本上是相同的,但减少了一个间接数(因为您可以将String视为指向str的指针).所以我们应该直接写出来

fn get_common_prefix<'a>(s1: &'a str, s2: &'a str) -> String { 
    ...
}

此外,返回String是没有意义的,这需要分配,而我们只需在该字符串上返回一个片段(因为我们是在取子字符串):

pub fn get_common_prefix<'a>(mut s1: &'a str, mut s2: &'a str) -> &'a str {
    let mut idx: usize = 0;
    if s1.len() > s2.len() {
        std::mem::swap(&mut s1, &mut s2);
    }
    
    while idx < s1.len() && s1.chars().nth(idx) == s2.chars().nth(idx) {
        idx += 1;    
    }
    return &s1[0..idx]
}

Notice that I changed both the return type and the last line.

现在,要使其发挥作用,我们还需要调整longest_common_prefix以使用字符串片:

pub fn longest_common_prefix(mut strs: Vec<String>) -> String {
    strs.sort();
    let mut longest_pref: &str = &strs[0];
    for i in 1..strs.len() {
        longest_pref = get_common_prefix(&longest_pref, &strs[i]);
    }
    return longest_pref.to_string();
}

我们回到了referencing,strs的第一个元素,而不是takingit.此外,我们只执行一次分配到String,而实际上我们必须返回String.

还有一些其他的优化工作要做.首先,排序strs是没有用的,它不会改变longest_common_prefix的结果,所以我们可以删除它

pub fn longest_common_prefix(strs: Vec<String>) -> String {
    let mut longest_pref: &str = &strs[0];
    for i in 1..strs.len() {
        longest_pref = get_common_prefix(&longest_pref, &strs[i]);
    }
    return longest_pref.to_string();
}

接下来,s1.chars().nth(i)是非常慢的(Θ(i)).要做到这一点,更有效的方法是重用相同的迭代器(s1.chars()),并在每一步推进它

for (c1, c2) in s1.chars().zip(s2.chars()) {
    if c1 != c2 {
        break;
    }
    idx += 1;    
}

.zip()不会从最长的字符串中提取任何剩余的字符,所以我们实际上可以完全删除swap,得到

pub fn get_common_prefix<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    let mut idx: usize = 0;
    for (c1, c2) in s1.chars().zip(s2.chars()) {
        if c1 != c2 {
            break;
        }
        idx += c1.len_utf8();    
    }
    return &s1[0..idx]
}

Note:正如@SebastianRedi所指出的,idx不应该增加1,而应该增加c1.len_utf8(),因为当索引字符串时,索引以字节表示,而不是以字符表示,并且有些字符的长度超过一个字节.

Rust相关问答推荐

Rust中的相互递归特性与默认实现

如何从接收&;self的方法克隆RC

通过解引用将值移出Box(以及它被脱糖到什么地方)?

同时从不同线程调用DLL的不同函数会出现分段错误或产生STATUS_STACK_BUFFER_OVERRUN

使用铁 rust S还原对多个数组执行顺序kronecker积

使用极点数据帧时,找不到枚举结果的方法lazy()

创建Rust中元对象协议的动态对象 Select /重新分配机制

在复制类型中使用std::ptr::WRITE_VILAR进行内部可变性的安全性(即没有UnSafeCell)

由于生存期原因,返回引用的闭包未编译

可选包装枚举的反序列化

Nom 解析器无法消耗无效输入

Rust 1.70 中未找到 Trait 实现

存储返回 impl Trait 作为特征对象的函数

unwrap 选项类型出现错误:无法移出共享引用后面的*foo

我如何取消转义,在 Rust 中多次转义的字符串?

为什么 `tokio::join!` 宏不需要 Rust 中的 `await` 关键字?

使用 rust 在 google cloud run (docker) 中访问环境变量的适当方法

Rust - 在线程之间不安全地共享没有互斥量的可变数据

有没有办法在 Rust 中对 BigInt 进行正确的位移?

如何在 Rust 中使用特征标志来捕获多行代码?