【问题标题】:How to generate a random String of alphanumeric chars?如何生成随机的字母数字字符串?
【发布时间】:2021-04-05 06:34:32
【问题描述】:

问题的第一部分可能很常见,并且有足够的代码示例来解释如何生成随机的字母数字字符串。我使用的这段代码来自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(&amp;Alphanumeric) 产生 u8 而不是 chars?
  2. 如何避免第二个变量s 直接将u8 解释为utf-8 字符?我猜内存中的表示根本不会改变?
  3. 这些字符串的长度应始终为 30。如何优化Vec 的堆分配?他们实际上也可以是char[] 而不是Strings。

【问题讨论】:

    标签: string random utf-8 rust char


    【解决方案1】:

    .sample_iter(&amp;Alphanumeric) 生成 u8 而不是字符?

    是的,这在 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
    

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

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

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

    或者,使用From&lt;u8&gt; 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_utf8 和 unwrap Result,这样您就会感到恐慌而不是 UB(即使代码永远不应该恐慌或UB):

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

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

    首先,相信我,你不想要字符数组。与他们一起工作并不有趣。如果你想要一个堆栈字符串,有一个u8 数组,然后使用像std::str::from_utf8 或更快的std::str::from_utf8_unchecked 之类的函数(同样只能使用,因为你知道会生成有效的utf8。)

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

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

    【讨论】:

    • 我认为你应该提到一个可能的原因,更喜欢Vec&lt;u8&gt; 而不是Vec&lt;char&gt;:char 始终是 4 个字节长,这意味着存储字符的任何 ASCII 字母数字序列将浪费三倍以上的内存比必要的。
    【解决方案2】:

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 2011-08-18
      • 2012-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-09
      相关资源
      最近更新 更多