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

时间:2018-05-29 00:10:22

标签: 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(_) => {}
}

然而,这会产生错误

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

似乎编译器在这里告诉我两个不同的东西,但在这两种情况下它似乎都需要一个额外的参考。但是,当我尝试将相关行更改为&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

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

1 个答案:

答案 0 :(得分: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()));
}
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`

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

pub fn contains(&self, x: &T) -> bool
where
    T: PartialEq<T>, 

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

my_data.retain(|ref d| names.contains(&d.name.unwrap().as_str()));
//                                    ^-- added
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

此错误已在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

然而,需要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)
    })
});

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