Closed freak82 closed 4 months ago
I think you need slice.rchunks. Slices know the entire data and length of it while our iterator adaptors don't know much about the underlying data.
use itertools::Itertools; // for intersperse
fn f(data: &str) -> String {
let b = data.as_bytes();
let it = b.rchunks(8).rev();
let nb_commas = it.len().saturating_sub(1);
let total_length = b.len() + nb_commas;
let mut result = String::with_capacity(total_length); // Single heap allocation.
result.extend(
it.intersperse(",".as_bytes() /* or just: &[b','] */)
.flatten()
.map(|x| *x as char),
);
result
// or
// let mut vec = Vec::with_capacity(total_length); // Single heap allocation.
// vec.extend(it.intersperse(",".as_bytes()).flatten().copied()); // copied: &u8 -> u8
// String::from_utf8(vec).unwrap() // Explicitely panics if there is an utf8 issue.
}
fn main() {
let s = f("fff1fffffff2fffffffe");
println!("{:?}", (s.len(), s.capacity(), s));
}
Aha, I see. Thank you for the response.
Hi there,
First of all, feel free to close this issue if you think it is not appropriate. I mean, that I have no real problem with the library I'm just struggling with a case where I want to translate given C++ ranges code to Rust using iterators and adapters. Additional "requirement" is the code to do as few allocations as possible.
So basically, I've a string which represents big hex number and it should be split by comma at every 8 character from right to left. For example, this
fff1fffffff2fffffffe
should become thisfff1,fffffff2,fffffffe
(this format is required by/sys/class/net/<interface>/queues/rx-<num>/rps_cpus
).The C++ code using ranges looks like this and do single allocation only for the final string because it can calculate it's size from the passed iterators:
I'm trying to achieve similarly looking and performing code using Rust iterators and adapters. However, my experience with Rust is pretty basic at this point and I'm not satisfied with the result of my "translation". The Rust code looks a bit "clumsy" due to the "manual" extension and join operations in the
fold
closure.I've troubles with the type returned from the
chuks
function. I couldn't make it work withintersperse
which I was trying to use to add the,
. I've tried flattening it but this created otherVec
objects i.e. it led to more memory allocations. Also note that the C++ code usesstd::string
while the Rust code usesVec<char
just because I couldn't find a way to reverse in placeString
otherwise the final reverse would require additional memory allocation. AFAIK, the RustString
isutf8
and that's why there is no way to reverse it without additional memory allocation.So, is there a way to make the Rust code look better and at the same time to decrease the number of allocations (currently it does 16-32-64-128 byte allocations, I guess when enlarging the final
Vec
. I guess, I could use some other adapters from theitertools
but I couldn't figure out which.Both C++ and Rust code is available in this godbolt link.
Thanks, Pavel.