如何在String中使用contains()和retain()?

cey*_*eyx -1 iterator rust

我正在尝试使用该retain(...)方法从向量中过滤掉值.目前,我的代码到目前为止

let results = get_data(); // Returns Result<Vec<MyData>, Box<Error>>

match results {
    Ok(mut my_data) => {

        // Make sure that `name` is not None
        myData.retain(|ref d| d.name.is_some());

        // Only keep data if 'name' is in this list
        let names = ["Alice", "Bob", "Claire"];
        my_data.retain(|ref d| names.contains(d.name.unwrap().as_str()));
    },
    Err(_) => {}
}
Run Code Online (Sandbox Code Playgroud)

但是,这会产生错误

|
|     my_data.retain(|ref d| names.contains(d.name.unwrap().as_str()));
|                                           ^^^^^^^^^^^^^^^^^^^^^^^^ expected &str, found str
|
= note: expected type `&&str`
           found type `&str`
Run Code Online (Sandbox Code Playgroud)

看起来编译器在这里告诉我两个不同的东西,但在这两种情况下它似乎都想要一个额外的参考.但是,当我尝试将相关行更改&names.contains(d.name.unwrap().as_str())为时,编译器会抛出以下错误.

|
|     my_data.retain(|ref d| names.contains(&d.name.unwrap().as_str()));
|                                            ^ cannot move out of borrowed content
Run Code Online (Sandbox Code Playgroud)

如何检查向量中是否包含借用的String(我认为这是什么类型name.unwrap())names

She*_*ter 6

这是你的问题的MCVE(你的原始代码有其他无关的问题,这里忽略了):

struct MyData {
    name: Option<String>,
}

fn main() {
    let mut my_data: Vec<MyData> = vec![];

    // Only keep data if 'name' is in this list
    let names = ["Alice", "Bob", "Claire"];
    my_data.retain(|ref d| names.contains(d.name.unwrap().as_str()));
}
Run Code Online (Sandbox Code Playgroud)
error[E0308]: mismatched types
  --> src/main.rs:10:43
   |
10 |     my_data.retain(|ref d| names.contains(d.name.unwrap().as_str()));
   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^ expected &str, found str
   |
   = note: expected type `&&str`
              found type `&str`
Run Code Online (Sandbox Code Playgroud)

如错误消息所述,slice::contains需要引用切片中包含的值类型:

pub fn contains(&self, x: &T) -> bool
where
    T: PartialEq<T>, 
Run Code Online (Sandbox Code Playgroud)

由于切片包含字符串文字(类型&'static str),因此需要&&str进行比较.我们将参考该as_str方法的结果:

my_data.retain(|ref d| names.contains(&d.name.unwrap().as_str()));
//                                    ^-- added
Run Code Online (Sandbox Code Playgroud)
error[E0507]: cannot move out of borrowed content
  --> src/main.rs:10:44
   |
10 |     my_data.retain(|ref d| names.contains(&d.name.unwrap().as_str()));
   |                                            ^ cannot move out of borrowed content
Run Code Online (Sandbox Code Playgroud)

Stack Overflow上的其他地方已经彻底解释了这个错误:

TL; DR - 您不拥有d,因此您无法解包d.name,消耗过程中的值.修复是使用Option::as_ref:

my_data.retain(|ref d| names.contains(&d.name.as_ref().unwrap().as_str()));
//                                           ^^^^^^^^^-- added
Run Code Online (Sandbox Code Playgroud)

然而,需要unwrap是丑陋的,理想的是避免.您可以将两个retain调用合并为一个,使用Option::map_or以下命令跳过展开:

my_data.retain(|d| {
    d.name.as_ref().map_or(false, |name| {
        let name = name.as_str();
        names.contains(&name)
    })
});
Run Code Online (Sandbox Code Playgroud)

这消除了潜在的恐慌并提高了代码的效率.