Combine Filter to include and Exclude I have some data which I want to filter. I have a collection of things to include and a second one of things to exclude.

Currently I'm looping my data and filter once for the including and a second time for the excluding.

Example coding:

#[derive(Debug)]
pub struct Dataset {
    pub data: Vec&lt;String&gt;
}

fn main() {
    //setup test data
    let mut ds1_data : Vec&lt;String&gt; = Vec::with_capacity(2);
    ds1_data.push("a".to_string());
    ds1_data.push("b".to_string());
    let ds1 = Dataset{data: ds1_data};

    let mut ds2_data : Vec&lt;String&gt; = Vec::with_capacity(2);
    ds2_data.push("a".to_string());
    ds2_data.push("c".to_string());
    let ds2 = Dataset{data: ds2_data};

    let mut datasets : Vec&lt;Dataset&gt; = Vec::with_capacity(2);
    datasets.push(ds1);
    datasets.push(ds2);

    let mut include : Vec&lt;String&gt; = Vec::with_capacity(2);
    include.push("a".to_string());

    let mut exclude : Vec&lt;String&gt; = Vec::with_capacity(1);
    exclude.push("b".to_string());

    //filter datasets
    for ds in datasets {
        if ds.data.iter().find(|d| include.contains(d)).is_none() {
            //skip not included
            continue;
        }        
        if ds.data.iter().find(|d| exclude.contains(d)).is_some() {
            //skip excluded
            continue;
        }

        println!("{:?}", ds);
    }
}


Also available in the rust playground:
https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=29c13f2b7ab3a50fbcd12b88b59f4b74

Is there way to combine the filter statements so I don't need to iterate ds.data two times?



To clarify:
The above coding is working as expected. I get datasets containing at least one element of include but no element of exclude
The vec! macro

Before we have a look at including and excluding, we should note that

let mut myvec: Vec&lt;T&gt; = Vec::with_capacity(2);
myvec.push(a);
myvec.push(b);
myvec.push(c);


can be written with the vec! macro as

let myvec: Vec&lt;T&gt; = vec![a,b,c];


This has the nice side-effect that myvec doesn't need to be mut anymore. If we apply this too all your code, we end up with    

#[derive(Debug)]
pub struct Dataset {
    pub data: Vec&lt;String&gt;,
}
fn main() {
    //setup test data
    let ds1_data = vec!["a".to_string(), "b".to_string()];
    let ds1 = Dataset { data: ds1_data };

    let ds2_data = vec!["a".to_string(), "c".to_string()];
    let ds2 = Dataset { data: ds2_data };

    let datasets = vec![ds1, ds2];

    let include = vec!["a".to_string()];
    let exclude = vec!["b".to_string()];

    //filter datasets
    for ds in datasets {
        if ds.data.iter().find(|d| include.contains(d)).is_none() {
            continue;
        }
        if ds.data.iter().find(|d| exclude.contains(d)).is_some() {
            continue;
        }

        println!("{:?}", ds);
    }
}


That's a lot less noise, perfect to concentrate on the original problem.

Including and excluding

First of all, instead of find(…).is_some(), we can use any, and instead of find(…).is_none() too. We could use that to simplify the expressions, but we would still traverse the set twice.

for ds in datasets {
    if !ds.data.iter().any(|d| include.contains(d)) {
        //skip not included
        continue;
    }        
    if ds.data.iter().any(|d| exclude.contains(d)) {
        //skip excluded
        continue;
    }

    println!("{:?}", ds);
}


Instead, let's think about the elements we have to look at. The answer is all, as exclude might match the last element in our dataset, even if include matched the first.

As we have to look at all elements either way, we might as well traverse them by hand:

    for ds in datasets {
        let mut included = false;
        let mut excluded = false;
        for d in &amp;ds.data {
            if included == false &amp;&amp; include.contains(d) {
                included = true;
            }
            if exclude.contains(d) {
                excluded = true;
                break;
            }
        }
        if included == false || excluded == true {
            continue;
        }

        println!("{:?}", ds);
    }


Now that we have this as a blueprint, we can check the iterators for a fitting method. We need to


check that at least one include matches
check that no exclude matches
stop as soon as soon as any exclude matches.


Unfortunately it's not possible to quick exit from a closure, so the last requirement cannot be done with fold or similar. However, we can use inspect and all to get something like our previous loop:

    for ds in datasets {
        let mut included = false;

        if !ds
            .data
            .iter()
            .inspect(|d| {
                if included == false || include.contains(d) {
                    included = true
                }
            })
            .all(|d| !exclude.contains(d))
            || !included
        {
            continue;
        }
        println!("{:?}", ds);
    }


But that's a lot less readable than our previous loop, which did the same. We could increase readablity if we introduced a function, but that's left as an exercise. In the end, we would need a fold-like function that supports an early exit, but such a function does not exist.