@@ -70,6 +70,35 @@ use serde::{Deserialize, Serialize};
70
70
#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
71
71
pub struct Alphanumeric ;
72
72
73
+ /// Sample a [`u8`], uniformly distributed over letters:
74
+ /// a-z and A-Z.
75
+ ///
76
+ /// # Example
77
+ ///
78
+ /// You're able to generate random Alphabetic characters via mapping or via the
79
+ /// [`SampleString::sample_string`] method like so:
80
+ ///
81
+ /// ```
82
+ /// use rand::Rng;
83
+ /// use rand::distr::{Alphabetic, SampleString};
84
+ ///
85
+ /// // Manual mapping
86
+ /// let mut rng = rand::rng();
87
+ /// let chars: String = (0..7).map(|_| rng.sample(Alphabetic) as char).collect();
88
+ /// println!("Random chars: {}", chars);
89
+ ///
90
+ /// // Using [`SampleString::sample_string`]
91
+ /// let string = Alphabetic.sample_string(&mut rand::rng(), 16);
92
+ /// println!("Random string: {}", string);
93
+ /// ```
94
+ ///
95
+ /// # Passwords
96
+ ///
97
+ /// Refer to [`Alphanumeric#Passwords`].
98
+ #[ derive( Debug , Clone , Copy , Default ) ]
99
+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
100
+ pub struct Alphabetic ;
101
+
73
102
// ----- Implementations of distributions -----
74
103
75
104
impl Distribution < char > for StandardUniform {
@@ -123,6 +152,17 @@ impl Distribution<u8> for Alphanumeric {
123
152
}
124
153
}
125
154
155
+ impl Distribution < u8 > for Alphabetic {
156
+ fn sample < R : Rng + ?Sized > ( & self , rng : & mut R ) -> u8 {
157
+ const RANGE : u8 = 26 + 26 ;
158
+
159
+ let offset = rng. random_range ( 0 ..RANGE ) + b'A' ;
160
+
161
+ // Account for upper-cases
162
+ offset + ( offset > b'Z' ) as u8 * ( b'a' - b'Z' - 1 )
163
+ }
164
+ }
165
+
126
166
#[ cfg( feature = "alloc" ) ]
127
167
impl SampleString for Alphanumeric {
128
168
fn append_string < R : Rng + ?Sized > ( & self , rng : & mut R , string : & mut String , len : usize ) {
@@ -133,6 +173,20 @@ impl SampleString for Alphanumeric {
133
173
}
134
174
}
135
175
176
+ #[ cfg( feature = "alloc" ) ]
177
+ impl SampleString for Alphabetic {
178
+ fn append_string < R : Rng + ?Sized > ( & self , rng : & mut R , string : & mut String , len : usize ) {
179
+ // SAFETY: With this distribution we guarantee that we're working with valid ASCII
180
+ // characters.
181
+ // See [#1590](https://github.com/rust-random/rand/issues/1590).
182
+ unsafe {
183
+ let v = string. as_mut_vec ( ) ;
184
+ v. reserve_exact ( len) ;
185
+ v. extend ( self . sample_iter ( rng) . take ( len) ) ;
186
+ }
187
+ }
188
+ }
189
+
136
190
impl Distribution < bool > for StandardUniform {
137
191
#[ inline]
138
192
fn sample < R : Rng + ?Sized > ( & self , rng : & mut R ) -> bool {
@@ -294,6 +348,20 @@ mod tests {
294
348
assert ! ( !incorrect) ;
295
349
}
296
350
351
+ #[ test]
352
+ fn test_alphabetic ( ) {
353
+ let mut rng = crate :: test:: rng ( 806 ) ;
354
+
355
+ // Test by generating a relatively large number of chars, so we also
356
+ // take the rejection sampling path.
357
+ let mut incorrect = false ;
358
+ for _ in 0 ..100 {
359
+ let c: char = rng. sample ( Alphabetic ) . into ( ) ;
360
+ incorrect |= !c. is_ascii_alphabetic ( ) ;
361
+ }
362
+ assert ! ( !incorrect) ;
363
+ }
364
+
297
365
#[ test]
298
366
fn value_stability ( ) {
299
367
fn test_samples < T : Copy + core:: fmt:: Debug + PartialEq , D : Distribution < T > > (
@@ -321,6 +389,7 @@ mod tests {
321
389
] ,
322
390
) ;
323
391
test_samples ( & Alphanumeric , 0 , & [ 104 , 109 , 101 , 51 , 77 ] ) ;
392
+ test_samples ( & Alphabetic , 0 , & [ 97 , 102 , 89 , 116 , 75 ] ) ;
324
393
test_samples ( & StandardUniform , false , & [ true , true , false , true , false ] ) ;
325
394
test_samples (
326
395
& StandardUniform ,
0 commit comments