堆栈上的 char 缓冲区/ASCII 字符串在 Rust 中的等价物是什么?

What is the Rust equivalent of a char buffer / ASCII string on the stack?

我正在尝试找到 Rust 等效于在堆栈上具有 ASCII 字符串缓冲区以具有与纯 C 代码相同的效率。

这里有一个简单的玩具练习的例子: 目标是生成一个随机内容和随机长度的 ASCII 字符串,最多 50 个字符长。因此,我在堆栈上保留了一个 char 缓冲区,用于迭代构造字符串。完成后,字符串将以恰到好处的 malloc 大小复制到堆上并返回给用户。

#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] = '[=10=]';
    return strdup(buffer);
}

int main(void) {
    printf("Generated string: %s\n", generate_string());
    return 0;
}

到目前为止我探索的内容:

你有什么建议?

编辑:

  1. 是的,它泄漏了内存。这不是我问题的重点,除非你想要更长的代码片段。
  2. 是的,它有不安全的随机字符。这不是我问题的重点。
  3. 为什么我要在每次 generate_string() 调用时在堆上分配一次缓冲区?使功能自包含,无状态且没有静态内存。它不需要外部预分配的缓冲区。

相当于C的char的Rust类型是u8,所以相当于栈上的一个char缓冲区是一个u8数组。

let mut buf = [0u8; 20];

for i in 0..20 {
    buf[i] = b'a' + i as u8;
}

要获取指向堆栈缓冲区的 &str 切片,您可以使用 std::str::from_utf8,它执行 UTF-8 检查和 returns 指针(如果它是有效的 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

您可以生成一个随机长度的 u8 数组(存储在堆栈中),并且仅在使用 from_utf8 方法将其转换为 String 时才在堆上分配内存。示例:

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()); 
    }
}

playground