【问题标题】:How to use contains() and retain() with String?如何将 contains() 和 retain() 与 String 一起使用?
【发布时间】:2018-05-29 00:10:21
【问题描述】:

我正在尝试使用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`

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

【问题讨论】:

  • 请包含错误的上下文,以便我们知道它指向的位置。
  • 请查看如何创建minimal reproducible example,然后查看edit 您的问题以包含它。我们无法分辨代码中存在哪些 crate、类型、特征、字段等。理想情况下,在Rust Playground 上生成可以重现您的错误的内容。尽可能减少你的代码——你需要返回一个Result吗?你需要一个自定义结构吗,你需要有多个retain语句等等。你的代码没有意义:你说你有一个Result ,但您的match 语句使用Result::OkOption::None
  • Idiomatic Rust 使用snake_case 表示变量、方法、宏和字段,使用UpperCamelCase 表示类型。请改用my_data

标签: iterator rust


【解决方案1】:

这是您的问题的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 reference `&&str`
              found reference `&str`

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

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

由于您的切片包含字符串文字(&amp;'static str 类型),它需要一个 &amp;&amp;str 进行比较。我们将引用as_str 方法的结果:

my_data.retain(|ref d| names.contains(&d.name.unwrap().as_str()));
//                                    ^-- added
error[E0507]: cannot move out of `d.name` which is behind a shared reference
  --> src/main.rs:10:44
   |
10 |     my_data.retain(|ref d| names.contains(&d.name.unwrap().as_str()));
   |                                            ^^^^^^
   |                                            |
   |                                            move occurs because `d.name` has type `std::option::Option<std::string::String>`, which does not implement the `Copy` trait
   |                                            help: consider borrowing the `Option`'s content: `d.name.as_ref()`

这个错误已经在 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)
    })
});

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

从 Rust 1.40 开始,这可以用 Option::as_deref 缩短:

my_data.retain(|d| {
    d.name
        .as_deref()
        .map_or(false, |name| names.contains(&name))
});

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 2013-07-26
    • 1970-01-01
    • 2010-09-16
    • 2018-04-06
    • 2014-07-18
    • 2017-06-25
    相关资源
    最近更新 更多