首页 > 解决方案 > 如何生成随机的字母数字字符串?

问题描述

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

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`

好的,生成的元素是 type 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并直接将 an 解释u8为 utf-8 字符?我猜内存中的表示根本不会改变?
  3. 这些字符串的长度应始终为 30。如何优化 a Vecaway 的堆分配?它们实际上也可以char[]代替Strings。

标签: stringrandomutf-8rustchar

解决方案


.sample_iter(&Alphanumeric)产生 u8 而不是字符?

是的,这在 rand v0.8 中已更改。您可以在 0.7.3 的文档中看到

impl Distribution<char> for Alphanumeric

但随后在 0.8.0 的文档中

impl Distribution<u8> for Alphanumeric

如何避免第二个变量s并直接将 an 解释u8为 utf-8 字符?我猜内存中的表示根本不会改变?

有几种方法可以做到这一点,最明显的是将 everyu8转换为 a char

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

或者,使用以下From<u8>实例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_utf8,所以你会得到一个恐慌而不是 UB(即使代码永远不应该恐慌或 UB):unwrapResult

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

这些字符串的长度应始终为 30。如何优化 a Vecaway 的堆分配?它们实际上也可以char[]代替字符串。

首先,相信我,你不想要字符数组。与他们一起工作并不有趣。如果您想要一个堆栈字符串,请有一个u8数组,然后使用类似std::str::from_utf8或更快的函数std::str::from_utf8_unchecked(再次仅可用,因为您知道将生成有效的 utf8。)

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

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


推荐阅读