Reputation: 1280
I am trying to write a function that takes in an input string and does some operation on it only if the string is alphanumeric, currently my code is:
fn update(&mut self, input_string: String) -> bool {
let chars = {
let mut chars = Vec::new();
for c in input_string.chars() {
if !c.is_alphanumeric() {
return false
} else {
chars.push(c);
}
}
chars
};
// do something with chars
todo!()
}
This does work, but I would prefer to do this with iterators rather than loops as I generally find the code cleaner and easier to work with. However, I can't find a way to early return from within an iterator e.g.
fn update(&mut self, input_string: String) -> bool {
let chars = input_string.chars().into_iter().map(|c| {
if c.is_alphanumeric() {
c
} else {
return false
}
});
// do something with chars
todo!()
}
doesn't compile. How can I do an early return from within an iterator
Upvotes: 0
Views: 81
Reputation: 1959
If you want do the operation on chars, but stop it as soon as you get the first non-alphanumeric character consider using one of Iterator::try_*
functions.
For example, Iterator::try_fold
:
fn foo(input_string: &str) -> Option<Vec<char>> {
input_string
.chars()
.try_fold(vec![], |mut chars, c| {
if c.is_alphanumeric() {
dbg!("alphanumeric", &c);
chars.push(c);
Some(chars)
} else {
dbg!("non-alphanumeric", &c);
None
}
})
}
fn main() {
dbg!(foo("Hello, world!"));
dbg!(foo("Helloworld"));
}
Here we process the string char by char, but stop immediately on condition failure, returning None
for non-alphanumeric strings and Some(...)
for successful processing.
Upvotes: 0
Reputation: 16910
I'm not certain it is more readable than the explicit loop, but here is my attempt for an iterator-only version.
Some remarks:
&str
) is probably enough as the parameter,false
immediately without using chars
,chars
immutable, we can start the second part with let chars = chars
.fn with_iterator(input_string: &str) -> bool {
let mut chars = Vec::with_capacity(input_string.len()); // worst case
input_string.chars().all(|c| {
chars.push(c);
c.is_alphanumeric()
}) && /* short-circuit */ {
// do something with chars
print!(" {:?}", chars);
true
}
}
fn explicit_loop(input_string: &str) -> bool {
let chars = {
let mut chars = Vec::new();
for c in input_string.chars() {
if !c.is_alphanumeric() {
return false;
} else {
chars.push(c);
}
}
chars
};
// do something with chars
print!(" {:?}", chars);
true
}
fn main() {
for s in ["James", "Bond-007", "Bond007"] {
println!("input_string {:?}", s);
print!("• iter");
println!(" {}", with_iterator(s));
print!("• loop");
println!(" {}", explicit_loop(s));
}
}
/*
input_string "James"
• iter ['J', 'a', 'm', 'e', 's'] true
• loop ['J', 'a', 'm', 'e', 's'] true
input_string "Bond-007"
• iter false
• loop false
input_string "Bond007"
• iter ['B', 'o', 'n', 'd', '0', '0', '7'] true
• loop ['B', 'o', 'n', 'd', '0', '0', '7'] true
*/
Upvotes: 0
Reputation: 532
Iterator trait provides a lot of cool methods for tackling this issue, one of them is Iterator::all
, and you can you use it this way:
fn update(input: String) -> bool {
if !input.chars().all(char::is_alphanumeric) {
return false;
}
// do something with original input, it's still intact
todo!()
}
// or ...
fn updatecool(input: String) -> Option<()> {
// one liner :)
input.chars().all(char::is_alphanumeric).then_some(())?;
// do something with original input, it's still intact
todo!()
}
all method will short circuit on first failed test and return early
Upvotes: 0