I'm attempting to learn Rust. And a recent problem I've encountered is the following:
given a String
, that is exactly some multiple of n, I want to split the string into chunks of size n, and insert a space in between these chunks, then collect back into a single string.
The issue I was running into, is that the chars()
method returns the Chars
struct, which for some reason doesn't implement the SliceConcatExt
trait, so chunks()
can't be called on it.
Furthermore, once I've successfully created a Chunks struct (by calling .bytes()
instead) I'm unsure how to call a .join(' ')
since the elements are now Chunks
of byte slices...
There has to be an elegant way to do this I'm missing.
For example here is an input / output that illustrates the situation:
given: whatupmyname, 4
output: what upmy name
This is my poorly written attempt:
let n = 4;
let text = "whatupmyname".into_string();
text.chars()
// compiler error on chunks() call
.chunks(n)
.collect::<Vec<String>>()
.join(' ')
Thank you for any help!
With the split() method, we can create an iterator containing the particular string's sub-strings. This method takes the separator as an argument. This separator determines where to start splitting the strings to an iterator of its sub-strings.
A String is always 24 bytes.
The str type, also called a 'string slice', is the most primitive string type. It is usually seen in its borrowed form, &str . It is also the type of string literals, &'static str . String slices are always valid UTF-8.
The problem here is that chars()
and bytes()
return Iterator
s, not slices. You could use as_bytes()
, which will give you a &[u8]
. However, you cannot directly get a &[char]
from a &str
, because there only exists the bytes themselves, and the char
s must be created by looking through and seeing how many bytes makes up each one. You'd have to do something like this:
text.chars()
.collect::<Vec<char>>()
.chunks(n)
.map(|c| c.iter().collect::<String>())
.collect::<Vec<String>>()
.join(" ");
However, I would NOT recommend this as it has to allocate a lot of temporary storage for Vec
s and String
s along the way. Instead, you could do something like this, which only has to allocate to create the final String
.
text.chars()
.enumerate()
.flat_map(|(i, c)| {
if i != 0 && i % n == 0 {
Some(' ')
} else {
None
}
.into_iter()
.chain(std::iter::once(c))
})
.collect::<String>()
This stays as iterators until the last collect, by flat_mapping with an iterator that is either just the character or a space and then the character.
If the size of the data you want to split in is fixed then:
use std::str;
fn main() {
let subs = "‌​‌​‌​​‌​‌".as_bytes()
.chunks(7)
.map(str::from_utf8)
.collect::<Result<Vec<&str>, _>>()
.unwrap();
println!("{:?}", subs);
}
// >> ["‌", "​", "‌", "​", "‌", "​", "​", "‌", "​", "‌"]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With