首先,请注意,我已经看到了How to filter a vector of custom structs?个,然而,它没有包含我问题的解决方案(或者至少我看不到一个).主要区别在于,在我的过滤器中,我try 使用字符串的CONTAIN()方法.如果我举个例子就更好了.

以下是我的 struct :

#[derive(Clone)]
pub struct Spell {
    pub title: String,
    pub code: String,
    pub category: String,
    pub sub_category: String,
    pub tool: String,
    pub file_name: String,
}

#[derive(Debug)]
pub struct SearchParameters {
    pub category: String,
    pub sub_category: String,
    pub tool: String,
    pub search_str: String,
}

下面是我try 在其中处理向量的函数:

pub fn search_spell(&self, search_parameters: SearchParameters) -> Vec<Spell> {
    let tmp_results: Vec<Spell> = self
        ._spell_list
        .clone()
        .into_iter()
        .filter(|spell| {
            if search_parameters.category.is_empty() {
                true
            } else {
                *spell.category == search_parameters.category
            }
        })
        .filter(|spell| {
            if search_parameters.sub_category.is_empty() {
                true
            } else {
                *spell.sub_category == search_parameters.sub_category
            }
        })
        .filter(|spell| {
            if search_parameters.tool.is_empty() {
                true
            } else {
                *spell.tool == search_parameters.tool
            }
        })
        .filter(|spell| {
            if search_parameters.search_str.is_empty() {
                true
            } else {
                //*spell.title.contains(search_parameters.search_str)
                true
            }
        })
        .collect();

    // TODO: replace this part by including in the filer above  
    let mut results: Vec<Spell> = Vec::new();
    for spell in tmp_results {
        if search_parameters.search_str.is_empty() {
            results.push(spell);
        } else {
            if spell
                .title
                .to_lowercase()
                .contains(&search_parameters.search_str.to_lowercase())
            {
                results.push(spell);
            }
        }
    }

    results
}

问题出在最后一个过滤器块(参见注释代码),其中我想使用.contains()方法,而不是==个比较.问题是,在编译时,编译器不能确定这个值到底是什么.

目前,我已经通过在此方法的末尾处理循环中的向量来解决这个问题,但如果您明白我的意思,这感觉有点勉强.我真的认为我应该能够在过滤器中以某种方式解决这个问题.

推荐答案

这里有几件事需要解决.让我们一个接一个地看一遍.

第一个是你所面临的问题.问题是,Pattern只对&str实现,但您试图给它String.这很容易解决,只需borrow String作为片段--只需在整个表达式前面加上&(您在for循环中就是这样做的).

然而,在filter闭包中调用to_lowercase()是浪费的:search_str的值不会改变,但是您将为原始集合中的每一项执行转换和分配.相反,在filter闭包之外调用to_lowercase(),并将结果移到闭包中.

最后,contains()返回bool,因此*是错误的.取消引用bool没有任何意义;只需省略*即可返回bool.

此外,你克隆整个Vec<Spell>个,然后对其进行过滤,这也是浪费.你克隆的东西可能永远不会被生输出来.相反,请考虑下面的方法链,它只克隆实际生产的项:

self._spell_list
    .iter()
    .filter(...)
    .filter(...)
    .cloned()
    .collect();

更好的是,让该方法返回impl Iterator<Item=&'_ Spell>并完全避免克隆!如果呼叫者需要克隆项目,他们可以,否则他们可以使用借来的Spell,而不需要克隆.

要使返回引用的迭代器工作,我们需要分解SearchParameters并将每个元素移动到各自的闭包中,或者将各种filter组合成一个filter,或者我们需要从调用方borrow SearchParameters而不是按值获取,但borrow 它也是一个很好的更改:调用方可以多次重用SearchParameters值,因为该函数不需要获得它的所有权.

最后,我们可以通过应用转换使每个filter的逻辑更简洁:if x { true } else { y }变成x || y.

将所有这些变化放在一起:

pub fn search_spell<'a>(&'a self, search_parameters: &'a SearchParameters)
    -> impl Iterator<Item=&'a Spell>
{
    let search_str = search_parameters.search_str.to_lowercase();
    
    self
        ._spell_list
        .iter()
        .filter(|spell| {
            search_parameters.category.is_empty() ||
            *spell.category == search_parameters.category
        })
        .filter(|spell| {
            search_parameters.sub_category.is_empty() ||
            *spell.sub_category == search_parameters.sub_category
        })
        .filter(|spell| {
            search_parameters.tool.is_empty() ||
            *spell.tool == search_parameters.tool
        })
        .filter(move |spell| {
            search_str.is_empty() ||
            spell.title.contains(&search_str)
        })
}

Rust相关问答推荐

为什么我可以跟踪以前borrow 过的变量?房主在哪里?

如何指定不同的类型来常量Rust中的泛型参数?

告诉Rust编译器返回值不包含构造函数中提供的引用

类型批注需要静态生存期

在Rust中声明和定义一个 struct 体有什么区别

tokio::sync::broadcast::Receiver 不是克隆

Boxing 如何将数据从堆栈移动到堆?

通过写入 std::io::stdout() 输出不可见

Rust 并行获取对 ndarray 的每个元素的可变引用

我可以用 Rust 编写一个不可变变量

在 Rust 中忽略 None 值的正确样式

Rust 文件未编译到 dll 中

Rust中如何实现一个与Sized相反的负特性(Unsized)

如何使用泛型满足 tokio 异步任务中的生命周期界限

在 Rust 中,将可变引用传递给函数的机制是什么?

如何获取包裹在 Arc<> 和 RwLock<> 中的 Rust HashMap<> 的长度?

将文件的第一行分别读取到文件的其余部分的最有效方法是什么?

如何将 while 循环内的用户输入添加到 Rust 中的向量?

为什么在使用 self 时会消耗 struct 而在解构时不会?

为什么我在这里得到一个闭包实现`FnMut`,所以对捕获变量的引用不能逃脱闭包?