堆栈上 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;
要获取指向堆栈缓冲区的&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 上使用 toupper 返回字符的 ascii 编号,而不是字符?