Reputation: 101
I am learning Rust and discovered this problem:
I would like to split a string by a pattern and remove all cases where the resultin substring is empty.
Here is an example:
let s = "s,o,m,e,";
for elem in s.split(",").skip_while(|&x| x.is_empty()) {
print!(" <{}> ", elem);
//print!(" <{}>({}) ", elem, elem.is_empty());
}
But the result is the following:
<s> <o> <m> <e> <>
My thoughts were: The struct Split
returned by split
implements Iterator
which provides skip_while
. IntelliSense told me the x
in the closure is of type &&str
so I would assume all the elements of the iterator (of type &str
) which are empty to be omitted.
But it doesn't skip the empty substring.
I also tried to print the result of the is_empty
function. It shows that the last slice is indeed empty. If I instead for the skip_while use skip_while(|&x| x == "s")
, it correctly leaves out the "s"
(printed with is_empty
here):
<o>(false) <m>(false) <e>(false) <>(true)
So somehow the slice behaves differently in the iterator?
Why is that or where am I mistaken?
Upvotes: 8
Views: 8627
Reputation: 27975
If you only need to omit the 1 empty string at the end of input, just use split_terminator
instead of split
. This adapter is basically just like split
but it treats the pattern argument as a terminator instead of a separator, so the empty string at the end of input is not considered a new element.
If you truly want to skip all the empty strings, keep reading.
skip_while
is doing exactly as its documentation says (emphasis mine):
skip_while()
takes a closure as an argument. It will call this closure on each element of the iterator, and ignore elements until it returnsfalse
.After
false
is returned,skip_while()
's job is over, and the rest of the elements are yielded.
Filtering out all the elements that match a predicate, regardless of where they are in the sequence, is the job of filter
:
let s = ",s,o,,m,e,";
for elem in s.split(",").filter(|&x| !x.is_empty()) {
print!(" <{}> ", elem);
}
The above code will have the effect you wanted.
Note that the predicate to filter
has the opposite meaning to skip_while
: instead of returning true
for elements that should not be yielded, it returns true
for elements that should be yielded.
Upvotes: 14
Reputation: 215047
skip_while
ignore elements until it returns false, that is it will take all elements after the first non empty string in your case. For instance, given:
let s = ",,s,o,m,e,";
skip_while
will ignore the first two empty strings but keep the last one:
let s = ",,s,o,m,e,";
for elem in s.split(",").skip_while(|&x| x.is_empty()) {
print!(" <{}> ", elem);
}
Will print:
<s> <o> <m> <e> <>
For your case, it seems you just need filter
:
let s = "s,o,m,e,";
for elem in s.split(",").filter(|&x| !x.is_empty()) {
print!(" <{}> ", elem);
}
Upvotes: 4