如何生成随机的字母数字字符串?

How to generate a random String of alphanumeric chars?

问题的第一部分可能很常见,并且有足够的代码示例可以解释如何生成随机的字母数字字符串。我使用的这段代码来自 here.

use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;

fn main() {
    let rand_string: String = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(30)
        .collect();

    println!("{}", rand_string);
}

这段代码无法编译,(注意:我在夜间):

error[E0277]: a value of type `String` cannot be built from an iterator over elements of type `u8`
 --> src/main.rs:8:10
  |
8 |         .collect();
  |          ^^^^^^^ value of type `String` cannot be built from `std::iter::Iterator<Item=u8>`
  |
  = help: the trait `FromIterator<u8>` is not implemented for `String`

好的,生成的元素是u8类型的。所以我猜这是 u8:

的数组或向量
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;

fn main() {
    let r = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(30)
        .collect::<Vec<_>>();
    let s = String::from_utf8_lossy(&r);
    println!("{}", s);
}

编译成功!

2dCsTqoNUR1f0EzRV60IiuHlaM4TfK

一切都很好,除了我想问问是否有人可以解释关于类型到底发生了什么以及如何优化它。

问题

  1. .sample_iter(&Alphanumeric) 产生 u8 而不是 chars?
  2. 如何避免第二个变量s并直接将u8解释为utf-8字符?我想内存中的表示根本不会改变?
  3. 这些字符串的长度应始终为 30。如何优化 Vec 的堆分配?它们实际上也可能是 char[] 而不是 Strings.

The first example in the docs for rand::distributions::Alphanumeric 表明如果你想将 u8s 转换成 chars 你应该 map 它们使用 char::from函数:

use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;

fn main() {
    let rand_string: String = thread_rng()
        .sample_iter(&Alphanumeric)
        .map(char::from) // map added here
        .take(30)
        .collect();

    println!("{}", rand_string);
}

playground

.sample_iter(&Alphanumeric) produces u8 and not chars?

是的,这是在 rand v0.8 中更改的。 You can see in the docs for 0.7.3:

impl Distribution<char> for Alphanumeric

But then in the docs for 0.8.0:

impl Distribution<u8> for Alphanumeric

How can I avoid the second variable s and directly interpret an u8 as a utf-8 character? I guess the representation in memory would not change at all?

有几种方法可以做到这一点,最明显的是将每个 u8 转换为 char:

let s: String = thread_rng()
    .sample_iter(&Alphanumeric)
    .take(30)
    .map(|x| x as char)
    .collect();

或者,使用 From<u8> instance of char:

let s: String = thread_rng()
    .sample_iter(&Alphanumeric)
    .take(30)
    .map(char::from)
    .collect();

当然在这里,因为你知道每个 u8 必须是有效的 UTF-8,你可以使用 String::from_utf8_unchecked,它比 from_utf8_lossy 快(虽然可能大约相同的速度作为 as char 方法):

let s = unsafe {
    String::from_utf8_unchecked(
        thread_rng()
            .sample_iter(&Alphanumeric)
            .take(30)
            .collect::<Vec<_>>(),
    )
};

如果由于某种原因,unsafe 困扰您并且您想保持安全,那么您可以使用速度较慢的 String::from_utf8unwrap 以及 Result 这样你得到一个 panic 而不是 UB(即使代码不应该 panic 或 UB):

let s = String::from_utf8(
    thread_rng()
        .sample_iter(&Alphanumeric)
        .take(30)
        .collect::<Vec<_>>(),
).unwrap();

The length of these strings should always be 30. How can I optimise the heap allocation of a Vec away? Also they could actually be char[] instead of Strings.

首先,相信我,您不需要字符数组。和他们一起工作并不有趣。如果你想要一个堆栈字符串,有一个 u8 数组,然后使用像 std::str::from_utf8 or the faster std::str::from_utf8_unchecked 这样的函数(同样只可用,因为你知道将生成有效的 utf8。)

关于优化堆分配,参考this answer。基本上,有点 hackiness/ugliness 是不可能的(例如制作自己的函数,将迭代器收集到 30 个元素的数组中)。

一旦 const generics 最终稳定下来,就会有一个更漂亮的解决方案。