堆栈上 char 缓冲区/ASCII 字符串的 Rust 等价物是啥?

Posted

技术标签:

【中文标题】堆栈上 char 缓冲区/ASCII 字符串的 Rust 等价物是啥?【英文标题】:What is the Rust equivalent of a char buffer / ASCII string on the stack?堆栈上 char 缓冲区/ASCII 字符串的 Rust 等价物是什么? 【发布时间】:2020-12-14 18:36:26 【问题描述】:

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

这里有一个简单的玩具练习的例子: 目标是生成最多 50 个字符长的随机内容和随机长度的 ASCII 字符串。因此,我在堆栈上保留了一个 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] = '\0';
    return strdup(buffer);


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

到目前为止我探索了什么:

使用缓冲区String::with_capacity(50)BytesMut,但这会在堆上分配缓冲区,我想避免这种情况。当然,这是过早的优化,但作为优化练习,让我们想象一下我调用generate_string() 十亿次。那是十亿次 malloc 调用来分配缓冲区。我不想使用静态内存。 在堆栈上使用字符数组,但仅 ASCII 字符占用 4 倍空间

你有什么建议?

编辑:

    是的,它会泄漏内存。这不是我的问题的重点,除非您想要更长的 sn-ps 代码。 是的,它包含不安全的随机字符。这不是我的问题的重点。 为什么每次generate_string() 调用都在堆上分配一次缓冲区?使函数自包含、无状态且没有静态内存。它不需要外部预先分配的缓冲区。

【问题讨论】:

(旁注:return strdup(buffer); - 您的 C 代码正在泄漏内存。) 看来How do I collect into an array?的答案可能会回答您的问题; Is it possible to have stack allocated arrays with the size determined at runtime in Rust?; How to set a Rust array length dynamically?。如果没有,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。 使用[u8; 50]有什么问题? 如果我调用generate_string() 十亿次,那就是十亿次额外的堆分配。 这通常是错误的;为什么要分配两次?分配一次并返回。如果您担心性能,为什么您的 C 代码不分配一次并直接写入缓冲区,而不是写入一次然后复制? 请不要使用random % something — Why do people say there is modulo bias when using a random number generator? 【参考方案1】:

您可以生成一个随机长度的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

【讨论】:

这正是我所需要的。谢谢!【参考方案2】:

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

let mut buf = [0u8; 20];

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

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

【讨论】:

以上是关于堆栈上 char 缓冲区/ASCII 字符串的 Rust 等价物是啥?的主要内容,如果未能解决你的问题,请参考以下文章

套接字数据(char)c ++的十六进制表示

在 char 上使用 toupper 返回字符的 ascii 编号,而不是字符?

在堆栈上分配 16kb 是否太多了?

Char* 在函数中使用 malloc 创建,编译器说地址在堆栈上,无法返回

C中的内联字符串数组是不是分配在堆栈上?

堆栈中的缓冲区溢出