I'm trying to find the Rust equivalent of having a ASCII string buffer on the stack to have the same efficiency as plain C code has.
Here an example on what I mean with a simplified toy exercise: the goal is to generate a random-content and random-length ASCII string that is at most 50 characters long. Thus I keep a char buffer on the stack that is used to iteratively construct the string. Once finished, the string is copied onto the heap with the just-right malloc size and returned to the user.
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#define ASCII_PRINTABLE_FIRST ' '
#define ASCII_PRINTABLE_AMOUNT 95
#define MAX_LEN 50
#define MAX_LEN_WITH_TERM (MAX_LEN + 1)
char* generate_string(void) {
char buffer[MAX_LEN_WITH_TERM];
srand((unsigned) time(NULL));
// Generate random string length
const int len = rand() % MAX_LEN_WITH_TERM;
int i;
for (i = 0; i < len; i++) {
// Fill with random ASCII printable character
buffer[i] = (char)
((rand() % ASCII_PRINTABLE_AMOUNT) + ASCII_PRINTABLE_FIRST);
}
buffer[i] = '\0';
return strdup(buffer);
}
int main(void) {
printf("Generated string: %s\n", generate_string());
return 0;
}
What I explored so far:
String::with_capacity(50)
or BytesMut
, but that allocates the buffer on the heap, which I would like to avoid. Sure, it's premature optimisation, but as an optimisation exercise let's image me calling generate_string()
a billion times. That is a billion malloc calls to allocate the buffer. I don't want to use static memory.What are your suggestions?
EDIT:
generate_string()
call? To make the function self contained, stateless and without static memory. It does not require a pre-allocated buffer externally. The Rust type that is equivalent to C's char
is u8
, so the equivalent to a char
buffer on the stack is an u8
array.
let mut buf = [0u8; 20];
for i in 0..20 {
buf[i] = b'a' + i as u8;
}
To obtain a &str
slice that points into the stack buffer, you can use std::str::from_utf8
, which performs a UTF-8 check and returns the pointer if it is valid UTF-8.
fn takes_a_string(a: &str) {
println!("{}", a);
}
fn main() {
let mut buf = [0u8; 20];
for i in 0..20 {
buf[i] = b'a' + i as u8;
}
// This calls takes_a_string with a reference to the stack buffer.
takes_a_string(std::str::from_utf8(&buf).unwrap());
}
abcdefghijklmnopqrst
You can generate a random length u8
array (stored on the stack) and only allocate memory on the heap when you convert it to a String
using the from_utf8
method. Example:
use rand::prelude::*;
const MAX_LEN: usize = 50;
const ASCII_START: u8 = 32;
const ASCII_END: u8 = 127;
fn generate_string() -> String {
let mut buffer = [0; MAX_LEN];
let mut rng = rand::thread_rng();
let buffer_len = rng.gen_range(0, MAX_LEN);
for i in 0..buffer_len {
buffer[i] = rng.gen_range(ASCII_START, ASCII_END);
}
String::from_utf8((&buffer[0..buffer_len]).to_vec()).unwrap()
}
fn main() {
for _ in 0..5 {
dbg!(generate_string());
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.