I have two implementations of a function that do the same thing, which in my case is to reverse words in a string. I want to be able to cleanly test both functions using the same inputs and expected outputs, and the following is the best I could come up with:
let fn_arr: Vec<fn(&str) -> String> = vec![reverse_words, reverse_words2::reverse_words];
for f in fn_arr.iter() {
assert_eq!(f("apple"), "elppa");
assert_eq!(
f("The quick brown fox jumps over the lazy dog."),
"ehT kciuq nworb xof spmuj revo eht yzal .god"
);
assert_eq!(f("a b c d"), "a b c d");
assert_eq!(f("double spaced words"), "elbuod decaps sdrow");
}
Is there any better way to do this? Are there built-in ways to make this cleaner using #[cfg(test)]
? I get that this might be a trivial thing to do, and in general, we should avoid functions that do the same thing, but I just want to make sure I am making the most of what Rust can do. Is there a general way to say that two functions are "equal" or equivalent?
Other details:
The first function's implementation in src/main.rs
:
fn reverse_words(str: &str) -> String {
let mut total_collector = Vec::new();
let mut mini_collector = Vec::new();
for (i, c) in str.chars().enumerate() {
if c.is_whitespace() {
if !mini_collector.is_empty() {
mini_collector.reverse();
total_collector.append(&mut mini_collector);
mini_collector.truncate(0);
}
total_collector.push(c);
} else {
mini_collector.push(c);
}
if i == str.len() - 1 {
mini_collector.reverse();
total_collector.append(&mut mini_collector);
mini_collector.truncate(0)
}
}
total_collector.into_iter().collect::<String>()
}
The second function's implementation in src/reverse_words2/mod.rs
:
pub fn reverse_words(str: &str) -> String {
/*
The following one-liner in the function only works because of
the following trick:
```Rust
let x = " a b c".to_string();
let d: Vec<_> = x.split(' ').collect();
assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]);
```
Note that the string has 4 spaces before a, 2 spaces before b,
and 1 space before c. However, when split, the resulting vector
has 4 spaces, then a, then 1 space, then b, then 0 spaces, then c.
Contiguous separators can lead to possibly surprising behavior
when whitespace is used as the separator. This trick allows this
to work on sentences with an arbitrary number of spaces in between
words.
*/
str.split(' ')
.map(|word| word.chars().rev().collect::<String>())
.collect::<Vec<String>>()
.join(" ")
}
I'm adding these because I'd also take any feedback on if these functions should be organized in different files.